@zenstackhq/cli 3.0.0-alpha.4 → 3.0.0-alpha.7
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 +22 -23
- package/dist/index.cjs +103 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +103 -153
- package/dist/index.js.map +1 -1
- package/eslint.config.js +4 -0
- package/package.json +12 -15
- package/src/actions/action-utils.ts +14 -8
- package/src/actions/db.ts +27 -29
- package/src/actions/generate.ts +15 -15
- package/src/actions/info.ts +9 -18
- package/src/actions/init.ts +6 -27
- package/src/actions/migrate.ts +50 -58
- package/src/index.ts +18 -39
- package/src/utils/exec-utils.ts +2 -5
- package/src/utils/version-utils.ts +9 -9
- package/test/db.test.ts +18 -0
- package/test/generate.test.ts +44 -0
- package/test/init.test.ts +13 -0
- package/test/migrate.test.ts +41 -0
- package/test/utils.ts +23 -0
- package/tsconfig.json +1 -1
- package/.turbo/turbo-lint.log +0 -18
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { CliError } from '../cli-error';
|
|
3
1
|
import { loadDocument } from '@zenstackhq/language';
|
|
2
|
+
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
|
|
4
3
|
import colors from 'colors';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { CliError } from '../cli-error';
|
|
5
7
|
|
|
6
8
|
export function getSchemaFile(file?: string) {
|
|
7
9
|
if (file) {
|
|
@@ -17,7 +19,7 @@ export function getSchemaFile(file?: string) {
|
|
|
17
19
|
return './schema.zmodel';
|
|
18
20
|
} else {
|
|
19
21
|
throw new CliError(
|
|
20
|
-
'Schema file not found in default locations ("./zenstack/schema.zmodel" or "./schema.zmodel").'
|
|
22
|
+
'Schema file not found in default locations ("./zenstack/schema.zmodel" or "./schema.zmodel").',
|
|
21
23
|
);
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -35,13 +37,17 @@ export async function loadSchemaDocument(schemaFile: string) {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
export function handleSubProcessError(err: unknown) {
|
|
38
|
-
if (
|
|
39
|
-
err instanceof Error &&
|
|
40
|
-
'status' in err &&
|
|
41
|
-
typeof err.status === 'number'
|
|
42
|
-
) {
|
|
40
|
+
if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
|
|
43
41
|
process.exit(err.status);
|
|
44
42
|
} else {
|
|
45
43
|
process.exit(1);
|
|
46
44
|
}
|
|
47
45
|
}
|
|
46
|
+
|
|
47
|
+
export async function generateTempPrismaSchema(zmodelPath: string) {
|
|
48
|
+
const model = await loadSchemaDocument(zmodelPath);
|
|
49
|
+
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
|
|
50
|
+
const prismaSchemaFile = path.resolve(path.dirname(zmodelPath), '~schema.prisma');
|
|
51
|
+
fs.writeFileSync(prismaSchemaFile, prismaSchema);
|
|
52
|
+
return prismaSchemaFile;
|
|
53
|
+
}
|
package/src/actions/db.ts
CHANGED
|
@@ -1,46 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
import { execPackage } from '../utils/exec-utils';
|
|
3
|
-
import { getSchemaFile, handleSubProcessError } from './action-utils';
|
|
4
|
-
import { run as runGenerate } from './generate';
|
|
3
|
+
import { generateTempPrismaSchema, getSchemaFile, handleSubProcessError } from './action-utils';
|
|
5
4
|
|
|
6
|
-
type
|
|
5
|
+
type Options = {
|
|
7
6
|
schema?: string;
|
|
8
|
-
|
|
7
|
+
acceptDataLoss?: boolean;
|
|
8
|
+
forceReset?: boolean;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* CLI action for db related commands
|
|
13
13
|
*/
|
|
14
|
-
export async function run(command: string, options:
|
|
15
|
-
const schemaFile = getSchemaFile(options.schema);
|
|
16
|
-
|
|
17
|
-
// run generate first
|
|
18
|
-
await runGenerate({
|
|
19
|
-
schema: schemaFile,
|
|
20
|
-
silent: true,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const prismaSchemaFile = path.join(
|
|
24
|
-
path.dirname(schemaFile),
|
|
25
|
-
'schema.prisma'
|
|
26
|
-
);
|
|
27
|
-
|
|
14
|
+
export async function run(command: string, options: Options) {
|
|
28
15
|
switch (command) {
|
|
29
16
|
case 'push':
|
|
30
|
-
await runPush(
|
|
17
|
+
await runPush(options);
|
|
31
18
|
break;
|
|
32
19
|
}
|
|
33
20
|
}
|
|
34
21
|
|
|
35
|
-
async function runPush(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
async function runPush(options: Options) {
|
|
23
|
+
// generate a temp prisma schema file
|
|
24
|
+
const schemaFile = getSchemaFile(options.schema);
|
|
25
|
+
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
|
|
26
|
+
|
|
39
27
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
// run prisma db push
|
|
29
|
+
const cmd = `prisma db push --schema "${prismaSchemaFile}"${
|
|
30
|
+
options.acceptDataLoss ? ' --accept-data-loss' : ''
|
|
31
|
+
}${options.forceReset ? ' --force-reset' : ''} --skip-generate`;
|
|
32
|
+
try {
|
|
33
|
+
await execPackage(cmd, {
|
|
34
|
+
stdio: 'inherit',
|
|
35
|
+
});
|
|
36
|
+
} catch (err) {
|
|
37
|
+
handleSubProcessError(err);
|
|
38
|
+
}
|
|
39
|
+
} finally {
|
|
40
|
+
if (fs.existsSync(prismaSchemaFile)) {
|
|
41
|
+
fs.unlinkSync(prismaSchemaFile);
|
|
42
|
+
}
|
|
45
43
|
}
|
|
46
44
|
}
|
package/src/actions/generate.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
import { invariant } from '@zenstackhq/common-helpers';
|
|
1
2
|
import { isPlugin, LiteralExpr, type Model } from '@zenstackhq/language/ast';
|
|
2
|
-
import type
|
|
3
|
-
import { PrismaSchemaGenerator, TsSchemaGenerator } from '@zenstackhq/sdk';
|
|
3
|
+
import { PrismaSchemaGenerator, TsSchemaGenerator, type CliGenerator } from '@zenstackhq/sdk';
|
|
4
4
|
import colors from 'colors';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import invariant from 'tiny-invariant';
|
|
8
7
|
import { getSchemaFile, loadSchemaDocument } from './action-utils';
|
|
9
8
|
|
|
10
9
|
type Options = {
|
|
11
10
|
schema?: string;
|
|
12
11
|
output?: string;
|
|
13
12
|
silent?: boolean;
|
|
13
|
+
savePrismaSchema?: string | boolean;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -29,8 +29,15 @@ export async function run(options: Options) {
|
|
|
29
29
|
await runPlugins(model, outputPath, tsSchemaFile);
|
|
30
30
|
|
|
31
31
|
// generate Prisma schema
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
}
|
|
34
41
|
|
|
35
42
|
if (!options.silent) {
|
|
36
43
|
console.log(colors.green('Generation completed successfully.'));
|
|
@@ -41,25 +48,18 @@ import { ZenStackClient } from '@zenstackhq/runtime';
|
|
|
41
48
|
import { schema } from '${outputPath}/schema';
|
|
42
49
|
|
|
43
50
|
const client = new ZenStackClient(schema, {
|
|
44
|
-
dialectConfig: { ... }
|
|
51
|
+
dialectConfig: { ... }
|
|
45
52
|
});
|
|
46
53
|
\`\`\`
|
|
47
54
|
`);
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
async function runPlugins(
|
|
52
|
-
model: Model,
|
|
53
|
-
outputPath: string,
|
|
54
|
-
tsSchemaFile: string
|
|
55
|
-
) {
|
|
58
|
+
async function runPlugins(model: Model, outputPath: string, tsSchemaFile: string) {
|
|
56
59
|
const plugins = model.declarations.filter(isPlugin);
|
|
57
60
|
for (const plugin of plugins) {
|
|
58
61
|
const providerField = plugin.fields.find((f) => f.name === 'provider');
|
|
59
|
-
invariant(
|
|
60
|
-
providerField,
|
|
61
|
-
`Plugin ${plugin.name} does not have a provider field`
|
|
62
|
-
);
|
|
62
|
+
invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
|
|
63
63
|
const provider = (providerField.value as LiteralExpr).value as string;
|
|
64
64
|
let useProvider = provider;
|
|
65
65
|
if (useProvider.startsWith('@core/')) {
|
package/src/actions/info.ts
CHANGED
|
@@ -7,9 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
export async function run(projectPath: string) {
|
|
8
8
|
const packages = await getZenStackPackages(projectPath);
|
|
9
9
|
if (!packages) {
|
|
10
|
-
console.error(
|
|
11
|
-
'Unable to locate package.json. Are you in a valid project directory?'
|
|
12
|
-
);
|
|
10
|
+
console.error('Unable to locate package.json. Are you in a valid project directory?');
|
|
13
11
|
return;
|
|
14
12
|
}
|
|
15
13
|
|
|
@@ -23,17 +21,11 @@ export async function run(projectPath: string) {
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
if (versions.size > 1) {
|
|
26
|
-
console.warn(
|
|
27
|
-
colors.yellow(
|
|
28
|
-
'WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'
|
|
29
|
-
)
|
|
30
|
-
);
|
|
24
|
+
console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
|
|
31
25
|
}
|
|
32
26
|
}
|
|
33
27
|
|
|
34
|
-
async function getZenStackPackages(
|
|
35
|
-
projectPath: string
|
|
36
|
-
): Promise<Array<{ pkg: string; version: string | undefined }>> {
|
|
28
|
+
async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: string; version: string | undefined }>> {
|
|
37
29
|
let pkgJson: {
|
|
38
30
|
dependencies: Record<string, unknown>;
|
|
39
31
|
devDependencies: Record<string, unknown>;
|
|
@@ -45,17 +37,16 @@ async function getZenStackPackages(
|
|
|
45
37
|
with: { type: 'json' },
|
|
46
38
|
})
|
|
47
39
|
).default;
|
|
48
|
-
} catch
|
|
40
|
+
} catch {
|
|
49
41
|
return [];
|
|
50
42
|
}
|
|
51
43
|
|
|
52
44
|
const packages = Array.from(
|
|
53
45
|
new Set(
|
|
54
|
-
[
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
46
|
+
[...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter(
|
|
47
|
+
(p) => p.startsWith('@zenstackhq/') || p === 'zenstack',
|
|
48
|
+
),
|
|
49
|
+
),
|
|
59
50
|
).sort();
|
|
60
51
|
|
|
61
52
|
const result = await Promise.all(
|
|
@@ -70,7 +61,7 @@ async function getZenStackPackages(
|
|
|
70
61
|
} catch {
|
|
71
62
|
return { pkg, version: undefined };
|
|
72
63
|
}
|
|
73
|
-
})
|
|
64
|
+
}),
|
|
74
65
|
);
|
|
75
66
|
|
|
76
67
|
return result;
|
package/src/actions/init.ts
CHANGED
|
@@ -28,9 +28,7 @@ export async function run(projectPath: string) {
|
|
|
28
28
|
...(pkg.dev ? [pm.agent === 'yarn' ? '--dev' : '--save-dev'] : []),
|
|
29
29
|
]);
|
|
30
30
|
if (!resolved) {
|
|
31
|
-
throw new CliError(
|
|
32
|
-
`Unable to determine how to install package "${pkg.name}". Please install it manually.`
|
|
33
|
-
);
|
|
31
|
+
throw new CliError(`Unable to determine how to install package "${pkg.name}". Please install it manually.`);
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
const spinner = ora(`Installing "${pkg.name}"`).start();
|
|
@@ -51,32 +49,13 @@ export async function run(projectPath: string) {
|
|
|
51
49
|
fs.mkdirSync(path.join(projectPath, generationFolder));
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
path.join(projectPath, generationFolder, 'schema.zmodel')
|
|
57
|
-
)
|
|
58
|
-
) {
|
|
59
|
-
fs.writeFileSync(
|
|
60
|
-
path.join(projectPath, generationFolder, 'schema.zmodel'),
|
|
61
|
-
STARTER_ZMODEL
|
|
62
|
-
);
|
|
52
|
+
if (!fs.existsSync(path.join(projectPath, generationFolder, 'schema.zmodel'))) {
|
|
53
|
+
fs.writeFileSync(path.join(projectPath, generationFolder, 'schema.zmodel'), STARTER_ZMODEL);
|
|
63
54
|
} else {
|
|
64
|
-
console.log(
|
|
65
|
-
colors.yellow(
|
|
66
|
-
'Schema file already exists. Skipping generation of sample.'
|
|
67
|
-
)
|
|
68
|
-
);
|
|
55
|
+
console.log(colors.yellow('Schema file already exists. Skipping generation of sample.'));
|
|
69
56
|
}
|
|
70
57
|
|
|
71
58
|
console.log(colors.green('ZenStack project initialized successfully!'));
|
|
72
|
-
console.log(
|
|
73
|
-
|
|
74
|
-
`See "${generationFolder}/schema.zmodel" for your database schema.`
|
|
75
|
-
)
|
|
76
|
-
);
|
|
77
|
-
console.log(
|
|
78
|
-
colors.gray(
|
|
79
|
-
'Run `zenstack generate` to compile the the schema into a TypeScript file.'
|
|
80
|
-
)
|
|
81
|
-
);
|
|
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.'));
|
|
82
61
|
}
|
package/src/actions/migrate.ts
CHANGED
|
@@ -1,109 +1,101 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
import { execPackage } from '../utils/exec-utils';
|
|
3
|
-
import { getSchemaFile } from './action-utils';
|
|
4
|
-
import { run as runGenerate } from './generate';
|
|
3
|
+
import { generateTempPrismaSchema, getSchemaFile } from './action-utils';
|
|
5
4
|
|
|
6
5
|
type CommonOptions = {
|
|
7
6
|
schema?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type DevOptions = CommonOptions & {
|
|
8
10
|
name?: string;
|
|
11
|
+
createOnly?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ResetOptions = CommonOptions & {
|
|
15
|
+
force?: boolean;
|
|
9
16
|
};
|
|
10
17
|
|
|
18
|
+
type DeployOptions = CommonOptions;
|
|
19
|
+
|
|
20
|
+
type StatusOptions = CommonOptions;
|
|
21
|
+
|
|
11
22
|
/**
|
|
12
23
|
* CLI action for migration-related commands
|
|
13
24
|
*/
|
|
14
25
|
export async function run(command: string, options: CommonOptions) {
|
|
15
26
|
const schemaFile = getSchemaFile(options.schema);
|
|
27
|
+
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const prismaSchemaFile = path.join(
|
|
24
|
-
path.dirname(schemaFile),
|
|
25
|
-
'schema.prisma'
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
switch (command) {
|
|
29
|
-
case 'dev':
|
|
30
|
-
await runDev(prismaSchemaFile, options);
|
|
31
|
-
break;
|
|
29
|
+
try {
|
|
30
|
+
switch (command) {
|
|
31
|
+
case 'dev':
|
|
32
|
+
await runDev(prismaSchemaFile, options as DevOptions);
|
|
33
|
+
break;
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
case 'reset':
|
|
36
|
+
await runReset(prismaSchemaFile, options as ResetOptions);
|
|
37
|
+
break;
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
case 'deploy':
|
|
40
|
+
await runDeploy(prismaSchemaFile, options as DeployOptions);
|
|
41
|
+
break;
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
case 'status':
|
|
44
|
+
await runStatus(prismaSchemaFile, options as StatusOptions);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
if (fs.existsSync(prismaSchemaFile)) {
|
|
49
|
+
fs.unlinkSync(prismaSchemaFile);
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
async function runDev(prismaSchemaFile: string,
|
|
54
|
+
async function runDev(prismaSchemaFile: string, options: DevOptions) {
|
|
48
55
|
try {
|
|
49
56
|
await execPackage(
|
|
50
|
-
`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate`,
|
|
57
|
+
`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate${options.name ? ` --name ${options.name}` : ''}${options.createOnly ? ' --create-only' : ''}`,
|
|
51
58
|
{
|
|
52
59
|
stdio: 'inherit',
|
|
53
|
-
}
|
|
60
|
+
},
|
|
54
61
|
);
|
|
55
62
|
} catch (err) {
|
|
56
63
|
handleSubProcessError(err);
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
async function runReset(prismaSchemaFile: string, options:
|
|
67
|
+
async function runReset(prismaSchemaFile: string, options: ResetOptions) {
|
|
61
68
|
try {
|
|
62
|
-
await execPackage(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}`,
|
|
66
|
-
{
|
|
67
|
-
stdio: 'inherit',
|
|
68
|
-
}
|
|
69
|
-
);
|
|
69
|
+
await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? ' --force' : ''}`, {
|
|
70
|
+
stdio: 'inherit',
|
|
71
|
+
});
|
|
70
72
|
} catch (err) {
|
|
71
73
|
handleSubProcessError(err);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
async function runDeploy(prismaSchemaFile: string, _options:
|
|
77
|
+
async function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
|
|
76
78
|
try {
|
|
77
|
-
await execPackage(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
stdio: 'inherit',
|
|
81
|
-
}
|
|
82
|
-
);
|
|
79
|
+
await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
|
|
80
|
+
stdio: 'inherit',
|
|
81
|
+
});
|
|
83
82
|
} catch (err) {
|
|
84
83
|
handleSubProcessError(err);
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
async function runStatus(prismaSchemaFile: string, _options:
|
|
87
|
+
async function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
|
|
89
88
|
try {
|
|
90
|
-
await execPackage(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
stdio: 'inherit',
|
|
94
|
-
}
|
|
95
|
-
);
|
|
89
|
+
await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`, {
|
|
90
|
+
stdio: 'inherit',
|
|
91
|
+
});
|
|
96
92
|
} catch (err) {
|
|
97
93
|
handleSubProcessError(err);
|
|
98
94
|
}
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
function handleSubProcessError(err: unknown) {
|
|
102
|
-
if (
|
|
103
|
-
err instanceof Error &&
|
|
104
|
-
'status' in err &&
|
|
105
|
-
typeof err.status === 'number'
|
|
106
|
-
) {
|
|
98
|
+
if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
|
|
107
99
|
process.exit(err.status);
|
|
108
100
|
} else {
|
|
109
101
|
process.exit(1);
|
package/src/index.ts
CHANGED
|
@@ -4,9 +4,7 @@ import { Command, Option } from 'commander';
|
|
|
4
4
|
import * as actions from './actions';
|
|
5
5
|
import { getVersion } from './utils/version-utils';
|
|
6
6
|
|
|
7
|
-
const generateAction = async (
|
|
8
|
-
options: Parameters<typeof actions.generate>[0]
|
|
9
|
-
): Promise<void> => {
|
|
7
|
+
const generateAction = async (options: Parameters<typeof actions.generate>[0]): Promise<void> => {
|
|
10
8
|
await actions.generate(options);
|
|
11
9
|
};
|
|
12
10
|
|
|
@@ -36,60 +34,52 @@ export function createProgram() {
|
|
|
36
34
|
program
|
|
37
35
|
.description(
|
|
38
36
|
`${colors.bold.blue(
|
|
39
|
-
'ζ'
|
|
40
|
-
)} ZenStack is a Prisma power pack for building full-stack apps.\n\nDocumentation: https://zenstack.dev
|
|
37
|
+
'ζ',
|
|
38
|
+
)} ZenStack is a Prisma power pack for building full-stack apps.\n\nDocumentation: https://zenstack.dev.`,
|
|
41
39
|
)
|
|
42
40
|
.showHelpAfterError()
|
|
43
41
|
.showSuggestionAfterError();
|
|
44
42
|
|
|
45
43
|
const schemaOption = new Option(
|
|
46
44
|
'--schema <file>',
|
|
47
|
-
`schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json
|
|
45
|
+
`schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json.`,
|
|
48
46
|
);
|
|
49
47
|
|
|
50
48
|
program
|
|
51
49
|
.command('generate')
|
|
52
50
|
.description('Run code generation.')
|
|
53
51
|
.addOption(schemaOption)
|
|
52
|
+
.addOption(new Option('--silent', 'do not print any output'))
|
|
54
53
|
.addOption(
|
|
55
54
|
new Option(
|
|
56
|
-
'-
|
|
57
|
-
'default output directory
|
|
58
|
-
)
|
|
55
|
+
'--save-prisma-schema [path]',
|
|
56
|
+
'save a Prisma schema file, by default into the output directory',
|
|
57
|
+
),
|
|
59
58
|
)
|
|
59
|
+
.addOption(new Option('-o, --output <path>', 'default output directory for core plugins'))
|
|
60
60
|
.action(generateAction);
|
|
61
61
|
|
|
62
|
-
const migrateCommand = program
|
|
63
|
-
.command('migrate')
|
|
64
|
-
.description('Update the database schema with migrations.');
|
|
62
|
+
const migrateCommand = program.command('migrate').description('Update the database schema with migrations.');
|
|
65
63
|
|
|
66
64
|
migrateCommand
|
|
67
65
|
.command('dev')
|
|
68
66
|
.addOption(schemaOption)
|
|
69
67
|
.addOption(new Option('-n, --name <name>', 'migration name'))
|
|
70
|
-
.addOption(
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
.description(
|
|
74
|
-
'Create a migration from changes in schema and apply it to the database.'
|
|
75
|
-
)
|
|
68
|
+
.addOption(new Option('--create-only', 'only create migration, do not apply'))
|
|
69
|
+
.description('Create a migration from changes in schema and apply it to the database.')
|
|
76
70
|
.action((options) => migrateAction('dev', options));
|
|
77
71
|
|
|
78
72
|
migrateCommand
|
|
79
73
|
.command('reset')
|
|
80
74
|
.addOption(schemaOption)
|
|
81
75
|
.addOption(new Option('--force', 'skip the confirmation prompt'))
|
|
82
|
-
.description(
|
|
83
|
-
'Reset your database and apply all migrations, all data will be lost.'
|
|
84
|
-
)
|
|
76
|
+
.description('Reset your database and apply all migrations, all data will be lost.')
|
|
85
77
|
.action((options) => migrateAction('reset', options));
|
|
86
78
|
|
|
87
79
|
migrateCommand
|
|
88
80
|
.command('deploy')
|
|
89
81
|
.addOption(schemaOption)
|
|
90
|
-
.description(
|
|
91
|
-
'Deploy your pending migrations to your production/staging database.'
|
|
92
|
-
)
|
|
82
|
+
.description('Deploy your pending migrations to your production/staging database.')
|
|
93
83
|
.action((options) => migrateAction('deploy', options));
|
|
94
84
|
|
|
95
85
|
migrateCommand
|
|
@@ -98,30 +88,19 @@ export function createProgram() {
|
|
|
98
88
|
.description('check the status of your database migrations.')
|
|
99
89
|
.action((options) => migrateAction('status', options));
|
|
100
90
|
|
|
101
|
-
const dbCommand = program
|
|
102
|
-
.command('db')
|
|
103
|
-
.description('Manage your database schema during development.');
|
|
91
|
+
const dbCommand = program.command('db').description('Manage your database schema during development.');
|
|
104
92
|
|
|
105
93
|
dbCommand
|
|
106
94
|
.command('push')
|
|
107
95
|
.description('Push the state from your schema to your database')
|
|
108
96
|
.addOption(schemaOption)
|
|
109
|
-
.addOption(
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
.addOption(
|
|
113
|
-
new Option(
|
|
114
|
-
'--force-reset',
|
|
115
|
-
'force a reset of the database before push'
|
|
116
|
-
)
|
|
117
|
-
)
|
|
97
|
+
.addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
|
|
98
|
+
.addOption(new Option('--force-reset', 'force a reset of the database before push'))
|
|
118
99
|
.action((options) => dbAction('push', options));
|
|
119
100
|
|
|
120
101
|
program
|
|
121
102
|
.command('info')
|
|
122
|
-
.description(
|
|
123
|
-
'Get information of installed ZenStack and related packages.'
|
|
124
|
-
)
|
|
103
|
+
.description('Get information of installed ZenStack and related packages.')
|
|
125
104
|
.argument('[path]', 'project path', '.')
|
|
126
105
|
.action(infoAction);
|
|
127
106
|
|
package/src/utils/exec-utils.ts
CHANGED
|
@@ -3,10 +3,7 @@ import { execSync as _exec, type ExecSyncOptions } from 'child_process';
|
|
|
3
3
|
/**
|
|
4
4
|
* Utility for executing command synchronously and prints outputs on current console
|
|
5
5
|
*/
|
|
6
|
-
export function execSync(
|
|
7
|
-
cmd: string,
|
|
8
|
-
options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }
|
|
9
|
-
): void {
|
|
6
|
+
export function execSync(cmd: string, options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }): void {
|
|
10
7
|
const { env, ...restOptions } = options ?? {};
|
|
11
8
|
const mergedEnv = env ? { ...process.env, ...env } : undefined;
|
|
12
9
|
_exec(cmd, {
|
|
@@ -22,7 +19,7 @@ export function execSync(
|
|
|
22
19
|
*/
|
|
23
20
|
export function execPackage(
|
|
24
21
|
cmd: string,
|
|
25
|
-
options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }
|
|
22
|
+
options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> },
|
|
26
23
|
): void {
|
|
27
24
|
const packageManager = process?.versions?.['bun'] ? 'bunx' : 'npx';
|
|
28
25
|
execSync(`${packageManager} ${cmd}`, options);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
export function getVersion() {
|
|
3
6
|
try {
|
|
4
|
-
|
|
7
|
+
// isomorphic __dirname
|
|
8
|
+
const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
return JSON.parse(fs.readFileSync(path.join(_dirname, '../package.json'), 'utf8')).version;
|
|
5
10
|
} catch {
|
|
6
|
-
|
|
7
|
-
// dev environment
|
|
8
|
-
return require('../../package.json').version;
|
|
9
|
-
} catch {
|
|
10
|
-
return undefined;
|
|
11
|
-
}
|
|
11
|
+
return undefined;
|
|
12
12
|
}
|
|
13
13
|
}
|
package/test/db.test.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { createProject, runCli } from './utils';
|
|
5
|
+
|
|
6
|
+
const model = `
|
|
7
|
+
model User {
|
|
8
|
+
id String @id @default(cuid())
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
describe('CLI db commands test', () => {
|
|
13
|
+
it('should generate a database with db push', () => {
|
|
14
|
+
const workDir = createProject(model);
|
|
15
|
+
runCli('db push', workDir);
|
|
16
|
+
expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
});
|