Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rename plugin id to key for consistency
  • Loading branch information
kingston committed Jul 29, 2025
commit 1c29e92878d592b77b5ed1089d7422cca11c597e
2 changes: 2 additions & 0 deletions .changeset/refactor-plugin-metadata-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ Refactor plugin metadata system to use single plugin.json files with configurabl
- Allow runtime configuration overrides for plugin discovery
- Add comprehensive test suite using memfs for realistic filesystem testing
- Update plugin packages to use new metadata system with automatic build copying
- **BREAKING**: Rename `id` to `key` in `PluginMetadataWithPaths` for consistency with model entity patterns
- Update all plugin metadata references to use the new `key` property for URL-safe identifiers
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('typescriptGenerator', () => {
describe('fileTask', () => {
const typescriptBundle = typescriptGenerator({});
const typescriptConfig = {
isComposite: false,
compilerOptions: {
paths: {
'@src/*': ['src/*'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ interface PluginAppCompilerOptions<TAppDefinition> {
}

interface PluginAppCompiler<TAppDefinition = AppConfig> {
pluginId: string;
pluginKey: string;
appType: AppEntryType<TAppDefinition>;
compile: (options: PluginAppCompilerOptions<TAppDefinition>) => void;
}
Expand Down
73 changes: 57 additions & 16 deletions packages/project-builder-lib/src/definition/plugins/plugin-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { SchemaParserContext } from '#src/parser/types.js';
import type {
PluginImplementationStore,
PluginMetadataWithPaths,
Expand All @@ -7,36 +8,39 @@ import type {
ProjectDefinition,
} from '#src/schema/index.js';

import { pluginConfigSpec } from '#src/plugins/index.js';
import {
getPluginMetadataByKeyOrThrow,
pluginConfigSpec,
} from '#src/plugins/index.js';
import { pluginEntityType } from '#src/schema/index.js';

function byId(
function byKey(
projectDefinition: ProjectDefinition,
id: string,
): BasePluginDefinition | null {
const pluginEntityId = pluginEntityType.idFromKey(id);
key: string,
): BasePluginDefinition | undefined {
const pluginEntityId = pluginEntityType.idFromKey(key);
const plugin = projectDefinition.plugins?.find(
(m) => m.id === pluginEntityId,
);
return plugin ?? null;
return plugin;
}

function byIdOrThrow(
function byKeyOrThrow(
projectDefinition: ProjectDefinition,
id: string,
): BasePluginDefinition {
const plugin = byId(projectDefinition, id);
const plugin = byKey(projectDefinition, id);
if (!plugin) {
throw new Error(`Could not find plugin with ID ${id}`);
}
return plugin;
}

function configByIdOrThrow(
function configByKeyOrThrow(
projectDefinition: ProjectDefinition,
id: string,
key: string,
): unknown {
const def = byIdOrThrow(projectDefinition, id);
const def = byKeyOrThrow(projectDefinition, key);
return def.config;
}

Expand All @@ -47,12 +51,12 @@ function setPluginConfig(
pluginImplementationStore: PluginImplementationStore,
): void {
const plugins = projectDefinition.plugins ?? [];
const pluginEntityId = pluginEntityType.idFromKey(plugin.id);
const pluginEntityId = pluginEntityType.idFromKey(plugin.key);

const pluginConfigService =
pluginImplementationStore.getPluginSpec(pluginConfigSpec);
const lastMigrationVersion = pluginConfigService.getLastMigrationVersion(
plugin.id,
plugin.key,
);

projectDefinition.plugins = plugins.some((p) => p.id === pluginEntityId)
Expand All @@ -72,9 +76,46 @@ function setPluginConfig(
];
}

/**
* Disables a plugin and all plugins that it manages recursively.
*
* @param projectDefinition The project definition to disable the plugin in
* @param pluginKey The ID of the plugin to disable
* @param context The schema parser context to use
*/
function disablePlugin(
projectDefinition: ProjectDefinition,
pluginKey: string,
context: SchemaParserContext,
): void {
// Get plugin metadata to be disabled
const pluginMetadata = getPluginMetadataByKeyOrThrow(
context.pluginStore,
pluginKey,
);

// Make sure we disable any plugins that are managed by this plugin
const managedPlugins = projectDefinition.plugins?.filter((p) => {
const managedPlugin = getPluginMetadataByKeyOrThrow(
context.pluginStore,
pluginEntityType.keyFromId(p.id),
);
return managedPlugin.managedBy === pluginMetadata.fullyQualifiedName;
});
if (managedPlugins) {
for (const p of managedPlugins)
disablePlugin(projectDefinition, p.id, context);
}

projectDefinition.plugins = projectDefinition.plugins?.filter(
(p) => p.id !== pluginEntityType.idFromKey(pluginKey),
);
}

export const PluginUtils = {
byId,
byIdOrThrow,
byKey,
byKeyOrThrow,
setPluginConfig,
configByIdOrThrow,
configByKeyOrThrow,
disablePlugin,
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('PluginUtils.setPluginConfig', () => {
const projectDefinition = createTestProjectDefinition();

const mockPlugin = createTestPluginMetadata({
id: 'test-plugin',
key: 'test-plugin',
name: 'TestPlugin',
packageName: '@test/plugin',
});
Expand Down Expand Up @@ -50,7 +50,7 @@ describe('PluginUtils.setPluginConfig', () => {
const projectDefinition = createTestProjectDefinition();

const mockPlugin = createTestPluginMetadata({
id: 'no-migrations-plugin',
key: 'no-migrations-plugin',
name: 'NoMigrationsPlugin',
packageName: '@test/no-migrations',
});
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('PluginUtils.setPluginConfig', () => {
});

const mockPlugin = createTestPluginMetadata({
id: 'existing-plugin',
key: 'existing-plugin',
name: 'ExistingPlugin',
packageName: '@test/existing',
});
Expand Down
2 changes: 1 addition & 1 deletion packages/project-builder-lib/src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function createPluginImplementationStore(
throw new Error(`Unable to find plugin ${pluginName}!`);
}
return {
id: plugin.metadata.id,
key: plugin.metadata.key,
name: pluginName,
pluginModules: plugin.modules,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,13 @@ async function populatePluginMetadataWithPaths(
);
return {
...metadata,
// URL safe ID
id: `${packageName
// URL safe key
key: `${packageName
.replace(/^@/, '')
.replace(/[^a-z0-9/]+/g, '-')
.replace(/\//g, '_')}_${metadata.name.replace(/[^a-z0-9]+/g, '-')}`,
packageName,
fullyQualifiedName: `${packageName}:${metadata.name}`,
pluginDirectory,
webBuildDirectory,
nodeModulePaths: nodeEntrypoints.map((e) => e.path),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ describe('plugin-loader', () => {
expect.objectContaining({
name: 'test-plugin',
displayName: 'Test Plugin',
key: `test_plugin-example_test-plugin`,
fullyQualifiedName: '@test/plugin-example:test-plugin',
packageName: mockPackageName,
pluginDirectory: path.join(mockPackageDirectory, 'dist/test-plugin'),
webBuildDirectory: path.join(mockPackageDirectory, 'dist/web'),
Expand Down Expand Up @@ -101,6 +103,8 @@ describe('plugin-loader', () => {
expect(result[0]).toEqual(
expect.objectContaining({
name: 'custom-plugin',
key: `test_custom-package_custom-plugin`,
fullyQualifiedName: '@test/custom-package:custom-plugin',
pluginDirectory: path.join(
mockPackageDirectory,
'custom/custom-plugin',
Expand Down
1 change: 1 addition & 0 deletions packages/project-builder-lib/src/plugins/imports/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { initializePlugins } from './loader.js';
export * from './types.js';
export * from './utils.js';
12 changes: 6 additions & 6 deletions packages/project-builder-lib/src/plugins/imports/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import type { KeyedPluginPlatformModule } from './types.js';
import { PluginImplementationStore } from '../schema/store.js';

export interface PluginWithPlatformModules {
id: string;
key: string;
name: string;
pluginModules: KeyedPluginPlatformModule[];
}

interface KeyedPlatformModuleWithPlugin extends KeyedPluginPlatformModule {
id: string;
name: string;
pluginId: string;
pluginKey: string;
pluginName: string;
}

Expand All @@ -27,9 +27,9 @@ export function extractPlatformModulesFromPlugins(
return plugins.flatMap((plugin) =>
plugin.pluginModules.map((m) => ({
...m,
id: `${plugin.id}/${m.key}`,
id: `${plugin.key}/${m.key}`,
name: `${plugin.name}/${m.key}`,
pluginId: plugin.id,
pluginKey: plugin.key,
pluginName: plugin.name,
})),
);
Expand Down Expand Up @@ -94,7 +94,7 @@ export function initializeOrderedPluginModules(
): Partial<Record<string, PluginSpecImplementation>> {
const specImplementations = { ...initialSpecImplementations };

for (const { name, module, pluginId } of orderedPluginModules) {
for (const { name, module, pluginKey } of orderedPluginModules) {
const dependencies = module.dependencies
? stripUndefinedValues(
mapValues(module.dependencies, (dep) => {
Expand All @@ -106,7 +106,7 @@ export function initializeOrderedPluginModules(
}),
)
: {};
const context = { pluginId };
const context = { pluginKey };
const exports = module.initialize(dependencies, context);
Object.entries(module.exports ?? {}).map(([key, spec]) => {
const exportedImplementation = exports[key] as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function createPlugin({
initialize,
}: { idx: number } & Partial<PluginPlatformModule>): PluginWithPlatformModules {
return {
id: `plugin-${idx}`,
key: `plugin-${idx}`,
name: `Plugin ${idx}`,
pluginModules: [
{
Expand Down
2 changes: 1 addition & 1 deletion packages/project-builder-lib/src/plugins/imports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type ExtractPluginImplementationFromSpecMap<T extends PluginSpecMap> = {
};

export interface PluginInitializerContext {
pluginId: string;
pluginKey: string;
}

export interface PluginPlatformModule<
Expand Down
36 changes: 36 additions & 0 deletions packages/project-builder-lib/src/plugins/imports/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { PluginMetadataWithPaths } from '../metadata/types.js';
import type { PluginStore } from './types.js';

/**
* Gets a plugin metadata object by its key.
*
* @param pluginStore The plugin store to use
* @param key The key of the plugin to get
* @returns The plugin metadata object or undefined if the plugin is not found
*/
export function getPluginMetadataByKey(
pluginStore: PluginStore,
key: string,
): PluginMetadataWithPaths | undefined {
return pluginStore.availablePlugins.find((p) => p.metadata.key === key)
?.metadata;
}

/**
* Gets a plugin metadata object by its key.
*
* @param pluginStore The plugin store to use
* @param key The key of the plugin to get
* @returns The plugin metadata object
* @throws An error if the plugin is not found
*/
export function getPluginMetadataByKeyOrThrow(
pluginStore: PluginStore,
key: string,
): PluginMetadataWithPaths {
const metadata = getPluginMetadataByKey(pluginStore, key);
if (!metadata) {
throw new Error(`Could not find plugin with key ${key}`);
}
return metadata;
}
1 change: 0 additions & 1 deletion packages/project-builder-lib/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './imports/index.js';
export * from './manifest/manifest.js';
export * from './metadata/index.js';
export * from './migrations/index.js';
export * from './schema/index.js';
Expand Down
8 changes: 0 additions & 8 deletions packages/project-builder-lib/src/plugins/manifest/manifest.ts

This file was deleted.

16 changes: 14 additions & 2 deletions packages/project-builder-lib/src/plugins/metadata/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ export const pluginMetadataSchema = z.object({
* A description of the plugin
*/
description: z.string(),
/**
* The fully qualified name of the plugin that manages this plugin,
* e.g. "@baseplate-dev/plugin-auth:auth0" would be managed by the "@baseplate-dev/plugin-auth:auth" plugin.
*
* Managed plugins do not appear in the plugin list but are managed by the base plugin. If the base
* plugin is disabled, the managed plugins will also be disabled.
*/
managedBy: z.string().optional(),
/**
* The version of the plugin using semver
*/
Expand Down Expand Up @@ -81,13 +89,17 @@ export type PluginJson = PluginMetadata;

export interface PluginMetadataWithPaths extends PluginMetadata {
/**
* The unique ID of the plugin generated for the project
* The unique key of the plugin generated for the project (URL-safe, globally unique)
*/
id: string;
key: string;
/**
* The name of the plugin package
*/
packageName: string;
/**
* The fully qualified name of the plugin, e.g. "@baseplate-dev/plugin-auth:auth0"
*/
fullyQualifiedName: string;
/**
* The path to the plugin directory
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export function createTestPluginMetadata(
const defaultId = overrides.name ?? 'test-plugin';

return {
id: defaultId,
key: defaultId,
name: defaultId,
packageName: `@test/${defaultId}`,
fullyQualifiedName: `@test/${defaultId}:${defaultId}`,
version: '1.0.0',
displayName: `Test ${defaultId}`,
description: `A test plugin for ${defaultId}`,
Expand Down
4 changes: 2 additions & 2 deletions packages/project-builder-lib/src/plugins/spec/config-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export interface PluginConfigSpec extends PluginSpecImplementation {
migrations: PluginConfigMigration[],
) => void;
getSchemaCreator(pluginKey: string): DefinitionSchemaCreator | undefined;
getMigrations(pluginId: string): PluginConfigMigration[] | undefined;
getLastMigrationVersion(pluginId: string): number | undefined;
getMigrations(pluginKey: string): PluginConfigMigration[] | undefined;
getLastMigrationVersion(pluginKey: string): number | undefined;
}

function sortAndValidateMigrations(
Expand Down
Loading