zenstack 0.3.10 → 0.3.11

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publisher": "zenstack",
4
4
  "displayName": "ZenStack Language Tools",
5
5
  "description": "A toolkit for modeling data and access policies in full-stack development with Next.js and Typescript",
6
- "version": "0.3.10",
6
+ "version": "0.3.11",
7
7
  "author": {
8
8
  "name": "ZenStack Team"
9
9
  },
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "main": "./bundle/extension.js",
67
67
  "dependencies": {
68
- "@zenstackhq/internal": "0.3.10",
68
+ "@zenstackhq/runtime": "0.3.11",
69
69
  "async-exit-hook": "^2.0.1",
70
70
  "change-case": "^4.1.2",
71
71
  "chevrotain": "^9.1.0",
@@ -6,8 +6,9 @@ import fs from 'fs';
6
6
  import { LangiumServices } from 'langium';
7
7
  import { NodeFileSystem } from 'langium/node';
8
8
  import path from 'path';
9
- import { ZenStackGenerator } from '../generator';
9
+ import { installPackage } from 'src/utils/pkg-utils';
10
10
  import { URI } from 'vscode-uri';
11
+ import { ZenStackGenerator } from '../generator';
11
12
  import { GENERATED_CODE_PATH } from '../generator/constants';
12
13
  import { Context, GeneratorError } from '../generator/types';
13
14
  import { CliError } from './cli-error';
@@ -17,19 +18,19 @@ import { CliError } from './cli-error';
17
18
  */
18
19
  export async function initProject(projectPath: string) {
19
20
  const schema = path.join(projectPath, 'zenstack', 'schema.zmodel');
21
+ let schemaGenerated = false;
22
+
20
23
  if (fs.existsSync(schema)) {
21
24
  console.warn(colors.yellow(`Model already exists: ${schema}`));
22
- throw new CliError(`schema file already exists`);
23
- }
24
-
25
- // create a default model
26
- if (!fs.existsSync(path.join(projectPath, 'zenstack'))) {
27
- fs.mkdirSync(path.join(projectPath, 'zenstack'));
28
- }
25
+ } else {
26
+ // create a default model
27
+ if (!fs.existsSync(path.join(projectPath, 'zenstack'))) {
28
+ fs.mkdirSync(path.join(projectPath, 'zenstack'));
29
+ }
29
30
 
30
- fs.writeFileSync(
31
- schema,
32
- `// This is a sample model to get you started.
31
+ fs.writeFileSync(
32
+ schema,
33
+ `// This is a sample model to get you started.
33
34
  // Learn how to model you app: https://zenstack.dev/#/modeling-your-app.
34
35
 
35
36
  /*
@@ -77,26 +78,34 @@ model Post {
77
78
  @@allow('all', author == auth())
78
79
  }
79
80
  `
80
- );
81
+ );
81
82
 
82
- // add zenstack/schema.prisma to .gitignore
83
- const gitIgnorePath = path.join(projectPath, '.gitignore');
84
- let gitIgnoreContent = '';
85
- if (fs.existsSync(gitIgnorePath)) {
86
- gitIgnoreContent =
87
- fs.readFileSync(gitIgnorePath, { encoding: 'utf-8' }) + '\n';
88
- }
83
+ // add zenstack/schema.prisma to .gitignore
84
+ const gitIgnorePath = path.join(projectPath, '.gitignore');
85
+ let gitIgnoreContent = '';
86
+ if (fs.existsSync(gitIgnorePath)) {
87
+ gitIgnoreContent =
88
+ fs.readFileSync(gitIgnorePath, { encoding: 'utf-8' }) + '\n';
89
+ }
89
90
 
90
- if (!gitIgnoreContent.includes('zenstack/schema.prisma')) {
91
- gitIgnoreContent += 'zenstack/schema.prisma\n';
92
- fs.writeFileSync(gitIgnorePath, gitIgnoreContent);
91
+ if (!gitIgnoreContent.includes('zenstack/schema.prisma')) {
92
+ gitIgnoreContent += 'zenstack/schema.prisma\n';
93
+ fs.writeFileSync(gitIgnorePath, gitIgnoreContent);
94
+ }
95
+
96
+ schemaGenerated = true;
93
97
  }
94
98
 
95
- console.log(`Sample model generated at: ${colors.green(schema)}
99
+ installPackage('zenstack', true, undefined, projectPath);
100
+ installPackage('@zenstackhq/runtime', false, undefined, projectPath);
101
+
102
+ if (schemaGenerated) {
103
+ console.log(`Sample model generated at: ${colors.green(schema)}
96
104
 
97
- Please check the following guide on how to model your app:
98
- https://zenstack.dev/#/modeling-your-app.
99
- `);
105
+ Please check the following guide on how to model your app:
106
+ https://zenstack.dev/#/modeling-your-app.
107
+ `);
108
+ }
100
109
  }
101
110
 
102
111
  /**
@@ -164,7 +173,7 @@ export async function loadDocument(
164
173
  }
165
174
 
166
175
  export async function runGenerator(
167
- options: { schema: string },
176
+ options: { schema: string; packageManager: string },
168
177
  includedGenerators?: string[],
169
178
  clearOutput = true
170
179
  ) {
package/src/cli/index.ts CHANGED
@@ -21,6 +21,7 @@ export const initAction = async (projectPath: string): Promise<void> => {
21
21
 
22
22
  export const generateAction = async (options: {
23
23
  schema: string;
24
+ packageManager: string;
24
25
  }): Promise<void> => {
25
26
  await telemetry.trackSpan(
26
27
  'cli:command:start',
@@ -116,11 +117,17 @@ export default async function (): Promise<void> {
116
117
  `schema file (with extension ${schemaExtensions})`
117
118
  ).default('./zenstack/schema.zmodel');
118
119
 
120
+ const pmOption = new Option(
121
+ '--package-manager, -p',
122
+ 'package manager to use: "npm", "yarn" or "pnpm"'
123
+ ).default('auto detect');
124
+
119
125
  //#region wraps Prisma commands
120
126
 
121
127
  program
122
128
  .command('init')
123
129
  .description('Set up a new ZenStack project.')
130
+ .addOption(pmOption)
124
131
  .argument('<path>', 'project path')
125
132
  .action(initAction);
126
133
 
@@ -130,6 +137,7 @@ export default async function (): Promise<void> {
130
137
  'Generates RESTful API and Typescript client for your data model.'
131
138
  )
132
139
  .addOption(schemaOption)
140
+ .addOption(pmOption)
133
141
  .action(generateAction);
134
142
 
135
143
  const migrate = program
@@ -1,4 +1,4 @@
1
- export const INTERNAL_PACKAGE = '@zenstackhq/internal';
1
+ export const RUNTIME_PACKAGE = '@zenstackhq/runtime';
2
2
  export const GUARD_FIELD_NAME = 'zenstack_guard';
3
3
  export const TRANSACTION_FIELD_NAME = 'zenstack_transaction';
4
4
  export const API_ROUTE_NAME = 'zenstack';
@@ -10,12 +10,12 @@ import {
10
10
  PolicyKind,
11
11
  PolicyOperationKind,
12
12
  RuntimeAttribute,
13
- } from '@zenstackhq/internal';
13
+ } from '@zenstackhq/runtime/server';
14
14
  import path from 'path';
15
15
  import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph';
16
16
  import {
17
17
  GUARD_FIELD_NAME,
18
- INTERNAL_PACKAGE,
18
+ RUNTIME_PACKAGE,
19
19
  UNKNOWN_USER_ID,
20
20
  } from '../constants';
21
21
  import { Context } from '../types';
@@ -38,7 +38,7 @@ export default class QueryGuardGenerator {
38
38
 
39
39
  sf.addImportDeclaration({
40
40
  namedImports: [{ name: 'QueryContext' }],
41
- moduleSpecifier: INTERNAL_PACKAGE,
41
+ moduleSpecifier: `${RUNTIME_PACKAGE}/server`,
42
42
  isTypeOnly: true,
43
43
  });
44
44
 
@@ -5,7 +5,7 @@ import { paramCase } from 'change-case';
5
5
  import { DataModel } from '@lang/generated/ast';
6
6
  import colors from 'colors';
7
7
  import { extractDataModelsWithAllowRules } from '../ast-utils';
8
- import { API_ROUTE_NAME } from '../constants';
8
+ import { API_ROUTE_NAME, RUNTIME_PACKAGE } from '../constants';
9
9
 
10
10
  /**
11
11
  * Generate react data query hooks code
@@ -51,7 +51,9 @@ export default class ReactHooksGenerator implements Generator {
51
51
  moduleSpecifier: '../../.prisma',
52
52
  });
53
53
  sf.addStatements([
54
- `import { request, validate, ServerErrorCode, RequestOptions } from '@zenstackhq/runtime/client';`,
54
+ `import * as request from '${RUNTIME_PACKAGE}/lib/request';`,
55
+ `import { ServerErrorCode, RequestOptions } from '${RUNTIME_PACKAGE}/lib/types';`,
56
+ `import { validate } from '${RUNTIME_PACKAGE}/lib/validation';`,
55
57
  `import { type SWRResponse } from 'swr';`,
56
58
  `import { ${this.getValidator(
57
59
  model,
@@ -2,7 +2,7 @@ import { Context, Generator } from '../types';
2
2
  import { Project } from 'ts-morph';
3
3
  import * as path from 'path';
4
4
  import colors from 'colors';
5
- import { INTERNAL_PACKAGE } from '../constants';
5
+ import { RUNTIME_PACKAGE } from '../constants';
6
6
 
7
7
  /**
8
8
  * Generates ZenStack service code
@@ -22,7 +22,7 @@ export default class ServiceGenerator implements Generator {
22
22
 
23
23
  sf.addStatements([
24
24
  `import { PrismaClient } from "../.prisma";`,
25
- `import { DefaultService } from "${INTERNAL_PACKAGE}";`,
25
+ `import { DefaultService } from "${RUNTIME_PACKAGE}/lib/service";`,
26
26
  ]);
27
27
 
28
28
  const cls = sf.addClass({
@@ -0,0 +1,63 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { execSync } from './exec-utils';
4
+
5
+ type PackageManagers = 'npm' | 'yarn' | 'pnpm';
6
+
7
+ function getPackageManager(projectPath = '.'): PackageManagers {
8
+ if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
9
+ return 'yarn';
10
+ } else if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
11
+ return 'pnpm';
12
+ } else {
13
+ return 'npm';
14
+ }
15
+ }
16
+
17
+ export function installPackage(
18
+ pkg: string,
19
+ dev: boolean,
20
+ pkgManager: PackageManagers | undefined = undefined,
21
+ projectPath = '.'
22
+ ) {
23
+ const manager = pkgManager ?? getPackageManager(projectPath);
24
+ console.log(`Installing package "${pkg}" with ${manager}`);
25
+ switch (manager) {
26
+ case 'yarn':
27
+ execSync(
28
+ `yarn add --cwd "${projectPath}" ${
29
+ dev ? ' --save-dev' : ''
30
+ } ${pkg}`
31
+ );
32
+ break;
33
+
34
+ case 'pnpm':
35
+ execSync(
36
+ `pnpm add -C "${projectPath}" ${
37
+ dev ? ' --save-dev' : ''
38
+ } ${pkg}`
39
+ );
40
+ break;
41
+
42
+ default:
43
+ execSync(
44
+ `npm install --prefix "${projectPath}" ${
45
+ dev ? ' --save-dev' : ''
46
+ } ${pkg}`
47
+ );
48
+ break;
49
+ }
50
+ }
51
+
52
+ export function ensurePackage(
53
+ pkg: string,
54
+ dev: boolean,
55
+ pkgManager: PackageManagers | undefined = undefined,
56
+ projectPath = '.'
57
+ ) {
58
+ try {
59
+ require(pkg);
60
+ } catch {
61
+ installPackage(pkg, dev, pkgManager, projectPath);
62
+ }
63
+ }