diff --git a/README.md b/README.md index fd162bb..7a1722f 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,14 @@ generator zero { // Optional list of Prisma Model names you want to exclude from the generated schema // This does *not* change tables that replicate to zero-cache - only the types on the client excludeTables = ["Posts", "Comments", ...] + // When true, skip generating the query builder (`zql` and `builder` exports) + skipBuilder = true + // When true, skip generating the module augmentation for default types in Zero + skipDeclare = true + // When true, enable legacy CRUD mutators (sets `enableLegacyMutators` to `true` in the generated schema) + enableLegacyMutators = true + // When true, enable legacy CRUD queries (sets `enableLegacyQueries` to `true` in the generated schema) + enableLegacyQueries = true } ``` diff --git a/integration/generated/zero/schema.ts b/integration/generated/zero/schema.ts index bf84057..2871bbd 100644 --- a/integration/generated/zero/schema.ts +++ b/integration/generated/zero/schema.ts @@ -555,9 +555,3 @@ export const zql = createBuilder(schema); * @deprecated Use `zql` instead. */ export const builder = zql; -/** Defines the default types for Zero */ -declare module '@rocicorp/zero' { - interface DefaultTypes { - schema: Schema; - } -} diff --git a/integration/schema.prisma b/integration/schema.prisma index fc05166..f2cbe92 100644 --- a/integration/schema.prisma +++ b/integration/schema.prisma @@ -8,6 +8,10 @@ generator zero { prettier = true // camelCase = true excludeTables = ["ExcludedModel"] + // skipBuilder = true + skipDeclare = true + // enableLegacyMutators = true + // enableLegacyQueries = true } // ============================================================================ diff --git a/package.json b/package.json index 8d104c2..16b2a6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prisma-zero", - "version": "0.1.0", + "version": "0.1.1", "description": "Generate Zero schemas from Prisma ORM schemas", "type": "module", "scripts": { diff --git a/src/generator.ts b/src/generator.ts index 586071a..697ddc9 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -24,13 +24,17 @@ export async function onGenerate(options: GeneratorOptions) { resolvePrettierConfig: generator.config.resolvePrettierConfig !== 'false', // Default true camelCase: generator.config.camelCase === 'true', // Default false excludeTables: loadExcludeTables(generator), + skipBuilder: generator.config.skipBuilder === 'true', // Default false + skipDeclare: generator.config.skipDeclare === 'true', // Default false + enableLegacyMutators: generator.config.enableLegacyMutators === 'true', // Default false + enableLegacyQueries: generator.config.enableLegacyQueries === 'true', // Default false } satisfies Config; // Transform the schema const transformedSchema = transformSchema(dmmf, config); // Generate code - let output = generateCode(transformedSchema); + let output = generateCode(transformedSchema, config); // Apply prettier if configured if (config.prettier) { diff --git a/src/generators/code-generator.ts b/src/generators/code-generator.ts index 629ae38..51e45ff 100644 --- a/src/generators/code-generator.ts +++ b/src/generators/code-generator.ts @@ -1,4 +1,5 @@ import type { + Config, TransformedSchema, ZeroModel, ZeroRelationship, @@ -6,13 +7,17 @@ import type { ZeroTypeMapping, } from '../types'; -function generateImports(schema: TransformedSchema): string { +function generateImports(schema: TransformedSchema, config: Config): string { const usedImports = new Set(); // These are always used usedImports.add('table'); usedImports.add('createSchema'); - usedImports.add('createBuilder'); + + // Only add createBuilder if not skipped + if (!config.skipBuilder) { + usedImports.add('createBuilder'); + } // Check which type functions are used in the schema schema.models.forEach(model => { @@ -150,7 +155,7 @@ ${relationshipsStr} return filteredRelationships.length > 0 ? filteredRelationships.join('') : ''; } -function generateSchema(schema: TransformedSchema): string { +function generateSchema(schema: TransformedSchema, config: Config): string { let output = '/**\n'; output += ' * The Zero schema object.\n'; output += @@ -178,6 +183,15 @@ function generateSchema(schema: TransformedSchema): string { output += ' ],\n'; } + // Add legacy options if enabled + if (config.enableLegacyMutators) { + output += ' enableLegacyMutators: true,\n'; + } + + if (config.enableLegacyQueries) { + output += ' enableLegacyQueries: true,\n'; + } + output += '});\n\n'; // Add types @@ -188,36 +202,45 @@ function generateSchema(schema: TransformedSchema): string { output += ' */\n'; output += 'export type Schema = typeof schema;\n'; - output += '/**\n'; - output += ' * Represents the ZQL query builder.\n'; - output += - ' * This type is auto-generated from your Prisma schema definition.\n'; - output += ' */\n'; - output += 'export const zql = createBuilder(schema);\n'; - - output += '/**\n'; - output += ' * Represents the Zero schema query builder.\n'; - output += - ' * This type is auto-generated from your Prisma schema definition.\n'; - output += ' *\n'; - output += ' * @deprecated Use `zql` instead.\n'; - output += ' */\n'; - output += 'export const builder = zql;\n'; + // Only generate builder if not skipped + if (!config.skipBuilder) { + output += '/**\n'; + output += ' * Represents the ZQL query builder.\n'; + output += + ' * This type is auto-generated from your Prisma schema definition.\n'; + output += ' */\n'; + output += 'export const zql = createBuilder(schema);\n'; + + output += '/**\n'; + output += ' * Represents the Zero schema query builder.\n'; + output += + ' * This type is auto-generated from your Prisma schema definition.\n'; + output += ' *\n'; + output += ' * @deprecated Use `zql` instead.\n'; + output += ' */\n'; + output += 'export const builder = zql;\n'; + } - output += '/** Defines the default types for Zero */\n'; - output += 'declare module "@rocicorp/zero" {\n'; - output += ' interface DefaultTypes {\n'; - output += ' schema: Schema;\n'; - output += ' }\n'; - output += '}\n'; + // Only generate declare module if not skipped + if (!config.skipDeclare) { + output += '/** Defines the default types for Zero */\n'; + output += 'declare module "@rocicorp/zero" {\n'; + output += ' interface DefaultTypes {\n'; + output += ' schema: Schema;\n'; + output += ' }\n'; + output += '}\n'; + } return output; } -export function generateCode(schema: TransformedSchema): string { +export function generateCode( + schema: TransformedSchema, + config: Config, +): string { let output = `${HEADER_PREFIX}\n\n`; - output += generateImports(schema); + output += generateImports(schema, config); output += generateUnionTypes(schema); @@ -227,7 +250,7 @@ export function generateCode(schema: TransformedSchema): string { output += generateRelationships(schema.models); - output += generateSchema(schema); + output += generateSchema(schema, config); return output; } diff --git a/src/types.ts b/src/types.ts index 57471e1..38bacc4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,10 @@ export type Config = { resolvePrettierConfig: boolean; camelCase: boolean; excludeTables?: string[]; + skipBuilder?: boolean; + skipDeclare?: boolean; + enableLegacyMutators?: boolean; + enableLegacyQueries?: boolean; }; export type ZeroTypeMapping = { diff --git a/tests/code-generator.test.ts b/tests/code-generator.test.ts index 4b3b2d4..409b9bf 100644 --- a/tests/code-generator.test.ts +++ b/tests/code-generator.test.ts @@ -1,8 +1,16 @@ import {describe, expect, it} from 'vitest'; import {generateCode} from '../src/generators/code-generator'; -import type {TransformedSchema} from '../src/types'; +import type {Config, TransformedSchema} from '../src/types'; describe('code generator', () => { + const defaultConfig: Config = { + name: 'test', + prettier: false, + resolvePrettierConfig: false, + camelCase: false, + excludeTables: [], + }; + it('handles models without relationships and unknown type strings', () => { const schema: TransformedSchema = { enums: [], @@ -21,7 +29,7 @@ describe('code generator', () => { ], }; - const output = generateCode(schema); + const output = generateCode(schema, defaultConfig); expect(output).toContain('export const weirdTable = table("Weird")'); expect(output).toContain('export const schema = createSchema({'); @@ -37,7 +45,7 @@ describe('code generator', () => { models: [], }; - const output = generateCode(schema); + const output = generateCode(schema, defaultConfig); expect(output).toContain('tables: [\n ],'); expect(output).not.toContain('relationships: ['); @@ -79,9 +87,188 @@ describe('code generator', () => { ], }; - const output = generateCode(schema); + const output = generateCode(schema, defaultConfig); expect(output).toContain('withRelTableRelationships'); expect(output).not.toContain('plainTableRelationships'); }); + + it('skips createBuilder import and exports when skipBuilder is true', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, {...defaultConfig, skipBuilder: true}); + + expect(output).not.toContain('createBuilder'); + expect(output).not.toContain('export const zql'); + expect(output).not.toContain('export const builder'); + expect(output).toContain('createSchema'); + }); + + it('includes createBuilder import and exports when skipBuilder is false', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, {...defaultConfig, skipBuilder: false}); + + expect(output).toContain('createBuilder'); + expect(output).toContain('export const zql = createBuilder(schema);'); + expect(output).toContain('export const builder = zql;'); + }); + + it('skips declare module when skipDeclare is true', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, {...defaultConfig, skipDeclare: true}); + + expect(output).not.toContain('declare module "@rocicorp/zero"'); + expect(output).not.toContain('interface DefaultTypes'); + }); + + it('includes declare module when skipDeclare is false', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, {...defaultConfig, skipDeclare: false}); + + expect(output).toContain('declare module "@rocicorp/zero"'); + expect(output).toContain('interface DefaultTypes'); + }); + + it('includes enableLegacyMutators in schema when enabled', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, { + ...defaultConfig, + enableLegacyMutators: true, + }); + + expect(output).toContain('enableLegacyMutators: true'); + }); + + it('includes enableLegacyQueries in schema when enabled', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, { + ...defaultConfig, + enableLegacyQueries: true, + }); + + expect(output).toContain('enableLegacyQueries: true'); + }); + + it('includes both legacy options when both are enabled', () => { + const schema: TransformedSchema = { + enums: [], + models: [ + { + tableName: 'User', + originalTableName: null, + modelName: 'User', + zeroTableName: 'userTable', + columns: { + id: {type: 'string()', isOptional: false, mappedName: null}, + }, + relationships: {}, + primaryKey: ['id'], + }, + ], + }; + + const output = generateCode(schema, { + ...defaultConfig, + enableLegacyMutators: true, + enableLegacyQueries: true, + }); + + expect(output).toContain('enableLegacyMutators: true'); + expect(output).toContain('enableLegacyQueries: true'); + }); });