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/bundle/cli/index.js +174 -174
- package/package.json +3 -3
- package/src/cli/cli-util.ts +40 -28
- package/src/cli/index.ts +23 -3
- package/src/generator/constants.ts +1 -1
- package/src/generator/prisma/query-guard-generator.ts +3 -3
- package/src/generator/react-hooks/index.ts +4 -2
- package/src/generator/service/index.ts +2 -2
- package/src/utils/pkg-utils.ts +63 -0
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.
|
|
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/
|
|
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",
|
package/src/cli/cli-util.ts
CHANGED
|
@@ -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 {
|
|
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(
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
102
|
+
installPackage('zenstack', true, packageManager, projectPath);
|
|
103
|
+
installPackage('@zenstackhq/runtime', false, packageManager, projectPath);
|
|
96
104
|
|
|
97
|
-
|
|
98
|
-
|
|
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 (
|
|
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
|
-
() =>
|
|
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
|
-
.
|
|
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
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
PolicyKind,
|
|
11
11
|
PolicyOperationKind,
|
|
12
12
|
RuntimeAttribute,
|
|
13
|
-
} from '@zenstackhq/
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 {
|
|
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 "${
|
|
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
|
+
}
|