Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions packages/cli/src/actions/action-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { findUp } from '@zenstackhq/common-helpers';
import { loadDocument } from '@zenstackhq/language';
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
import colors from 'colors';
Expand All @@ -13,6 +14,14 @@ export function getSchemaFile(file?: string) {
return file;
}

const pkgJsonConfig = getPkgJsonConfig(process.cwd());
if (pkgJsonConfig.schema) {
if (!fs.existsSync(pkgJsonConfig.schema)) {
throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
}
return pkgJsonConfig.schema;
}

if (fs.existsSync('./zenstack/schema.zmodel')) {
return './zenstack/schema.zmodel';
} else if (fs.existsSync('./schema.zmodel')) {
Expand Down Expand Up @@ -51,3 +60,26 @@ export async function generateTempPrismaSchema(zmodelPath: string) {
fs.writeFileSync(prismaSchemaFile, prismaSchema);
return prismaSchemaFile;
}

export function getPkgJsonConfig(startPath: string) {
const result: { schema: string | undefined; output: string | undefined } = { schema: undefined, output: undefined };
const pkgJsonFile = findUp(['package.json'], startPath, false);

if (!pkgJsonFile) {
return result;
}

let pkgJson: any = undefined;
try {
pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
} catch {
return result;
}

if (pkgJson.zenstack && typeof pkgJson.zenstack === 'object') {
result.schema = pkgJson.zenstack.schema && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema);
result.output = pkgJson.zenstack.output && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output);
}

return result;
}
16 changes: 14 additions & 2 deletions packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PrismaSchemaGenerator, TsSchemaGenerator, type CliGenerator } from '@ze
import colors from 'colors';
import fs from 'node:fs';
import path from 'node:path';
import { getSchemaFile, loadSchemaDocument } from './action-utils';
import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils';

