@zenstackhq/cli 3.0.0-alpha.9 → 3.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +11 -11
- package/dist/index.cjs +649 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -4
- package/dist/index.d.ts +1 -4
- package/dist/index.js +651 -111
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- package/src/actions/action-utils.ts +69 -4
- package/src/actions/check.ts +22 -0
- package/src/actions/db.ts +9 -6
- package/src/actions/generate.ts +108 -36
- package/src/actions/index.ts +2 -1
- package/src/actions/info.ts +4 -1
- package/src/actions/migrate.ts +51 -16
- package/src/actions/templates.ts +4 -3
- package/src/constants.ts +2 -0
- package/src/index.ts +99 -28
- package/src/plugins/index.ts +2 -0
- package/src/plugins/prisma.ts +21 -0
- package/src/plugins/typescript.ts +21 -0
- package/src/telemetry.ts +139 -0
- package/src/utils/is-ci.ts +5 -0
- package/src/utils/is-container.ts +23 -0
- package/src/utils/is-docker.ts +31 -0
- package/src/utils/is-wsl.ts +18 -0
- package/src/utils/machine-id-utils.ts +76 -0
- package/src/utils/version-utils.ts +37 -0
- package/test/check.test.ts +101 -0
- package/test/generate.test.ts +12 -9
- package/test/init.test.ts +2 -1
- package/test/migrate.test.ts +33 -1
- package/test/plugins/custom-plugin.test.ts +50 -0
- package/test/plugins/prisma-plugin.test.ts +60 -0
- package/test/ts-schema-gen.test.ts +180 -1
- package/tsconfig.json +0 -3
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +1 -1
package/src/actions/generate.ts
CHANGED
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
import { invariant } from '@zenstackhq/common-helpers';
|
|
2
|
-
import { isPlugin, LiteralExpr, type Model } from '@zenstackhq/language/ast';
|
|
3
|
-
import {
|
|
2
|
+
import { isPlugin, LiteralExpr, Plugin, type Model } from '@zenstackhq/language/ast';
|
|
3
|
+
import { getLiteral, getLiteralArray } from '@zenstackhq/language/utils';
|
|
4
|
+
import { type CliPlugin } from '@zenstackhq/sdk';
|
|
4
5
|
import colors from 'colors';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import {
|
|
7
|
+
import ora, { type Ora } from 'ora';
|
|
8
|
+
import { CliError } from '../cli-error';
|
|
9
|
+
import * as corePlugins from '../plugins';
|
|
10
|
+
import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils';
|
|
8
11
|
|
|
9
12
|
type Options = {
|
|
10
13
|
schema?: string;
|
|
11
14
|
output?: string;
|
|
12
|
-
silent
|
|
13
|
-
savePrismaSchema?: string | boolean;
|
|
15
|
+
silent: boolean;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* CLI action for generating code from schema
|
|
18
20
|
*/
|
|
19
21
|
export async function run(options: Options) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
|
|
20
24
|
const schemaFile = getSchemaFile(options.schema);
|
|
21
25
|
|
|
22
26
|
const model = await loadSchemaDocument(schemaFile);
|
|
23
|
-
const outputPath = options
|
|
24
|
-
|
|
25
|
-
// generate TS schema
|
|
26
|
-
const tsSchemaFile = path.join(outputPath, 'schema.ts');
|
|
27
|
-
await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile);
|
|
27
|
+
const outputPath = getOutputPath(options, schemaFile);
|
|
28
28
|
|
|
29
|
-
await runPlugins(model, outputPath,
|
|
30
|
-
|
|
31
|
-
// generate Prisma schema
|
|
32
|
-
if (options.savePrismaSchema) {
|
|
33
|
-
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
|
|
34
|
-
let prismaSchemaFile = path.join(outputPath, 'schema.prisma');
|
|
35
|
-
if (typeof options.savePrismaSchema === 'string') {
|
|
36
|
-
prismaSchemaFile = path.resolve(outputPath, options.savePrismaSchema);
|
|
37
|
-
fs.mkdirSync(path.dirname(prismaSchemaFile), { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
fs.writeFileSync(prismaSchemaFile, prismaSchema);
|
|
40
|
-
}
|
|
29
|
+
await runPlugins(schemaFile, model, outputPath, options);
|
|
41
30
|
|
|
42
31
|
if (!options.silent) {
|
|
43
|
-
console.log(colors.green(
|
|
32
|
+
console.log(colors.green(`Generation completed successfully in ${Date.now() - start}ms.\n`));
|
|
44
33
|
console.log(`You can now create a ZenStack client with it.
|
|
45
34
|
|
|
46
35
|
\`\`\`ts
|
|
@@ -48,25 +37,108 @@ import { ZenStackClient } from '@zenstackhq/runtime';
|
|
|
48
37
|
import { schema } from '${outputPath}/schema';
|
|
49
38
|
|
|
50
39
|
const client = new ZenStackClient(schema, {
|
|
51
|
-
|
|
40
|
+
dialect: { ... }
|
|
52
41
|
});
|
|
53
42
|
\`\`\`
|
|
54
|
-
|
|
43
|
+
|
|
44
|
+
Check documentation: https://zenstack.dev/docs/3.x`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getOutputPath(options: Options, schemaFile: string) {
|
|
49
|
+
if (options.output) {
|
|
50
|
+
return options.output;
|
|
51
|
+
}
|
|
52
|
+
const pkgJsonConfig = getPkgJsonConfig(process.cwd());
|
|
53
|
+
if (pkgJsonConfig.output) {
|
|
54
|
+
return pkgJsonConfig.output;
|
|
55
|
+
} else {
|
|
56
|
+
return path.dirname(schemaFile);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
async function runPlugins(model: Model, outputPath: string,
|
|
60
|
+
async function runPlugins(schemaFile: string, model: Model, outputPath: string, options: Options) {
|
|
59
61
|
const plugins = model.declarations.filter(isPlugin);
|
|
62
|
+
const processedPlugins: { cliPlugin: CliPlugin; pluginOptions: Record<string, unknown> }[] = [];
|
|
63
|
+
|
|
60
64
|
for (const plugin of plugins) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const provider = getPluginProvider(plugin);
|
|
66
|
+
|
|
67
|
+
let cliPlugin: CliPlugin;
|
|
68
|
+
if (provider.startsWith('@core/')) {
|
|
69
|
+
cliPlugin = (corePlugins as any)[provider.slice('@core/'.length)];
|
|
70
|
+
if (!cliPlugin) {
|
|
71
|
+
throw new CliError(`Unknown core plugin: ${provider}`);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
let moduleSpec = provider;
|
|
75
|
+
if (moduleSpec.startsWith('.')) {
|
|
76
|
+
// relative to schema's path
|
|
77
|
+
moduleSpec = path.resolve(path.dirname(schemaFile), moduleSpec);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
cliPlugin = (await import(moduleSpec)).default as CliPlugin;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw new CliError(`Failed to load plugin ${provider}: ${error}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
processedPlugins.push({ cliPlugin, pluginOptions: getPluginOptions(plugin) });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const defaultPlugins = [corePlugins['typescript']].reverse();
|
|
90
|
+
defaultPlugins.forEach((d) => {
|
|
91
|
+
if (!processedPlugins.some((p) => p.cliPlugin === d)) {
|
|
92
|
+
processedPlugins.push({ cliPlugin: d, pluginOptions: {} });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
for (const { cliPlugin, pluginOptions } of processedPlugins) {
|
|
97
|
+
invariant(
|
|
98
|
+
typeof cliPlugin.generate === 'function',
|
|
99
|
+
`Plugin ${cliPlugin.name} does not have a generate function`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// run plugin generator
|
|
103
|
+
let spinner: Ora | undefined;
|
|
104
|
+
|
|
105
|
+
if (!options.silent) {
|
|
106
|
+
spinner = ora(cliPlugin.statusText ?? `Running plugin ${cliPlugin.name}`).start();
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
await cliPlugin.generate({
|
|
110
|
+
schemaFile,
|
|
111
|
+
model,
|
|
112
|
+
defaultOutputPath: outputPath,
|
|
113
|
+
pluginOptions,
|
|
114
|
+
});
|
|
115
|
+
spinner?.succeed();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
spinner?.fail();
|
|
118
|
+
console.error(err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getPluginProvider(plugin: Plugin) {
|
|
124
|
+
const providerField = plugin.fields.find((f) => f.name === 'provider');
|
|
125
|
+
invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
|
|
126
|
+
const provider = (providerField.value as LiteralExpr).value as string;
|
|
127
|
+
return provider;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getPluginOptions(plugin: Plugin): Record<string, unknown> {
|
|
131
|
+
const result: Record<string, unknown> = {};
|
|
132
|
+
for (const field of plugin.fields) {
|
|
133
|
+
if (field.name === 'provider') {
|
|
134
|
+
continue; // skip provider
|
|
135
|
+
}
|
|
136
|
+
const value = getLiteral(field.value) ?? getLiteralArray(field.value);
|
|
137
|
+
if (value === undefined) {
|
|
138
|
+
console.warn(`Plugin "${plugin.name}" option "${field.name}" has unsupported value, skipping`);
|
|
139
|
+
continue;
|
|
67
140
|
}
|
|
68
|
-
|
|
69
|
-
console.log('Running generator:', provider);
|
|
70
|
-
await generator({ model, outputPath, tsSchemaFile });
|
|
141
|
+
result[field.name] = value;
|
|
71
142
|
}
|
|
143
|
+
return result;
|
|
72
144
|
}
|
package/src/actions/index.ts
CHANGED
|
@@ -3,5 +3,6 @@ import { run as generate } from './generate';
|
|
|
3
3
|
import { run as info } from './info';
|
|
4
4
|
import { run as init } from './init';
|
|
5
5
|
import { run as migrate } from './migrate';
|
|
6
|
+
import { run as check } from './check';
|
|
6
7
|
|
|
7
|
-
export { db, generate, info, init, migrate };
|
|
8
|
+
export { db, generate, info, init, migrate, check };
|
package/src/actions/info.ts
CHANGED
|
@@ -57,6 +57,9 @@ async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: st
|
|
|
57
57
|
with: { type: 'json' },
|
|
58
58
|
})
|
|
59
59
|
).default;
|
|
60
|
+
if (depPkgJson.private) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
60
63
|
return { pkg, version: depPkgJson.version as string };
|
|
61
64
|
} catch {
|
|
62
65
|
return { pkg, version: undefined };
|
|
@@ -64,5 +67,5 @@ async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: st
|
|
|
64
67
|
}),
|
|
65
68
|
);
|
|
66
69
|
|
|
67
|
-
return result;
|
|
70
|
+
return result.filter((p) => !!p);
|
|
68
71
|
}
|
package/src/actions/migrate.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from '../cli-error';
|
|
2
4
|
import { execPackage } from '../utils/exec-utils';
|
|
3
5
|
import { generateTempPrismaSchema, getSchemaFile } from './action-utils';
|
|
4
6
|
|
|
5
7
|
type CommonOptions = {
|
|
6
8
|
schema?: string;
|
|
9
|
+
migrations?: string;
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
type DevOptions = CommonOptions & {
|
|
@@ -19,12 +22,18 @@ type DeployOptions = CommonOptions;
|
|
|
19
22
|
|
|
20
23
|
type StatusOptions = CommonOptions;
|
|
21
24
|
|
|
25
|
+
type ResolveOptions = CommonOptions & {
|
|
26
|
+
applied?: string;
|
|
27
|
+
rolledBack?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
22
30
|
/**
|
|
23
31
|
* CLI action for migration-related commands
|
|
24
32
|
*/
|
|
25
33
|
export async function run(command: string, options: CommonOptions) {
|
|
26
34
|
const schemaFile = getSchemaFile(options.schema);
|
|
27
|
-
const
|
|
35
|
+
const prismaSchemaDir = options.migrations ? path.dirname(options.migrations) : undefined;
|
|
36
|
+
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir);
|
|
28
37
|
|
|
29
38
|
try {
|
|
30
39
|
switch (command) {
|
|
@@ -43,6 +52,10 @@ export async function run(command: string, options: CommonOptions) {
|
|
|
43
52
|
case 'status':
|
|
44
53
|
await runStatus(prismaSchemaFile, options as StatusOptions);
|
|
45
54
|
break;
|
|
55
|
+
|
|
56
|
+
case 'resolve':
|
|
57
|
+
await runResolve(prismaSchemaFile, options as ResolveOptions);
|
|
58
|
+
break;
|
|
46
59
|
}
|
|
47
60
|
} finally {
|
|
48
61
|
if (fs.existsSync(prismaSchemaFile)) {
|
|
@@ -53,12 +66,15 @@ export async function run(command: string, options: CommonOptions) {
|
|
|
53
66
|
|
|
54
67
|
async function runDev(prismaSchemaFile: string, options: DevOptions) {
|
|
55
68
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
|
|
69
|
+
const cmd = [
|
|
70
|
+
'prisma migrate dev',
|
|
71
|
+
` --schema "${prismaSchemaFile}"`,
|
|
72
|
+
' --skip-generate',
|
|
73
|
+
options.name ? ` --name ${options.name}` : '',
|
|
74
|
+
options.createOnly ? ' --create-only' : '',
|
|
75
|
+
].join('');
|
|
76
|
+
|
|
77
|
+
await execPackage(cmd);
|
|
62
78
|
} catch (err) {
|
|
63
79
|
handleSubProcessError(err);
|
|
64
80
|
}
|
|
@@ -66,9 +82,11 @@ async function runDev(prismaSchemaFile: string, options: DevOptions) {
|
|
|
66
82
|
|
|
67
83
|
async function runReset(prismaSchemaFile: string, options: ResetOptions) {
|
|
68
84
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
const cmd = ['prisma migrate reset', ` --schema "${prismaSchemaFile}"`, options.force ? ' --force' : ''].join(
|
|
86
|
+
'',
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
await execPackage(cmd);
|
|
72
90
|
} catch (err) {
|
|
73
91
|
handleSubProcessError(err);
|
|
74
92
|
}
|
|
@@ -76,9 +94,9 @@ async function runReset(prismaSchemaFile: string, options: ResetOptions) {
|
|
|
76
94
|
|
|
77
95
|
async function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
|
|
78
96
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
const cmd = ['prisma migrate deploy', ` --schema "${prismaSchemaFile}"`].join('');
|
|
98
|
+
|
|
99
|
+
await execPackage(cmd);
|
|
82
100
|
} catch (err) {
|
|
83
101
|
handleSubProcessError(err);
|
|
84
102
|
}
|
|
@@ -86,9 +104,26 @@ async function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
|
|
|
86
104
|
|
|
87
105
|
async function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
|
|
88
106
|
try {
|
|
89
|
-
await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
handleSubProcessError(err);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function runResolve(prismaSchemaFile: string, options: ResolveOptions) {
|
|
114
|
+
if (!options.applied && !options.rolledBack) {
|
|
115
|
+
throw new CliError('Either --applied or --rolled-back option must be provided');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const cmd = [
|
|
120
|
+
'prisma migrate resolve',
|
|
121
|
+
` --schema "${prismaSchemaFile}"`,
|
|
122
|
+
options.applied ? ` --applied ${options.applied}` : '',
|
|
123
|
+
options.rolledBack ? ` --rolled-back ${options.rolledBack}` : '',
|
|
124
|
+
].join('');
|
|
125
|
+
|
|
126
|
+
await execPackage(cmd);
|
|
92
127
|
} catch (err) {
|
|
93
128
|
handleSubProcessError(err);
|
|
94
129
|
}
|
package/src/actions/templates.ts
CHANGED
|
@@ -27,14 +27,15 @@ model Post {
|
|
|
27
27
|
`;
|
|
28
28
|
|
|
29
29
|
export const STARTER_MAIN_TS = `import { ZenStackClient } from '@zenstackhq/runtime';
|
|
30
|
-
import { schema } from './zenstack/schema';
|
|
31
30
|
import SQLite from 'better-sqlite3';
|
|
31
|
+
import { SqliteDialect } from 'kysely';
|
|
32
|
+
import { schema } from './zenstack/schema';
|
|
32
33
|
|
|
33
34
|
async function main() {
|
|
34
35
|
const client = new ZenStackClient(schema, {
|
|
35
|
-
|
|
36
|
+
dialect: new SqliteDialect({
|
|
36
37
|
database: new SQLite('./zenstack/dev.db'),
|
|
37
|
-
},
|
|
38
|
+
}),
|
|
38
39
|
});
|
|
39
40
|
const user = await client.user.create({
|
|
40
41
|
data: {
|
package/src/constants.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
import { ZModelLanguageMetaData } from '@zenstackhq/language';
|
|
2
2
|
import colors from 'colors';
|
|
3
|
-
import { Command, Option } from 'commander';
|
|
3
|
+
import { Command, CommanderError, Option } from 'commander';
|
|
4
4
|
import * as actions from './actions';
|
|
5
|
-
import {
|
|
5
|
+
import { CliError } from './cli-error';
|
|
6
|
+
import { telemetry } from './telemetry';
|
|
7
|
+
import { checkNewVersion, getVersion } from './utils/version-utils';
|
|
6
8
|
|
|
7
9
|
const generateAction = async (options: Parameters<typeof actions.generate>[0]): Promise<void> => {
|
|
8
|
-
await actions.generate(options);
|
|
10
|
+
await telemetry.trackCommand('generate', () => actions.generate(options));
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
const migrateAction = async (
|
|
12
|
-
await actions.migrate(
|
|
13
|
+
const migrateAction = async (subCommand: string, options: any): Promise<void> => {
|
|
14
|
+
await telemetry.trackCommand(`migrate ${subCommand}`, () => actions.migrate(subCommand, options));
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
const dbAction = async (
|
|
16
|
-
await actions.db(
|
|
17
|
+
const dbAction = async (subCommand: string, options: any): Promise<void> => {
|
|
18
|
+
await telemetry.trackCommand(`db ${subCommand}`, () => actions.db(subCommand, options));
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
const infoAction = async (projectPath: string): Promise<void> => {
|
|
20
|
-
await actions.info(projectPath);
|
|
22
|
+
await telemetry.trackCommand('info', () => actions.info(projectPath));
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const initAction = async (projectPath: string): Promise<void> => {
|
|
24
|
-
await actions.init(projectPath);
|
|
26
|
+
await telemetry.trackCommand('init', () => actions.init(projectPath));
|
|
25
27
|
};
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const checkAction = async (options: Parameters<typeof actions.check>[0]): Promise<void> => {
|
|
30
|
+
await telemetry.trackCommand('check', () => actions.check(options));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function createProgram() {
|
|
34
|
+
const program = new Command('zen');
|
|
29
35
|
|
|
30
36
|
program.version(getVersion()!, '-v --version', 'display CLI version');
|
|
31
37
|
|
|
@@ -35,37 +41,37 @@ export function createProgram() {
|
|
|
35
41
|
.description(
|
|
36
42
|
`${colors.bold.blue(
|
|
37
43
|
'ζ',
|
|
38
|
-
)} ZenStack is
|
|
44
|
+
)} ZenStack is the data layer for modern TypeScript apps.\n\nDocumentation: https://zenstack.dev/docs/3.x`,
|
|
39
45
|
)
|
|
40
46
|
.showHelpAfterError()
|
|
41
47
|
.showSuggestionAfterError();
|
|
42
48
|
|
|
43
49
|
const schemaOption = new Option(
|
|
44
50
|
'--schema <file>',
|
|
45
|
-
`schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json.`,
|
|
51
|
+
`schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`,
|
|
46
52
|
);
|
|
47
53
|
|
|
54
|
+
const noVersionCheckOption = new Option('--no-version-check', 'do not check for new version');
|
|
55
|
+
|
|
48
56
|
program
|
|
49
57
|
.command('generate')
|
|
50
|
-
.description('Run code generation.')
|
|
58
|
+
.description('Run code generation plugins.')
|
|
51
59
|
.addOption(schemaOption)
|
|
52
|
-
.addOption(
|
|
53
|
-
.addOption(
|
|
54
|
-
|
|
55
|
-
'--save-prisma-schema [path]',
|
|
56
|
-
'save a Prisma schema file, by default into the output directory',
|
|
57
|
-
),
|
|
58
|
-
)
|
|
59
|
-
.addOption(new Option('-o, --output <path>', 'default output directory for core plugins'))
|
|
60
|
+
.addOption(noVersionCheckOption)
|
|
61
|
+
.addOption(new Option('-o, --output <path>', 'default output directory for code generation'))
|
|
62
|
+
.addOption(new Option('--silent', 'suppress all output except errors').default(false))
|
|
60
63
|
.action(generateAction);
|
|
61
64
|
|
|
62
|
-
const migrateCommand = program.command('migrate').description('
|
|
65
|
+
const migrateCommand = program.command('migrate').description('Run database schema migration related tasks.');
|
|
66
|
+
const migrationsOption = new Option('--migrations <path>', 'path that contains the "migrations" directory');
|
|
63
67
|
|
|
64
68
|
migrateCommand
|
|
65
69
|
.command('dev')
|
|
66
70
|
.addOption(schemaOption)
|
|
71
|
+
.addOption(noVersionCheckOption)
|
|
67
72
|
.addOption(new Option('-n, --name <name>', 'migration name'))
|
|
68
73
|
.addOption(new Option('--create-only', 'only create migration, do not apply'))
|
|
74
|
+
.addOption(migrationsOption)
|
|
69
75
|
.description('Create a migration from changes in schema and apply it to the database.')
|
|
70
76
|
.action((options) => migrateAction('dev', options));
|
|
71
77
|
|
|
@@ -73,45 +79,110 @@ export function createProgram() {
|
|
|
73
79
|
.command('reset')
|
|
74
80
|
.addOption(schemaOption)
|
|
75
81
|
.addOption(new Option('--force', 'skip the confirmation prompt'))
|
|
82
|
+
.addOption(migrationsOption)
|
|
83
|
+
.addOption(noVersionCheckOption)
|
|
76
84
|
.description('Reset your database and apply all migrations, all data will be lost.')
|
|
77
85
|
.action((options) => migrateAction('reset', options));
|
|
78
86
|
|
|
79
87
|
migrateCommand
|
|
80
88
|
.command('deploy')
|
|
81
89
|
.addOption(schemaOption)
|
|
90
|
+
.addOption(noVersionCheckOption)
|
|
91
|
+
.addOption(migrationsOption)
|
|
82
92
|
.description('Deploy your pending migrations to your production/staging database.')
|
|
83
93
|
.action((options) => migrateAction('deploy', options));
|
|
84
94
|
|
|
85
95
|
migrateCommand
|
|
86
96
|
.command('status')
|
|
87
97
|
.addOption(schemaOption)
|
|
88
|
-
.
|
|
98
|
+
.addOption(noVersionCheckOption)
|
|
99
|
+
.addOption(migrationsOption)
|
|
100
|
+
.description('Check the status of your database migrations.')
|
|
89
101
|
.action((options) => migrateAction('status', options));
|
|
90
102
|
|
|
103
|
+
migrateCommand
|
|
104
|
+
.command('resolve')
|
|
105
|
+
.addOption(schemaOption)
|
|
106
|
+
.addOption(noVersionCheckOption)
|
|
107
|
+
.addOption(migrationsOption)
|
|
108
|
+
.addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
|
|
109
|
+
.addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
|
|
110
|
+
.description('Resolve issues with database migrations in deployment databases.')
|
|
111
|
+
.action((options) => migrateAction('resolve', options));
|
|
112
|
+
|
|
91
113
|
const dbCommand = program.command('db').description('Manage your database schema during development.');
|
|
92
114
|
|
|
93
115
|
dbCommand
|
|
94
116
|
.command('push')
|
|
95
|
-
.description('Push the state from your schema to your database')
|
|
117
|
+
.description('Push the state from your schema to your database.')
|
|
96
118
|
.addOption(schemaOption)
|
|
119
|
+
.addOption(noVersionCheckOption)
|
|
97
120
|
.addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
|
|
98
121
|
.addOption(new Option('--force-reset', 'force a reset of the database before push'))
|
|
99
122
|
.action((options) => dbAction('push', options));
|
|
100
123
|
|
|
101
124
|
program
|
|
102
125
|
.command('info')
|
|
103
|
-
.description('Get information of installed ZenStack
|
|
126
|
+
.description('Get information of installed ZenStack packages.')
|
|
104
127
|
.argument('[path]', 'project path', '.')
|
|
128
|
+
.addOption(noVersionCheckOption)
|
|
105
129
|
.action(infoAction);
|
|
106
130
|
|
|
107
131
|
program
|
|
108
132
|
.command('init')
|
|
109
133
|
.description('Initialize an existing project for ZenStack.')
|
|
110
134
|
.argument('[path]', 'project path', '.')
|
|
135
|
+
.addOption(noVersionCheckOption)
|
|
111
136
|
.action(initAction);
|
|
112
137
|
|
|
138
|
+
program
|
|
139
|
+
.command('check')
|
|
140
|
+
.description('Check a ZModel schema for syntax or semantic errors.')
|
|
141
|
+
.addOption(schemaOption)
|
|
142
|
+
.addOption(noVersionCheckOption)
|
|
143
|
+
.action(checkAction);
|
|
144
|
+
|
|
145
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
146
|
+
if (actionCommand.getOptionValue('versionCheck') !== false) {
|
|
147
|
+
await checkNewVersion();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
113
151
|
return program;
|
|
114
152
|
}
|
|
115
153
|
|
|
116
|
-
|
|
117
|
-
|
|
154
|
+
async function main() {
|
|
155
|
+
let exitCode = 0;
|
|
156
|
+
|
|
157
|
+
const program = createProgram();
|
|
158
|
+
program.exitOverride();
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
await telemetry.trackCli(async () => {
|
|
162
|
+
await program.parseAsync();
|
|
163
|
+
});
|
|
164
|
+
} catch (e) {
|
|
165
|
+
if (e instanceof CommanderError) {
|
|
166
|
+
// ignore
|
|
167
|
+
exitCode = e.exitCode;
|
|
168
|
+
} else if (e instanceof CliError) {
|
|
169
|
+
// log
|
|
170
|
+
console.error(colors.red(e.message));
|
|
171
|
+
exitCode = 1;
|
|
172
|
+
} else {
|
|
173
|
+
console.error(colors.red(`Unhandled error: ${e}`));
|
|
174
|
+
exitCode = 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (telemetry.isTracking) {
|
|
179
|
+
// give telemetry a chance to send events before exit
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
process.exit(exitCode);
|
|
182
|
+
}, 200);
|
|
183
|
+
} else {
|
|
184
|
+
process.exit(exitCode);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
main();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PrismaSchemaGenerator, type CliPlugin } from '@zenstackhq/sdk';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const plugin: CliPlugin = {
|
|
6
|
+
name: 'Prisma Schema Generator',
|
|
7
|
+
statusText: 'Generating Prisma schema',
|
|
8
|
+
async generate({ model, schemaFile, defaultOutputPath, pluginOptions }) {
|
|
9
|
+
let outFile = path.join(defaultOutputPath, 'schema.prisma');
|
|
10
|
+
if (typeof pluginOptions['output'] === 'string') {
|
|
11
|
+
outFile = path.resolve(path.dirname(schemaFile), pluginOptions['output']);
|
|
12
|
+
if (!fs.existsSync(path.dirname(outFile))) {
|
|
13
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
|
|
17
|
+
fs.writeFileSync(outFile, prismaSchema);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default plugin;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CliPlugin } from '@zenstackhq/sdk';
|
|
2
|
+
import { TsSchemaGenerator } from '@zenstackhq/sdk';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const plugin: CliPlugin = {
|
|
7
|
+
name: 'TypeScript Schema Generator',
|
|
8
|
+
statusText: 'Generating TypeScript schema',
|
|
9
|
+
async generate({ model, defaultOutputPath, pluginOptions }) {
|
|
10
|
+
let outDir = defaultOutputPath;
|
|
11
|
+
if (typeof pluginOptions['output'] === 'string') {
|
|
12
|
+
outDir = path.resolve(defaultOutputPath, pluginOptions['output']);
|
|
13
|
+
if (!fs.existsSync(outDir)) {
|
|
14
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
await new TsSchemaGenerator().generate(model, outDir);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default plugin;
|