@zenstackhq/cli 3.1.1 → 3.2.0

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.
Files changed (46) hide show
  1. package/dist/index.cjs +88 -5
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +89 -6
  4. package/dist/index.js.map +1 -1
  5. package/package.json +17 -9
  6. package/.turbo/turbo-build.log +0 -22
  7. package/eslint.config.js +0 -4
  8. package/scripts/post-build.ts +0 -20
  9. package/src/actions/action-utils.ts +0 -146
  10. package/src/actions/check.ts +0 -22
  11. package/src/actions/db.ts +0 -51
  12. package/src/actions/format.ts +0 -27
  13. package/src/actions/generate.ts +0 -226
  14. package/src/actions/index.ts +0 -10
  15. package/src/actions/info.ts +0 -71
  16. package/src/actions/init.ts +0 -61
  17. package/src/actions/migrate.ts +0 -149
  18. package/src/actions/seed.ts +0 -38
  19. package/src/actions/templates.ts +0 -58
  20. package/src/cli-error.ts +0 -4
  21. package/src/constants.ts +0 -5
  22. package/src/index.ts +0 -233
  23. package/src/plugins/index.ts +0 -2
  24. package/src/plugins/prisma.ts +0 -21
  25. package/src/plugins/typescript.ts +0 -40
  26. package/src/telemetry.ts +0 -139
  27. package/src/utils/exec-utils.ts +0 -61
  28. package/src/utils/is-ci.ts +0 -5
  29. package/src/utils/is-container.ts +0 -23
  30. package/src/utils/is-docker.ts +0 -31
  31. package/src/utils/is-wsl.ts +0 -18
  32. package/src/utils/machine-id-utils.ts +0 -76
  33. package/src/utils/version-utils.ts +0 -50
  34. package/test/check.test.ts +0 -101
  35. package/test/db.test.ts +0 -61
  36. package/test/format.test.ts +0 -33
  37. package/test/generate.test.ts +0 -76
  38. package/test/init.test.ts +0 -14
  39. package/test/migrate.test.ts +0 -72
  40. package/test/plugins/custom-plugin.test.ts +0 -50
  41. package/test/plugins/prisma-plugin.test.ts +0 -81
  42. package/test/ts-schema-gen.test.ts +0 -445
  43. package/test/utils.ts +0 -23
  44. package/tsconfig.json +0 -4
  45. package/tsup.config.ts +0 -13
  46. package/vitest.config.ts +0 -4
