Skip to content

Commit 881240d

Browse files
committed
fix: cjs interop
1 parent 33a1743 commit 881240d

2 files changed

Lines changed: 150 additions & 5 deletions

File tree

src/lib/index.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ import {
77

88
export type PrismaOpenApiSchemaOptions = Partial<PrismaOpenApiOptions>;
99

10-
export async function generateOpenApiSchema(
10+
async function generateOpenApiSchema(
1111
prismaSchema: string,
1212
options: PrismaOpenApiSchemaOptions = {},
1313
): Promise<string> {
1414
if (!prismaSchema.trim()) {
1515
throw new Error('Prisma schema must be a non-empty string.');
1616
}
1717

18+
// Handle CJS/ESM interop - @prisma/internals is CJS, exports may be on .default
19+
20+
const prismaInternals: any = await import('@prisma/internals');
1821
// eslint-disable-next-line @typescript-eslint/naming-convention
19-
const {getDMMF} = await import('@prisma/internals');
22+
const getDMMF = prismaInternals.default?.getDMMF ?? prismaInternals.getDMMF;
23+
2024
const dmmf = await getDMMF({datamodel: prismaSchema});
2125
const prismaOpenApiOptions: PrismaOpenApiOptions = {
2226
...defaultOptions,
@@ -32,19 +36,20 @@ export async function generateOpenApiSchema(
3236
);
3337

3438
if (includeModelsList && includeModelsList.length > 0) {
35-
filteredModels = filteredModels.filter((model) =>
36-
includeModelsList.includes(model.name),
39+
filteredModels = filteredModels.filter((model: any) =>
40+
includeModelsList.includes(model.name as string),
3741
);
3842
}
3943

4044
if (excludeModelsList && excludeModelsList.length > 0) {
4145
filteredModels = filteredModels.filter(
42-
(model) => !excludeModelsList.includes(model.name),
46+
(model: any) => !excludeModelsList.includes(model.name as string),
4347
);
4448
}
4549

4650
const openApiBuilder = generateOpenApiSpec(
4751
filteredModels,
52+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
4853
dmmf.datamodel.enums,
4954
prismaOpenApiOptions,
5055
);
@@ -59,3 +64,10 @@ export async function generateOpenApiSchema(
5964

6065
return openApiBuilder.getSpecAsJson();
6166
}
67+
68+
// Named export for ESM consumers
69+
export {generateOpenApiSchema};
70+
71+
// Default export for CJS interop - handles cases where imports resolve to .default
72+
const libExports = {generateOpenApiSchema};
73+
export default libExports;

tests/lib-api.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {describe, expect, it} from 'vitest';
2+
import {generateOpenApiSchema} from '../src/lib/index.js';
3+
4+
const simplePrismaSchema = `
5+
datasource db {
6+
provider = "postgresql"
7+
}
8+
9+
model User {
10+
id Int @id @default(autoincrement())
11+
email String @unique
12+
name String?
13+
}
14+
`;
15+
16+
describe('Library API - generateOpenApiSchema', () => {
17+
it('should generate YAML OpenAPI schema by default', async () => {
18+
const result = await generateOpenApiSchema(simplePrismaSchema);
19+
20+
expect(result).toBeDefined();
21+
expect(result).toContain('openapi:');
22+
expect(result).toContain('User:');
23+
});
24+
25+
it('should generate YAML when generateYaml is true', async () => {
26+
const result = await generateOpenApiSchema(simplePrismaSchema, {
27+
generateYaml: true,
28+
});
29+
30+
expect(result).toBeDefined();
31+
expect(result).toContain('openapi:');
32+
expect(result).toContain('User:');
33+
});
34+
35+
it('should generate JSON when generateJson is true', async () => {
36+
const result = await generateOpenApiSchema(simplePrismaSchema, {
37+
generateJson: true,
38+
generateYaml: false,
39+
});
40+
41+
expect(result).toBeDefined();
42+
const parsed = JSON.parse(result);
43+
expect(parsed.openapi).toBe('3.1.0');
44+
});
45+
46+
it('should throw error for empty schema', async () => {
47+
await expect(generateOpenApiSchema('')).rejects.toThrow(
48+
'Prisma schema must be a non-empty string.',
49+
);
50+
});
51+
52+
it('should throw error for whitespace-only schema', async () => {
53+
await expect(generateOpenApiSchema(' \n ')).rejects.toThrow(
54+
'Prisma schema must be a non-empty string.',
55+
);
56+
});
57+
58+
it('should respect includeModels option', async () => {
59+
const schemaWithMultipleModels = `
60+
datasource db {
61+
provider = "postgresql"
62+
}
63+
64+
model User {
65+
id Int @id @default(autoincrement())
66+
email String @unique
67+
}
68+
69+
model Post {
70+
id Int @id @default(autoincrement())
71+
title String
72+
}
73+
`;
74+
const result = await generateOpenApiSchema(schemaWithMultipleModels, {
75+
includeModels: 'User',
76+
generateJson: true,
77+
generateYaml: false,
78+
});
79+
80+
const parsed = JSON.parse(result);
81+
expect(parsed.components?.schemas?.User).toBeDefined();
82+
expect(parsed.components?.schemas?.Post).toBeUndefined();
83+
});
84+
85+
it('should respect excludeModels option', async () => {
86+
const schemaWithMultipleModels = `
87+
datasource db {
88+
provider = "postgresql"
89+
}
90+
91+
model User {
92+
id Int @id @default(autoincrement())
93+
email String @unique
94+
}
95+
96+
model Post {
97+
id Int @id @default(autoincrement())
98+
title String
99+
}
100+
`;
101+
const result = await generateOpenApiSchema(schemaWithMultipleModels, {
102+
excludeModels: 'Post',
103+
generateJson: true,
104+
generateYaml: false,
105+
});
106+
107+
const parsed = JSON.parse(result);
108+
expect(parsed.components?.schemas?.User).toBeDefined();
109+
expect(parsed.components?.schemas?.Post).toBeUndefined();
110+
});
111+
112+
it('should use custom title when provided', async () => {
113+
const result = await generateOpenApiSchema(simplePrismaSchema, {
114+
title: 'My Custom API',
115+
generateJson: true,
116+
generateYaml: false,
117+
});
118+
119+
const parsed = JSON.parse(result);
120+
expect(parsed.info?.title).toBe('My Custom API');
121+
});
122+
123+
it('should use custom description when provided', async () => {
124+
const result = await generateOpenApiSchema(simplePrismaSchema, {
125+
description: 'My API description',
126+
generateJson: true,
127+
generateYaml: false,
128+
});
129+
130+
const parsed = JSON.parse(result);
131+
expect(parsed.info?.description).toBe('My API description');
132+
});
133+
});

0 commit comments

Comments
 (0)