prisma-generator-express 1.40.0 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +444 -12
  2. package/dist/generators/generateOperationCore.d.ts +3 -1
  3. package/dist/generators/generateOperationCore.js +266 -160
  4. package/dist/generators/generateOperationCore.js.map +1 -1
  5. package/dist/generators/generateRouteConfigType.d.ts +3 -1
  6. package/dist/generators/generateRouteConfigType.js +36 -31
  7. package/dist/generators/generateRouteConfigType.js.map +1 -1
  8. package/dist/generators/generateRouter.d.ts +4 -2
  9. package/dist/generators/generateRouter.js +186 -132
  10. package/dist/generators/generateRouter.js.map +1 -1
  11. package/dist/generators/generateRouterFastify.d.ts +3 -1
  12. package/dist/generators/generateRouterFastify.js +12 -10
  13. package/dist/generators/generateRouterFastify.js.map +1 -1
  14. package/dist/generators/generateRouterHono.d.ts +3 -1
  15. package/dist/generators/generateRouterHono.js +12 -9
  16. package/dist/generators/generateRouterHono.js.map +1 -1
  17. package/dist/generators/generateUnifiedDocs.d.ts +2 -1
  18. package/dist/generators/generateUnifiedDocs.js +6 -4
  19. package/dist/generators/generateUnifiedDocs.js.map +1 -1
  20. package/dist/index.js +16 -21
  21. package/dist/index.js.map +1 -1
  22. package/dist/utils/copyFiles.d.ts +2 -1
  23. package/dist/utils/copyFiles.js +39 -34
  24. package/dist/utils/copyFiles.js.map +1 -1
  25. package/dist/utils/importExt.d.ts +2 -0
  26. package/dist/utils/importExt.js +11 -0
  27. package/dist/utils/importExt.js.map +1 -0
  28. package/dist/utils/resolveImportStyle.d.ts +3 -0
  29. package/dist/utils/resolveImportStyle.js +211 -0
  30. package/dist/utils/resolveImportStyle.js.map +1 -0
  31. package/dist/utils/writeFileSafely.js +6 -9
  32. package/dist/utils/writeFileSafely.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/copy/routeConfig.express.ts +39 -5
  35. package/src/copy/routeConfig.fastify.ts +8 -4
  36. package/src/copy/routeConfig.hono.ts +7 -3
  37. package/src/copy/routeConfig.ts +42 -2
  38. package/src/generators/generateOperationCore.ts +273 -169
  39. package/src/generators/generateRouteConfigType.ts +43 -36
  40. package/src/generators/generateRouter.ts +189 -133
  41. package/src/generators/generateRouterFastify.ts +14 -9
  42. package/src/generators/generateRouterHono.ts +14 -8
  43. package/src/generators/generateUnifiedDocs.ts +8 -3
  44. package/src/index.ts +25 -47
  45. package/src/utils/copyFiles.ts +45 -45
  46. package/src/utils/importExt.ts +7 -0
  47. package/src/utils/resolveImportStyle.ts +187 -0
  48. package/src/utils/writeFileSafely.ts +6 -22
@@ -1,16 +1,21 @@
1
1
  import { DMMF } from '@prisma/generator-helper'
2
2
  import { toCamelCase } from '../utils/strings'
3
3
  import { generateRouteConfigType } from './generateRouteConfigType'
4
+ import { ImportStyle } from '../utils/resolveImportStyle'
5
+ import { importExt } from '../utils/importExt'
4
6
 
