feat(emigrate): add some rough support for generating new migration files
And add some CLI args parsing and usage messages for upcoming commands as well
This commit is contained in:
parent
ce4693d957
commit
0081f77e86
4 changed files with 291 additions and 20 deletions
|
|
@ -49,7 +49,10 @@
|
|||
},
|
||||
"xo": {
|
||||
"space": true,
|
||||
"prettier": true
|
||||
"prettier": true,
|
||||
"rules": {
|
||||
"complexity": 0
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@changesets/cli": "2.26.2",
|
||||
|
|
|
|||
|
|
@ -25,16 +25,10 @@
|
|||
"devDependencies": {
|
||||
"@emigrate/tsconfig": "workspace:*"
|
||||
},
|
||||
"author": {
|
||||
"name": "Aboviq AB",
|
||||
"email": "dev@aboviq.com",
|
||||
"url": "https://www.aboviq.com"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Joakim Carlstein",
|
||||
"email": "joakim@aboviq.com"
|
||||
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emigrate/plugin-tools": "workspace:*",
|
||||
"import-from-esm": "1.1.3"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,248 @@
|
|||
#!/usr/bin/env node
|
||||
import { emigrate } from '.';
|
||||
import process from 'node:process';
|
||||
import { parseArgs } from 'node:util';
|
||||
import { isGeneratorPlugin } from '@emigrate/plugin-tools';
|
||||
import { type GeneratorPlugin } from '@emigrate/plugin-tools/types';
|
||||
|
||||
emigrate();
|
||||
type Action = (args: string[]) => Promise<void>;
|
||||
|
||||
const up: Action = async (args) => {
|
||||
const { values } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
help: {
|
||||
type: 'boolean',
|
||||
short: 'h',
|
||||
},
|
||||
dir: {
|
||||
type: 'string',
|
||||
short: 'd',
|
||||
},
|
||||
plugin: {
|
||||
type: 'string',
|
||||
short: 'p',
|
||||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
allowPositionals: false,
|
||||
});
|
||||
|
||||
const showHelp = !values.dir || values.help;
|
||||
|
||||
if (!values.dir) {
|
||||
console.error('Missing required option: --dir\n');
|
||||
}
|
||||
|
||||
if (showHelp) {
|
||||
console.log(`Usage: emigrate up [options]
|
||||
|
||||
Run all pending migrations
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
-d, --dir The directory where the migration files are located (required)
|
||||
-p, --plugin The plugin(s) to use (can be specified multiple times)
|
||||
|
||||
Examples:
|
||||
|
||||
emigrate up --dir src/migrations
|
||||
emigrate up --dir ./migrations --plugin @emigrate/plugin-storage-mysql
|
||||
`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(values);
|
||||
};
|
||||
|
||||
const newMigration: Action = async (args) => {
|
||||
const { values, positionals } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
help: {
|
||||
type: 'boolean',
|
||||
short: 'h',
|
||||
},
|
||||
dir: {
|
||||
type: 'string',
|
||||
short: 'd',
|
||||
},
|
||||
plugin: {
|
||||
type: 'string',
|
||||
short: 'p',
|
||||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
allowPositionals: true,
|
||||
});
|
||||
|
||||
const hasPositionals = positionals.join('').trim() !== '';
|
||||
const showHelp = !values.dir || !hasPositionals || values.help;
|
||||
|
||||
if (!values.dir) {
|
||||
console.error('Missing required option: --dir\n');
|
||||
}
|
||||
|
||||
if (!hasPositionals) {
|
||||
console.error('Missing required migration name: <name>\n');
|
||||
}
|
||||
|
||||
if (showHelp) {
|
||||
console.log(`Usage: emigrate new [options] <name>
|
||||
|
||||
Run all pending migrations
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
-d, --dir The directory where the migration files are located (required)
|
||||
-p, --plugin The plugin(s) to use (can be specified multiple times)
|
||||
|
||||
Examples:
|
||||
|
||||
emigrate new --dir src/migrations create users table
|
||||
emigrate new --dir ./migrations --plugin @emigrate/plugin-generate-sql create_users_table
|
||||
`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const { plugin: plugins = [] } = values;
|
||||
|
||||
if (plugins.length > 0) {
|
||||
let generatorPlugin: GeneratorPlugin | undefined;
|
||||
|
||||
const path = await import('node:path');
|
||||
|
||||
for await (const plugin of plugins) {
|
||||
const pluginPath = plugin.startsWith('.') ? path.resolve(process.cwd(), plugin) : plugin;
|
||||
|
||||
try {
|
||||
const pluginModule: unknown = await import(pluginPath);
|
||||
|
||||
if (isGeneratorPlugin(pluginModule)) {
|
||||
generatorPlugin = pluginModule;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
pluginModule &&
|
||||
typeof pluginModule === 'object' &&
|
||||
'default' in pluginModule &&
|
||||
isGeneratorPlugin(pluginModule.default)
|
||||
) {
|
||||
generatorPlugin = pluginModule.default;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load plugin: ${plugin}`);
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!generatorPlugin) {
|
||||
console.error('No generator plugin found, please specify a generator plugin using the --plugin option\n');
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const fs = await import('node:fs/promises');
|
||||
|
||||
const { filename, content } = await generatorPlugin.generate(positionals.join(' '));
|
||||
|
||||
const directory = path.resolve(process.cwd(), values.dir!);
|
||||
|
||||
try {
|
||||
await fs.mkdir(directory, { recursive: true });
|
||||
} catch (error) {
|
||||
console.error(`Failed to create migration directory: ${directory}`);
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const file = path.resolve(directory, filename);
|
||||
|
||||
try {
|
||||
await fs.writeFile(file, content);
|
||||
|
||||
console.log(`Created migration file: ${path.relative(process.cwd(), file)}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to write migration file: ${file}`);
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const list: Action = async (args) => {
|
||||
const { values } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
help: {
|
||||
type: 'boolean',
|
||||
short: 'h',
|
||||
},
|
||||
plugin: {
|
||||
type: 'string',
|
||||
short: 'p',
|
||||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
allowPositionals: false,
|
||||
});
|
||||
|
||||
console.log(values);
|
||||
};
|
||||
|
||||
const commands: Record<string, Action> = {
|
||||
up,
|
||||
list,
|
||||
new: newMigration,
|
||||
};
|
||||
|
||||
const command = process.argv[2];
|
||||
const action = command ? commands[command] : undefined;
|
||||
|
||||
if (!action) {
|
||||
if (command) {
|
||||
console.error(`Unknown command: ${command}\n`);
|
||||
} else {
|
||||
console.error('No command specified\n');
|
||||
}
|
||||
|
||||
console.log(`Usage: emigrate <command>
|
||||
|
||||
Commands:
|
||||
|
||||
up Run all pending migrations
|
||||
new Create a new migration file
|
||||
list List all migrations
|
||||
`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await action(process.argv.slice(3));
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
|
|||
40
pnpm-lock.yaml
generated
40
pnpm-lock.yaml
generated
|
|
@ -1,5 +1,9 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
|
|
@ -45,9 +49,32 @@ importers:
|
|||
version: 0.56.0(webpack@5.89.0)
|
||||
|
||||
packages/emigrate:
|
||||
dependencies:
|
||||
'@emigrate/plugin-tools':
|
||||
specifier: workspace:*
|
||||
version: link:../plugin-tools
|
||||
import-from-esm:
|
||||
specifier: 1.1.3
|
||||
version: 1.1.3
|
||||
devDependencies:
|
||||
'@emigrate/tsconfig':
|
||||
specifier: 0.0.0
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
|
||||
packages/plugin-generate-js:
|
||||
dependencies:
|
||||
'@emigrate/plugin-tools':
|
||||
specifier: workspace:*
|
||||
version: link:../plugin-tools
|
||||
devDependencies:
|
||||
'@emigrate/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
|
||||
packages/plugin-tools:
|
||||
devDependencies:
|
||||
'@emigrate/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
|
||||
packages/tsconfig: {}
|
||||
|
|
@ -3107,6 +3134,13 @@ packages:
|
|||
resolve-from: 4.0.0
|
||||
dev: false
|
||||
|
||||
/import-from-esm@1.1.3:
|
||||
resolution: {integrity: sha512-1BxFAthpQf5qabfPBaFBRAGIh8TVt6WB4ujqedfoF4oVjwyl6S/dZv26gL5kgPhbO1XBqu4hcELUlV/+IPsC3A==}
|
||||
engines: {node: '>=16.20'}
|
||||
dependencies:
|
||||
import-meta-resolve: 4.0.0
|
||||
dev: false
|
||||
|
||||
/import-meta-resolve@4.0.0:
|
||||
resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==}
|
||||
dev: false
|
||||
|
|
@ -5849,7 +5883,3 @@ packages:
|
|||
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: false
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue