@kubb/plugin-faker 5.0.0-alpha.9 → 5.0.0-beta.10

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 (44) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +25 -7
  3. package/dist/Faker-BMgoFj8b.d.ts +27 -0
  4. package/dist/Faker-CWtonujy.js +334 -0
  5. package/dist/Faker-CWtonujy.js.map +1 -0
  6. package/dist/Faker-D39THFJ-.cjs +418 -0
  7. package/dist/Faker-D39THFJ-.cjs.map +1 -0
  8. package/dist/components.cjs +2 -2
  9. package/dist/components.d.ts +2 -31
  10. package/dist/components.js +1 -1
  11. package/dist/fakerGenerator-B-QnVz9o.d.ts +9 -0
  12. package/dist/fakerGenerator-B-XuVREg.cjs +570 -0
  13. package/dist/fakerGenerator-B-XuVREg.cjs.map +1 -0
  14. package/dist/fakerGenerator-DH6hN3yb.js +560 -0
  15. package/dist/fakerGenerator-DH6hN3yb.js.map +1 -0
  16. package/dist/generators.cjs +1 -1
  17. package/dist/generators.d.ts +2 -505
  18. package/dist/generators.js +1 -1
  19. package/dist/index.cjs +237 -84
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.ts +28 -4
  22. package/dist/index.js +229 -83
  23. package/dist/index.js.map +1 -1
  24. package/dist/printerFaker-W0pLunAj.d.ts +213 -0
  25. package/extension.yaml +357 -0
  26. package/package.json +48 -51
  27. package/src/components/Faker.tsx +124 -78
  28. package/src/generators/fakerGenerator.tsx +233 -134
  29. package/src/index.ts +7 -2
  30. package/src/plugin.ts +60 -121
  31. package/src/printers/printerFaker.ts +341 -0
  32. package/src/resolvers/resolverFaker.ts +85 -0
  33. package/src/types.ts +134 -81
  34. package/src/utils.ts +268 -0
  35. package/dist/components-BkBIov4R.js +0 -419
  36. package/dist/components-BkBIov4R.js.map +0 -1
  37. package/dist/components-IdP8GXXX.cjs +0 -461
  38. package/dist/components-IdP8GXXX.cjs.map +0 -1
  39. package/dist/fakerGenerator-CYUCNH3Q.cjs +0 -204
  40. package/dist/fakerGenerator-CYUCNH3Q.cjs.map +0 -1
  41. package/dist/fakerGenerator-M5oCrPmy.js +0 -200
  42. package/dist/fakerGenerator-M5oCrPmy.js.map +0 -1
  43. package/dist/types-r7BubMLO.d.ts +0 -132
  44. package/src/parser.ts +0 -453
package/src/plugin.ts CHANGED
@@ -1,149 +1,88 @@
1
- import path from 'node:path'
2
1
  import { camelCase } from '@internals/utils'
3
- import { createPlugin, type Group, getBarrelFiles, getMode } from '@kubb/core'
4
- import { OperationGenerator, pluginOasName, SchemaGenerator } from '@kubb/plugin-oas'
2
+ import { definePlugin, type Group } from '@kubb/core'
5
3
  import { pluginTsName } from '@kubb/plugin-ts'
6
4
  import { fakerGenerator } from './generators/fakerGenerator.tsx'
5
+ import { resolverFaker } from './resolvers/resolverFaker.ts'
7
6
  import type { PluginFaker } from './types.ts'
8
7
 
8
+ /**
9
+ * Canonical plugin name for `@kubb/plugin-faker`, used in driver lookups and warnings.
10
+ */
9
11
  export const pluginFakerName = 'plugin-faker' satisfies PluginFaker['name']
10
12
 
