feat(plugins): automatically prefix plugins when loading if necessary
I.e. when specifying only "--plugin generate-js" Emigrate will load the @emigrate/plugin-generate-js plugin.
This commit is contained in:
parent
ca3ab9ec62
commit
9c239e0ae5
8 changed files with 83 additions and 29 deletions
5
.changeset/nine-mugs-prove.md
Normal file
5
.changeset/nine-mugs-prove.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'emigrate': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Automatically prefix plugin names when loading them if necessary. I.e. when specifying only "--plugin generate-js" Emigrate will load the @emigrate/plugin-generate-js plugin. It has a priority order that is: 1. the provided plugin name as is, 2. the name prefixed with "@emigrate/plugin-", 3. the name prefixed with "emigrate-plugin-"
|
||||||
5
.changeset/tall-zebras-march.md
Normal file
5
.changeset/tall-zebras-march.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@emigrate/plugin-tools': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Use import-from-esm to resolve plugins relative to the current working directory and add a convenient plugin loader helper (loadPlugin)
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
|
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emigrate/plugin-tools": "workspace:*",
|
"@emigrate/plugin-tools": "workspace:*"
|
||||||
"import-from-esm": "1.1.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { getTimestampPrefix, sanitizeMigrationName, isGeneratorPlugin } from '@emigrate/plugin-tools';
|
import { getTimestampPrefix, sanitizeMigrationName, loadPlugin } from '@emigrate/plugin-tools';
|
||||||
import { type GeneratorPlugin } from '@emigrate/plugin-tools/types';
|
import { type GeneratorPlugin } from '@emigrate/plugin-tools/types';
|
||||||
import { ShowUsageError } from './show-usage-error.js';
|
import { ShowUsageError } from './show-usage-error.js';
|
||||||
|
|
||||||
|
|
@ -45,28 +45,11 @@ export default async function newCommand({ directory, template, plugins, name }:
|
||||||
let generatorPlugin: GeneratorPlugin | undefined;
|
let generatorPlugin: GeneratorPlugin | undefined;
|
||||||
|
|
||||||
for await (const plugin of plugins) {
|
for await (const plugin of plugins) {
|
||||||
const pluginPath = plugin.startsWith('.') ? path.resolve(process.cwd(), plugin) : plugin;
|
generatorPlugin = await loadPlugin('generator', plugin);
|
||||||
|
|
||||||
try {
|
if (generatorPlugin) {
|
||||||
const pluginModule: unknown = await import(pluginPath);
|
|
||||||
|
|
||||||
if (isGeneratorPlugin(pluginModule)) {
|
|
||||||
generatorPlugin = pluginModule;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
pluginModule &&
|
|
||||||
typeof pluginModule === 'object' &&
|
|
||||||
'default' in pluginModule &&
|
|
||||||
isGeneratorPlugin(pluginModule.default)
|
|
||||||
) {
|
|
||||||
generatorPlugin = pluginModule.default;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`Failed to load plugin: ${plugin}`, { cause: error });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!generatorPlugin) {
|
if (!generatorPlugin) {
|
||||||
|
|
|
||||||
|
|
@ -32,5 +32,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emigrate/tsconfig": "workspace:*"
|
"@emigrate/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"import-from-esm": "1.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { type GeneratorPlugin, type StoragePlugin } from './types.js';
|
import process from 'node:process';
|
||||||
|
import { type PluginFromType, type PluginType, type GeneratorPlugin, type StoragePlugin } from './types.js';
|
||||||
|
|
||||||
export const createStoragePlugin = (initialize: StoragePlugin['initialize']): StoragePlugin => {
|
export const createStoragePlugin = (initialize: StoragePlugin['initialize']): StoragePlugin => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -38,6 +39,59 @@ export const isStoragePlugin = (plugin: any): plugin is StoragePlugin => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isPluginOfType = <T extends PluginType>(type: T, plugin: any): plugin is PluginFromType<T> => {
|
||||||
|
if (type === 'generator') {
|
||||||
|
return isGeneratorPlugin(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'storage') {
|
||||||
|
return isStoragePlugin(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown plugin type: ${type}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadPlugin = async <T extends PluginType>(
|
||||||
|
type: T,
|
||||||
|
plugin: string,
|
||||||
|
): Promise<PluginFromType<T> | undefined> => {
|
||||||
|
let importFromEsm = await import('import-from-esm');
|
||||||
|
|
||||||
|
// Because of "allowSyntheticDefaultImports" we need to do this ugly hack
|
||||||
|
if ((importFromEsm as any).default) {
|
||||||
|
importFromEsm = (importFromEsm as any).default as unknown as typeof importFromEsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importsToTry = plugin.startsWith('.')
|
||||||
|
? [plugin]
|
||||||
|
: [plugin, `@emigrate/plugin-${plugin}`, `emigrate-plugin-${plugin}`];
|
||||||
|
|
||||||
|
for await (const importPath of importsToTry) {
|
||||||
|
try {
|
||||||
|
const pluginModule: unknown = await importFromEsm(process.cwd(), importPath);
|
||||||
|
|
||||||
|
// Support module.exports = ...
|
||||||
|
if (isPluginOfType(type, pluginModule)) {
|
||||||
|
return pluginModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support export default ...
|
||||||
|
if (
|
||||||
|
pluginModule &&
|
||||||
|
typeof pluginModule === 'object' &&
|
||||||
|
'default' in pluginModule &&
|
||||||
|
isPluginOfType(type, pluginModule.default)
|
||||||
|
) {
|
||||||
|
return pluginModule.default;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a timestamp string in the format YYYYMMDDHHmmssmmm based on the current time (UTC)
|
* Get a timestamp string in the format YYYYMMDDHHmmssmmm based on the current time (UTC)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -86,3 +86,7 @@ export type GeneratorPlugin = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Plugin = StoragePlugin | GeneratorPlugin;
|
export type Plugin = StoragePlugin | GeneratorPlugin;
|
||||||
|
|
||||||
|
export type PluginType = Plugin['type'];
|
||||||
|
|
||||||
|
export type PluginFromType<T extends PluginType> = Extract<Plugin, { type: T }>;
|
||||||
|
|
|
||||||
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
|
|
@ -53,9 +53,6 @@ importers:
|
||||||
'@emigrate/plugin-tools':
|
'@emigrate/plugin-tools':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../plugin-tools
|
version: link:../plugin-tools
|
||||||
import-from-esm:
|
|
||||||
specifier: 1.1.3
|
|
||||||
version: 1.1.3
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@emigrate/tsconfig':
|
'@emigrate/tsconfig':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
|
|
@ -72,6 +69,10 @@ importers:
|
||||||
version: link:../tsconfig
|
version: link:../tsconfig
|
||||||
|
|
||||||
packages/plugin-tools:
|
packages/plugin-tools:
|
||||||
|
dependencies:
|
||||||
|
import-from-esm:
|
||||||
|
specifier: 1.2.1
|
||||||
|
version: 1.2.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@emigrate/tsconfig':
|
'@emigrate/tsconfig':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
|
|
@ -3134,8 +3135,8 @@ packages:
|
||||||
resolve-from: 4.0.0
|
resolve-from: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/import-from-esm@1.1.3:
|
/import-from-esm@1.2.1:
|
||||||
resolution: {integrity: sha512-1BxFAthpQf5qabfPBaFBRAGIh8TVt6WB4ujqedfoF4oVjwyl6S/dZv26gL5kgPhbO1XBqu4hcELUlV/+IPsC3A==}
|
resolution: {integrity: sha512-Nly5Ab75rWZmOwtMa0B0NQNnHGcHOQ2zkU/bVENwK2lbPq+kamPDqNKNJ0hF7w7lR/ETD5nGgJq0XbofsZpYCA==}
|
||||||
engines: {node: '>=16.20'}
|
engines: {node: '>=16.20'}
|
||||||
dependencies:
|
dependencies:
|
||||||
import-meta-resolve: 4.0.0
|
import-meta-resolve: 4.0.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue