@kubb/plugin-faker 5.0.0-beta.98 → 5.0.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.
- package/dist/index.cjs +116 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +117 -53
- package/dist/index.js.map +1 -1
- package/package.json +6 -9
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
2
|
import { posix } from "node:path";
|
|
3
|
-
import { Resolver, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
3
|
+
import { Resolver, ast, containsCircularRef, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
4
4
|
import { buildParams, createFunctionParameter, createFunctionParameters, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
5
5
|
import { File, Function, jsxRenderer } from "kubb/jsx";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
@@ -576,8 +576,9 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
|
|
|
576
576
|
})();
|
|
577
577
|
const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
|
|
578
578
|
if (!useGenericOverride) {
|
|
579
|
+
const dataParamName = /\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data";
|
|
579
580
|
const params = createFunctionParameters({ params: [createFunctionParameter({
|
|
580
|
-
name:
|
|
581
|
+
name: dataParamName,
|
|
581
582
|
type: dataType,
|
|
582
583
|
optional: true
|
|
583
584
|
})] });
|
|
@@ -601,7 +602,7 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
|
|
|
601
602
|
const functionSignature = `${description ? `/**\n * @description ${jsStringEscape(description)}\n */\n ` : ""}export function ${name}<TData extends Partial<${typeName}> = object>(data?: TData)`;
|
|
602
603
|
const seedCode = seed ? `faker.seed(${JSON.stringify(seed)})\n ` : "";
|
|
603
604
|
const { cyclicSchemas, schemaName } = printer.options;
|
|
604
|
-
const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) =>
|
|
605
|
+
const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => containsCircularRef(p.schema, {
|
|
605
606
|
circularSchemas: cyclicSchemas,
|
|
606
607
|
excludeName: schemaName
|
|
607
608
|
})) ? `{
|
|
@@ -628,45 +629,64 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
|
|
|
628
629
|
}
|
|
629
630
|
//#endregion
|
|
630
631
|
//#region ../../internals/shared/src/params.ts
|
|
631
|
-
const caseParamsCache = /* @__PURE__ */ new WeakMap();
|
|
632
632
|
/**
|
|
633
|
-
*
|
|
633
|
+
* Drops parameters that share the same name, keeping the first.
|
|
634
634
|
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
635
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
636
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
637
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
638
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
638
639
|
*/
|
|
639
|
-
function
|
|
640
|
-
if (!casing) return params;
|
|
641
|
-
const cached = caseParamsCache.get(params);
|
|
642
|
-
if (cached) return cached;
|
|
643
|
-
const result = params.map((param) => ({
|
|
644
|
-
...param,
|
|
645
|
-
name: camelCase(param.name)
|
|
646
|
-
}));
|
|
647
|
-
caseParamsCache.set(params, result);
|
|
648
|
-
return result;
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* Drops parameters that collapse to the same property identity once camelCased, keeping the first.
|
|
652
|
-
*
|
|
653
|
-
* Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
|
|
654
|
-
* `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
|
|
655
|
-
* an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
|
|
656
|
-
* identity so the resulting group is collision-free regardless of the names each caller carries.
|
|
657
|
-
*/
|
|
658
|
-
function dedupeByCasedName(params) {
|
|
640
|
+
function dedupeParams(params) {
|
|
659
641
|
const seen = /* @__PURE__ */ new Set();
|
|
660
642
|
return params.filter((param) => {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
seen.add(key);
|
|
643
|
+
if (seen.has(param.name)) return false;
|
|
644
|
+
seen.add(param.name);
|
|
664
645
|
return true;
|
|
665
646
|
});
|
|
666
647
|
}
|
|
667
648
|
//#endregion
|
|
668
649
|
//#region ../../internals/shared/src/operation.ts
|
|
669
650
|
/**
|
|
651
|
+
* Builds the `ResolverFileParams` every operation generator passes to
|
|
652
|
+
* `resolver.file`: a file named `name`, tagged by the operation's first
|
|
653
|
+
* tag (or `'default'`), at the operation's path. Centralizes the entry object
|
|
654
|
+
* that was repeated at dozens of call sites across the client and query plugins.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```ts
|
|
658
|
+
* resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
|
|
659
|
+
* ```
|
|
660
|
+
*/
|
|
661
|
+
function operationFileEntry(node, name, extname = ".ts") {
|
|
662
|
+
return {
|
|
663
|
+
name,
|
|
664
|
+
extname,
|
|
665
|
+
tag: node.tags[0] ?? "default",
|
|
666
|
+
path: node.path
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
|
|
671
|
+
* current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
|
|
672
|
+
* same dependency for the same operation in one pass (a query plugin's several hook generators, the
|
|
673
|
+
* MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
|
|
674
|
+
*
|
|
675
|
+
* @example Cache `plugin-ts`'s file for the current operation
|
|
676
|
+
* ```ts
|
|
677
|
+
* const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
function resolveDependencyOperationFile(options) {
|
|
681
|
+
const { cache, node, resolver, root, output, group } = options;
|
|
682
|
+
return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
|
|
683
|
+
...operationFileEntry(node, node.operationId),
|
|
684
|
+
root,
|
|
685
|
+
output,
|
|
686
|
+
group: group ?? void 0
|
|
687
|
+
}));
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
670
690
|
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
671
691
|
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
672
692
|
*/
|
|
@@ -714,14 +734,24 @@ function resolveContentTypeVariants(entries, baseName) {
|
|
|
714
734
|
};
|
|
715
735
|
});
|
|
716
736
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
737
|
+
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
738
|
+
/**
|
|
739
|
+
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
740
|
+
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
741
|
+
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
742
|
+
* same parameters once per plugin.
|
|
743
|
+
*/
|
|
744
|
+
function getOperationParameters(node) {
|
|
745
|
+
const cached = operationParameterGroupsByNode.get(node);
|
|
746
|
+
if (cached) return cached;
|
|
747
|
+
const groups = {
|
|
748
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
749
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
750
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
751
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
724
752
|
};
|
|
753
|
+
operationParameterGroupsByNode.set(node, groups);
|
|
754
|
+
return groups;
|
|
725
755
|
}
|
|
726
756
|
//#endregion
|
|
727
757
|
//#region ../../internals/shared/src/resolver.ts
|
|
@@ -830,7 +860,41 @@ function createGroupConfig(group) {
|
|
|
830
860
|
};
|
|
831
861
|
}
|
|
832
862
|
//#endregion
|
|
863
|
+
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
864
|
+
/**
|
|
865
|
+
* Maps each member of a union or intersection schema to its transformed output, pairing every
|
|
866
|
+
* result with the original member.
|
|
867
|
+
*/
|
|
868
|
+
function mapSchemaMembers(node, transform) {
|
|
869
|
+
return (node.members ?? []).map((schema) => ({
|
|
870
|
+
schema,
|
|
871
|
+
output: transform(schema)
|
|
872
|
+
}));
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
876
|
+
* the original item.
|
|
877
|
+
*/
|
|
878
|
+
function mapSchemaItems(node, transform) {
|
|
879
|
+
return (node.items ?? []).map((schema) => ({
|
|
880
|
+
schema,
|
|
881
|
+
output: transform(schema)
|
|
882
|
+
}));
|
|
883
|
+
}
|
|
884
|
+
//#endregion
|
|
833
885
|
//#region src/printers/printerFaker.ts
|
|
886
|
+
/**
|
|
887
|
+
* Formats that put a whole number inside a `type: 'string'` schema, the way ProtoJSON encodes
|
|
888
|
+
* 64-bit integers, so the mock has to be digits rather than letters.
|
|
889
|
+
*
|
|
890
|
+
* @see https://protobuf.dev/programming-guides/json/#int64-strings
|
|
891
|
+
*/
|
|
892
|
+
const integerFormats = /* @__PURE__ */ new Set([
|
|
893
|
+
"int32",
|
|
894
|
+
"int64",
|
|
895
|
+
"uint64"
|
|
896
|
+
]);
|
|
897
|
+
const maxInt32 = 2147483647;
|
|
834
898
|
const fakerKeywordMapper = {
|
|
835
899
|
any: () => "undefined",
|
|
836
900
|
unknown: () => "undefined",
|
|
@@ -848,6 +912,10 @@ const fakerKeywordMapper = {
|
|
|
848
912
|
return "faker.number.int()";
|
|
849
913
|
},
|
|
850
914
|
bigint: () => "faker.number.bigInt()",
|
|
915
|
+
integerString: (format) => {
|
|
916
|
+
if (format === "int32") return `faker.number.int({ max: ${maxInt32} }).toString()`;
|
|
917
|
+
return "faker.number.bigInt().toString()";
|
|
918
|
+
},
|
|
851
919
|
string: (min, max) => {
|
|
852
920
|
if (max !== void 0 && min !== void 0) return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`;
|
|
853
921
|
if (max !== void 0) return `faker.string.alpha({ length: ${max} })`;
|
|
@@ -947,6 +1015,7 @@ const printerFaker = ast.createPrinter((options) => {
|
|
|
947
1015
|
null: () => fakerKeywordMapper.null(),
|
|
948
1016
|
string(node) {
|
|
949
1017
|
if (node.pattern) return fakerKeywordMapper.matches(node.pattern, this.options.regexGenerator);
|
|
1018
|
+
if (node.format && integerFormats.has(node.format)) return fakerKeywordMapper.integerString(node.format);
|
|
950
1019
|
return fakerKeywordMapper.string(node.min, node.max);
|
|
951
1020
|
},
|
|
952
1021
|
email: () => fakerKeywordMapper.email(),
|
|
@@ -981,7 +1050,7 @@ const printerFaker = ast.createPrinter((options) => {
|
|
|
981
1050
|
union(node) {
|
|
982
1051
|
const { discriminatorPropertyName } = node;
|
|
983
1052
|
const baseTypeName = this.options.typeName;
|
|
984
|
-
const items =
|
|
1053
|
+
const items = mapSchemaMembers(node, (member) => {
|
|
985
1054
|
const value = discriminatorPropertyName ? getDiscriminatorValue(member, discriminatorPropertyName) : void 0;
|
|
986
1055
|
if (baseTypeName && value !== void 0) {
|
|
987
1056
|
const typeName = `Extract<NonNullable<${baseTypeName}>, { ${JSON.stringify(discriminatorPropertyName)}: ${parseEnumValue(value)} }>`;
|
|
@@ -999,11 +1068,11 @@ const printerFaker = ast.createPrinter((options) => {
|
|
|
999
1068
|
return fakerKeywordMapper.union(items);
|
|
1000
1069
|
},
|
|
1001
1070
|
intersection(node) {
|
|
1002
|
-
const items =
|
|
1071
|
+
const items = mapSchemaMembers(node, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
|
|
1003
1072
|
return fakerKeywordMapper.and(items);
|
|
1004
1073
|
},
|
|
1005
1074
|
array(node) {
|
|
1006
|
-
const items =
|
|
1075
|
+
const items = mapSchemaItems(node, (member) => printNested(member, {
|
|
1007
1076
|
typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : void 0,
|
|
1008
1077
|
nestedInObject: true
|
|
1009
1078
|
})).map(({ output }) => output).filter((item) => Boolean(item));
|
|
@@ -1023,7 +1092,7 @@ const printerFaker = ast.createPrinter((options) => {
|
|
|
1023
1092
|
typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
|
|
1024
1093
|
nestedInObject: true
|
|
1025
1094
|
}) ?? "undefined";
|
|
1026
|
-
if (cyclicSchemas &&
|
|
1095
|
+
if (cyclicSchemas && containsCircularRef(property.schema, {
|
|
1027
1096
|
circularSchemas: cyclicSchemas,
|
|
1028
1097
|
excludeName: this.options.schemaName
|
|
1029
1098
|
})) return `get ${objectKey(property.name)}() { const _value = ${value}; Object.defineProperty(this, ${JSON.stringify(property.name)}, { value: _value, configurable: true, writable: true, enumerable: true }); return _value }`;
|
|
@@ -1172,11 +1241,7 @@ const fakerGenerator = defineGenerator({
|
|
|
1172
1241
|
const pluginTs = ctx.driver.getPlugin(pluginTsName);
|
|
1173
1242
|
if (!pluginTs) return;
|
|
1174
1243
|
const tsResolver = ctx.driver.getResolver(pluginTsName);
|
|
1175
|
-
const
|
|
1176
|
-
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters({
|
|
1177
|
-
...node,
|
|
1178
|
-
parameters: params
|
|
1179
|
-
}, { paramsCasing: "original" });
|
|
1244
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1180
1245
|
const paramGroups = [
|
|
1181
1246
|
{
|
|
1182
1247
|
params: pathParams,
|
|
@@ -1256,14 +1321,13 @@ const fakerGenerator = defineGenerator({
|
|
|
1256
1321
|
output,
|
|
1257
1322
|
group: group ?? void 0
|
|
1258
1323
|
}),
|
|
1259
|
-
typeFile:
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
path: node.path,
|
|
1324
|
+
typeFile: resolveDependencyOperationFile({
|
|
1325
|
+
cache: ctx.cache,
|
|
1326
|
+
node,
|
|
1327
|
+
resolver: tsResolver,
|
|
1264
1328
|
root,
|
|
1265
1329
|
output: pluginTs.options?.output ?? output,
|
|
1266
|
-
group: pluginTs.options?.group
|
|
1330
|
+
group: pluginTs.options?.group
|
|
1267
1331
|
})
|
|
1268
1332
|
};
|
|
1269
1333
|
function resolveMockImports(schema) {
|