type Options = {
schema?: string;
Expand All @@ -20,7 +20,7 @@ export async function run(options: Options) {
const schemaFile = getSchemaFile(options.schema);

const model = await loadSchemaDocument(schemaFile);
const outputPath = options.output ?? path.dirname(schemaFile);
const outputPath = getOutputPath(options, schemaFile);

// generate TS schema
const tsSchemaFile = path.join(outputPath, 'schema.ts');
Expand Down Expand Up @@ -55,6 +55,18 @@ const client = new ZenStackClient(schema, {
}
}

function getOutputPath(options: Options, schemaFile: string) {
if (options.output) {
return options.output;
}
const pkgJsonConfig = getPkgJsonConfig(process.cwd());
if (pkgJsonConfig.output) {
return pkgJsonConfig.output;
} else {
return path.dirname(schemaFile);
}
}

async function runPlugins(model: Model, outputPath: string, tsSchemaFile: string) {
const plugins = model.declarations.filter(isPlugin);
for (const plugin of plugins) {
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/test/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,19 @@ describe('CLI generate command test', () => {
runCli('generate --save-prisma-schema "../prisma/schema.prisma"', workDir);
expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true);
});

it('should respect package.json config', () => {
const workDir = createProject(model);
fs.mkdirSync(path.join(workDir, 'foo'));
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
fs.rmdirSync(path.join(workDir, 'zenstack'));
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
pkgJson.zenstack = {
schema: './foo/schema.zmodel',
output: './bar',
};
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
runCli('generate', workDir);
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
});
});
34 changes: 34 additions & 0 deletions packages/common-helpers/src/find-up.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import fs from 'fs';
import path from 'path';

/**
* A type named FindUp that takes a type parameter e which extends boolean.
*/
export type FindUpResult<Multiple extends boolean> = Multiple extends true ? string[] | undefined : string | undefined;

/**
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
* Optionally return a single path or multiple paths.
* If multiple allowed, return all paths found.
* If no paths are found, return undefined.
*
* @param names An array of strings representing names to search for within the directory
* @param cwd A string representing the current working directory
* @param multiple A boolean flag indicating whether to search for multiple levels. Useful for finding node_modules directories...
* @param An array of strings representing the accumulated results used in multiple results
* @returns Path(s) to a specific file or folder within the directory or parent directories
*/
export function findUp<Multiple extends boolean = false>(
names: string[],
cwd: string = process.cwd(),
multiple: Multiple = false as Multiple,
result: string[] = [],
): FindUpResult<Multiple> {
if (!names.some((name) => !!name)) return undefined;
const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
if (multiple === false && target) return path.join(cwd, target) as FindUpResult<Multiple>;
if (target) result.push(path.join(cwd, target));
const up = path.resolve(cwd, '..');
if (up === cwd) return (multiple && result.length > 0 ? result : undefined) as FindUpResult<Multiple>; // it'll fail anyway
return findUp(names, up, multiple, result);
}
1 change: 1 addition & 0 deletions packages/common-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './find-up';
export * from './is-plain-object';
export * from './lower-case-first';
export * from './param-case';
Expand Down
16 changes: 13 additions & 3 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
"default": "./dist/schema.cjs"
}
},
"./helpers": {
"import": {
"types": "./dist/helpers.d.ts",
"default": "./dist/helpers.js"
},
"require": {
"types": "./dist/helpers.d.cts",
"default": "./dist/helpers.cjs"
}
},
"./plugins/policy": {
"import": {
"types": "./dist/plugins/policy.d.ts",
Expand All @@ -63,12 +73,12 @@
"nanoid": "^5.0.9",
"ts-pattern": "catalog:",
"ulid": "^3.0.0",
"uuid": "^11.0.5"
"uuid": "^11.0.5",
"zod": "catalog:"
},
"peerDependencies": {
"better-sqlite3": "^11.8.1",
"pg": "^8.13.1",
"zod": "catalog:"
"pg": "^8.13.1"
},
"peerDependenciesMeta": {
"better-sqlite3": {
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime/src/client/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ export type ClientContract<Schema extends SchemaDef> = {
[Key in GetModels<Schema> as Uncapitalize<Key>]: ModelOperations<Schema, Key>;
} & Procedures<Schema>;

/**
* The contract for a client in a transaction.
*/
export type TransactionClientContract<Schema extends SchemaDef> = Omit<
ClientContract<Schema>,
TransactionUnsupportedMethods
>;

type _TypeMap = {
String: string;
Int: number;
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/client/crud/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { invariant } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import stableStringify from 'json-stable-stringify';
import { match, P } from 'ts-pattern';
import { z, ZodType } from 'zod/v4';
import { z, ZodType } from 'zod';
import type { BuiltinType, EnumDef, FieldDef, GetModels, SchemaDef } from '../../schema';
import { NUMERIC_FIELD_TYPES } from '../constants';
import {
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { sql } from 'kysely';
1 change: 1 addition & 0 deletions packages/runtime/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default defineConfig({
entry: {
index: 'src/index.ts',
schema: 'src/schema/index.ts',
helpers: 'src/helpers.ts',
'plugins/policy': 'src/plugins/policy/index.ts',
},
outDir: 'dist',
Expand Down
2 changes: 1 addition & 1 deletion packages/zod/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FieldDef, GetModels, SchemaDef } from '@zenstackhq/runtime/schema';
import { match, P } from 'ts-pattern';
import { z, ZodType } from 'zod/v4';
import { z, ZodType } from 'zod';
import type { SelectSchema } from './types';

export function makeSelectSchema<Schema extends SchemaDef, Model extends GetModels<Schema>>(
Expand Down
2 changes: 1 addition & 1 deletion packages/zod/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FieldType, GetModels, ScalarFields, SchemaDef } from '@zenstackhq/runtime/schema';
import type { ZodBoolean, ZodNumber, ZodObject, ZodString, ZodUnknown } from 'zod/v4';
import type { ZodBoolean, ZodNumber, ZodObject, ZodString, ZodUnknown } from 'zod';

export type SelectSchema<Schema extends SchemaDef, Model extends GetModels<Schema>> = ZodObject<{
[Key in ScalarFields<Schema, Model>]: MapScalarType<Schema, Model, Key>;
Expand Down
23 changes: 7 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ packages:
- tests/**
catalog:
kysely: ^0.27.6
zod: ^3.25.67
zod: ^4.0.0
prisma: ^6.0.0
langium: 3.5.0
langium-cli: 3.5.0
Expand Down