@zenstackhq/cli 3.0.0-beta.22 → 3.0.0-beta.24
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 +8 -8
- package/dist/index.cjs +207 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +208 -138
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/actions/action-utils.ts +13 -5
- package/src/actions/db.ts +3 -3
- package/src/actions/format.ts +27 -0
- package/src/actions/generate.ts +13 -1
- package/src/actions/index.ts +3 -2
- package/src/actions/migrate.ts +18 -22
- package/src/index.ts +28 -14
- package/src/plugins/typescript.ts +12 -1
- package/src/utils/exec-utils.ts +27 -0
- package/test/format.test.ts +33 -0
- package/test/generate.test.ts +15 -0
- package/test/migrate.test.ts +1 -2
package/src/actions/migrate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { CliError } from '../cli-error';
|
|
4
|
-
import {
|
|
4
|
+
import { execPrisma } from '../utils/exec-utils';
|
|
5
5
|
import { generateTempPrismaSchema, getSchemaFile } from './action-utils';
|
|
6
6
|
|
|
7
7
|
type CommonOptions = {
|
|
@@ -64,69 +64,65 @@ export async function run(command: string, options: CommonOptions) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
function runDev(prismaSchemaFile: string, options: DevOptions) {
|
|
68
68
|
try {
|
|
69
69
|
const cmd = [
|
|
70
|
-
'
|
|
70
|
+
'migrate dev',
|
|
71
71
|
` --schema "${prismaSchemaFile}"`,
|
|
72
72
|
' --skip-generate',
|
|
73
|
-
options.name ? ` --name ${options.name}` : '',
|
|
73
|
+
options.name ? ` --name "${options.name}"` : '',
|
|
74
74
|
options.createOnly ? ' --create-only' : '',
|
|
75
75
|
].join('');
|
|
76
|
-
|
|
77
|
-
await execPackage(cmd);
|
|
76
|
+
execPrisma(cmd);
|
|
78
77
|
} catch (err) {
|
|
79
78
|
handleSubProcessError(err);
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
function runReset(prismaSchemaFile: string, options: ResetOptions) {
|
|
84
83
|
try {
|
|
85
84
|
const cmd = [
|
|
86
|
-
'
|
|
85
|
+
'migrate reset',
|
|
87
86
|
` --schema "${prismaSchemaFile}"`,
|
|
88
87
|
' --skip-generate',
|
|
89
88
|
options.force ? ' --force' : '',
|
|
90
89
|
].join('');
|
|
91
|
-
|
|
92
|
-
await execPackage(cmd);
|
|
90
|
+
execPrisma(cmd);
|
|
93
91
|
} catch (err) {
|
|
94
92
|
handleSubProcessError(err);
|
|
95
93
|
}
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
|
|
99
97
|
try {
|
|
100
|
-
const cmd = ['
|
|
101
|
-
|
|
102
|
-
await execPackage(cmd);
|
|
98
|
+
const cmd = ['migrate deploy', ` --schema "${prismaSchemaFile}"`].join('');
|
|
99
|
+
execPrisma(cmd);
|
|
103
100
|
} catch (err) {
|
|
104
101
|
handleSubProcessError(err);
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
|
|
105
|
+
function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
|
|
109
106
|
try {
|
|
110
|
-
|
|
107
|
+
execPrisma(`migrate status --schema "${prismaSchemaFile}"`);
|
|
111
108
|
} catch (err) {
|
|
112
109
|
handleSubProcessError(err);
|
|
113
110
|
}
|
|
114
111
|
}
|
|
115
112
|
|
|
116
|
-
|
|
113
|
+
function runResolve(prismaSchemaFile: string, options: ResolveOptions) {
|
|
117
114
|
if (!options.applied && !options.rolledBack) {
|
|
118
115
|
throw new CliError('Either --applied or --rolled-back option must be provided');
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
try {
|
|
122
119
|
const cmd = [
|
|
123
|
-
'
|
|
120
|
+
'migrate resolve',
|
|
124
121
|
` --schema "${prismaSchemaFile}"`,
|
|
125
|
-
options.applied ? ` --applied ${options.applied}` : '',
|
|
126
|
-
options.rolledBack ? ` --rolled-back ${options.rolledBack}` : '',
|
|
122
|
+
options.applied ? ` --applied "${options.applied}"` : '',
|
|
123
|
+
options.rolledBack ? ` --rolled-back "${options.rolledBack}"` : '',
|
|
127
124
|
].join('');
|
|
128
|
-
|
|
129
|
-
await execPackage(cmd);
|
|
125
|
+
execPrisma(cmd);
|
|
130
126
|
} catch (err) {
|
|
131
127
|
handleSubProcessError(err);
|
|
132
128
|
}
|
package/src/index.ts
CHANGED
|
@@ -30,10 +30,15 @@ const checkAction = async (options: Parameters<typeof actions.check>[0]): Promis
|
|
|
30
30
|
await telemetry.trackCommand('check', () => actions.check(options));
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const formatAction = async (options: Parameters<typeof actions.format>[0]): Promise<void> => {
|
|
34
|
+
await telemetry.trackCommand('format', () => actions.format(options));
|
|
35
|
+
};
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
function createProgram() {
|
|
38
|
+
const program = new Command('zen')
|
|
39
|
+
.alias('zenstack')
|
|
40
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
41
|
+
.version(getVersion()!, '-v --version', 'Show CLI version');
|
|
37
42
|
|
|
38
43
|
const schemaExtensions = ZModelLanguageMetaData.fileExtensions.join(', ');
|
|
39
44
|
|
|
@@ -55,7 +60,7 @@ function createProgram() {
|
|
|
55
60
|
|
|
56
61
|
program
|
|
57
62
|
.command('generate')
|
|
58
|
-
.description('Run code generation plugins
|
|
63
|
+
.description('Run code generation plugins')
|
|
59
64
|
.addOption(schemaOption)
|
|
60
65
|
.addOption(noVersionCheckOption)
|
|
61
66
|
.addOption(new Option('-o, --output <path>', 'default output directory for code generation'))
|
|
@@ -74,7 +79,7 @@ function createProgram() {
|
|
|
74
79
|
.addOption(new Option('-n, --name <name>', 'migration name'))
|
|
75
80
|
.addOption(new Option('--create-only', 'only create migration, do not apply'))
|
|
76
81
|
.addOption(migrationsOption)
|
|
77
|
-
.description('Create a migration from changes in schema and apply it to the database
|
|
82
|
+
.description('Create a migration from changes in schema and apply it to the database')
|
|
78
83
|
.action((options) => migrateAction('dev', options));
|
|
79
84
|
|
|
80
85
|
migrateCommand
|
|
@@ -83,7 +88,7 @@ function createProgram() {
|
|
|
83
88
|
.addOption(new Option('--force', 'skip the confirmation prompt'))
|
|
84
89
|
.addOption(migrationsOption)
|
|
85
90
|
.addOption(noVersionCheckOption)
|
|
86
|
-
.description('Reset your database and apply all migrations, all data will be lost
|
|
91
|
+
.description('Reset your database and apply all migrations, all data will be lost')
|
|
87
92
|
.action((options) => migrateAction('reset', options));
|
|
88
93
|
|
|
89
94
|
migrateCommand
|
|
@@ -91,7 +96,7 @@ function createProgram() {
|
|
|
91
96
|
.addOption(schemaOption)
|
|
92
97
|
.addOption(noVersionCheckOption)
|
|
93
98
|
.addOption(migrationsOption)
|
|
94
|
-
.description('Deploy your pending migrations to your production/staging database
|
|
99
|
+
.description('Deploy your pending migrations to your production/staging database')
|
|
95
100
|
.action((options) => migrateAction('deploy', options));
|
|
96
101
|
|
|
97
102
|
migrateCommand
|
|
@@ -99,7 +104,7 @@ function createProgram() {
|
|
|
99
104
|
.addOption(schemaOption)
|
|
100
105
|
.addOption(noVersionCheckOption)
|
|
101
106
|
.addOption(migrationsOption)
|
|
102
|
-
.description('Check the status of your database migrations
|
|
107
|
+
.description('Check the status of your database migrations')
|
|
103
108
|
.action((options) => migrateAction('status', options));
|
|
104
109
|
|
|
105
110
|
migrateCommand
|
|
@@ -109,14 +114,14 @@ function createProgram() {
|
|
|
109
114
|
.addOption(migrationsOption)
|
|
110
115
|
.addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
|
|
111
116
|
.addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
|
|
112
|
-
.description('Resolve issues with database migrations in deployment databases
|
|
117
|
+
.description('Resolve issues with database migrations in deployment databases')
|
|
113
118
|
.action((options) => migrateAction('resolve', options));
|
|
114
119
|
|
|
115
|
-
const dbCommand = program.command('db').description('Manage your database schema during development
|
|
120
|
+
const dbCommand = program.command('db').description('Manage your database schema during development');
|
|
116
121
|
|
|
117
122
|
dbCommand
|
|
118
123
|
.command('push')
|
|
119
|
-
.description('Push the state from your schema to your database
|
|
124
|
+
.description('Push the state from your schema to your database')
|
|
120
125
|
.addOption(schemaOption)
|
|
121
126
|
.addOption(noVersionCheckOption)
|
|
122
127
|
.addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
|
|
@@ -125,25 +130,34 @@ function createProgram() {
|
|
|
125
130
|
|
|
126
131
|
program
|
|
127
132
|
.command('info')
|
|
128
|
-
.description('Get information of installed ZenStack packages
|
|
133
|
+
.description('Get information of installed ZenStack packages')
|
|
129
134
|
.argument('[path]', 'project path', '.')
|
|
130
135
|
.addOption(noVersionCheckOption)
|
|
131
136
|
.action(infoAction);
|
|
132
137
|
|
|
133
138
|
program
|
|
134
139
|
.command('init')
|
|
135
|
-
.description('Initialize an existing project for ZenStack
|
|
140
|
+
.description('Initialize an existing project for ZenStack')
|
|
136
141
|
.argument('[path]', 'project path', '.')
|
|
137
142
|
.addOption(noVersionCheckOption)
|
|
138
143
|
.action(initAction);
|
|
139
144
|
|
|
140
145
|
program
|
|
141
146
|
.command('check')
|
|
142
|
-
.description('Check a ZModel schema for syntax or semantic errors
|
|
147
|
+
.description('Check a ZModel schema for syntax or semantic errors')
|
|
143
148
|
.addOption(schemaOption)
|
|
144
149
|
.addOption(noVersionCheckOption)
|
|
145
150
|
.action(checkAction);
|
|
146
151
|
|
|
152
|
+
program
|
|
153
|
+
.command('format')
|
|
154
|
+
.description('Format a ZModel schema file')
|
|
155
|
+
.addOption(schemaOption)
|
|
156
|
+
.addOption(noVersionCheckOption)
|
|
157
|
+
.action(formatAction);
|
|
158
|
+
|
|
159
|
+
program.addHelpCommand('help [command]', 'Display help for a command');
|
|
160
|
+
|
|
147
161
|
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
148
162
|
if (actionCommand.getOptionValue('versionCheck') !== false) {
|
|
149
163
|
await checkNewVersion();
|
|
@@ -22,7 +22,18 @@ const plugin: CliPlugin = {
|
|
|
22
22
|
// liteOnly mode
|
|
23
23
|
const liteOnly = pluginOptions['liteOnly'] === true;
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// add .js extension when importing
|
|
26
|
+
const importWithFileExtension = pluginOptions['importWithFileExtension'];
|
|
27
|
+
if (importWithFileExtension && typeof importWithFileExtension !== 'string') {
|
|
28
|
+
throw new Error('The "importWithFileExtension" option must be a string if specified.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await new TsSchemaGenerator().generate(model, {
|
|
32
|
+
outDir,
|
|
33
|
+
lite,
|
|
34
|
+
liteOnly,
|
|
35
|
+
importWithFileExtension: importWithFileExtension as string | undefined,
|
|
36
|
+
});
|
|
26
37
|
},
|
|
27
38
|
};
|
|
28
39
|
|
package/src/utils/exec-utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync as _exec, type ExecSyncOptions } from 'child_process';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Utility for executing command synchronously and prints outputs on current console
|
|
@@ -24,3 +25,29 @@ export function execPackage(
|
|
|
24
25
|
const packageManager = process?.versions?.['bun'] ? 'bunx' : 'npx';
|
|
25
26
|
execSync(`${packageManager} ${cmd}`, options);
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Utility for running prisma commands
|
|
31
|
+
*/
|
|
32
|
+
export function execPrisma(args: string, options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }) {
|
|
33
|
+
let prismaPath: string | undefined;
|
|
34
|
+
try {
|
|
35
|
+
if (typeof import.meta.resolve === 'function') {
|
|
36
|
+
// esm
|
|
37
|
+
prismaPath = fileURLToPath(import.meta.resolve('prisma/build/index.js'));
|
|
38
|
+
} else {
|
|
39
|
+
// cjs
|
|
40
|
+
prismaPath = require.resolve('prisma/build/index.js');
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// ignore and fallback
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!prismaPath) {
|
|
47
|
+
// fallback to npx/bunx execute
|
|
48
|
+
execPackage(`prisma ${args}`, options);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
execSync(`node ${prismaPath} ${args}`, options);
|
|
53
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createProject, runCli } from './utils';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
|
|
5
|
+
const model = `
|
|
6
|
+
model User {
|
|
7
|
+
id String @id @default(cuid())
|
|
8
|
+
email String @unique
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
describe('CLI format command test', () => {
|
|
13
|
+
it('should format a valid schema successfully', () => {
|
|
14
|
+
const workDir = createProject(model);
|
|
15
|
+
expect(() => runCli('format', workDir)).not.toThrow();
|
|
16
|
+
const updatedContent = fs.readFileSync(`${workDir}/zenstack/schema.zmodel`, 'utf-8');
|
|
17
|
+
expect(
|
|
18
|
+
updatedContent.includes(`model User {
|
|
19
|
+
id String @id @default(cuid())
|
|
20
|
+
email String @unique
|
|
21
|
+
}`),
|
|
22
|
+
).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should silently ignore invalid schema', () => {
|
|
26
|
+
const invalidModel = `
|
|
27
|
+
model User {
|
|
28
|
+
id String @id @default(cuid())
|
|
29
|
+
`;
|
|
30
|
+
const workDir = createProject(invalidModel);
|
|
31
|
+
expect(() => runCli('format', workDir)).not.toThrow();
|
|
32
|
+
});
|
|
33
|
+
});
|
package/test/generate.test.ts
CHANGED
|
@@ -45,6 +45,21 @@ describe('CLI generate command test', () => {
|
|
|
45
45
|
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
it('should respect package.json schema dir config', () => {
|
|
49
|
+
const workDir = createProject(model);
|
|
50
|
+
fs.mkdirSync(path.join(workDir, 'foo'));
|
|
51
|
+
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
|
|
52
|
+
fs.rmdirSync(path.join(workDir, 'zenstack'));
|
|
53
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
54
|
+
pkgJson.zenstack = {
|
|
55
|
+
schema: './foo',
|
|
56
|
+
output: './bar',
|
|
57
|
+
};
|
|
58
|
+
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
59
|
+
runCli('generate', workDir);
|
|
60
|
+
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
48
63
|
it('should respect lite option', () => {
|
|
49
64
|
const workDir = createProject(model);
|
|
50
65
|
runCli('generate --lite', workDir);
|
package/test/migrate.test.ts
CHANGED
|
@@ -9,8 +9,7 @@ model User {
|
|
|
9
9
|
}
|
|
10
10
|
`;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
describe.skip('CLI migrate commands test', () => {
|
|
12
|
+
describe('CLI migrate commands test', () => {
|
|
14
13
|
it('should generate a database with migrate dev', () => {
|
|
15
14
|
const workDir = createProject(model);
|
|
16
15
|
runCli('migrate dev --name init', workDir);
|