5
7
  export function generateFastifyRouterFunction({
6
8
  model,
7
9
  enums,
8
10
  guardShapesImport,
11
+ importStyle,
9
12
  }: {
10
13
  model: DMMF.Model
11
14
  enums: DMMF.DatamodelEnum[]
12
15
  guardShapesImport: string | null
16
+ importStyle: ImportStyle
13
17
  }): string {
18
+ const ext = importExt(importStyle)
14
19
  const modelName = model.name
15
20
  const prefix = toCamelCase(modelName)
16
21
  const modelNameLower = modelName.toLowerCase()
@@ -59,14 +64,14 @@ import {
59
64
  ${prefix}Aggregate,
60
65
  ${prefix}Count,
61
66
  ${prefix}GroupBy,
62
- } from './${modelName}Handlers'
63
- import type { RouteConfig, FastifyHookHandler } from '../routeConfig.target'
64
- import { parseQueryParams } from '../parseQueryParams'
65
- import { sanitizeKeys } from '../misc'
66
- import { buildModelOpenApi } from '../buildModelOpenApi'
67
- import { mapError, transformResult, HttpError } from '../operationRuntime'
68
-
69
- ${generateRouteConfigType(modelName, 'FastifyHookHandler', guardShapesImport)}
67
+ } from './${modelName}Handlers${ext}'
68
+ import type { RouteConfig, FastifyHookHandler } from '../routeConfig.target${ext}'
69
+ import { parseQueryParams } from '../parseQueryParams${ext}'
70
+ import { sanitizeKeys } from '../misc${ext}'
71
+ import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
72
+ import { mapError, transformResult, HttpError } from '../operationRuntime${ext}'
73
+
74
+ ${generateRouteConfigType(modelName, 'FastifyHookHandler', guardShapesImport, importStyle, 'fastify')}
70
75
  const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
71
76
 
72
77
  const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
@@ -181,7 +186,7 @@ export async function ${routerFunctionName}<TCtx = unknown>(
181
186
  if (qbEnabled) {
182
187
  const qbConfig = getQueryBuilderConfig(config)
183
188
  if (qbConfig) {
184
- try { require('../queryBuilder').startQueryBuilder(qbConfig) } catch (err) { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) }
189
+ try { require('../queryBuilder${ext}').startQueryBuilder(qbConfig) } catch (err) { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) }
185
190
  }
186
191
  }
187
192
 
@@ -1,16 +1,21 @@
1
1
  import { DMMF } from '@prisma/generator-helper'
2
2
  import { toCamelCase } from '../utils/strings'
3
3
  import { generateRouteConfigType } from './generateRouteConfigType'
4
+ import { ImportStyle } from '../utils/resolveImportStyle'
5
+ import { importExt } from '../utils/importExt'
4
6
 
