zenstack 0.3.10 → 0.3.12

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.12",
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.12",
69
69
  "async-exit-hook": "^2.0.1",
70
70
  "change-case": "^4.1.2",
71
71
  "chevrotain": "^9.1.0",
@@ -118,7 +118,7 @@
118
118
  "vscode:prepublish": "cp ../../README.md ./ && pnpm lint && pnpm build",
119
119
  "vscode:package": "vsce package --no-dependencies",
120
120
  "clean": "rimraf bundle",
121
- "build": "pnpm langium:generate && tsc --noEmit && pnpm bundle && cp -r src/res/* bundle/res/",
121
+ "build": "pnpm -C ../runtime build && pnpm langium:generate && tsc --noEmit && pnpm bundle && cp -r src/res/* bundle/res/",
122
122
  "bundle": "npm run clean && node build/bundle.js --minify",
123
123
  "bundle-watch": "node build/bundle.js --watch",
124
124
  "ts:watch": "tsc --watch --noEmit",
@@ -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, PackageManagers } from '../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';
@@ -15,21 +16,24 @@ import { CliError } from './cli-error';
15
16
  /**
16
17
  * Initializes an existing project for ZenStack
17
18
  */
18
- export async function initProject(projectPath: string) {
19
+ export async function initProject(
20
+ projectPath: string,
21
+ packageManager: PackageManagers | undefined
22
+ ) {
19
23
  const schema = path.join(projectPath, 'zenstack', 'schema.zmodel');
24
+ let schemaGenerated = false;
25
+
20
26
  if (fs.existsSync(schema)) {
21
27
  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
- }
28
+ } else {
29
+ // create a default model
30
+ if (!fs.existsSync(path.join(projectPath, 'zenstack'))) {
31
+ fs.mkdirSync(path.join(projectPath, 'zenstack'));
32
+ }
29
33
 
30
- fs.writeFileSync(
31
- schema,
32
- `// This is a sample model to get you started.
34
+ fs.writeFileSync(
35
+ schema,
36
+ `// This is a sample model to get you started.
33
37
  // Learn how to model you app: https://zenstack.dev/#/modeling-your-app.
34
38
 
35
39
  /*
@@ -77,26 +81,34 @@ model Post {
77
81
  @@allow('all', author == auth())
78
82
  }
79
83
  `
80
- );
84
+ );
81
85
 
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
- }
86
+ // add zenstack/schema.prisma to .gitignore
87
+ const gitIgnorePath = path.join(projectPath, '.gitignore');
88
+ let gitIgnoreContent = '';
89
+ if (fs.existsSync(gitIgnorePath)) {
90
+ gitIgnoreContent =
91
+ fs.readFileSync(gitIgnorePath, { encoding: 'utf-8' }) + '\n';
92
+ }
89
93
 
90
- if (!gitIgnoreContent.includes('zenstack/schema.prisma')) {
91
- gitIgnoreContent += 'zenstack/schema.prisma\n';
92
- fs.writeFileSync(gitIgnorePath, gitIgnoreContent);
94
+ if (!gitIgnoreContent.includes('zenstack/schema.prisma')) {
95
+ gitIgnoreContent += 'zenstack/schema.prisma\n';
96
+ fs.writeFileSync(gitIgnorePath, gitIgnoreContent);
97
+ }
98
+
99
+ schemaGenerated = true;
93
100
  }
94
101
 
95
- console.log(`Sample model generated at: ${colors.green(schema)}
102
+ installPackage('zenstack', true, packageManager, projectPath);
103
+ installPackage('@zenstackhq/runtime', false, packageManager, projectPath);
96
104
 
97
- Please check the following guide on how to model your app:
98
- https://zenstack.dev/#/modeling-your-app.
99
- `);
105
+ if (schemaGenerated) {
106
+ console.log(`Sample model generated at: ${colors.green(schema)}
107
+
108
+ Please check the following guide on how to model your app:
109
+ https://zenstack.dev/#/modeling-your-app.
110
+ `);
111
+ }
100
112
  }
101
113
 
102
114
  /**
@@ -164,7 +176,7 @@ export async function loadDocument(
164
176
  }
165
177
 
166
178
  export async function runGenerator(
167
- options: { schema: string },
179
+ options: { schema: string; packageManager: string },
168
180
  includedGenerators?: string[],
169
181
  clearOutput = true
170
182
  ) {
package/src/cli/index.ts CHANGED
@@ -3,24 +3,37 @@ import { paramCase } from 'change-case';
3
3
  import colors from 'colors';
4
4
  import { Command, Option } from 'commander';
5
5
  import path from 'path';
6
+ import { PackageManagers } from '../utils/pkg-utils';
6
7
  import { ZModelLanguageMetaData } from '../language-server/generated/module';
7
8
  import telemetry from '../telemetry';
8
9
  import { execSync } from '../utils/exec-utils';
9
10
  import { CliError } from './cli-error';
10
11
  import { initProject, runGenerator } from './cli-util';
11
12
 
12
- export const initAction = async (projectPath: string): Promise<void> => {
13
+ export const initAction = async (
14
+ projectPath: string,
15
+ options: {
16
+ packageManager: PackageManagers | 'auto detect';
17
+ }
18
+ ): Promise<void> => {
13
19
  await telemetry.trackSpan(
14
20
  'cli:command:start',
15
21
  'cli:command:complete',
16
22
  'cli:command:error',
17
23
  { command: 'init' },
18
- () => initProject(projectPath)
24
+ () =>
25
+ initProject(
26
+ projectPath ?? '.',
27
+ options.packageManager === 'auto detect'
28
+ ? undefined
29
+ : options.packageManager
30
+ )
19
31
  );
20
32
  };
21
33
 
22
34
  export const generateAction = async (options: {
23
35
  schema: string;
36
+ packageManager: string;
24
37
  }): Promise<void> => {
25
38
  await telemetry.trackSpan(
26
39
  'cli:command:start',
@@ -116,12 +129,18 @@ export default async function (): Promise<void> {
116
129
  `schema file (with extension ${schemaExtensions})`
117
130
  ).default('./zenstack/schema.zmodel');
118
131
 
132
+ const pmOption = new Option(
133
+ '--package-manager, -p',
134
+ 'package manager to use: "npm", "yarn" or "pnpm"'
135
+ ).default('auto detect');
136
+
119
137
  //#region wraps Prisma commands
120
138
 
121
139
  program
122
140
  .command('init')
123
141
  .description('Set up a new ZenStack project.')
124
- .argument('<path>', 'project path')
142
+ .addOption(pmOption)
143
+ .argument('[path]', 'project path')
125
144
  .action(initAction);
126
145
 
127
146
  program
@@ -130,6 +149,7 @@ export default async function (): Promise<void> {
130
149
  'Generates RESTful API and Typescript client for your data model.'
131
150
  )
132
151
  .addOption(schemaOption)
152
+ .addOption(pmOption)
133
153
  .action(generateAction);
134
154
 
135
155
  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
+ export 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}@dev" with ${manager}`);
25
+ switch (manager) {
26
+ case 'yarn':
27
+ execSync(
28
+ `yarn --cwd "${projectPath}" add ${pkg} ${
29
+ dev ? ' --save-dev' : ''
30
+ } --ignore-engines`
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
+ }