diff --git a/package.json b/package.json index cd0358ae9..c4d344673 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-monorepo", - "version": "0.2.10", + "version": "0.2.11", "description": "", "scripts": { "build": "pnpm -r build", diff --git a/packages/internal/package.json b/packages/internal/package.json index 64b09387d..9ba9462d9 100644 --- a/packages/internal/package.json +++ b/packages/internal/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/internal", - "version": "0.2.10", + "version": "0.2.11", "displayName": "ZenStack Internal Library", "description": "ZenStack internal runtime library. This package is for supporting runtime functionality of ZenStack and not supposed to be used directly.", "repository": { @@ -42,6 +42,7 @@ "@types/jest": "^29.0.3", "@types/node": "^14.18.29", "@types/uuid": "^8.3.4", + "eslint": "^8.27.0", "jest": "^29.0.3", "ts-jest": "^29.0.1", "ts-node": "^10.9.1", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index ae62c212e..f27c34861 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/runtime", "displayName": "ZenStack Runtime Library", - "version": "0.2.10", + "version": "0.2.11", "description": "This package contains runtime library for consuming client and server side code generated by ZenStack.", "repository": { "type": "git", diff --git a/packages/schema/package.json b/packages/schema/package.json index 485692d6e..c104fa434 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack Language Tools", "description": "ZenStack is a toolkit that simplifies full-stack development", - "version": "0.2.10", + "version": "0.2.11", "author": { "name": "ZenStack Team" }, diff --git a/packages/schema/src/cli/cli-util.ts b/packages/schema/src/cli/cli-util.ts index 28beee3d0..8c08aac62 100644 --- a/packages/schema/src/cli/cli-util.ts +++ b/packages/schema/src/cli/cli-util.ts @@ -1,10 +1,15 @@ import { STD_LIB_MODULE_NAME } from '@lang/constants'; import { Model } from '@lang/generated/ast'; +import { createZModelServices } from '@lang/zmodel-module'; import colors from 'colors'; import fs from 'fs'; import { LangiumServices } from 'langium'; +import { NodeFileSystem } from 'langium/node'; import path from 'path'; +import { ZenStackGenerator } from '../generator'; import { URI } from 'vscode-uri'; +import { GENERATED_CODE_PATH } from '../generator/constants'; +import { Context, GeneratorError } from '../generator/types'; /** * Loads a zmodel document from a file. @@ -69,3 +74,32 @@ export async function loadDocument( return document.parseResult.value as Model; } + +export async function runGenerator( + options: { schema: string }, + includedGenerators?: string[], + clearOutput = true +) { + const services = createZModelServices(NodeFileSystem).ZModel; + const model = await loadDocument(options.schema, services); + + const context: Context = { + schema: model, + outDir: path.dirname(options.schema), + // TODO: make this configurable + generatedCodeDir: GENERATED_CODE_PATH, + }; + + try { + await new ZenStackGenerator().generate( + context, + includedGenerators, + clearOutput + ); + } catch (err) { + if (err instanceof GeneratorError) { + console.error(colors.red(err.message)); + process.exit(1); + } + } +} diff --git a/packages/schema/src/cli/index.ts b/packages/schema/src/cli/index.ts index 095bd6a46..84fd1d9f2 100644 --- a/packages/schema/src/cli/index.ts +++ b/packages/schema/src/cli/index.ts @@ -1,38 +1,16 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Command, Option } from 'commander'; -import { NodeFileSystem } from 'langium/node'; import { ZModelLanguageMetaData } from '../language-server/generated/module'; -import { createZModelServices } from '../language-server/zmodel-module'; -import { Context, GeneratorError } from '../generator/types'; -import { ZenStackGenerator } from '../generator'; -import { GENERATED_CODE_PATH } from '../generator/constants'; import colors from 'colors'; import { execSync } from '../utils/exec-utils'; import { paramCase } from 'change-case'; import path from 'path'; -import { loadDocument } from './cli-util'; +import { runGenerator } from './cli-util'; export const generateAction = async (options: { schema: string; }): Promise => { - const services = createZModelServices(NodeFileSystem).ZModel; - const model = await loadDocument(options.schema, services); - - const context: Context = { - schema: model, - outDir: path.dirname(options.schema), - // TODO: make this configurable - generatedCodeDir: GENERATED_CODE_PATH, - }; - - try { - await new ZenStackGenerator().generate(context); - } catch (err) { - if (err instanceof GeneratorError) { - console.error(colors.red(err.message)); - process.exit(1); - } - } + await runGenerator(options); }; function prismaAction(prismaCmd: string): (...args: any[]) => Promise { @@ -50,6 +28,10 @@ function prismaAction(prismaCmd: string): (...args: any[]) => Promise { ); }) .join(' '); + + // regenerate prisma schema first + await runGenerator(options, ['prisma'], false); + const prismaExec = `npx prisma ${prismaCmd} ${command.name()} ${optStr}`; console.log(prismaExec); try { diff --git a/packages/schema/src/generator/index.ts b/packages/schema/src/generator/index.ts index 1b232095b..5781f5bdc 100644 --- a/packages/schema/src/generator/index.ts +++ b/packages/schema/src/generator/index.ts @@ -1,13 +1,12 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -import { Context, GeneratorError } from './types'; +import { Context } from './types'; import * as fs from 'fs'; import colors from 'colors'; import PrismaGenerator from './prisma'; import ServiceGenerator from './service'; import ReactHooksGenerator from './react-hooks'; import NextAuthGenerator from './next-auth'; -import path from 'path'; -import { execSync } from '../utils/exec-utils'; +import { TypescriptCompilation } from './tsc'; /** * ZenStack code generator @@ -16,20 +15,26 @@ export class ZenStackGenerator { /** * Runs a series of nested generators */ - async generate(context: Context): Promise { - // folder that stores generated prisma schema and migrations + async generate( + context: Context, + includeGenerators?: string[], + clearOutput = true + ): Promise { + // ensure folder that stores generated prisma schema and migrations if (!fs.existsSync(context.outDir)) { fs.mkdirSync(context.outDir); } - // folder that stores generated zenstack code - if (fs.existsSync(context.generatedCodeDir)) { - fs.rmSync(context.generatedCodeDir, { - force: true, - recursive: true, - }); + if (clearOutput) { + // recreate folder that stores generated zenstack code + if (fs.existsSync(context.generatedCodeDir)) { + fs.rmSync(context.generatedCodeDir, { + force: true, + recursive: true, + }); + } + fs.mkdirSync(context.generatedCodeDir); } - fs.mkdirSync(context.generatedCodeDir); const version = require('../../package.json').version; console.log(colors.bold(`⌛️ Running ZenStack generator v${version}`)); @@ -40,49 +45,19 @@ export class ZenStackGenerator { new ServiceGenerator(), new ReactHooksGenerator(), new NextAuthGenerator(), + new TypescriptCompilation(), ]; for (const generator of generators) { + if ( + includeGenerators && + !includeGenerators.includes(generator.name) + ) { + continue; + } await generator.generate(context); } - // generate package.json - const packageJson = require(path.join( - __dirname, - '../res', - 'package.template.json' - )); - fs.writeFileSync( - path.join(context.generatedCodeDir, 'package.json'), - JSON.stringify(packageJson, undefined, 4) - ); - - // compile ts sources - const tsConfig = require(path.join( - __dirname, - '../res', - 'tsconfig.template.json' - )); - fs.writeFileSync( - path.join(context.generatedCodeDir, 'tsconfig.json'), - JSON.stringify(tsConfig, undefined, 4) - ); - - try { - execSync( - `npx tsc -p "${path.join( - context.generatedCodeDir, - 'tsconfig.json' - )}"` - ); - } catch { - throw new GeneratorError( - 'Something went wrong, generated runtime code failed to compile...\nPlease check errors above.' - ); - } - - console.log(colors.blue(' ✔️ Typescript source files transpiled')); - console.log( colors.green( colors.bold('👻 All generators completed successfully!') diff --git a/packages/schema/src/generator/next-auth/index.ts b/packages/schema/src/generator/next-auth/index.ts index 0af334c9e..a043104f6 100644 --- a/packages/schema/src/generator/next-auth/index.ts +++ b/packages/schema/src/generator/next-auth/index.ts @@ -9,6 +9,10 @@ import { execSync } from 'child_process'; * Generates NextAuth adaptor code */ export default class NextAuthGenerator implements Generator { + get name() { + return 'next-auth'; + } + private findModel(schema: Model, name: string) { return schema.declarations.find( (d) => isDataModel(d) && d.name === name diff --git a/packages/schema/src/generator/prisma/index.ts b/packages/schema/src/generator/prisma/index.ts index 6fdc06e40..183c468ef 100644 --- a/packages/schema/src/generator/prisma/index.ts +++ b/packages/schema/src/generator/prisma/index.ts @@ -8,6 +8,10 @@ import QueryGuardGenerator from './query-guard-generator'; * Generates Prisma schema and db client */ export default class PrismaGenerator implements Generator { + get name() { + return 'prisma'; + } + async generate(context: Context): Promise { // generate prisma schema const schemaFile = await new PrismaSchemaGenerator(context).generate(); diff --git a/packages/schema/src/generator/react-hooks/index.ts b/packages/schema/src/generator/react-hooks/index.ts index 6e0968638..313619f01 100644 --- a/packages/schema/src/generator/react-hooks/index.ts +++ b/packages/schema/src/generator/react-hooks/index.ts @@ -11,6 +11,10 @@ import { API_ROUTE_NAME, INTERNAL_PACKAGE } from '../constants'; * Generate react data query hooks code */ export default class ReactHooksGenerator implements Generator { + get name() { + return 'react-hooks'; + } + async generate(context: Context): Promise { const project = new Project(); diff --git a/packages/schema/src/generator/service/index.ts b/packages/schema/src/generator/service/index.ts index 167b42905..fb883b03d 100644 --- a/packages/schema/src/generator/service/index.ts +++ b/packages/schema/src/generator/service/index.ts @@ -8,6 +8,10 @@ import { INTERNAL_PACKAGE } from '../constants'; * Generates ZenStack service code */ export default class ServiceGenerator implements Generator { + get name() { + return 'service'; + } + async generate(context: Context): Promise { const project = new Project(); const sf = project.createSourceFile( diff --git a/packages/schema/src/generator/tsc/index.ts b/packages/schema/src/generator/tsc/index.ts new file mode 100644 index 000000000..50321f73d --- /dev/null +++ b/packages/schema/src/generator/tsc/index.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import colors from 'colors'; +import * as fs from 'fs'; +import path from 'path'; +import { execSync } from '../../utils/exec-utils'; +import { Context, Generator, GeneratorError } from '../types'; + +export class TypescriptCompilation implements Generator { + get name(): string { + return 'tsc'; + } + + async generate(context: Context) { + // generate package.json + const packageJson = require(path.join( + __dirname, + '../res', + 'package.template.json' + )); + + fs.writeFileSync( + path.join(context.generatedCodeDir, 'package.json'), + JSON.stringify(packageJson, undefined, 4) + ); + + // compile ts sources + const tsConfig = require(path.join( + __dirname, + '../res', + 'tsconfig.template.json' + )); + fs.writeFileSync( + path.join(context.generatedCodeDir, 'tsconfig.json'), + JSON.stringify(tsConfig, undefined, 4) + ); + + try { + execSync( + `npx tsc -p "${path.join( + context.generatedCodeDir, + 'tsconfig.json' + )}"` + ); + } catch { + throw new GeneratorError( + 'Something went wrong, generated runtime code failed to compile...\nPlease check errors above.' + ); + } + + console.log(colors.blue(' ✔️ Typescript source files transpiled')); + } +} diff --git a/packages/schema/src/generator/types.ts b/packages/schema/src/generator/types.ts index 87a5c1546..49733e443 100644 --- a/packages/schema/src/generator/types.ts +++ b/packages/schema/src/generator/types.ts @@ -7,6 +7,7 @@ export interface Context { } export interface Generator { + get name(): string; generate(context: Context): Promise; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4b934fbc..d41794e3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,7 @@ importers: colors: ^1.4.0 cuid: ^2.1.8 deepcopy: ^2.1.0 + eslint: ^8.27.0 jest: ^29.0.3 next: 12.3.1 react: ^17.0.2 || ^18 @@ -41,6 +42,7 @@ importers: '@types/jest': 29.0.3 '@types/node': 14.18.29 '@types/uuid': 8.3.4 + eslint: 8.27.0 jest: 29.0.3_johvxhudwcpndp4mle25vwrlq4 ts-jest: 29.0.1_poggjixajg6vd6yquly7s7dsj4 ts-node: 10.9.1_ck2axrxkiif44rdbzjywaqjysa diff --git a/samples/todo/package-lock.json b/samples/todo/package-lock.json index 12f7a96c5..c786f3797 100644 --- a/samples/todo/package-lock.json +++ b/samples/todo/package-lock.json @@ -1,17 +1,17 @@ { "name": "todo", - "version": "0.2.10", + "version": "0.2.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "todo", - "version": "0.2.10", + "version": "0.2.11", "dependencies": { "@heroicons/react": "^2.0.12", "@prisma/client": "^4.4.0", - "@zenstackhq/internal": "^0.2.10", - "@zenstackhq/runtime": "^0.2.10", + "@zenstackhq/internal": "^0.2.11", + "@zenstackhq/runtime": "^0.2.11", "bcryptjs": "^2.4.3", "daisyui": "^2.31.0", "moment": "^2.29.4", @@ -35,7 +35,7 @@ "postcss": "^8.4.16", "tailwindcss": "^3.1.8", "typescript": "^4.6.2", - "zenstack": "^0.2.10" + "zenstack": "^0.2.11" } }, "node_modules/@babel/code-frame": { @@ -722,9 +722,9 @@ } }, "node_modules/@zenstackhq/internal": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@zenstackhq/internal/-/internal-0.2.10.tgz", - "integrity": "sha512-w7yqWrbB+y2QVkKbEJmBtklIQ4gtK0LjzzGxph7ZHIQHP6E1nWFuqwweu4ALdkbufA9pGoxmoTJ0LZ5H1zpF/Q==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@zenstackhq/internal/-/internal-0.2.11.tgz", + "integrity": "sha512-BVh7MRBJwDpoRR90fDgiqAbUDj9fYsC3lVRUcOWSz6MaXCOztNX6y+YOKaoM+2lxjqjXfR82HNB1pSWlDlCkmQ==", "dependencies": { "bcryptjs": "^2.4.3", "colors": "^1.4.0", @@ -740,9 +740,9 @@ } }, "node_modules/@zenstackhq/runtime": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.2.10.tgz", - "integrity": "sha512-Xk2BALRdXXUZC7XmlqaD3oH+fp8ix3sSbYjc+mxfNIkZK67am6QkGF+4ggnKZwWi1BDHXP75+mtAHbWcJkEEOw==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.2.11.tgz", + "integrity": "sha512-gWzM2WRqg0sbTkXGe2yG1E7y1LFw03g4oUy52OGTzHTCc51cszAJsYacEY0/eWFjmUZD0QF8jokD0NcgrMDKng==", "dependencies": { "@zenstackhq/internal": "latest" }, @@ -4520,12 +4520,12 @@ } }, "node_modules/zenstack": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.2.10.tgz", - "integrity": "sha512-AsIP25k0qV/u7AegDrG38TbzOFIeccU1RIT9XDr4ae7c05RwD9+dniIgjNxO1AzvqZWl+hYQH7/ZGrMEcXQ0Mw==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.2.11.tgz", + "integrity": "sha512-ddrTHkAGC1KbxVNTCx0X7zt8E75w83YgZpz/bxpmSVWaRWusrcA53mV0feR/vSJZTiezWKiJl7DQj2cMfdoTog==", "dev": true, "dependencies": { - "@zenstackhq/internal": "0.2.10", + "@zenstackhq/internal": "0.2.11", "change-case": "^4.1.2", "chevrotain": "^9.1.0", "colors": "^1.4.0", @@ -4546,7 +4546,7 @@ "zenstack": "bin/cli" }, "engines": { - "vscode": "^1.72.0" + "vscode": "^1.56.0" } }, "node_modules/zenstack/node_modules/uuid": { @@ -5017,9 +5017,9 @@ } }, "@zenstackhq/internal": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@zenstackhq/internal/-/internal-0.2.10.tgz", - "integrity": "sha512-w7yqWrbB+y2QVkKbEJmBtklIQ4gtK0LjzzGxph7ZHIQHP6E1nWFuqwweu4ALdkbufA9pGoxmoTJ0LZ5H1zpF/Q==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@zenstackhq/internal/-/internal-0.2.11.tgz", + "integrity": "sha512-BVh7MRBJwDpoRR90fDgiqAbUDj9fYsC3lVRUcOWSz6MaXCOztNX6y+YOKaoM+2lxjqjXfR82HNB1pSWlDlCkmQ==", "requires": { "bcryptjs": "^2.4.3", "colors": "^1.4.0", @@ -5029,9 +5029,9 @@ } }, "@zenstackhq/runtime": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.2.10.tgz", - "integrity": "sha512-Xk2BALRdXXUZC7XmlqaD3oH+fp8ix3sSbYjc+mxfNIkZK67am6QkGF+4ggnKZwWi1BDHXP75+mtAHbWcJkEEOw==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@zenstackhq/runtime/-/runtime-0.2.11.tgz", + "integrity": "sha512-gWzM2WRqg0sbTkXGe2yG1E7y1LFw03g4oUy52OGTzHTCc51cszAJsYacEY0/eWFjmUZD0QF8jokD0NcgrMDKng==", "requires": { "@zenstackhq/internal": "latest" } @@ -7766,12 +7766,12 @@ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "zenstack": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.2.10.tgz", - "integrity": "sha512-AsIP25k0qV/u7AegDrG38TbzOFIeccU1RIT9XDr4ae7c05RwD9+dniIgjNxO1AzvqZWl+hYQH7/ZGrMEcXQ0Mw==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/zenstack/-/zenstack-0.2.11.tgz", + "integrity": "sha512-ddrTHkAGC1KbxVNTCx0X7zt8E75w83YgZpz/bxpmSVWaRWusrcA53mV0feR/vSJZTiezWKiJl7DQj2cMfdoTog==", "dev": true, "requires": { - "@zenstackhq/internal": "0.2.10", + "@zenstackhq/internal": "0.2.11", "change-case": "^4.1.2", "chevrotain": "^9.1.0", "colors": "^1.4.0", diff --git a/samples/todo/package.json b/samples/todo/package.json index 62e2a17e4..f6d3e5443 100644 --- a/samples/todo/package.json +++ b/samples/todo/package.json @@ -1,6 +1,6 @@ { "name": "todo", - "version": "0.2.10", + "version": "0.2.11", "private": true, "scripts": { "dev": "next dev", @@ -11,14 +11,17 @@ "db:migrate": "zenstack migrate dev", "db:deploy": "zenstack migrate deploy", "db:reset": "zenstack migrate reset", + "db:browse": "zenstack studio", "generate": "zenstack generate", - "vercel-build": "npm run build && npm run db:deploy" + "vercel-build": "npm run build && npm run db:deploy", + "deps-local": "npm i -D ../../packages/schema && npm i ../../packages/internal ../../packages/runtime", + "deps-npm": "npm i -D zenstack@latest && npm i @zenstackhq/internal@latest @zenstackhq/runtime@latest" }, "dependencies": { "@heroicons/react": "^2.0.12", "@prisma/client": "^4.4.0", - "@zenstackhq/internal": "^0.2.10", - "@zenstackhq/runtime": "^0.2.10", + "@zenstackhq/internal": "^0.2.11", + "@zenstackhq/runtime": "^0.2.11", "bcryptjs": "^2.4.3", "daisyui": "^2.31.0", "moment": "^2.29.4", @@ -42,6 +45,6 @@ "postcss": "^8.4.16", "tailwindcss": "^3.1.8", "typescript": "^4.6.2", - "zenstack": "^0.2.10" + "zenstack": "^0.2.11" } }