Skip to content

Commit abfa3a6

Browse files
authored
feat(client): add /browser entrypoint to prisma-client (#28068)
This PR replaces #27978. It partially addresses #27341.
1 parent 5715816 commit abfa3a6

File tree

10 files changed

+128
-27
lines changed

10 files changed

+128
-27
lines changed

packages/client-generator-ts/src/TSClient/GenerateContext.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { GenericArgsInfo } from '../GenericsArgsInfo'
77
export interface GenerateContextOptions {
88
dmmf: DMMFHelper
99
genericArgsInfo: GenericArgsInfo
10+
runtimeBase: string
1011
runtimeImport: string
1112
outputFileName: FileNameMapper
1213
importFileName: FileNameMapper
@@ -16,6 +17,7 @@ export interface GenerateContextOptions {
1617
export class GenerateContext implements GenerateContextOptions {
1718
dmmf: DMMFHelper
1819
genericArgsInfo: GenericArgsInfo
20+
runtimeBase: string
1921
runtimeImport: string
2022
outputFileName: FileNameMapper
2123
importFileName: FileNameMapper
@@ -24,13 +26,15 @@ export class GenerateContext implements GenerateContextOptions {
2426
constructor({
2527
dmmf,
2628
genericArgsInfo,
29+
runtimeBase,
2730
runtimeImport,
2831
outputFileName,
2932
importFileName,
3033
generator,
3134
}: GenerateContextOptions) {
3235
this.dmmf = dmmf
3336
this.genericArgsInfo = genericArgsInfo
37+
this.runtimeBase = runtimeBase
3438
this.runtimeImport = runtimeImport
3539
this.outputFileName = outputFileName
3640
this.importFileName = importFileName
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as ts from '@prisma/ts-builders'
2+
3+
import { GenerateContext } from './GenerateContext'
4+
5+
export function modelExports(context: GenerateContext): string[] {
6+
return (
7+
Object.values(context.dmmf.typeAndModelMap)
8+
// choose models that have output types
9+
.filter((model) => context.dmmf.outputTypeMap.model[model.name])
10+
11+
// generate export statements
12+
.map((model) => {
13+
const docLines = model.documentation ?? ''
14+
const modelLine = `Model ${model.name}\n`
15+
const docs = `${modelLine}${docLines}`
16+
const suffix = 'Model'
17+
18+
const modelTypeExport = ts
19+
// e.g., `export type User = Prisma.UserModel` when `model.name` is `User`
20+
.moduleExport(ts.typeDeclaration(model.name, ts.namedType(`Prisma.${model.name}${suffix}`)))
21+
// add JSDoc comment with the model name and documentation
22+
.setDocComment(ts.docComment(docs))
23+
24+
return ts.stringify(modelTypeExport)
25+
})
26+
)
27+
}

packages/client-generator-ts/src/TSClient/TSClient.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { generatedFileNameMapper, importFileNameMapper } from '../file-extension
55
import type { FileMap } from '../generateClient'
66
import { GenerateClientOptions } from '../generateClient'
77
import { GenericArgsInfo } from '../GenericsArgsInfo'
8+
import { createBrowserFile } from './file-generators/BrowserFile'
89
import { createClassFile } from './file-generators/ClassFile'
910
import { createClientFile } from './file-generators/ClientFile'
1011
import { createCommonInputTypeFiles } from './file-generators/CommonInputTypesFile'
1112
import { createEnumsFile } from './file-generators/EnumsFile'
1213
import { createModelFile } from './file-generators/ModelFile'
1314
import { createModelsFile } from './file-generators/ModelsFile'
15+
import { createPrismaNamespaceBrowserFile } from './file-generators/PrismaNamespaceBrowserFile'
1416
import { createPrismaNamespaceFile } from './file-generators/PrismaNamespaceFile'
1517
import { GenerateContext } from './GenerateContext'
1618

@@ -44,6 +46,7 @@ export class TSClient {
4446
const context = new GenerateContext({
4547
dmmf: this.dmmf,
4648
genericArgsInfo: this.genericsInfo,
49+
runtimeBase: this.options.runtimeBase,
4750
runtimeImport: `${this.options.runtimeBase}/${this.options.runtimeName}`,
4851
outputFileName: generatedFileNameMapper(this.options.generatedFileExtension),
4952
importFileName: importFileNameMapper(this.options.importFileExtension),
@@ -61,12 +64,14 @@ export class TSClient {
6164

6265
return {
6366
[context.outputFileName('client')]: createClientFile(context, this.options),
67+
[context.outputFileName('browser')]: createBrowserFile(context, this.options),
6468
[context.outputFileName('enums')]: createEnumsFile(context),
6569
[context.outputFileName('commonInputTypes')]: createCommonInputTypeFiles(context),
6670
[context.outputFileName('models')]: createModelsFile(context, modelNames),
6771
models: modelsFileMap,
6872
internal: {
6973
[context.outputFileName('prismaNamespace')]: createPrismaNamespaceFile(context, this.options),
74+
[context.outputFileName('prismaNamespaceBrowser')]: createPrismaNamespaceBrowserFile(context),
7075
[context.outputFileName('class')]: createClassFile(context, this.options),
7176
},
7277
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { getClientEngineType } from '@prisma/internals'
2+
3+
import { GenerateContext } from '../GenerateContext'
4+
import { modelExports } from '../ModelExports'
5+
import { TSClientOptions } from '../TSClient'
6+
7+
const jsDocHeader = `/*
8+
* This file should be your main import to use Prisma-related types and utilities in a browser.
9+
* Use it to get access to models, enums, and input types.
10+
*
11+
* This file does not contain a \`PrismaClient\` class, nor several other helpers that are intended as server-side only.
12+
* See \`client.ts\` for the standard, server-side entry point.
13+
*
14+
* 🟢 You can import this file directly.
15+
*/
16+
`
17+
18+
export function createBrowserFile(context: GenerateContext, options: TSClientOptions): string {
19+
const clientEngineType = getClientEngineType(options.generator)
20+
options.generator.config.engineType = clientEngineType
21+
22+
return `${jsDocHeader}
23+
import * as Prisma from '${context.importFileName('./internal/prismaNamespaceBrowser')}'
24+
export { Prisma }
25+
export * as $Enums from '${context.importFileName('./enums')}'
26+
export * from '${context.importFileName('./enums')}';
27+
${modelExports(context).join('\n')}
28+
`
29+
}

packages/client-generator-ts/src/TSClient/file-generators/ClientFile.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import * as ts from '@prisma/ts-builders'
66
import { ModuleFormat } from '../../module-format'
77
import { buildNFTAnnotations } from '../../utils/buildNFTAnnotations'
88
import { GenerateContext } from '../GenerateContext'
9+
import { modelExports } from '../ModelExports'
910
import { getPrismaClientClassDocComment } from '../PrismaClient'
1011
import { TSClientOptions } from '../TSClient'
1112

1213
const jsDocHeader = `/*
1314
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
14-
*
15+
* If you're looking for something you can import in the client-side of your application, please refer to the \`browser.ts\` file instead.
16+
*
1517
* 🟢 You can import this file directly.
1618
*/
1719
`
@@ -65,20 +67,6 @@ export function createClientFile(context: GenerateContext, options: TSClientOpti
6567
),
6668
].map((e) => ts.stringify(e))
6769

68-
const modelExports = Object.values(context.dmmf.typeAndModelMap)
69-
.filter((model) => context.dmmf.outputTypeMap.model[model.name])
70-
.map((model) => {
71-
const docLines = model.documentation ?? ''
72-
const modelLine = `Model ${model.name}\n`
73-
const docs = `${modelLine}${docLines}`
74-
75-
const modelTypeExport = ts
76-
.moduleExport(ts.typeDeclaration(model.name, ts.namedType(`Prisma.${model.name}Model`)))
77-
.setDocComment(ts.docComment(docs))
78-
79-
return ts.stringify(modelTypeExport)
80-
})
81-
8270
const binaryTargets =
8371
clientEngineType === ClientEngineType.Library
8472
? (Object.keys(options.binaryPaths.libqueryEngine ?? {}) as BinaryTarget[])
@@ -97,7 +85,7 @@ export { Prisma }
9785
9886
${buildNFTAnnotations(options.edge || !options.copyEngine, clientEngineType, binaryTargets, relativeOutdir)}
9987
100-
${modelExports.join('\n')}
88+
${modelExports(context).join('\n')}
10189
`
10290
}
10391

packages/client-generator-ts/src/TSClient/file-generators/CommonInputTypesFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const jsDocHeader = `/*
1212

1313
export function createCommonInputTypeFiles(context: GenerateContext) {
1414
const imports = [
15-
ts.moduleImport(context.runtimeImport).asNamespace('runtime'),
15+
ts.moduleImport(context.runtimeImport).asNamespace('runtime').typeOnly(),
1616
ts.moduleImport(context.importFileName(`./enums`)).asNamespace('$Enums'),
1717
ts.moduleImport(context.importFileName(`./internal/prismaNamespace`)).asNamespace('Prisma').typeOnly(),
1818
].map((i) => ts.stringify(i))

packages/client-generator-ts/src/TSClient/file-generators/ModelFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function createModelFile(context: GenerateContext, modelName: string): st
1212
`
1313

1414
const imports = [
15-
ts.moduleImport(context.runtimeImport).asNamespace('runtime'),
15+
ts.moduleImport(context.runtimeImport).asNamespace('runtime').typeOnly(),
1616
ts.moduleImport(context.importFileName(`../enums`)).asNamespace('$Enums').typeOnly(),
1717
ts.moduleImport(context.importFileName(`../internal/prismaNamespace`)).asNamespace('Prisma').typeOnly(),
1818
]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as ts from '@prisma/ts-builders'
2+
3+
import { Enum } from '../Enum'
4+
import { GenerateContext } from '../GenerateContext'
5+
6+
const jsDocHeader = `/*
7+
* WARNING: This is an internal file that is subject to change!
8+
*
9+
* 🛑 Under no circumstances should you import this file directly! 🛑
10+
*
11+
* All exports from this file are wrapped under a \`Prisma\` namespace object in the browser.ts file.
12+
* While this enables partial backward compatibility, it is not part of the stable public API.
13+
*
14+
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
15+
* model files in the \`model\` directory!
16+
*/
17+
`
18+
export function createPrismaNamespaceBrowserFile(context: GenerateContext): string {
19+
const prismaEnums = context.dmmf.schema.enumTypes.prisma?.map((type) => new Enum(type, true).toTS())
20+
21+
return `${jsDocHeader}
22+
${ts.stringify(ts.moduleImport(`${context.runtimeBase}/index-browser`).asNamespace('runtime'))}
23+
export type * from '${context.importFileName(`../models`)}'
24+
export type * from '${context.importFileName(`./prismaNamespace`)}'
25+
export const Decimal = runtime.Decimal
26+
${new Enum(
27+
{
28+
name: 'ModelName',
29+
values: context.dmmf.mappings.modelOperations.map((m) => m.model),
30+
},
31+
true,
32+
).toTS()}
33+
/**
34+
* Enums
35+
*/
36+
${prismaEnums?.join('\n\n')}
37+
`
38+
}

packages/client/helpers/build.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,18 @@ function wasmBindgenRuntimeConfig(
9494
}
9595

9696
// we define the config for browser
97-
const browserBuildConfig: BuildOptions = {
98-
name: 'browser',
99-
entryPoints: ['src/runtime/index-browser.ts'],
100-
outfile: 'runtime/index-browser',
101-
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
102-
bundle: true,
103-
minify: shouldMinify,
104-
sourcemap: 'linked',
97+
function browserBuildConfigs(): BuildOptions[] {
98+
return MODULE_FORMATS.map((format) => ({
99+
format,
100+
name: 'browser',
101+
entryPoints: ['src/runtime/index-browser.ts'],
102+
outfile: 'runtime/index-browser',
103+
outExtension: getOutExtension(format),
104+
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
105+
bundle: true,
106+
minify: shouldMinify,
107+
sourcemap: 'linked',
108+
}))
105109
}
106110

107111
/**
@@ -308,7 +312,7 @@ function* allWasmBindgenRuntimeConfigs(): Generator<BuildOptions> {
308312
void build([
309313
generatorBuildConfig,
310314
...allNodeRuntimeBuildConfigs(),
311-
browserBuildConfig,
315+
...browserBuildConfigs(),
312316
edgeRuntimeBuildConfig,
313317
edgeEsmRuntimeBuildConfig,
314318
...allWasmEdgeRuntimeConfigs(),

packages/client/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@
136136
"import": "./runtime/react-native.js",
137137
"default": "./runtime/react-native.js"
138138
},
139+
"./runtime/index-browser": {
140+
"types": "./runtime/index-browser.d.ts",
141+
"require": "./runtime/index-browser.js",
142+
"import": "./runtime/index-browser.mjs",
143+
"default": "./runtime/index-browser.mjs"
144+
},
139145
"./generator-build": {
140146
"require": "./generator-build/index.js",
141147
"import": "./generator-build/index.js",

0 commit comments

Comments
 (0)