@zenstackhq/cli 3.0.0-beta.23 → 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/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
- function createProgram() {
34
- const program = new Command('zen');
33
+ const formatAction = async (options: Parameters<typeof actions.format>[0]): Promise<void> => {
34
+ await telemetry.trackCommand('format', () => actions.format(options));
35
+ };
35
36
 
36
- program.version(getVersion()!, '-v --version', 'display CLI version');
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
- await new TsSchemaGenerator().generate(model, { outDir, lite, liteOnly });
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
 
@@ -30,13 +30,24 @@ export function execPackage(
30
30
  * Utility for running prisma commands
31
31
  */
32
32
  export function execPrisma(args: string, options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }) {
33
- let prismaPath: string;
34
- if (typeof import.meta.resolve === 'function') {
35
- // esm
36
- prismaPath = fileURLToPath(import.meta.resolve('prisma/build/index.js'));
37
- } else {
38
- // cjs
39
- prismaPath = require.resolve('prisma/build/index.js');
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
40
44
  }
45
+
46
+ if (!prismaPath) {
47
+ // fallback to npx/bunx execute
48
+ execPackage(`prisma ${args}`, options);
49
+ return;
50
+ }
51
+
41
52
  execSync(`node ${prismaPath} ${args}`, options);
42
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
+ });