@@ -1,146 +0,0 @@
1
- import { loadDocument } from '@zenstackhq/language';
2
- import { isDataSource } from '@zenstackhq/language/ast';
3
- import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
4
- import colors from 'colors';
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import { CliError } from '../cli-error';
8
-
9
- export function getSchemaFile(file?: string) {
10
- if (file) {
11
- if (!fs.existsSync(file)) {
12
- throw new CliError(`Schema file not found: ${file}`);
13
- }
14
- return file;
15
- }
16
-
17
- const pkgJsonConfig = getPkgJsonConfig(process.cwd());
18
- if (pkgJsonConfig.schema) {
19
- if (!fs.existsSync(pkgJsonConfig.schema)) {
20
- throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
21
- }
22
- if (fs.statSync(pkgJsonConfig.schema).isDirectory()) {
23
- const schemaPath = path.join(pkgJsonConfig.schema, 'schema.zmodel');
24
- if (!fs.existsSync(schemaPath)) {
25
- throw new CliError(`Schema file not found: ${schemaPath}`);
26
- }
27
- return schemaPath;
28
- } else {
29
- return pkgJsonConfig.schema;
30
- }
31
- }
32
-
33
- if (fs.existsSync('./schema.zmodel')) {
34
- return './schema.zmodel';
35
- } else if (fs.existsSync('./zenstack/schema.zmodel')) {
36
- return './zenstack/schema.zmodel';
37
- } else {
38
- throw new CliError(
39
- 'Schema file not found in default locations ("./schema.zmodel" or "./zenstack/schema.zmodel").',
40
- );
41
- }
42
- }
43
-
44
- export async function loadSchemaDocument(schemaFile: string) {
45
- const loadResult = await loadDocument(schemaFile);
46
- if (!loadResult.success) {
47
- loadResult.errors.forEach((err) => {
48
- console.error(colors.red(err));
49
- });
50
- throw new CliError('Schema contains errors. See above for details.');
51
- }
52
- loadResult.warnings.forEach((warn) => {
53
- console.warn(colors.yellow(warn));
54
- });
55
- return loadResult.model;
56
- }
57
-
58
- export function handleSubProcessError(err: unknown) {
59
- if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
60
- process.exit(err.status);
61
- } else {
62
- process.exit(1);
63
- }
64
- }
65
-
66
- export async function generateTempPrismaSchema(zmodelPath: string, folder?: string) {
67
- const model = await loadSchemaDocument(zmodelPath);
68
- if (!model.declarations.some(isDataSource)) {
69
- throw new CliError('Schema must define a datasource');
70
- }
71
- const prismaSchema = await new PrismaSchemaGenerator(model).generate();
72
- if (!folder) {
73
- folder = path.dirname(zmodelPath);
74
- }
75
- const prismaSchemaFile = path.resolve(folder, '~schema.prisma');
76
- fs.writeFileSync(prismaSchemaFile, prismaSchema);
77
- return prismaSchemaFile;
78
- }
79
-
80
- export function getPkgJsonConfig(startPath: string) {
81
- const result: { schema: string | undefined; output: string | undefined; seed: string | undefined } = {
82
- schema: undefined,
83
- output: undefined,
84
- seed: undefined,
85
- };
86
- const pkgJsonFile = findUp(['package.json'], startPath, false);
87
-
88
- if (!pkgJsonFile) {
89
- return result;
90
- }
91
-
92
- let pkgJson: any = undefined;
93
- try {
94
- pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
95
- } catch {
96
- return result;
97
- }
98
-
99
- if (pkgJson.zenstack && typeof pkgJson.zenstack === 'object') {
100
- result.schema =
101
- pkgJson.zenstack.schema && typeof pkgJson.zenstack.schema === 'string'
102
- ? path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema)
103
- : undefined;
104
- result.output =
105
- pkgJson.zenstack.output && typeof pkgJson.zenstack.output === 'string'
106
- ? path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output)
107
- : undefined;
108
- result.seed =
109
- typeof pkgJson.zenstack.seed === 'string' && pkgJson.zenstack.seed ? pkgJson.zenstack.seed : undefined;
110
- }
111
-
112
- return result;
113
- }
114
-
115
- type FindUpResult<Multiple extends boolean> = Multiple extends true ? string[] | undefined : string | undefined;
116
-
117
- function findUp<Multiple extends boolean = false>(
118
- names: string[],
119
- cwd: string = process.cwd(),
120
- multiple: Multiple = false as Multiple,
121
- result: string[] = [],
122
- ): FindUpResult<Multiple> {
123
- if (!names.some((name) => !!name)) {
124
- return undefined;
125
- }
126
- const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
127
- if (multiple === false && target) {
128
- return path.join(cwd, target) as FindUpResult<Multiple>;
129
- }
130
- if (target) {
131
- result.push(path.join(cwd, target));
132
- }
133
- const up = path.resolve(cwd, '..');
134
- if (up === cwd) {
135
- return (multiple && result.length > 0 ? result : undefined) as FindUpResult<Multiple>;
136
- }
137
- return findUp(names, up, multiple, result);
138
- }
139
-
140
- export async function requireDataSourceUrl(schemaFile: string) {
141
- const zmodel = await loadSchemaDocument(schemaFile);
142
- const dataSource = zmodel.declarations.find(isDataSource);
143
- if (!dataSource?.fields.some((f) => f.name === 'url')) {
144
- throw new CliError('The schema\'s "datasource" must have a "url" field to use this command.');
145
- }
146
- }
@@ -1,22 +0,0 @@
1
- import colors from 'colors';
2
- import { getSchemaFile, loadSchemaDocument } from './action-utils';
3
-
4
- type Options = {
5
- schema?: string;
6
- };
7
-
8
- /**
9
- * CLI action for checking a schema's validity.
10
- */
11
- export async function run(options: Options) {
12
- const schemaFile = getSchemaFile(options.schema);
13
-
14
- try {
15
- await loadSchemaDocument(schemaFile);
16
- console.log(colors.green('✓ Schema validation completed successfully.'));
17
- } catch (error) {
18
- console.error(colors.red('✗ Schema validation failed.'));
19
- // Re-throw to maintain CLI exit code behavior
20
- throw error;
21
- }
22
- }
package/src/actions/db.ts DELETED
@@ -1,51 +0,0 @@
1
- import fs from 'node:fs';
2
- import { execPrisma } from '../utils/exec-utils';
3
- import { generateTempPrismaSchema, getSchemaFile, handleSubProcessError, requireDataSourceUrl } from './action-utils';
4
-
5
- type Options = {
6
- schema?: string;
7
- acceptDataLoss?: boolean;
8
- forceReset?: boolean;
9
- };
10
-
11
- /**
12
- * CLI action for db related commands
13
- */
14
- export async function run(command: string, options: Options) {
15
- switch (command) {
16
- case 'push':
17
- await runPush(options);
18
- break;
19
- }
20
- }
21
-
22
- async function runPush(options: Options) {
23
- const schemaFile = getSchemaFile(options.schema);
24
-
25
- // validate datasource url exists
26
- await requireDataSourceUrl(schemaFile);
27
-
28
- // generate a temp prisma schema file
29
- const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
30
-
31
- try {
32
- // run prisma db push
33
- const cmd = [
34
- 'db push',
35
- ` --schema "${prismaSchemaFile}"`,
36
- options.acceptDataLoss ? ' --accept-data-loss' : '',
37
- options.forceReset ? ' --force-reset' : '',
38
- ' --skip-generate',
39
- ].join('');
40
-
41
- try {
42
- execPrisma(cmd);
43
- } catch (err) {
44
- handleSubProcessError(err);
45
- }
46
- } finally {
47
- if (fs.existsSync(prismaSchemaFile)) {
48
- fs.unlinkSync(prismaSchemaFile);
49
- }
50
- }
51
- }
@@ -1,27 +0,0 @@
1
- import { formatDocument } from '@zenstackhq/language';
2
- import colors from 'colors';
3
- import fs from 'node:fs';
4
- import { getSchemaFile } from './action-utils';
5
-
6
- type Options = {
7
- schema?: string;
8
- };
9
-
10
- /**
11
- * CLI action for formatting a ZModel schema file.
12
- */
13
- export async function run(options: Options) {
14
- const schemaFile = getSchemaFile(options.schema);
15
- let formattedContent: string;
16
-
17
- try {
18
- formattedContent = await formatDocument(fs.readFileSync(schemaFile, 'utf-8'));
19
- } catch (error) {
20
- console.error(colors.red('✗ Schema formatting failed.'));
21
- // Re-throw to maintain CLI exit code behavior
22
- throw error;
23
- }
24
-
25
- fs.writeFileSync(schemaFile, formattedContent, 'utf-8');
26
- console.log(colors.green('✓ Schema formatting completed successfully.'));
27
- }
@@ -1,226 +0,0 @@
1
- import { invariant } from '@zenstackhq/common-helpers';
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';
5
- import colors from 'colors';
6
- import { createJiti } from 'jiti';
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
- import { pathToFileURL } from 'node:url';
10
- import ora, { type Ora } from 'ora';
11
- import { CliError } from '../cli-error';
12
- import * as corePlugins from '../plugins';
13
- import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils';
14
-
15
- type Options = {
16
- schema?: string;
17
- output?: string;
18
- silent: boolean;
19
- lite: boolean;
20
- liteOnly: boolean;
21
- };
22
-
23
- /**
24
- * CLI action for generating code from schema
25
- */
26
- export async function run(options: Options) {
27
- const start = Date.now();
28
-
29
- const schemaFile = getSchemaFile(options.schema);
30
-
31
- const model = await loadSchemaDocument(schemaFile);
32
- const outputPath = getOutputPath(options, schemaFile);
33
-
34
- await runPlugins(schemaFile, model, outputPath, options);
35
-
36
- if (!options.silent) {
37
- console.log(colors.green(`Generation completed successfully in ${Date.now() - start}ms.\n`));
38
- console.log(`You can now create a ZenStack client with it.
39
-
40
- \`\`\`ts
41
- import { ZenStackClient } from '@zenstackhq/orm';
42
- import { schema } from '${path.relative('.', outputPath)}/schema';
43
-
44
- const client = new ZenStackClient(schema, {
45
- dialect: { ... }
46
- });
47
- \`\`\`
48
-
49
- Check documentation: https://zenstack.dev/docs/`);
50
- }
51
- }
52
-
53
- function getOutputPath(options: Options, schemaFile: string) {
54
- if (options.output) {
55
- return options.output;
56
- }
57
- const pkgJsonConfig = getPkgJsonConfig(process.cwd());
58
- if (pkgJsonConfig.output) {
59
- return pkgJsonConfig.output;
60
- } else {
61
- return path.dirname(schemaFile);
62
- }
63
- }
64
-
65
- async function runPlugins(schemaFile: string, model: Model, outputPath: string, options: Options) {
66
- const plugins = model.declarations.filter(isPlugin);
67
- const processedPlugins: { cliPlugin: CliPlugin; pluginOptions: Record<string, unknown> }[] = [];
68
-
69
- for (const plugin of plugins) {
70
- const provider = getPluginProvider(plugin);
71
-
72
- let cliPlugin: CliPlugin | undefined;
73
- if (provider.startsWith('@core/')) {
74
- cliPlugin = (corePlugins as any)[provider.slice('@core/'.length)];
75
- if (!cliPlugin) {
76
- throw new CliError(`Unknown core plugin: ${provider}`);
77
- }
78
- } else {
79
- cliPlugin = await loadPluginModule(provider, path.dirname(schemaFile));
80
- }
81
-
82
- if (cliPlugin) {
83
- const pluginOptions = getPluginOptions(plugin);
84
-
85
- // merge CLI options
86
- if (provider === '@core/typescript') {
87
- if (pluginOptions['lite'] === undefined) {
88
- pluginOptions['lite'] = options.lite;
89
- }
90
- if (pluginOptions['liteOnly'] === undefined) {
91
- pluginOptions['liteOnly'] = options.liteOnly;
92
- }
93
- }
94
-
95
- processedPlugins.push({ cliPlugin, pluginOptions });
96
- }
97
- }
98
-
99
- const defaultPlugins = [
100
- {
101
- plugin: corePlugins['typescript'],
102
- options: { lite: options.lite, liteOnly: options.liteOnly },
103
- },
104
- ];
105
- defaultPlugins.forEach(({ plugin, options }) => {
106
- if (!processedPlugins.some((p) => p.cliPlugin === plugin)) {
107
- // default plugins are run before user plugins
108
- processedPlugins.unshift({ cliPlugin: plugin, pluginOptions: options });
109
- }
110
- });
111
-
112
- for (const { cliPlugin, pluginOptions } of processedPlugins) {
113
- invariant(
114
- typeof cliPlugin.generate === 'function',
115
- `Plugin ${cliPlugin.name} does not have a generate function`,
116
- );
117
-
118
- // run plugin generator
119
- let spinner: Ora | undefined;
120
-
121
- if (!options.silent) {
122
- spinner = ora(cliPlugin.statusText ?? `Running plugin ${cliPlugin.name}`).start();
123
- }
124
- try {
125
- await cliPlugin.generate({
126
- schemaFile,
127
- model,
128
- defaultOutputPath: outputPath,
129
- pluginOptions,
130
- });
131
- spinner?.succeed();
132
- } catch (err) {
133
- spinner?.fail();
134
- console.error(err);
135
- }
136
- }
137
- }
138
-
139
- function getPluginProvider(plugin: Plugin) {
140
- const providerField = plugin.fields.find((f) => f.name === 'provider');
141
- invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
142
- const provider = (providerField.value as LiteralExpr).value as string;
143
- return provider;
144
- }
145
-
146
- function getPluginOptions(plugin: Plugin): Record<string, unknown> {
147
- const result: Record<string, unknown> = {};
148
- for (const field of plugin.fields) {
149
- if (field.name === 'provider') {
150
- continue; // skip provider
151
- }
152
- const value = getLiteral(field.value) ?? getLiteralArray(field.value);
153
- if (value === undefined) {
154
- console.warn(`Plugin "${plugin.name}" option "${field.name}" has unsupported value, skipping`);
155
- continue;
156
- }
157
- result[field.name] = value;
158
- }
159
- return result;
160
- }
161
-
162
- async function loadPluginModule(provider: string, basePath: string) {
163
- let moduleSpec = provider;
164
- if (moduleSpec.startsWith('.')) {
165
- // relative to schema's path
166
- moduleSpec = path.resolve(basePath, moduleSpec);
167
- }
168
-
169
- const importAsEsm = async (spec: string) => {
170
- try {
171
- const result = (await import(spec)).default as CliPlugin;
172
- return result;
173
- } catch (err) {
174
- throw new CliError(`Failed to load plugin module from ${spec}: ${(err as Error).message}`);
175
- }
176
- };
177
-
178
- const jiti = createJiti(pathToFileURL(basePath).toString());
179
- const importAsTs = async (spec: string) => {
180
- try {
181
- const result = (await jiti.import(spec, { default: true })) as CliPlugin;
182
- return result;
183
- } catch (err) {
184
- throw new CliError(`Failed to load plugin module from ${spec}: ${(err as Error).message}`);
185
- }
186
- };
187
-
188
- const esmSuffixes = ['.js', '.mjs'];
189
- const tsSuffixes = ['.ts', '.mts'];
190
-
191
- if (fs.existsSync(moduleSpec) && fs.statSync(moduleSpec).isFile()) {
192
- // try provider as ESM file
193
- if (esmSuffixes.some((suffix) => moduleSpec.endsWith(suffix))) {
194
- return await importAsEsm(pathToFileURL(moduleSpec).toString());
195
- }
196
-
197
- // try provider as TS file
198
- if (tsSuffixes.some((suffix) => moduleSpec.endsWith(suffix))) {
199
- return await importAsTs(moduleSpec);
200
- }
201
- }
202
-
203
- // try ESM index files in provider directory
204
- for (const suffix of esmSuffixes) {
205
- const indexPath = path.join(moduleSpec, `index${suffix}`);
206
- if (fs.existsSync(indexPath)) {
207
- return await importAsEsm(pathToFileURL(indexPath).toString());
208
- }
209
- }
210
-
211
- // try TS index files in provider directory
212
- for (const suffix of tsSuffixes) {
213
- const indexPath = path.join(moduleSpec, `index${suffix}`);
214
- if (fs.existsSync(indexPath)) {
215
- return await importAsTs(indexPath);
216
- }
217
- }
218
-
219
- // last resort, try to import as esm directly
220
- try {
221
- return (await import(moduleSpec)).default as CliPlugin;
222
- } catch {
223
- // plugin may not export a generator so we simply ignore the error here
224
- return undefined;
225
- }
226
- }
@@ -1,10 +0,0 @@
1
- import { run as check } from './check';
2
- import { run as db } from './db';
3
- import { run as format } from './format';
4
- import { run as generate } from './generate';
5
- import { run as info } from './info';
6
- import { run as init } from './init';
7
- import { run as migrate } from './migrate';
8
- import { run as seed } from './seed';
9
-
10
- export { check, db, format, generate, info, init, migrate, seed };
@@ -1,71 +0,0 @@
1
- import colors from 'colors';
2
- import path from 'node:path';
3
-
4
- /**
5
- * CLI action for getting information about installed ZenStack packages
6
- */
7
- export async function run(projectPath: string) {
8
- const packages = await getZenStackPackages(projectPath);
9
- if (!packages) {
10
- console.error('Unable to locate package.json. Are you in a valid project directory?');
11
- return;
12
- }
13
-
14
- console.log('Installed ZenStack Packages:');
15
- const versions = new Set<string>();
16
- for (const { pkg, version } of packages) {
17
- if (version) {
18
- versions.add(version);
19
- }
20
- console.log(` ${colors.green(pkg.padEnd(20))}\t${version}`);
21
- }
22
-
23
- if (versions.size > 1) {
24
- console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
25
- }
26
- }
27
-
28
- async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: string; version: string | undefined }>> {
29
- let pkgJson: {
30
- dependencies: Record<string, unknown>;
31
- devDependencies: Record<string, unknown>;
32
- };
33
- const resolvedPath = path.resolve(projectPath);
34
- try {
35
- pkgJson = (
36
- await import(path.join(resolvedPath, 'package.json'), {
37
- with: { type: 'json' },
38
- })
39
- ).default;
40
- } catch {
41
- return [];
42
- }
43
-
44
- const packages = Array.from(
45
- new Set(
46
- [...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter(
47
- (p) => p.startsWith('@zenstackhq/') || p === 'zenstack',
48
- ),
49
- ),
50
- ).sort();
51
-
52
- const result = await Promise.all(
53
- packages.map(async (pkg) => {
54
- try {
55
- const depPkgJson = (
56
- await import(`${pkg}/package.json`, {
57
- with: { type: 'json' },
58
- })
59
- ).default;
60
- if (depPkgJson.private) {
61
- return undefined;
62
- }
63
- return { pkg, version: depPkgJson.version as string };
64
- } catch {
65
- return { pkg, version: undefined };
66
- }
67
- }),
68
- );
69
-
70
- return result.filter((p) => !!p);
71
- }
@@ -1,61 +0,0 @@
1
- import colors from 'colors';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import ora from 'ora';
5
- import { detect, resolveCommand } from 'package-manager-detector';
6
- import { CliError } from '../cli-error';
7
- import { execSync } from '../utils/exec-utils';
8
- import { STARTER_ZMODEL } from './templates';
9
-
10
- /**
11
- * CLI action for getting information about installed ZenStack packages
12
- */
13
- export async function run(projectPath: string) {
14
- const packages = [
15
- { name: '@zenstackhq/cli@latest', dev: true },
16
- { name: '@zenstackhq/orm@latest', dev: false },
17
- ];
18
- let pm = await detect();
19
- if (!pm) {
20
- pm = { agent: 'npm', name: 'npm' };
21
- }
22
-
23
- console.log(colors.gray(`Using package manager: ${pm.agent}`));
24
-
25
- for (const pkg of packages) {
26
- const resolved = resolveCommand(pm.agent, 'add', [
27
- pkg.name,
28
- ...(pkg.dev ? [pm.agent.startsWith('yarn') || pm.agent === 'bun' ? '--dev' : '--save-dev'] : []),
29
- ]);
30
- if (!resolved) {
31
- throw new CliError(`Unable to determine how to install package "${pkg.name}". Please install it manually.`);
32
- }
33
-
34
- const spinner = ora(`Installing "${pkg.name}"`).start();
35
- try {
36
- execSync(`${resolved.command} ${resolved.args.join(' ')}`, {
37
- cwd: projectPath,
38
- });
39
- spinner.succeed();
40
- } catch (e) {
41
- spinner.fail();
42
- throw e;
43
- }
44
- }
45
-
46
- const generationFolder = 'zenstack';
47
-
48
- if (!fs.existsSync(path.join(projectPath, generationFolder))) {
49
- fs.mkdirSync(path.join(projectPath, generationFolder));
50
- }
51
-
52
- if (!fs.existsSync(path.join(projectPath, generationFolder, 'schema.zmodel'))) {
53
- fs.writeFileSync(path.join(projectPath, generationFolder, 'schema.zmodel'), STARTER_ZMODEL);
54
- } else {
55
- console.log(colors.yellow('Schema file already exists. Skipping generation of sample.'));
56
- }
57
-
58
- console.log(colors.green('ZenStack project initialized successfully!'));
59
- console.log(colors.gray(`See "${generationFolder}/schema.zmodel" for your database schema.`));
60
- console.log(colors.gray('Run `zenstack generate` to compile the the schema into a TypeScript file.'));
61
- }