@zenstackhq/cli 3.0.0-alpha.6 → 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.
@@ -1,57 +1,70 @@
1
- import path from 'node:path';
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
- // run generate first
18
- await runGenerate({
19
- schema: schemaFile,
20
- silent: true,
21
- });
22
-
23
- const prismaSchemaFile = path.join(path.dirname(schemaFile), 'schema.prisma');
24
-
25
- switch (command) {
26
- case 'dev':
27
- await runDev(prismaSchemaFile, options);
28
- break;
29
+ try {
30
+ switch (command) {
31
+ case 'dev':
32
+ await runDev(prismaSchemaFile, options as DevOptions);
33
+ break;
29
34
 
30
- case 'reset':
31
- await runReset(prismaSchemaFile, options as any);
32
- break;
35
+ case 'reset':
36
+ await runReset(prismaSchemaFile, options as ResetOptions);
37
+ break;
33
38
 
34
- case 'deploy':
35
- await runDeploy(prismaSchemaFile, options);
36
- break;
39
+ case 'deploy':
40
+ await runDeploy(prismaSchemaFile, options as DeployOptions);
41
+ break;
37
42
 
38
- case 'status':
39
- await runStatus(prismaSchemaFile, options);
40
- break;
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
+ }
41
51
  }
42
52
  }
43
53
 
44
- async function runDev(prismaSchemaFile: string, _options: unknown) {
54
+ async function runDev(prismaSchemaFile: string, options: DevOptions) {
45
55
  try {
46
- await execPackage(`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate`, {
47
- stdio: 'inherit',
48
- });
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
+ );
49
62
  } catch (err) {
50
63
  handleSubProcessError(err);
51
64
  }
52
65
  }
53
66
 
54
- async function runReset(prismaSchemaFile: string, options: { force: boolean }) {
67
+ async function runReset(prismaSchemaFile: string, options: ResetOptions) {
55
68
  try {
56
69
  await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? ' --force' : ''}`, {
57
70
  stdio: 'inherit',
@@ -61,7 +74,7 @@ async function runReset(prismaSchemaFile: string, options: { force: boolean }) {
61
74
  }
62
75
  }
63
76
 
64
- async function runDeploy(prismaSchemaFile: string, _options: unknown) {
77
+ async function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
65
78
  try {
66
79
  await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
67
80
  stdio: 'inherit',
@@ -71,7 +84,7 @@ async function runDeploy(prismaSchemaFile: string, _options: unknown) {
71
84
  }
72
85
  }
73
86
 
74
- async function runStatus(prismaSchemaFile: string, _options: unknown) {
87
+ async function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
75
88
  try {
76
89
  await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`, {
77
90
  stdio: 'inherit',
package/src/index.ts CHANGED
@@ -49,6 +49,13 @@ export function createProgram() {
49
49
  .command('generate')
50
50
  .description('Run code generation.')
51
51
  .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
+ )
52
59
  .addOption(new Option('-o, --output <path>', 'default output directory for core plugins'))
53
60
  .action(generateAction);
54
61
 
@@ -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
+ });
@@ -0,0 +1,44 @@
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 generate command test', () => {
13
+ it('should generate a TypeScript schema', () => {
14
+ const workDir = createProject(model);
15
+ runCli('generate', workDir);
16
+ expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
17
+ expect(fs.existsSync(path.join(workDir, 'zenstack/schema.prisma'))).toBe(false);
18
+ });
19
+
20
+ it('should respect custom output directory', () => {
21
+ const workDir = createProject(model);
22
+ runCli('generate --output ./zen', workDir);
23
+ expect(fs.existsSync(path.join(workDir, 'zen/schema.ts'))).toBe(true);
24
+ });
25
+
26
+ it('should respect custom schema location', () => {
27
+ const workDir = createProject(model);
28
+ fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'zenstack/foo.zmodel'));
29
+ runCli('generate --schema ./zenstack/foo.zmodel', workDir);
30
+ expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
31
+ });
32
+
33
+ it('should respect save prisma schema option', () => {
34
+ const workDir = createProject(model);
35
+ runCli('generate --save-prisma-schema', workDir);
36
+ expect(fs.existsSync(path.join(workDir, 'zenstack/schema.prisma'))).toBe(true);
37
+ });
38
+
39
+ it('should respect save prisma schema custom path option', () => {
40
+ const workDir = createProject(model);
41
+ runCli('generate --save-prisma-schema "../prisma/schema.prisma"', workDir);
42
+ expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true);
43
+ });
44
+ });
@@ -0,0 +1,13 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import tmp from 'tmp';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { runCli } from './utils';
6
+
7
+ describe('Cli init command tests', () => {
8
+ it('should create a new project', () => {
9
+ const { name: workDir } = tmp.dirSync({ unsafeCleanup: true });
10
+ runCli('init', workDir);
11
+ expect(fs.existsSync(path.join(workDir, 'zenstack/schema.zmodel'))).toBe(true);
12
+ });
13
+ });
@@ -0,0 +1,41 @@
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 migrate commands test', () => {
13
+ it('should generate a database with migrate dev', () => {
14
+ const workDir = createProject(model);
15
+ runCli('migrate dev --name init', workDir);
16
+ expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
17
+ expect(fs.existsSync(path.join(workDir, 'zenstack/migrations'))).toBe(true);
18
+ });
19
+
20
+ it('should reset the database with migrate reset', () => {
21
+ const workDir = createProject(model);
22
+ runCli('db push', workDir);
23
+ expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
24
+ runCli('migrate reset --force', workDir);
25
+ expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
26
+ });
27
+
28
+ it('should reset the database with migrate deploy', () => {
29
+ const workDir = createProject(model);
30
+ runCli('migrate dev --name init', workDir);
31
+ fs.rmSync(path.join(workDir, 'zenstack/dev.db'));
32
+ runCli('migrate deploy', workDir);
33
+ expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
34
+ });
35
+
36
+ it('supports migrate status', () => {
37
+ const workDir = createProject(model);
38
+ runCli('migrate dev --name init', workDir);
39
+ runCli('migrate status', workDir);
40
+ });
41
+ });
package/test/utils.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { createTestProject } from '@zenstackhq/testtools';
2
+ import { execSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ const ZMODEL_PRELUDE = `datasource db {
7
+ provider = "sqlite"
8
+ url = "file:./dev.db"
9
+ }
10
+ `;
11
+
12
+ export function createProject(zmodel: string, addPrelude = true) {
13
+ const workDir = createTestProject();
14
+ fs.mkdirSync(path.join(workDir, 'zenstack'), { recursive: true });
15
+ const schemaPath = path.join(workDir, 'zenstack/schema.zmodel');
16
+ fs.writeFileSync(schemaPath, addPrelude ? `${ZMODEL_PRELUDE}\n\n${zmodel}` : zmodel);
17
+ return workDir;
18
+ }
19
+
20
+ export function runCli(command: string, cwd: string) {
21
+ const cli = path.join(__dirname, '../dist/index.js');
22
+ execSync(`node ${cli} ${command}`, { cwd });
23
+ }