@kubb/plugin-faker 5.0.0-beta.10 → 5.0.0-beta.100

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.
@@ -1,341 +0,0 @@
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
- })
@@ -1,85 +0,0 @@
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
- })
package/src/types.ts DELETED
@@ -1,179 +0,0 @@
1
- import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
- import type { PrinterFakerNodes } from './printers/printerFaker.ts'
3
-
4
- /**
5
- * Resolver for Faker that provides naming methods for mock functions.
6
- */
7
- export type ResolverFaker = Resolver &
8
- ast.OperationParamsResolver & {
9
- /**
10
- * Resolves the faker function name for a schema.
11
- *
12
- * @example Resolving faker function names
13
- * `resolver.resolveName('show pet by id') // -> 'showPetById'`
14
- */
15
- resolveName(this: ResolverFaker, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
16
- /**
17
- * Resolves the output file name for a faker module.
18
- *
19
- * @example Resolving faker file names
20
- * `resolver.resolvePathName('show pet by id', 'file') // -> 'showPetById'`
21
- */
22
- resolvePathName(this: ResolverFaker, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
23
- /**
24
- * Resolves the faker function name for a request body.
25
- *
26
- * @example Resolving data function names
27
- * `resolver.resolveDataName(node) // -> 'createPetsData'`
28
- */
29
- resolveDataName(this: ResolverFaker, node: ast.OperationNode): string
30
- /**
31
- * Resolves the faker function name for a response by status code.
32
- *
33
- * @example Response status names
34
- * `resolver.resolveResponseStatusName(node, 200) // -> 'listPetsStatus200'`
35
- */
36
- resolveResponseStatusName(this: ResolverFaker, node: ast.OperationNode, statusCode: ast.StatusCode): string
37
- /**
38
- * Resolves the faker function name for the response union.
39
- *
40
- * @example Response union names
41
- * `resolver.resolveResponseName(node) // -> 'listPetsResponse'`
42
- */
43
- resolveResponseName(this: ResolverFaker, node: ast.OperationNode): string
44
- /**
45
- * Resolves the faker function name for the response collection.
46
- *
47
- * @example Responses collection names
48
- * `resolver.resolveResponsesName(node) // -> 'listPetsResponses'`
49
- */
50
- resolveResponsesName(this: ResolverFaker, node: ast.OperationNode): string
51
- /**
52
- * Resolves the faker function name for path parameters.
53
- *
54
- * @example Path parameters names
55
- * `resolver.resolvePathParamsName(node, param) // -> 'showPetByIdPathPetId'`
56
- */
57
- resolvePathParamsName(this: ResolverFaker, node: ast.OperationNode, param: ast.ParameterNode): string
58
- /**
59
- * Resolves the faker function name for query parameters.
60
- *
61
- * @example Query parameters names
62
- * `resolver.resolveQueryParamsName(node, param) // -> 'listPetsQueryLimit'`
63
- */
64
- resolveQueryParamsName(this: ResolverFaker, node: ast.OperationNode, param: ast.ParameterNode): string
65
- /**
66
- * Resolves the faker function name for header parameters.
67
- *
68
- * @example Header parameters names
69
- * `resolver.resolveHeaderParamsName(node, param) // -> 'deletePetHeaderApiKey'`
70
- */
71
- resolveHeaderParamsName(this: ResolverFaker, node: ast.OperationNode, param: ast.ParameterNode): string
72
- }
73
-
74
- export type Options = {
75
- /**
76
- * Specify the export location for the files and define the behavior of the output.
77
- * @default { path: 'mocks', barrelType: 'named' }
78
- */
79
- output?: Output
80
- /**
81
- * Group the Faker mocks based on the provided name.
82
- */
83
- group?: Group
84
- /**
85
- * Tags, operations, or paths to exclude from generation.
86
- */
87
- exclude?: Array<Exclude>
88
- /**
89
- * Tags, operations, or paths to include in generation.
90
- */
91
- include?: Array<Include>
92
- /**
93
- * Override options for specific tags, operations, or paths.
94
- */
95
- override?: Array<Override<ResolvedOptions>>
96
- /**
97
- * Parser to use when formatting date/time values as strings.
98
- *
99
- * @default 'faker'
100
- */
101
- dateParser?: 'faker' | 'dayjs' | 'moment' | (string & {})
102
- /**
103
- * Generator to use for RegExp patterns.
104
- *
105
- * @default 'faker'
106
- */
107
- regexGenerator?: 'faker' | 'randexp'
108
- /**
109
- * Provide per-property faker expressions keyed by property name.
110
- */
111
- mapper?: Record<string, string>
112
- /**
113
- * Locale for generating mock data.
114
- * Imports the matching localized `@faker-js/faker` instance so names, addresses,
115
- * and phone numbers reflect the target region.
116
- *
117
- * @default 'en'
118
- *
119
- * @example German
120
- * `locale: 'de'`
121
- *
122
- * @example Austrian German
123
- * `locale: 'de_AT'`
124
- *
125
- * @see https://fakerjs.dev/api/localization.html
126
- */
127
- locale?: string
128
- /**
129
- * Seed faker for deterministic output.
130
- */
131
- seed?: number | number[]
132
- /**
133
- * Apply casing to parameter names to match your configuration.
134
- */
135
- paramsCasing?: 'camelcase'
136
- /**
137
- * Additional generators alongside the default generators.
138
- */
139
- generators?: Array<Generator<PluginFaker>>
140
- /**
141
- * Override naming conventions for function names and types.
142
- */
143
- resolver?: Partial<ResolverFaker> & ThisType<ResolverFaker>
144
- /**
145
- * AST visitor to transform generated nodes.
146
- */
147
- transformer?: ast.Visitor
148
- /**
149
- * Override individual faker printer node handlers.
150
- */
151
- printer?: {
152
- nodes?: PrinterFakerNodes
153
- }
154
- }
155
-
156
- type ResolvedOptions = {
157
- output: Output
158
- group: Group | undefined
159
- exclude: NonNullable<Options['exclude']>
160
- include: Options['include']
161
- override: NonNullable<Options['override']>
162
- dateParser: NonNullable<Options['dateParser']>
163
- regexGenerator: NonNullable<Options['regexGenerator']>
164
- mapper: NonNullable<Options['mapper']>
165
- seed: NonNullable<Options['seed']> | undefined
166
- locale: Options['locale']
167
- paramsCasing: Options['paramsCasing']
168
- printer: Options['printer']
169
- }
170
-
171
- export type PluginFaker = PluginFactoryOptions<'plugin-faker', Options, ResolvedOptions, ResolverFaker>
172
-
173
- declare global {
174
- namespace Kubb {
175
- interface PluginRegistry {
176
- 'plugin-faker': PluginFaker
177
- }
178
- }
179
- }