Plugin SDK > Migrating from legacy plugins

    Migrating from legacy plugins

    A completely revamped plugin SDK was released in November 2021. Plugins leveraging the legacy SDK will continue to work indefinitely, but are much more limited in their possibilities, as they can only manage what in the new SKD are called manual field extensions. All the other extension points are not available.

    Legacy SDK docs

    If you're looking for the Legacy SDK documentation, it is still available here.

    If you are interested in migrating to the new SDK, please note the following points:

    Global configuration parameters

    Global parameters no longer need to be declared in the package.json, but are configurable through the config-screen hooks.

    The data storage format is now also completely custom, as well as the interface that is shown to end users.

    Manual extensions

    The old options:

    • "Type of plugin" (field editor, field add-on or sidebar widget), and

    • "Types of field" (specifying where it's possible to use the legacy plugin)

    do not have to be declared in the package.json anymore, but are configurable through the manualFieldExtensions hook. The old "sidebar widget" plugin type is nothing but an additional asSidebarPanel option you can pass to the new ManualFieldExtension type:

    import { connect, Field, InitCtx } from 'datocms-plugins-sdk';
    connect({
    manualFieldExtensions(ctx: InitCtx) {
    return [
    {
    id: 'sidebarWidget',
    name: 'My sidebar widget',
    type: 'editor',
    fieldTypes: ['integer'],
    asSidebarPanel: true,
    },
    ];
    },
    renderFieldExtension(id, ctx) {
    // ...
    },
    });

    Instance configuration options

    Instance parameters no longer need to be declared in the package.json, but are now completely arbitrary, as well as the interface that is shown to end users. Take a look at this part of the documentation.

    plugin.xxx methods and properties

    All methods and information previously available through plugin.xxx calls is now available through the ctx argument of the renderFieldExtension hook.

    Migrating appearance on associated fields

    Since new plugins can expose multiple manual field extensions, you need to implement an onBoot hook to properly set the fieldExtensionId attribute on each field that was previously hooked with the plugin.

    Example to migrate old field editors or sidebar widgets
    connect({
    // plugin exposes a `myExtension` manual field extension
    manualFieldExtensions(ctx: InitCtx) {
    return [
    {
    id: 'myExtension',
    name: 'Foo bar',
    type: 'editor',
    fieldTypes: ['integer'],
    },
    ];
    },
    async onBoot(ctx: OnBootCtx) {
    // if we already performed the migration, skip
    if (ctx.plugin.attributes.parameters.migratedFromLegacyPlugin) {
    return;
    }
    // if the current user cannot edit fields' settings, skip
    if (!ctx.currentRole.meta.final_permissions.can_edit_schema) {
    return;
    }
    // get all the fields currently associated to the plugin...
    const fields = await ctx.loadFieldsUsingPlugin();
    // ... and for each of them...
    await Promise.all(
    fields.map(async (field) => {
    // set the fieldExtensionId to be the new one
    await ctx.updateFieldAppearance(field.id, [{
    operation: 'updateEditor',
    newFieldExtensionId: 'myExtension',
    }]);
    }),
    );
    // save in configuration the fact that we already performed the migration
    ctx.updatePluginParameters({
    ...ctx.plugin.attributes.parameters,
    migratedFromLegacyPlugin: true,
    });
    },
    });
    Example to migrate old field addons
    connect({
    // plugin exposes a `myExtension` manual field extension
    manualFieldExtensions(ctx: InitCtx) {
    return [
    {
    id: 'myExtension',
    name: 'Foo bar',
    type: 'addon',
    fieldTypes: ['integer'],
    },
    ];
    },
    async onBoot(ctx: OnBootCtx) {
    // if we already performed the migration, skip
    if (ctx.plugin.attributes.parameters.migratedFromLegacyPlugin) {
    return;
    }
    // if the current user cannot edit fields' settings, skip
    if (!ctx.currentRole.meta.final_permissions.can_edit_schema) {
    return;
    }
    // get all the fields currently associated to the plugin...
    const fields = await ctx.loadFieldsUsingPlugin();
    // ... and for each of them...
    await Promise.all(
    fields.map(async (field) => {
    const { appearance } = field.attributes;
    const changes: FieldAppearanceChange[] = [];
    // find where our plugin is used...
    appearance.addons.forEach((addon, index) => {
    // set the fieldExtensionId to be the new one
    changes.push({
    operation: 'updateAddon',
    index,
    newFieldExtensionId: 'myExtension',
    });
    });
    await ctx.updateFieldAppearance(field.id, changes);
    }),
    );
    // save in configuration the fact that we already performed the migration
    ctx.updatePluginParameters({
    ...ctx.plugin.attributes.parameters,
    migratedFromLegacyPlugin: true,
    });
    },
    });