Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/update-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ jobs:
npm install "$pkg@next"
done

# Finally run zenstack generate
npx zen generate
# Finally run generate
npm run generate

- name: Commit and push changes
if: steps.check-package.outputs.exists == 'true'
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ Even without using advanced features, ZenStack offers the following benefits as

# Quick start

> You can also check the [blog sample](./samples/blog) for a complete example.
- [ORM](./samples/orm): A simple example demonstrating ZenStack ORM usage.
- [Next.js + TanStack Query](./samples/next.js): A full-stack sample demonstrating using TanStack Query to consume ZenStack's automatic CRUD services in a Next.js app.

## Installation

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0-beta.15",
"version": "3.0.0-beta.16",
"description": "ZenStack",
"packageManager": "[email protected]",
"scripts": {
Expand Down Expand Up @@ -31,7 +31,8 @@
"typescript": "catalog:",
"typescript-eslint": "^8.34.1",
"vitest": "^3.2.4",
"yaml": "^2.8.0"
"yaml": "^2.8.0",
"prisma": "catalog:"
},
"pnpm": {
"onlyBuiltDependencies": [
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.0.0-beta.15",
"version": "3.0.0-beta.16",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand Down Expand Up @@ -45,7 +45,7 @@
"prisma": "catalog:"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.13",
"@types/better-sqlite3": "catalog:",
"@types/semver": "^7.7.0",
"@types/tmp": "catalog:",
"@zenstackhq/eslint-config": "workspace:*",
Expand Down
15 changes: 11 additions & 4 deletions packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type Options = {
schema?: string;
output?: string;
silent: boolean;
lite: boolean;
liteOnly: boolean;
};

/**
Expand Down Expand Up @@ -88,10 +90,15 @@ async function runPlugins(schemaFile: string, model: Model, outputPath: string,
}
}

const defaultPlugins = [corePlugins['typescript']].reverse();
defaultPlugins.forEach((d) => {
if (!processedPlugins.some((p) => p.cliPlugin === d)) {
processedPlugins.push({ cliPlugin: d, pluginOptions: {} });
const defaultPlugins = [
{
plugin: corePlugins['typescript'],
options: { lite: options.lite, liteOnly: options.liteOnly },
},
];
defaultPlugins.forEach(({ plugin, options }) => {
if (!processedPlugins.some((p) => p.cliPlugin === plugin)) {
processedPlugins.push({ cliPlugin: plugin, pluginOptions: options });
}
});

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ function createProgram() {
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(new Option('-o, --output <path>', 'default output directory for code generation'))
.addOption(new Option('--lite', 'also generate a lite version of schema without attributes').default(false))
.addOption(new Option('--lite-only', 'only generate lite version of schema without attributes').default(false))
.addOption(new Option('--silent', 'suppress all output except errors').default(false))
.action(generateAction);

Expand Down
10 changes: 9 additions & 1 deletion packages/cli/src/plugins/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ const plugin: CliPlugin = {
name: 'TypeScript Schema Generator',
statusText: 'Generating TypeScript schema',
async generate({ model, defaultOutputPath, pluginOptions }) {
// output path
let outDir = defaultOutputPath;
if (typeof pluginOptions['output'] === 'string') {
outDir = path.resolve(defaultOutputPath, pluginOptions['output']);
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
}
await new TsSchemaGenerator().generate(model, outDir);

// lite mode
const lite = pluginOptions['lite'] === true;

// liteOnly mode
const liteOnly = pluginOptions['liteOnly'] === true;

await new TsSchemaGenerator().generate(model, { outDir, lite, liteOnly });
},
};

Expand Down
14 changes: 14 additions & 0 deletions packages/cli/test/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,18 @@ describe('CLI generate command test', () => {
runCli('generate', workDir);
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
});

it('should respect lite option', () => {
const workDir = createProject(model);
runCli('generate --lite', workDir);
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
expect(fs.existsSync(path.join(workDir, 'zenstack/schema-lite.ts'))).toBe(true);
});

it('should respect liteOnly option', () => {
const workDir = createProject(model);
runCli('generate --lite-only', workDir);
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(false);
expect(fs.existsSync(path.join(workDir, 'zenstack/schema-lite.ts'))).toBe(true);
});
});
22 changes: 22 additions & 0 deletions packages/cli/test/ts-schema-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,4 +420,26 @@ model User {
},
});
});

it('supports lite schema generation', async () => {
const { schemaLite } = await generateTsSchema(
`
model User {
id String @id @default(uuid())
name String
email String @unique

@@map('users')
}
`,
undefined,
undefined,
undefined,
true,
);

expect(schemaLite!.models.User.attributes).toBeUndefined();
expect(schemaLite!.models.User.fields.id.attributes).toBeUndefined();
expect(schemaLite!.models.User.fields.email.attributes).toBeUndefined();
});
});
71 changes: 71 additions & 0 deletions packages/clients/tanstack-query/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@zenstackhq/tanstack-query",
"version": "3.0.0-beta.16",
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
"main": "index.js",
"type": "module",
"private": true,
"scripts": {
"build": "tsc --noEmit && tsup-node && pnpm test:generate && pnpm test:typecheck",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"test": "vitest run",
"pack": "pnpm pack",
"test:generate": "tsx scripts/generate.ts",
"test:typecheck": "tsc --noEmit --project tsconfig.test.json"
},
"keywords": [
"tanstack-query",
"react-query",
"zenstack",
"orm",
"fullstack"
],
"author": "ZenStack Team",
"license": "MIT",
"exports": {
"./react": {
"import": {
"types": "./dist/react.d.ts",
"default": "./dist/react.js"
},
"require": {
"types": "./dist/react.d.cts",
"default": "./dist/react.cjs"
}
}
},
"dependencies": {
"@zenstackhq/common-helpers": "workspace:*",
"@zenstackhq/orm": "workspace:*",
"@zenstackhq/schema": "workspace:*",
"decimal.js": "catalog:",
"superjson": "^2.2.3"
},
"devDependencies": {
"@tanstack/react-query": "catalog:",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/react": "catalog:",
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/language": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"happy-dom": "^20.0.10",
"nock": "^14.0.10",
"react": "catalog:"
},
"peerDependencies": {
"@tanstack/react-query": "^5.0.0",
"react": "^18 || ^19"
},
"peerDependenciesMeta": {
"@tanstack/react-query": {
"optional": true
},
"react": {
"optional": true
}
}
}
27 changes: 27 additions & 0 deletions packages/clients/tanstack-query/scripts/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { loadDocument } from '@zenstackhq/language';
import { TsSchemaGenerator } from '@zenstackhq/sdk';
import { glob } from 'glob';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const dir = path.dirname(fileURLToPath(import.meta.url));

async function main() {
const zmodelFiles = glob.sync(path.resolve(dir, '../test/**/*.zmodel'));
for (const file of zmodelFiles) {
console.log(`Generating TS schema for: ${file}`);
await generate(file);
}
}

async function generate(schemaPath: string) {
const generator = new TsSchemaGenerator();
const outDir = path.dirname(schemaPath);
const result = await loadDocument(schemaPath);
if (!result.success) {
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
}
await generator.generate(result.model, { outDir, liteOnly: true });
}

main();
Loading