@zenstackhq/cli 3.0.0-alpha.8 → 3.0.0-beta.1

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.
@@ -1,46 +1,35 @@
1
1
  import { invariant } from '@zenstackhq/common-helpers';
2
- import { isPlugin, LiteralExpr, type Model } from '@zenstackhq/language/ast';
3
- import { PrismaSchemaGenerator, TsSchemaGenerator, type CliGenerator } from '@zenstackhq/sdk';
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 { getSchemaFile, loadSchemaDocument } from './action-utils';
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?: boolean;
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.output ?? path.dirname(schemaFile);
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, tsSchemaFile);
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('Generation completed successfully.'));
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
- dialectConfig: { ... }
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, tsSchemaFile: 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 providerField = plugin.fields.find((f) => f.name === 'provider');
62
- invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
63
- const provider = (providerField.value as LiteralExpr).value as string;
64
- let useProvider = provider;
65
- if (useProvider.startsWith('@core/')) {
66
- useProvider = `@zenstackhq/runtime/plugins/${useProvider.slice(6)}`;
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
- const generator = (await import(useProvider)).default as CliGenerator;
69
- console.log('Running generator:', provider);
70
- await generator({ model, outputPath, tsSchemaFile });
141
+ result[field.name] = value;
71
142
  }
143
+ return result;
72
144
  }
@@ -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 };
@@ -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
  }
@@ -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 prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
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
- await execPackage(
57
- `prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate${options.name ? ` --name ${options.name}` : ''}${options.createOnly ? ' --create-only' : ''}`,
58
- {
59
- stdio: 'inherit',
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
- await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? ' --force' : ''}`, {
70
- stdio: 'inherit',
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
- await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
80
- stdio: 'inherit',
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
- stdio: 'inherit',
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
  }
@@ -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
- dialectConfig: {
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: {
@@ -0,0 +1,2 @@
1
+ // replaced at build time
2
+ export const TELEMETRY_TRACKING_TOKEN = '<TELEMETRY_TRACKING_TOKEN>';
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 { getVersion } from './utils/version-utils';
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 (command: string, options: any): Promise<void> => {
12
- await actions.migrate(command, options);
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 (command: string, options: any): Promise<void> => {
16
- await actions.db(command, options);
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
- export function createProgram() {
28
- const program = new Command('zenstack');
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 a Prisma power pack for building full-stack apps.\n\nDocumentation: https://zenstack.dev.`,
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(new Option('--silent', 'do not print any output'))
53
- .addOption(
54
- new Option(
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('Update the database schema with migrations.');
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
- .description('check the status of your database migrations.')
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 and related packages.')
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
- const program = createProgram();
117
- program.parse(process.argv);
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,2 @@
1
+ export { default as prisma } from './prisma';
2
+ export { default as typescript } from './typescript';
@@ -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;