11
- export const pluginFaker = createPlugin<PluginFaker>((options) => {
13
+ /**
14
+ * Generates Faker mock data factories from OpenAPI/AST specification.
15
+ *
16
+ * Creates randomized test data and mock helpers from schema definitions.
17
+ *
18
+ * @example
19
+ * `import pluginFaker from '@kubb/plugin-faker'; export default defineConfig({ plugins: [pluginFaker({ output: { path: 'mocks' } })], })`
20
+ */
21
+ export const pluginFaker = definePlugin<PluginFaker>((options) => {
12
22
  const {
13
23
  output = { path: 'mocks', barrelType: 'named' },
14
24
  seed,
25
+ locale,
15
26
  group,
16
27
  exclude = [],
17
28
  include,
18
29
  override = [],
19
- transformers = {},
20
30
  mapper = {},
21
- unknownType = 'any',
22
- emptySchemaType = unknownType,
23
- dateType = 'string',
24
- integerType = 'number',
25
31
  dateParser = 'faker',
26
- generators = [fakerGenerator].filter(Boolean),
32
+ generators: userGenerators = [],
27
33
  regexGenerator = 'faker',
28
34
  paramsCasing,
29
- contentType,
35
+ printer,
36
+ resolver: userResolver,
37
+ transformer: userTransformer,
30
38
  } = options
31
39
 
32
- // @deprecated Will be removed in v5 when collisionDetection defaults to true
33
- const usedEnumNames = {}
34
-
35
- return {
36
- name: pluginFakerName,
37
- options: {
38
- output,
39
- transformers,
40
- seed,
41
- dateType,
42
- integerType,
43
- unknownType,
44
- emptySchemaType,
45
- dateParser,
46
- mapper,
47
- override,
48
- regexGenerator,
49
- paramsCasing,
50
- group,
51
- usedEnumNames,
52
- },
53
- pre: [pluginOasName, pluginTsName],
54
- resolvePath(baseName, pathMode, options) {
55
- const root = path.resolve(this.config.root, this.config.output.path)
56
- const mode = pathMode ?? getMode(path.resolve(root, output.path))
57
-
58
- if (mode === 'single') {
59
- /**
60
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
61
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
62
- */
63
- return path.resolve(root, output.path)
64
- }
65
-
66
- if (group && (options?.group?.path || options?.group?.tag)) {
67
- const groupName: Group['name'] = group?.name
40
+ const groupConfig = group
41
+ ? ({
42
+ ...group,
43
+ name: group.name
68
44
  ? group.name
69
- : (ctx) => {
70
- if (group?.type === 'path') {
45
+ : (ctx: { group: string }) => {
46
+ if (group.type === 'path') {
71
47
  return `${ctx.group.split('/')[1]}`
72
48
  }
73
- return `${camelCase(ctx.group)}Controller`
74
- }
75
-
76
- return path.resolve(
77
- root,
78
- output.path,
79
- groupName({
80
- group: group.type === 'path' ? options.group.path! : options.group.tag!,
81
- }),
82
- baseName,
83
- )
84
- }
85
-
86
- return path.resolve(root, output.path, baseName)
87
- },
88
- resolveName(name, type) {
89
- const resolvedName = camelCase(name, {
90
- prefix: type ? 'create' : undefined,
91
- isFile: type === 'file',
92
- })
93
-
94
- if (type) {
95
- return transformers?.name?.(resolvedName, type) || resolvedName
96
- }
97
49
 
98
- return resolvedName
99
- },
100
- async install() {
101
- const root = path.resolve(this.config.root, this.config.output.path)
102
- const mode = getMode(path.resolve(root, output.path))
103
- const oas = await this.getOas()
104
-
105
- const schemaGenerator = new SchemaGenerator(this.plugin.options, {
106
- fabric: this.fabric,
107
- oas,
108
- driver: this.driver,
109
- events: this.events,
110
- plugin: this.plugin,
111
- contentType,
112
- include: undefined,
113
- override,
114
- mode,
115
- output: output.path,
116
- })
117
-
118
- const schemaFiles = await schemaGenerator.build(...generators)
119
- await this.upsertFile(...schemaFiles)
120
-
121
- const operationGenerator = new OperationGenerator(this.plugin.options, {
122
- fabric: this.fabric,
123
- oas,
124
- driver: this.driver,
125
- events: this.events,
126
- plugin: this.plugin,
127
- contentType,
128
- exclude,
129
- include,
130
- override,
131
- mode,
132
- })
133
-
134
- const operationFiles = await operationGenerator.build(...generators)
135
- await this.upsertFile(...operationFiles)
136
-
137
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
138
- type: output.barrelType ?? 'named',
139
- root,
140
- output,
141
- meta: {
142
- pluginName: this.plugin.name,
143
- },
144
- })
50
+ return `${camelCase(ctx.group)}Controller`
51
+ },
52
+ } satisfies Group)
53
+ : undefined
145
54
 
146
- await this.upsertFile(...barrelFiles)
55
+ return {
56
+ name: pluginFakerName,
57
+ options,
58
+ dependencies: [pluginTsName],
59
+ hooks: {
60
+ 'kubb:plugin:setup'(ctx) {
61
+ ctx.setOptions({
62
+ output,
63
+ seed,
64
+ locale,
65
+ exclude,
66
+ include,
67
+ override,
68
+ group: groupConfig,
69
+ mapper,
70
+ dateParser,
71
+ regexGenerator,
72
+ paramsCasing,
73
+ printer,
74
+ })
75
+ ctx.setResolver(userResolver ? { ...resolverFaker, ...userResolver } : resolverFaker)
76
+ if (userTransformer) {
77
+ ctx.setTransformer(userTransformer)
78
+ }
79
+ ctx.addGenerator(fakerGenerator)
80
+ for (const generator of userGenerators) {
81
+ ctx.addGenerator(generator)
82
+ }
83
+ },
147
84
  },
148
85
  }
149
86
  })
87
+
88
+ export default pluginFaker
@@ -0,0 +1,341 @@
1
+ import { stringify, toRegExpString } from '@internals/utils'
2
+ import { ast } from '@kubb/core'
3
+ import type { PluginFaker, ResolverFaker } from '../types.ts'
4
+
5
+ /**
6
+ * Partial printer nodes for Faker generation, mapping schema types to output strings.
7
+ */
8
+ export type PrinterFakerNodes = ast.PrinterPartial<string, PrinterFakerOptions>
9
+
10
+ /**
11
+ * Configuration options for the Faker printer, including resolvers, mappers, and cyclic schema tracking.
12
+ */
13
+ export type PrinterFakerOptions = {
14
+ dateParser?: PluginFaker['resolvedOptions']['dateParser']
15
+ regexGenerator?: PluginFaker['resolvedOptions']['regexGenerator']
16
+ mapper?: PluginFaker['resolvedOptions']['mapper']
17
+ resolver: ResolverFaker
18
+ typeName?: string
19
+ schemaName?: string
20
+ nestedInObject?: boolean
21
+ nodes?: PrinterFakerNodes
22
+ /**
23
+ * Names of schemas that participate in a circular dependency chain.
24
+ * Properties whose schema transitively references one of these are emitted
25
+ * as lazy getters so that user overrides via the `data` parameter prevent
26
+ * the recursive faker call from ever executing (avoiding stack overflow).
27
+ */
28
+ cyclicSchemas?: ReadonlySet<string>
29
+ }
30
+
31
+ /**
32
+ * Factory options for the Faker printer, defining input/output types and configuration.
33
+ */
34
+ export type PrinterFakerFactory = ast.PrinterFactoryOptions<'faker', PrinterFakerOptions, string, string>
35
+
36
+ const fakerKeywordMapper = {
37
+ any: () => 'undefined',
38
+ unknown: () => 'undefined',
39
+ void: () => 'undefined',
40
+ number: (min?: number, max?: number) => {
41
+ if (max !== undefined && min !== undefined) {
42
+ return `faker.number.float({ min: ${min}, max: ${max} })`
43
+ }
44
+
45
+ if (max !== undefined) {
46
+ return `faker.number.float({ max: ${max} })`
47
+ }
48
+
49
+ if (min !== undefined) {
50
+ return `faker.number.float({ min: ${min} })`
51
+ }
52
+
53
+ return 'faker.number.float()'
54
+ },
55
+ integer: (min?: number, max?: number) => {
56
+ if (max !== undefined && min !== undefined) {
57
+ return `faker.number.int({ min: ${min}, max: ${max} })`
58
+ }
59
+
60
+ if (max !== undefined) {
61
+ return `faker.number.int({ max: ${max} })`
62
+ }
63
+
64
+ if (min !== undefined) {
65
+ return `faker.number.int({ min: ${min} })`
66
+ }
67
+
68
+ return 'faker.number.int()'
69
+ },
70
+ bigint: () => 'faker.number.bigInt()',
71
+ string: (min?: number, max?: number) => {
72
+ if (max !== undefined && min !== undefined) {
73
+ return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`
74
+ }
75
+
76
+ if (max !== undefined) {
77
+ return `faker.string.alpha({ length: ${max} })`
78
+ }
79
+
80
+ if (min !== undefined) {
81
+ return `faker.string.alpha({ length: ${min} })`
82
+ }
83
+
84
+ return 'faker.string.alpha()'
85
+ },
86
+ boolean: () => 'faker.datatype.boolean()',
87
+ null: () => 'null',
88
+ array: (items: string[] = [], min?: number, max?: number) => {
89
+ if (items.length > 1) {
90
+ return `faker.helpers.arrayElements([${items.join(', ')}])`
91
+ }
92
+
93
+ const item = items.at(0)
94
+
95
+ if (min !== undefined && max !== undefined) {
96
+ return `faker.helpers.multiple(() => (${item}), { count: { min: ${min}, max: ${max} }})`
97
+ }
98
+
99
+ if (min !== undefined) {
100
+ return `faker.helpers.multiple(() => (${item}), { count: ${min} })`
101
+ }
102
+
103
+ if (max !== undefined) {
104
+ return `faker.helpers.multiple(() => (${item}), { count: { min: 0, max: ${max} }})`
105
+ }
106
+
107
+ return `faker.helpers.multiple(() => (${item}))`
108
+ },
109
+ tuple: (items: string[] = []) => `[${items.join(', ')}]`,
110
+ enum: (items: Array<string | number | boolean | undefined> = [], type = 'any') => `faker.helpers.arrayElement<${type}>([${items.join(', ')}])`,
111
+ union: (items: string[] = []) => `faker.helpers.arrayElement<any>([${items.join(', ')}])`,
112
+ datetime: () => 'faker.date.anytime().toISOString()',
113
+ date: (representation: 'date' | 'string' = 'string', parser: PluginFaker['resolvedOptions']['dateParser'] = 'faker') => {
114
+ if (representation === 'string') {
115
+ if (parser !== 'faker') {
116
+ return `${parser}(faker.date.anytime()).format("YYYY-MM-DD")`
117
+ }
118
+
119
+ return 'faker.date.anytime().toISOString().substring(0, 10)'
120
+ }
121
+
122
+ if (parser !== 'faker') {
123
+ throw new Error(`type '${representation}' and parser '${parser}' can not work together`)
124
+ }
125
+
126
+ return 'faker.date.anytime()'
127
+ },
128
+ time: (representation: 'date' | 'string' = 'string', parser: PluginFaker['resolvedOptions']['dateParser'] = 'faker') => {
129
+ if (representation === 'string') {
130
+ if (parser !== 'faker') {
131
+ return `${parser}(faker.date.anytime()).format("HH:mm:ss")`
132
+ }
133
+
134
+ return 'faker.date.anytime().toISOString().substring(11, 19)'
135
+ }
136
+
137
+ if (parser !== 'faker') {
138
+ throw new Error(`type '${representation}' and parser '${parser}' can not work together`)
139
+ }
140
+
141
+ return 'faker.date.anytime()'
142
+ },
143
+ uuid: () => 'faker.string.uuid()',
144
+ url: () => 'faker.internet.url()',
145
+ and: (items: string[] = []) => {
146
+ if (items.length === 0) {
147
+ return '{}'
148
+ }
149
+
150
+ if (items.length === 1) {
151
+ return items[0] ?? '{}'
152
+ }
153
+
154
+ return `{...${items.join(', ...')}}`
155
+ },
156
+ matches: (value = '', regexGenerator: 'faker' | 'randexp' = 'faker') => {
157
+ if (regexGenerator === 'randexp') {
158
+ return `${toRegExpString(value, 'RandExp')}.gen()`
159
+ }
160
+
161
+ return `faker.helpers.fromRegExp("${value}")`
162
+ },
163
+ email: () => 'faker.internet.email()',
164
+ blob: () => 'faker.image.url() as unknown as Blob',
165
+ } as const
166
+
167
+ function getEnumValues(node: ast.EnumSchemaNode): Array<string | number | boolean | undefined> {
168
+ if (node.namedEnumValues?.length) {
169
+ return node.namedEnumValues.map((item) => item.value as string | number | boolean | undefined)
170
+ }
171
+
172
+ return (node.enumValues ?? []) as Array<string | number | boolean | undefined>
173
+ }
174
+
175
+ function parseEnumValue(value: string | number | boolean | undefined) {
176
+ if (typeof value === 'string') {
177
+ return stringify(value)
178
+ }
179
+
180
+ return value
181
+ }
182
+
183
+ /**
184
+ * Creates a Faker printer that generates mock data generation code from schema nodes.
185
+ * Handles circular references gracefully by emitting memoizing getters for cyclic properties.
186
+ */
187
+ export const printerFaker: (options: PrinterFakerOptions) => ast.Printer<PrinterFakerFactory> = ast.definePrinter<PrinterFakerFactory>((options) => {
188
+ const printNested = (node: ast.SchemaNode, overrideOptions: Partial<PrinterFakerOptions> = {}): string => {
189
+ return (
190
+ printerFaker({
191
+ ...options,
192
+ ...overrideOptions,
193
+ nodes: options.nodes,
194
+ }).print(node) ?? 'undefined'
195
+ )
196
+ }
197
+
198
+ return {
199
+ name: 'faker',
200
+ options,
201
+ nodes: {
202
+ any: () => fakerKeywordMapper.any(),
203
+ unknown: () => fakerKeywordMapper.unknown(),
204
+ void: () => fakerKeywordMapper.void(),
205
+ boolean: () => fakerKeywordMapper.boolean(),
206
+ null: () => fakerKeywordMapper.null(),
207
+ string(node) {
208
+ if (node.pattern) {
209
+ return fakerKeywordMapper.matches(node.pattern, this.options.regexGenerator)
210
+ }
211
+
212
+ return fakerKeywordMapper.string(node.min, node.max)
213
+ },
214
+ email: () => fakerKeywordMapper.email(),
215
+ url: () => fakerKeywordMapper.url(),
216
+ uuid: () => fakerKeywordMapper.uuid(),
217
+ number(node) {
218
+ return fakerKeywordMapper.number(node.min, node.max)
219
+ },
220
+ integer(node) {
221
+ return fakerKeywordMapper.integer(node.min, node.max)
222
+ },
223
+ bigint: () => fakerKeywordMapper.bigint(),
224
+ blob: () => fakerKeywordMapper.blob(),
225
+ datetime: () => fakerKeywordMapper.datetime(),
226
+ date(node) {
227
+ return fakerKeywordMapper.date(node.representation ?? 'string', this.options.dateParser)
228
+ },
229
+ time(node) {
230
+ return fakerKeywordMapper.time(node.representation ?? 'string', this.options.dateParser)
231
+ },
232
+ ref(node) {
233
+ // Parser-generated refs (with $ref) carry raw schema names that need resolving.
234
+ // Use the canonical name from the $ref path — node.name may have been overridden
235
+ // (e.g. by single-member allOf flatten using the property-derived child name).
236
+ // Inline refs (without $ref) from faker utils already carry resolved helper names.
237
+ const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name ?? node.schema?.name) : (node.name ?? node.schema?.name)
238
+
239
+ if (!refName) {
240
+ throw new Error('Name not defined for ref node')
241
+ }
242
+
243
+ if (this.options.schemaName && refName === this.options.schemaName) {
244
+ return 'undefined as any'
245
+ }
246
+
247
+ // Internal helper refs (for generated response/data helpers) are already
248
+ // emitted with resolver output and should not be transformed twice.
249
+ const resolvedName = node.ref ? this.options.resolver.resolveName(refName) : refName
250
+
251
+ if (!this.options.nestedInObject) {
252
+ return `${resolvedName}(data)`
253
+ }
254
+
255
+ return `${resolvedName}()`
256
+ },
257
+ enum(node) {
258
+ return fakerKeywordMapper.enum(getEnumValues(node).map(parseEnumValue), this.options.typeName)
259
+ },
260
+ union(node): string {
261
+ const items: string[] = (node.members ?? [])
262
+ .map((member) =>
263
+ printNested(member, {
264
+ nestedInObject: true,
265
+ }),
266
+ )
267
+ .filter((item): item is string => Boolean(item))
268
+
269
+ return fakerKeywordMapper.union(items)
270
+ },
271
+ intersection(node): string {
272
+ const items: string[] = (node.members ?? [])
273
+ .map((member) =>
274
+ printNested(member, {
275
+ nestedInObject: true,
276
+ }),
277
+ )
278
+ .filter((item): item is string => Boolean(item))
279
+
280
+ return fakerKeywordMapper.and(items)
281
+ },
282
+ array(node): string {
283
+ const items: string[] = (node.items ?? [])
284
+ .map((member) =>
285
+ printNested(member, {
286
+ typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : undefined,
287
+ nestedInObject: true,
288
+ }),
289
+ )
290
+ .filter((item): item is string => Boolean(item))
291
+
292
+ return fakerKeywordMapper.array(items, node.min, node.max)
293
+ },
294
+ tuple(node): string {
295
+ const items: string[] = (node.items ?? [])
296
+ .map((member, index) =>
297
+ printNested(member, {
298
+ typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[${index}]` : undefined,
299
+ nestedInObject: true,
300
+ }),
301
+ )
302
+ .filter((item): item is string => Boolean(item))
303
+
304
+ return fakerKeywordMapper.tuple(items)
305
+ },
306
+ object(node): string {
307
+ const cyclicSchemas = this.options.cyclicSchemas
308
+ const properties = (node.properties ?? [])
309
+ .map((property): string => {
310
+ if (this.options.mapper && Object.hasOwn(this.options.mapper, property.name)) {
311
+ return `"${property.name}": ${this.options.mapper[property.name]}`
312
+ }
313
+
314
+ const value: string =
315
+ printNested(property.schema, {
316
+ typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[${JSON.stringify(property.name)}]` : undefined,
317
+ nestedInObject: true,
318
+ }) ?? 'undefined'
319
+
320
+ // When the property's schema transitively references a schema that is
321
+ // part of a circular dependency (other than the current schema itself),
322
+ // emit a memoizing lazy getter. On first access it computes the value,
323
+ // replaces itself with a plain data property via Object.defineProperty,
324
+ // and returns the cached value – so every subsequent read is stable.
325
+ if (cyclicSchemas && ast.containsCircularRef(property.schema, { circularSchemas: cyclicSchemas, excludeName: this.options.schemaName })) {
326
+ return `get ${property.name}() { const _value = ${value}; Object.defineProperty(this, ${JSON.stringify(property.name)}, { value: _value, configurable: true, writable: true, enumerable: true }); return _value }`
327
+ }
328
+
329
+ return `"${property.name}": ${value}`
330
+ })
331
+ .join(',')
332
+
333
+ return `{${properties}}`
334
+ },
335
+ ...options.nodes,
336
+ },
337
+ print(node) {
338
+ return this.transform(node) ?? null
339
+ },
340
+ }
341
+ })
@@ -0,0 +1,85 @@
1
+ import { createHash } from 'node:crypto'
2
+ import path from 'node:path'
3
+ import { camelCase, isValidVarName } from '@internals/utils'
4
+ import { defineResolver, PluginDriver } from '@kubb/core'
5
+ import type { PluginFaker } from '../types.ts'
6
+
7
+ /**
8
+ * Naming convention resolver for Faker plugin.
9
+ *
10
+ * Provides default naming helpers using camelCase with a `create` prefix for factory functions and files.
11
+ *
12
+ * @example
13
+ * `resolverFaker.default('list pets', 'function') // → 'createListPets'`
14
+ */
15
+ export const resolverFaker = defineResolver<PluginFaker>(() => {
16
+ return {
17
+ name: 'default',
18
+ pluginName: 'plugin-faker',
19
+ default(name, type) {
20
+ const resolvedName = camelCase(name, { isFile: type === 'file', prefix: 'create' })
21
+
22
+ if (type === 'file' || isValidVarName(resolvedName)) {
23
+ return resolvedName
24
+ }
25
+
26
+ return `_${resolvedName}`
27
+ },
28
+ resolveName(name, type) {
29
+ return this.default(name, type)
30
+ },
31
+ resolvePathName(name, type) {
32
+ return this.default(name, type)
33
+ },
34
+ resolveFile({ name, extname, tag, path: groupPath }, context) {
35
+ const pathMode = PluginDriver.getMode(path.resolve(context.root, context.output.path))
36
+ const baseName = `${pathMode === 'single' ? '' : this.resolveName(name, 'file')}${extname}` as `${string}.${string}`
37
+ const filePath = this.resolvePath(
38
+ {
39
+ baseName,
40
+ pathMode,
41
+ tag,
42
+ path: groupPath,
43
+ },
44
+ context,
45
+ )
46
+
47
+ return {
48
+ kind: 'File',
49
+ id: createHash('sha256').update(filePath).digest('hex'),
50
+ name: path.basename(filePath, extname),
51
+ path: filePath,
52
+ baseName,
53
+ extname,
54
+ meta: { pluginName: this.pluginName },
55
+ sources: [],
56
+ imports: [],
57
+ exports: [],
58
+ }
59
+ },
60
+ resolveParamName(node, param) {
61
+ return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
62
+ },
63
+ resolveDataName(node) {
64
+ return this.resolveName(`${node.operationId} Data`)
65
+ },
66
+ resolveResponseStatusName(node, statusCode) {
67
+ return this.resolveName(`${node.operationId} Status ${statusCode}`)
68
+ },
69
+ resolveResponseName(node) {
70
+ return this.resolveName(`${node.operationId} Response`)
71
+ },
72
+ resolveResponsesName(node) {
73
+ return this.resolveName(`${node.operationId} Responses`)
74
+ },
75
+ resolvePathParamsName(node, param) {
76
+ return this.resolveParamName(node, param)
77
+ },
78
+ resolveQueryParamsName(node, param) {
79
+ return this.resolveParamName(node, param)
80
+ },
81
+ resolveHeaderParamsName(node, param) {
82
+ return this.resolveParamName(node, param)
83
+ },
84
+ }
85
+ })