5
7
  export function generateHonoRouterFunction({
6
8
  model,
7
9
  enums,
8
10
  guardShapesImport,
11
+ importStyle,
9
12
  }: {
10
13
  model: DMMF.Model
11
14
  enums: DMMF.DatamodelEnum[]
12
15
  guardShapesImport: string | null
16
+ importStyle: ImportStyle
13
17
  }): string {
18
+ const ext = importExt(importStyle)
14
19
  const modelName = model.name
15
20
  const prefix = toCamelCase(modelName)
16
21
  const modelNameLower = modelName.toLowerCase()
@@ -61,14 +66,15 @@ import {
61
66
  ${prefix}Aggregate,
62
67
  ${prefix}Count,
63
68
  ${prefix}GroupBy,
64
- } from './${modelName}Handlers'
65
- import type { RouteConfig, HonoHookHandler } from '../routeConfig.target'
66
- import { parseQueryParams } from '../parseQueryParams'
67
- import { sanitizeKeys } from '../misc'
68
- import { buildModelOpenApi } from '../buildModelOpenApi'
69
- import { mapError, transformResult, HttpError } from '../operationRuntime'
70
-
71
- ${generateRouteConfigType(modelName, 'HonoHookHandler', guardShapesImport)}
69
+ } from './${modelName}Handlers${ext}'
70
+ import type { RouteConfig, HonoHookHandler } from '../routeConfig.target${ext}'
71
+ import { parseQueryParams } from '../parseQueryParams${ext}'
72
+ import { sanitizeKeys } from '../misc${ext}'
73
+ import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
74
+ import { mapError, transformResult, HttpError } from '../operationRuntime${ext}'
75
+
76
+ ${generateRouteConfigType(modelName, 'HonoHookHandler', guardShapesImport, importStyle, 'hono')}
77
+
72
78
  type HonoVariables = {
73
79
  prisma: any
74
80
  postgres?: any
@@ -1,11 +1,16 @@
1
1
  import { Target } from '../constants'
2
+ import { ImportStyle } from '../utils/resolveImportStyle'
3
+ import { importExt } from '../utils/importExt'
2
4
 
3
5
  export function generateUnifiedDocs(
4
6
  models: string[],
5
7
  target: Target = 'express',
8
+ importStyle: ImportStyle = 'none',
6
9
  ): string {
10
+ const ext = importExt(importStyle)
11
+
7
12
  const imports = models
8
- .map((model) => `import { ${model}Docs } from './${model}/${model}Docs'`)
13
+ .map((model) => `import { ${model}Docs } from './${model}/${model}Docs${ext}'`)
9
14
  .join('\n')
10
15
 
11
16
  const handlersEntries = models
@@ -17,9 +22,9 @@ export function generateUnifiedDocs(
17
22
  ? `import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'`
18
23
  : target === 'hono'
19
24
  ? `import type { Hono, Context } from 'hono'`
20
- : `import { Request, Response } from 'express'`
25
+ : `import type { Request, Response } from 'express'`
21
26
 
22
- const routeConfigImport = `import type { RouteConfig } from './routeConfig.target'`
27
+ const routeConfigImport = `import type { RouteConfig } from './routeConfig.target${ext}'`
23
28
 
24
29
  const handlerType =
25
30
  target === 'fastify'
package/src/index.ts CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- generatorHandler,
3
- GeneratorOptions,
4
- DMMF,
5
- } from '@prisma/generator-helper'
1
+ import { generatorHandler, GeneratorOptions, DMMF } from '@prisma/generator-helper'
6
2
  import path from 'path'
7
3
  import { generateUnifiedHandler } from './generators/generateUnifiedHandler'
8
4
  import { generateFastifyHandler } from './generators/generateFastifyHandler'
@@ -13,26 +9,17 @@ import { generateHonoRouterFunction } from './generators/generateRouterHono'
13
9
  import { generateScalarUIHandler } from './generators/generateUnifiedScalarUI'
14
10
  import { generateUnifiedDocs } from './generators/generateUnifiedDocs'
15
11
  import { generateQueryBuilderHelper } from './generators/generateQueryBuilderHelper'
16
- import {
17
- generateOperationRuntime,
18
- generateModelCore,
19
- } from './generators/generateOperationCore'
20
- import {
21
- getRelativeClientPath,
22
- getGuardShapesImport,
23
- } from './generators/generateImportPrismaStatement'
12
+ import { generateOperationRuntime, generateModelCore } from './generators/generateOperationCore'
13
+ import { getRelativeClientPath, getGuardShapesImport } from './generators/generateImportPrismaStatement'
24
14
  import { writeFileSafely } from './utils/writeFileSafely'
25
15
  import { copyFiles } from './utils/copyFiles'
16
+ import { resolveImportStyle } from './utils/resolveImportStyle'
26
17
  import { GENERATOR_NAME, Target } from './constants'
27
18
 
28
19
  function getTarget(options: GeneratorOptions): Target {
29
- const raw = String(
30
- (options.generator.config as Record<string, unknown>).target ?? 'express',
31
- ).toLowerCase()
20
+ const raw = String((options.generator.config as Record<string, unknown>).target ?? 'express').toLowerCase()
32
21
  if (raw === 'express' || raw === 'fastify' || raw === 'hono') return raw
33
- throw new Error(
34
- `Invalid target "${raw}". Expected "express", "fastify", or "hono".`,
35
- )
22
+ throw new Error(`Invalid target "${raw}". Expected "express", "fastify", or "hono".`)
36
23
  }
37
24
 
38
25
  generatorHandler({
@@ -46,7 +33,6 @@ generatorHandler({
46
33
 
47
34
  async onGenerate(options: GeneratorOptions) {
48
35
  const target = getTarget(options)
49
-
50
36
  const hasExplicitOutput = !!options.generator.output?.fromEnvVar
51
37
  || (options.generator.config as Record<string, unknown>).output !== undefined
52
38
 
@@ -56,52 +42,46 @@ generatorHandler({
56
42
  options.generator.output = { value: outputPath, fromEnvVar: null }
57
43
  }
58
44
 
45
+ const importStyle = resolveImportStyle(options)
46
+
59
47
  console.log(`\n═══ Prisma Generator (${target.toUpperCase()}) ═══`)
60
48
  console.log(` Target: ${target}`)
61
49
  console.log(` Output: ${options.generator.output?.value}`)
50
+ console.log(` Import style: ${importStyle}`)
62
51
 
63
- await copyFiles(options, target)
52
+ await copyFiles(options, target, importStyle)
64
53
 
65
54
  await writeFileSafely({
66
- content: generateOperationRuntime(),
55
+ content: generateOperationRuntime(importStyle),
67
56
  options,
68
57
  operation: 'operationRuntime',
69
58
  })
70
59
 
71
60
  const modelNames: string[] = []
72
-
73
61
  const generateHandler =
74
- target === 'fastify'
75
- ? generateFastifyHandler
76
- : target === 'hono'
77
- ? generateHonoHandler
78
- : generateUnifiedHandler
62
+ target === 'fastify' ? generateFastifyHandler :
63
+ target === 'hono' ? generateHonoHandler :
64
+ generateUnifiedHandler
79
65
 
80
66
  for (const model of options.dmmf.datamodel.models) {
81
- if (
82
- model.documentation &&
83
- model.documentation.includes('generator off')
84
- ) {
67
+ if (model.documentation && model.documentation.includes('generator off')) {
85
68
  console.log(` Skipping: ${model.name} (generator off)`)
86
69
  continue
87
70
  }
88
-
89
71
  modelNames.push(model.name)
90
72
 
91
73
  const relativeClientPath = getRelativeClientPath(options, model.name)
92
74
  const guardShapesImport = getGuardShapesImport(options, model.name)
93
75
 
94
76
  await writeFileSafely({
95
- content: generateModelCore({ model: model as DMMF.Model }),
77
+ content: generateModelCore({ model: model as DMMF.Model, importStyle }),
96
78
  options,
97
79
  model: model as DMMF.Model,
98
80
  operation: 'Core',
99
81
  })
100
82
 
101
83
  await writeFileSafely({
102
- content: generateHandler({
103
- model: model as DMMF.Model,
104
- }),
84
+ content: generateHandler({ model: model as DMMF.Model, importStyle } as any),
105
85
  options,
106
86
  model: model as DMMF.Model,
107
87
  operation: 'Handlers',
@@ -113,18 +93,21 @@ generatorHandler({
113
93
  model: model as DMMF.Model,
114
94
  enums: options.dmmf.datamodel.enums as DMMF.DatamodelEnum[],
115
95
  guardShapesImport,
116
- })
96
+ importStyle,
97
+ } as any)
117
98
  : target === 'hono'
118
99
  ? generateHonoRouterFunction({
119
100
  model: model as DMMF.Model,
120
101
  enums: options.dmmf.datamodel.enums as DMMF.DatamodelEnum[],
121
102
  guardShapesImport,
122
- })
103
+ importStyle,
104
+ } as any)
123
105
  : generateRouterFunction({
124
106
  model: model as DMMF.Model,
125
107
  enums: options.dmmf.datamodel.enums as DMMF.DatamodelEnum[],
126
108
  relativeClientPath,
127
109
  guardShapesImport,
110
+ importStyle,
128
111
  })
129
112
 
130
113
  await writeFileSafely({
@@ -139,7 +122,8 @@ generatorHandler({
139
122
  model: model as DMMF.Model,
140
123
  enums: options.dmmf.datamodel.enums as DMMF.DatamodelEnum[],
141
124
  target,
142
- }),
125
+ importStyle,
126
+ } as any),
143
127
  options,
144
128
  model: model as DMMF.Model,
145
129
  operation: 'Docs',
@@ -147,7 +131,7 @@ generatorHandler({
147
131
  }
148
132
 
149
133
  await writeFileSafely({
150
- content: generateUnifiedDocs(modelNames, target),
134
+ content: generateUnifiedDocs(modelNames, target, importStyle),
151
135
  options,
152
136
  operation: 'combinedDocs',
153
137
  })
@@ -160,12 +144,6 @@ generatorHandler({
160
144
 
161
145
  console.log('\n═══ Generation Complete ═══')
162
146
  console.log(`✓ ${modelNames.length} models (${target})`)
163
- console.log(`✓ OpenAPI documentation generated`)
164
- if (target === 'hono') {
165
- console.log(`✓ Query builder helper generated (not auto-started for Hono)`)
166
- } else {
167
- console.log(`✓ Query builder helper generated`)
168
- }
169
147
  console.log('')
170
148
  },
171
149
  })
@@ -2,6 +2,8 @@ import { GeneratorOptions } from '@prisma/generator-helper'
2
2
  import * as fs from 'fs'
3
3
  import * as path from 'path'
4
4
  import { Target } from '../constants'
5
+ import { ImportStyle } from './resolveImportStyle'
6
+ import { importExt } from './importExt'
5
7
 
6
8
  const SHARED_FILES = [
7
9
  'parseQueryParams.ts',
@@ -21,17 +23,11 @@ interface CopyFileOptions {
21
23
  function resolveTemplateDir(subpath: string): string {
22
24
  const fromSrc = path.join(__dirname, '..', '..', 'src', subpath)
23
25
  if (fs.existsSync(fromSrc)) return fromSrc
24
-
25
26
  const fromDist = path.join(__dirname, '..', subpath)
26
27
  if (fs.existsSync(fromDist)) return fromDist
27
-
28
28
  throw new Error(
29
- `Template directory "${subpath}" not found.\n` +
30
- ` Searched:\n` +
31
- ` ${fromSrc}\n` +
32
- ` ${fromDist}\n` +
33
- ` __dirname: ${__dirname}\n` +
34
- ` Ensure template files are included in the published package.`,
29
+ 'Template directory "' + subpath + '" not found.\n Searched:\n ' +
30
+ fromSrc + '\n ' + fromDist + '\n __dirname: ' + __dirname,
35
31
  )
36
32
  }
37
33
 
@@ -39,10 +35,26 @@ function escapeRegex(str: string): string {
39
35
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
40
36
  }
41
37
 
38
+ const RELATIVE_IMPORT_RE =
39
+ /(\b(?:import|export)\b[^'"\n;]*?\bfrom\s+|\bimport\s+|\bimport\s*\(\s*)(['"])(\.{1,2}\/[^'"\n]+)\2/g
40
+
41
+ const SKIP_EXTENSION_RE = /\.(m?[jt]sx?|json|css|svg|png|jpe?g|gif|webp|wasm)$/i
42
+
43
+ function applyExtension(content: string, style: ImportStyle): string {
44
+ if (style === 'none') return content
45
+ const ext = importExt(style)
46
+ if (!ext) return content
47
+ return content.replace(RELATIVE_IMPORT_RE, (_match, prefix, quote, spec) => {
48
+ if (SKIP_EXTENSION_RE.test(spec)) return prefix + quote + spec + quote
49
+ return prefix + quote + spec + ext + quote
50
+ })
51
+ }
52
+
42
53
  function copyFileSync(
43
54
  srcDir: string,
44
55
  destDir: string,
45
56
  filename: string,
57
+ importStyle: ImportStyle,
46
58
  options?: CopyFileOptions,
47
59
  ): string | null {
48
60
  const srcPath = path.join(srcDir, filename)
@@ -50,38 +62,33 @@ function copyFileSync(
50
62
  const destPath = path.join(destDir, destFilename)
51
63
 
52
64
  if (!fs.existsSync(srcPath)) {
53
- if (options?.required) {
54
- return `Required file not found: ${srcPath}`
55
- }
65
+ if (options?.required) return 'Required file not found: ' + srcPath
56
66
  return null
57
67
  }
58
68
 
59
69
  const destDirPath = path.dirname(destPath)
60
- if (!fs.existsSync(destDirPath)) {
61
- fs.mkdirSync(destDirPath, { recursive: true })
62
- }
70
+ if (!fs.existsSync(destDirPath)) fs.mkdirSync(destDirPath, { recursive: true })
63
71
 
64
72
  let content = fs.readFileSync(srcPath, 'utf-8')
65
73
 
66
74
  if (options?.importRewrites) {
67
75
  for (const rewrite of options.importRewrites) {
68
76
  content = content.replace(
69
- new RegExp(`from ['"]${escapeRegex(rewrite.from)}['"]`, 'g'),
70
- `from '${rewrite.to}'`,
77
+ new RegExp('from [\'"]' + escapeRegex(rewrite.from) + '[\'"]', 'g'),
78
+ "from '" + rewrite.to + "'",
71
79
  )
72
80
  }
73
81
  }
74
82
 
75
- const header = `/**
76
- * Auto-generated by prisma-generator-express
77
- * DO NOT EDIT MANUALLY
78
- */
83
+ const header =
84
+ '/**\n' +
85
+ ' * Auto-generated by prisma-generator-express\n' +
86
+ ' * DO NOT EDIT MANUALLY\n' +
87
+ ' */\n\n'
79
88
 
80
- `
89
+ if (!content.startsWith('/**')) content = header + content
81
90
 
82
- if (!content.startsWith('/**')) {
83
- content = header + content
84
- }
91
+ content = applyExtension(content, importStyle)
85
92
 
86
93
  fs.writeFileSync(destPath, content)
87
94
  return null
@@ -90,6 +97,7 @@ function copyFileSync(
90
97
  export async function copyFiles(
91
98
  options: GeneratorOptions,
92
99
  target: Target,
100
+ importStyle: ImportStyle,
93
101
  ): Promise<void> {
94
102
  const outputPath = options.generator.output?.value
95
103
  if (!outputPath) return
@@ -97,15 +105,15 @@ export async function copyFiles(
97
105
  const copyBase = resolveTemplateDir('copy')
98
106
  const errors: string[] = []
99
107
 
100
- console.log(` Copying utility files to: ${outputPath} (target: ${target})`)
108
+ console.log(' Copying utility files to: ' + outputPath + ' (target: ' + target + ')')
101
109
 
102
110
  for (const file of SHARED_FILES) {
103
- const err = copyFileSync(copyBase, outputPath, file, { required: true })
111
+ const err = copyFileSync(copyBase, outputPath, file, importStyle, { required: true })
104
112
  if (err) errors.push(err)
105
113
  }
106
114
 
107
- const targetConfigFile = `routeConfig.${target}.ts`
108
- const err = copyFileSync(copyBase, outputPath, targetConfigFile, {
115
+ const targetConfigFile = 'routeConfig.' + target + '.ts'
116
+ const err = copyFileSync(copyBase, outputPath, targetConfigFile, importStyle, {
109
117
  required: true,
110
118
  destFilename: 'routeConfig.target.ts',
111
119
  importRewrites: [{ from: './routeConfig', to: './routeConfig' }],
@@ -113,30 +121,22 @@ export async function copyFiles(
113
121
  if (err) errors.push(err)
114
122
 
115
123
  const clientDir = path.join(outputPath, 'client')
116
- if (!fs.existsSync(clientDir)) {
117
- fs.mkdirSync(clientDir, { recursive: true })
118
- }
124
+ if (!fs.existsSync(clientDir)) fs.mkdirSync(clientDir, { recursive: true })
119
125
 
120
126
  const clientSrcDir = resolveTemplateDir('client')
121
- const clientErr = copyFileSync(
122
- clientSrcDir,
123
- clientDir,
124
- 'encodeQueryParams.ts',
125
- {
126
- required: true,
127
- importRewrites: [{ from: '../copy/misc', to: '../misc' }],
128
- },
129
- )
127
+ const clientErr = copyFileSync(clientSrcDir, clientDir, 'encodeQueryParams.ts', importStyle, {
128
+ required: true,
129
+ importRewrites: [{ from: '../copy/misc', to: '../misc' }],
130
+ })
130
131
  if (clientErr) errors.push(clientErr)
131
132
 
132
133
  if (errors.length > 0) {
133
134
  throw new Error(
134
- `Failed to copy ${errors.length} required file(s):\n` +
135
- errors.map((e) => ` - ${e}`).join('\n') +
136
- `\n copyBase: ${copyBase}` +
137
- `\n __dirname: ${__dirname}`,
135
+ 'Failed to copy ' + errors.length + ' required file(s):\n' +
136
+ errors.map((e) => ' - ' + e).join('\n') +
137
+ '\n copyBase: ' + copyBase + '\n __dirname: ' + __dirname,
138
138
  )
139
139
  }
140
140
 
141
- console.log(` ✓ Utility files copied successfully`)
141
+ console.log(' ✓ Utility files copied successfully')
142
142
  }
@@ -0,0 +1,7 @@
1
+ import { ImportStyle } from './resolveImportStyle'
2
+
3
+ export function importExt(style: ImportStyle): string {
4
+ if (style === 'js') return '.js'
5
+ if (style === 'ts') return '.ts'
6
+ return ''
7
+ }
@@ -0,0 +1,187 @@
1
+ import * as fs from 'fs'
2
+ import * as path from 'path'
3
+ import type { GeneratorOptions } from '@prisma/generator-helper'
4
+
5
+ export type ImportStyle = 'none' | 'js' | 'ts'
6
+
7
+ const VALID_OVERRIDES = new Set(['auto', 'none', 'js', 'ts'])
8
+ const NODE_MODULE_KEYWORDS = new Set(['node16', 'node18', 'nodenext'])
9
+
10
+ function stripJsonComments(input: string): string {
11
+ let out = ''
12
+ let i = 0
13
+ let inString = false
14
+ let stringChar = ''
15
+ while (i < input.length) {
16
+ const c = input[i]
17
+ const next = input[i + 1]
18
+ if (inString) {
19
+ out += c
20
+ if (c === '\\' && i + 1 < input.length) {
21
+ out += input[i + 1]
22
+ i += 2
23
+ continue
24
+ }
25
+ if (c === stringChar) inString = false
26
+ i++
27
+ continue
28
+ }
29
+ if (c === '"' || c === "'") {
30
+ inString = true
31
+ stringChar = c
32
+ out += c
33
+ i++
34
+ continue
35
+ }
36
+ if (c === '/' && next === '/') {
37
+ while (i < input.length && input[i] !== '\n') i++
38
+ continue
39
+ }
40
+ if (c === '/' && next === '*') {
41
+ i += 2
42
+ while (i < input.length && !(input[i] === '*' && input[i + 1] === '/')) i++
43
+ i += 2
44
+ continue
45
+ }
46
+ out += c
47
+ i++
48
+ }
49
+ return out.replace(/,\s*(?=[\]}])/g, '')
50
+ }
51
+
52
+ function findUpwards(startDir: string, filename: string): string | null {
53
+ let dir = path.resolve(startDir)
54
+ const root = path.parse(dir).root
55
+ let result: string | null = null
56
+ let stop = false
57
+ while (!stop) {
58
+ const candidate = path.join(dir, filename)
59
+ if (fs.existsSync(candidate)) {
60
+ result = candidate
61
+ stop = true
62
+ break
63
+ }
64
+ if (dir === root) {
65
+ stop = true
66
+ break
67
+ }
68
+ const parent = path.dirname(dir)
69
+ if (parent === dir) {
70
+ stop = true
71
+ break
72
+ }
73
+ dir = parent
74
+ }
75
+ return result
76
+ }
77
+
78
+ function resolveExtendsTarget(extendsValue: string, fromFile: string): string | null {
79
+ const fromDir = path.dirname(fromFile)
80
+ if (extendsValue.startsWith('.') || path.isAbsolute(extendsValue)) {
81
+ const direct = path.resolve(fromDir, extendsValue)
82
+ if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct
83
+ const withExt = direct + '.json'
84
+ if (fs.existsSync(withExt)) return withExt
85
+ const asIndex = path.join(direct, 'tsconfig.json')
86
+ if (fs.existsSync(asIndex)) return asIndex
87
+ return null
88
+ }
89
+ try {
90
+ return require.resolve(extendsValue, { paths: [fromDir] })
91
+ } catch {
92
+ try {
93
+ return require.resolve(extendsValue + '/tsconfig.json', { paths: [fromDir] })
94
+ } catch {
95
+ return null
96
+ }
97
+ }
98
+ }
99
+
100
+ type TsConfigRaw = {
101
+ extends?: string | string[]
102
+ compilerOptions?: Record<string, unknown>
103
+ }
104
+
105
+ function readTsConfig(filePath: string, seen: Set<string> = new Set()): Record<string, unknown> {
106
+ const abs = path.resolve(filePath)
107
+ if (seen.has(abs)) return {}
108
+ seen.add(abs)
109
+
110
+ let parsed: TsConfigRaw
111
+ try {
112
+ const raw = fs.readFileSync(abs, 'utf-8')
113
+ parsed = JSON.parse(stripJsonComments(raw)) as TsConfigRaw
114
+ } catch {
115
+ return {}
116
+ }
117
+
118
+ const extendsList: string[] = Array.isArray(parsed.extends)
119
+ ? parsed.extends
120
+ : parsed.extends
121
+ ? [parsed.extends]
122
+ : []
123
+
124
+ let merged: Record<string, unknown> = {}
125
+ for (const ext of extendsList) {
126
+ const target = resolveExtendsTarget(ext, abs)
127
+ if (target) merged = { ...merged, ...readTsConfig(target, seen) }
128
+ }
129
+ if (parsed.compilerOptions) merged = { ...merged, ...parsed.compilerOptions }
130
+ return merged
131
+ }
132
+
133
+ function readPackageType(startDir: string): string | null {
134
+ const pkgPath = findUpwards(startDir, 'package.json')
135
+ if (!pkgPath) return null
136
+ try {
137
+ const json = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
138
+ return typeof json.type === 'string' ? json.type : null
139
+ } catch {
140
+ return null
141
+ }
142
+ }
143
+
144
+ export function resolveImportStyle(options: GeneratorOptions): ImportStyle {
145
+ const config = (options.generator.config || {}) as Record<string, string>
146
+ const raw = typeof config.importStyle === 'string'
147
+ ? config.importStyle.toLowerCase()
148
+ : 'auto'
149
+
150
+ if (!VALID_OVERRIDES.has(raw)) {
151
+ console.warn(
152
+ '[prisma-generator-express] Invalid importStyle "' + config.importStyle +
153
+ '". Expected one of: auto, none, js, ts. Falling back to auto.',
154
+ )
155
+ } else if (raw !== 'auto') {
156
+ return raw as ImportStyle
157
+ }
158
+
159
+ const outputPath = options.generator.output?.value
160
+ const schemaDir = options.schemaPath ? path.dirname(options.schemaPath) : process.cwd()
161
+ const startDir = outputPath ? path.dirname(outputPath) : schemaDir
162
+
163
+ const tsconfigPath =
164
+ findUpwards(startDir, 'tsconfig.json') ||
165
+ findUpwards(schemaDir, 'tsconfig.json')
166
+
167
+ const compilerOptions: Record<string, unknown> = tsconfigPath
168
+ ? readTsConfig(tsconfigPath)
169
+ : {}
170
+
171
+ if (compilerOptions.allowImportingTsExtensions === true) return 'ts'
172
+
173
+ const moduleVal = String(compilerOptions.module ?? '').toLowerCase()
174
+ const moduleResolution = String(compilerOptions.moduleResolution ?? '').toLowerCase()
175
+
176
+ if (NODE_MODULE_KEYWORDS.has(moduleVal) || NODE_MODULE_KEYWORDS.has(moduleResolution)) {
177
+ return 'js'
178
+ }
179
+ if (moduleResolution === 'bundler' || moduleResolution === 'classic' || moduleResolution === 'node') {
180
+ return 'none'
181
+ }
182
+
183
+ const pkgType = readPackageType(startDir) || readPackageType(schemaDir)
184
+ if (pkgType === 'module') return 'js'
185
+
186
+ return 'none'
187
+ }