svelte-reflector 1.3.1 → 1.3.3
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/core/method/Method.d.ts +1 -0
- package/dist/core/method/MethodBuilder.js +8 -3
- package/dist/core/method/MethodResponseAnalyzer.d.ts +2 -0
- package/dist/core/method/MethodResponseAnalyzer.js +21 -6
- package/dist/core/method/generators/MethodApiCallBuilder.js +2 -1
- package/dist/core/method/generators/MethodGenerator.js +4 -1
- package/dist/core/module/ModuleMethodProcessor.js +7 -7
- package/dist/main.d.ts +29 -0
- package/dist/main.js +142 -6
- package/dist/method.d.ts +1 -0
- package/dist/module.js +1 -1
- package/dist/props/primitive.property.d.ts +2 -0
- package/dist/props/primitive.property.js +19 -7
- package/package.json +1 -1
|
@@ -24,10 +24,11 @@ export class MethodBuilder {
|
|
|
24
24
|
apiType,
|
|
25
25
|
parameters: this.requestAnalyzer.paths,
|
|
26
26
|
hasEnumResponse: this.responseAnalyzer.hasEnumResponse,
|
|
27
|
+
isPrimitiveResponse: this.responseAnalyzer.isPrimitiveResponse,
|
|
27
28
|
},
|
|
28
29
|
props,
|
|
29
30
|
};
|
|
30
|
-
const responseTypeInterface = this.buildResponseTypeInterface(analyzers.request.responseType);
|
|
31
|
+
const responseTypeInterface = this.buildResponseTypeInterface(analyzers.request.responseType, analyzers.request.isPrimitiveResponse);
|
|
31
32
|
return new Method({
|
|
32
33
|
name,
|
|
33
34
|
endpoint: operation.endpoint,
|
|
@@ -44,7 +45,11 @@ export class MethodBuilder {
|
|
|
44
45
|
return "listAll";
|
|
45
46
|
return extracted ?? this.apiTypeAnalyzer.analyze(operation).apiType;
|
|
46
47
|
}
|
|
47
|
-
buildResponseTypeInterface(responseType) {
|
|
48
|
-
|
|
48
|
+
buildResponseTypeInterface(responseType, isPrimitiveResponse) {
|
|
49
|
+
if (!responseType)
|
|
50
|
+
return "null";
|
|
51
|
+
if (isPrimitiveResponse)
|
|
52
|
+
return responseType;
|
|
53
|
+
return `${responseType}Interface`;
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -2,7 +2,9 @@ import type { ResponsesObject } from "../../types/open-api-spec.interface.js";
|
|
|
2
2
|
export declare class MethodResponseAnalyzer {
|
|
3
3
|
responseType: string | null;
|
|
4
4
|
hasEnumResponse: boolean;
|
|
5
|
+
isPrimitiveResponse: boolean;
|
|
5
6
|
analyze(responses: ResponsesObject): void;
|
|
7
|
+
private normalizePrimitive;
|
|
6
8
|
private isRef;
|
|
7
9
|
private componentName;
|
|
8
10
|
private getFromContent;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
const PRIMITIVE_RESPONSE_TYPES = new Set(["string", "number", "boolean", "integer", "any", "object", "array"]);
|
|
1
2
|
export class MethodResponseAnalyzer {
|
|
2
3
|
responseType = null;
|
|
3
4
|
hasEnumResponse = false;
|
|
5
|
+
isPrimitiveResponse = false;
|
|
4
6
|
analyze(responses) {
|
|
5
7
|
for (const response of Object.values(responses)) {
|
|
6
8
|
if (!response || this.isRef(response))
|
|
@@ -8,11 +10,21 @@ export class MethodResponseAnalyzer {
|
|
|
8
10
|
const schemaOrType = this.getFromContent(response.content);
|
|
9
11
|
const type = this.typeFromSchemaOrType(schemaOrType);
|
|
10
12
|
if (type !== undefined) {
|
|
11
|
-
this.
|
|
13
|
+
this.isPrimitiveResponse = PRIMITIVE_RESPONSE_TYPES.has(type);
|
|
14
|
+
this.responseType = this.normalizePrimitive(type);
|
|
12
15
|
break;
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
}
|
|
19
|
+
normalizePrimitive(type) {
|
|
20
|
+
if (type === "integer")
|
|
21
|
+
return "number";
|
|
22
|
+
if (type === "array")
|
|
23
|
+
return "unknown[]";
|
|
24
|
+
if (type === "object")
|
|
25
|
+
return "unknown";
|
|
26
|
+
return type;
|
|
27
|
+
}
|
|
16
28
|
isRef(v) {
|
|
17
29
|
return !!v && typeof v === "object" && "$ref" in v;
|
|
18
30
|
}
|
|
@@ -46,11 +58,14 @@ export class MethodResponseAnalyzer {
|
|
|
46
58
|
this.hasEnumResponse = true;
|
|
47
59
|
return this.extractEnumType(schema);
|
|
48
60
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
if (schema.allOf) {
|
|
62
|
+
for (const entry of schema.allOf) {
|
|
63
|
+
if (this.isRef(entry))
|
|
64
|
+
continue;
|
|
65
|
+
const t = this.typeFromProperties(entry.properties);
|
|
66
|
+
if (t !== undefined)
|
|
67
|
+
return t;
|
|
68
|
+
}
|
|
54
69
|
}
|
|
55
70
|
if (schema.type === "array" && schema.items) {
|
|
56
71
|
const items = schema.items;
|
|
@@ -31,9 +31,10 @@ export class MethodApiCallBuilder {
|
|
|
31
31
|
}
|
|
32
32
|
buildEntityCall(method, responseType) {
|
|
33
33
|
const rType = method.analyzers.request.responseType;
|
|
34
|
+
const isPrimitive = method.analyzers.request.isPrimitiveResponse;
|
|
34
35
|
const querys = this.buildQuerys(method.analyzers.props.querys);
|
|
35
36
|
const entityName = treatByUppercase(rType ?? "");
|
|
36
|
-
const buildedThisResponseType = rType
|
|
37
|
+
const buildedThisResponseType = rType && !isPrimitive
|
|
37
38
|
? `this.${entityName} = new ${method.analyzers.request.responseType}({ data: response })`
|
|
38
39
|
: "";
|
|
39
40
|
const inside = `
|
|
@@ -62,7 +62,7 @@ export class MethodGenerator {
|
|
|
62
62
|
return `/** ${method.description ?? ""} */`;
|
|
63
63
|
}
|
|
64
64
|
buildMethodReturn(method) {
|
|
65
|
-
const { attributeType, responseType, hasEnumResponse } = method.analyzers.request;
|
|
65
|
+
const { attributeType, responseType, hasEnumResponse, isPrimitiveResponse } = method.analyzers.request;
|
|
66
66
|
if (attributeType === "list") {
|
|
67
67
|
return "this.list";
|
|
68
68
|
}
|
|
@@ -72,6 +72,9 @@ export class MethodGenerator {
|
|
|
72
72
|
if (hasEnumResponse) {
|
|
73
73
|
return "response.data";
|
|
74
74
|
}
|
|
75
|
+
if (isPrimitiveResponse) {
|
|
76
|
+
return "response";
|
|
77
|
+
}
|
|
75
78
|
return `new ${responseType}({ data: response })`;
|
|
76
79
|
}
|
|
77
80
|
}
|
|
@@ -7,21 +7,21 @@ export class ModuleMethodProcessor {
|
|
|
7
7
|
shouldSkipMethod(method) {
|
|
8
8
|
const { bodyType, responseType, attributeType } = method.request;
|
|
9
9
|
if (bodyType === "string") {
|
|
10
|
-
createDangerMessage(`
|
|
10
|
+
createDangerMessage(`Method ${method.name} was skipped because it has an invalid body.`);
|
|
11
11
|
return true;
|
|
12
12
|
}
|
|
13
13
|
const isNullResponse = !responseType || responseType === "null";
|
|
14
14
|
if ((attributeType === "entity" || attributeType === "list") && isNullResponse) {
|
|
15
|
-
createDangerMessage(`
|
|
15
|
+
createDangerMessage(`Method ${method.name} was skipped because it has a null response.`);
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
|
-
//
|
|
18
|
+
// Check that all endpoint path params are declared as parameters
|
|
19
19
|
const endpointParams = [...method.endpoint.matchAll(/\{(\w+)\}/g)].map((m) => m[1]).filter(Boolean);
|
|
20
20
|
if (endpointParams.length > 0) {
|
|
21
21
|
const declaredPaths = new Set(method.paths.map((p) => p.name));
|
|
22
22
|
const undeclared = endpointParams.filter((p) => !declaredPaths.has(p));
|
|
23
23
|
if (undeclared.length > 0) {
|
|
24
|
-
createDangerMessage(`
|
|
24
|
+
createDangerMessage(`Method ${method.name} was skipped because it has undeclared path params: ${undeclared.join(", ")}`);
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -42,7 +42,7 @@ export class ModuleMethodProcessor {
|
|
|
42
42
|
const cookieMap = new Map();
|
|
43
43
|
for (const method of methods) {
|
|
44
44
|
const { request, headers, cookies, paths, querys } = method;
|
|
45
|
-
const { bodyType, responseType, attributeType } = request;
|
|
45
|
+
const { bodyType, responseType, attributeType, isPrimitiveResponse } = request;
|
|
46
46
|
headers.forEach((h) => headerMap.set(h.name, h));
|
|
47
47
|
cookies.forEach((c) => cookieMap.set(c.name, c));
|
|
48
48
|
paths.forEach((p) => pathMap.set(p.name, p));
|
|
@@ -57,7 +57,7 @@ export class ModuleMethodProcessor {
|
|
|
57
57
|
this.imports.addReflectorImport("isFormValid");
|
|
58
58
|
}
|
|
59
59
|
buildedMethods.push(method.build());
|
|
60
|
-
if (attributeType === "entity") {
|
|
60
|
+
if (attributeType === "entity" && !isPrimitiveResponse) {
|
|
61
61
|
const entityName = treatByUppercase(method.request.responseType ?? "");
|
|
62
62
|
methodsAttributes.add(`${entityName} = $state<${responseType} | undefined>()`);
|
|
63
63
|
methodsInit.add(`this.clear${capitalizeFirstLetter(entityName)}()`);
|
|
@@ -74,7 +74,7 @@ export class ModuleMethodProcessor {
|
|
|
74
74
|
if (bodyType) {
|
|
75
75
|
entries.add(bodyType);
|
|
76
76
|
}
|
|
77
|
-
if (responseType && responseType !== "response") {
|
|
77
|
+
if (responseType && responseType !== "response" && !isPrimitiveResponse) {
|
|
78
78
|
entries.add(`type ${responseType}Interface`);
|
|
79
79
|
entries.add(responseType);
|
|
80
80
|
}
|
package/dist/main.d.ts
CHANGED
|
@@ -28,6 +28,35 @@ export declare class Reflector {
|
|
|
28
28
|
typeImports: TypeImports;
|
|
29
29
|
apiImport: string;
|
|
30
30
|
});
|
|
31
|
+
/**
|
|
32
|
+
* Promotes inline object request-body schemas to named components.
|
|
33
|
+
* Without this, `MethodBodyAnalyzer` falls back to `schema.type` ("object")
|
|
34
|
+
* because inline bodies aren't referenced by a `$ref`, and the resulting
|
|
35
|
+
* body type name collides with the JS primitive — so the generated module
|
|
36
|
+
* imports a non-existent `{ object }` class and the schema file is never
|
|
37
|
+
* emitted (resolveTransitiveDeps finds nothing in schemaMap).
|
|
38
|
+
*/
|
|
39
|
+
private extractInlineBodies;
|
|
40
|
+
private promoteInlineBody;
|
|
41
|
+
/**
|
|
42
|
+
* Promotes inline response `data` schemas to named components.
|
|
43
|
+
* Responses in this API follow a `{success, data, message}` envelope where
|
|
44
|
+
* `data` is either declared directly or overlaid via `allOf`. When `data` is
|
|
45
|
+
* an inline object/array (no `$ref`), the response analyzer can only
|
|
46
|
+
* extract the raw `schema.type` ("object"/"array"), which isn't a valid
|
|
47
|
+
* class name. Promoting `data` to a named component gives the analyzer a
|
|
48
|
+
* proper `$ref` to resolve.
|
|
49
|
+
*/
|
|
50
|
+
private extractInlineResponses;
|
|
51
|
+
private promoteInlineResponse;
|
|
52
|
+
private findSuccessResponse;
|
|
53
|
+
/**
|
|
54
|
+
* Locates the inline `data` sub-schema inside a response envelope, whether
|
|
55
|
+
* it lives at `schema.properties.data` or inside an `allOf` entry. Returns
|
|
56
|
+
* a handle that lets the caller swap the inline schema for a `$ref`.
|
|
57
|
+
*/
|
|
58
|
+
private findInlineDataHolder;
|
|
59
|
+
private hasExtractableContent;
|
|
31
60
|
private getSchemas;
|
|
32
61
|
private getModules;
|
|
33
62
|
build(): Promise<{}>;
|
package/dist/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { Source } from "./file.js";
|
|
4
|
-
import { getEndpoint, isReferenceObject, splitByUppercase } from "./helpers/helpers.js";
|
|
4
|
+
import { capitalizeFirstLetter, getEndpoint, isReferenceObject, splitByUppercase } from "./helpers/helpers.js";
|
|
5
5
|
import { Schema } from "./schema.js";
|
|
6
6
|
import { Module } from "./module.js";
|
|
7
7
|
import { generatedDir } from "./vars.global.js";
|
|
@@ -28,18 +28,154 @@ export class Reflector {
|
|
|
28
28
|
const { components, paths, fieldConfigs, typeImports, apiImport } = params;
|
|
29
29
|
this.apiImport = apiImport;
|
|
30
30
|
this.typeImports = typeImports;
|
|
31
|
-
//
|
|
31
|
+
// Clear global state between runs
|
|
32
32
|
enumTypes.clear();
|
|
33
33
|
mockedParams.clear();
|
|
34
34
|
this.clearSrc();
|
|
35
35
|
this.components = components;
|
|
36
36
|
this.paths = paths;
|
|
37
|
+
this.extractInlineBodies();
|
|
38
|
+
this.extractInlineResponses();
|
|
37
39
|
this.files = [];
|
|
38
40
|
this.modules = this.getModules();
|
|
39
41
|
const { propertiesNames, schemas } = this.getSchemas({ fieldConfigs });
|
|
40
42
|
this.propertiesNames = propertiesNames;
|
|
41
43
|
this.schemas = schemas;
|
|
42
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Promotes inline object request-body schemas to named components.
|
|
47
|
+
* Without this, `MethodBodyAnalyzer` falls back to `schema.type` ("object")
|
|
48
|
+
* because inline bodies aren't referenced by a `$ref`, and the resulting
|
|
49
|
+
* body type name collides with the JS primitive — so the generated module
|
|
50
|
+
* imports a non-existent `{ object }` class and the schema file is never
|
|
51
|
+
* emitted (resolveTransitiveDeps finds nothing in schemaMap).
|
|
52
|
+
*/
|
|
53
|
+
extractInlineBodies() {
|
|
54
|
+
this.components.schemas ??= {};
|
|
55
|
+
const schemas = this.components.schemas;
|
|
56
|
+
const usedNames = new Set(Object.keys(schemas));
|
|
57
|
+
const httpMethods = ["get", "put", "post", "delete", "patch", "options", "head", "trace"];
|
|
58
|
+
for (const [pathStr, pathItem] of Object.entries(this.paths)) {
|
|
59
|
+
if (!pathItem)
|
|
60
|
+
continue;
|
|
61
|
+
for (const httpMethod of httpMethods) {
|
|
62
|
+
this.promoteInlineBody(pathItem[httpMethod], httpMethod, pathStr, schemas, usedNames);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
promoteInlineBody(operation, httpMethod, pathStr, schemas, usedNames) {
|
|
67
|
+
if (!operation?.requestBody || "$ref" in operation.requestBody)
|
|
68
|
+
return;
|
|
69
|
+
const firstEntry = Object.values(operation.requestBody.content ?? {})[0];
|
|
70
|
+
const schema = firstEntry?.schema;
|
|
71
|
+
if (!schema || "$ref" in schema || !schema.properties)
|
|
72
|
+
return;
|
|
73
|
+
const base = operation.operationId
|
|
74
|
+
? capitalizeFirstLetter(operation.operationId)
|
|
75
|
+
: capitalizeFirstLetter(httpMethod) + pathStr.replaceAll(/[^a-zA-Z0-9]/g, "");
|
|
76
|
+
let name = `${base}Body`;
|
|
77
|
+
let suffix = 2;
|
|
78
|
+
while (usedNames.has(name)) {
|
|
79
|
+
name = `${base}Body${suffix++}`;
|
|
80
|
+
}
|
|
81
|
+
usedNames.add(name);
|
|
82
|
+
schemas[name] = schema;
|
|
83
|
+
firstEntry.schema = { $ref: `#/components/schemas/${name}` };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Promotes inline response `data` schemas to named components.
|
|
87
|
+
* Responses in this API follow a `{success, data, message}` envelope where
|
|
88
|
+
* `data` is either declared directly or overlaid via `allOf`. When `data` is
|
|
89
|
+
* an inline object/array (no `$ref`), the response analyzer can only
|
|
90
|
+
* extract the raw `schema.type` ("object"/"array"), which isn't a valid
|
|
91
|
+
* class name. Promoting `data` to a named component gives the analyzer a
|
|
92
|
+
* proper `$ref` to resolve.
|
|
93
|
+
*/
|
|
94
|
+
extractInlineResponses() {
|
|
95
|
+
this.components.schemas ??= {};
|
|
96
|
+
const schemas = this.components.schemas;
|
|
97
|
+
const usedNames = new Set(Object.keys(schemas));
|
|
98
|
+
const httpMethods = ["get", "put", "post", "delete", "patch", "options", "head", "trace"];
|
|
99
|
+
for (const [pathStr, pathItem] of Object.entries(this.paths)) {
|
|
100
|
+
if (!pathItem)
|
|
101
|
+
continue;
|
|
102
|
+
for (const httpMethod of httpMethods) {
|
|
103
|
+
this.promoteInlineResponse(pathItem[httpMethod], httpMethod, pathStr, schemas, usedNames);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
promoteInlineResponse(operation, httpMethod, pathStr, schemas, usedNames) {
|
|
108
|
+
if (!operation?.responses)
|
|
109
|
+
return;
|
|
110
|
+
const successResponse = this.findSuccessResponse(operation.responses);
|
|
111
|
+
if (!successResponse || "$ref" in successResponse || !successResponse.content)
|
|
112
|
+
return;
|
|
113
|
+
const firstEntry = Object.values(successResponse.content)[0];
|
|
114
|
+
const schema = firstEntry?.schema;
|
|
115
|
+
if (!schema || "$ref" in schema)
|
|
116
|
+
return;
|
|
117
|
+
const dataHolder = this.findInlineDataHolder(schema);
|
|
118
|
+
if (!dataHolder)
|
|
119
|
+
return;
|
|
120
|
+
const base = operation.operationId
|
|
121
|
+
? capitalizeFirstLetter(operation.operationId)
|
|
122
|
+
: capitalizeFirstLetter(httpMethod) + pathStr.replaceAll(/[^a-zA-Z0-9]/g, "");
|
|
123
|
+
let name = `${base}Response`;
|
|
124
|
+
let suffix = 2;
|
|
125
|
+
while (usedNames.has(name)) {
|
|
126
|
+
name = `${base}Response${suffix++}`;
|
|
127
|
+
}
|
|
128
|
+
usedNames.add(name);
|
|
129
|
+
schemas[name] = dataHolder.data;
|
|
130
|
+
dataHolder.replace({ $ref: `#/components/schemas/${name}` });
|
|
131
|
+
}
|
|
132
|
+
findSuccessResponse(responses) {
|
|
133
|
+
for (const [code, response] of Object.entries(responses)) {
|
|
134
|
+
if (code.startsWith("2"))
|
|
135
|
+
return response;
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Locates the inline `data` sub-schema inside a response envelope, whether
|
|
141
|
+
* it lives at `schema.properties.data` or inside an `allOf` entry. Returns
|
|
142
|
+
* a handle that lets the caller swap the inline schema for a `$ref`.
|
|
143
|
+
*/
|
|
144
|
+
findInlineDataHolder(schema) {
|
|
145
|
+
const holders = [];
|
|
146
|
+
if (schema.properties)
|
|
147
|
+
holders.push(schema);
|
|
148
|
+
if (schema.allOf) {
|
|
149
|
+
for (const entry of schema.allOf) {
|
|
150
|
+
if (!("$ref" in entry) && entry.properties) {
|
|
151
|
+
holders.push(entry);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const holder of holders) {
|
|
156
|
+
const data = holder.properties?.["data"];
|
|
157
|
+
if (!data || "$ref" in data)
|
|
158
|
+
continue;
|
|
159
|
+
if (!this.hasExtractableContent(data))
|
|
160
|
+
continue;
|
|
161
|
+
return {
|
|
162
|
+
data,
|
|
163
|
+
replace: (ref) => {
|
|
164
|
+
holder.properties["data"] = ref;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
hasExtractableContent(schema) {
|
|
171
|
+
if (schema.properties)
|
|
172
|
+
return true;
|
|
173
|
+
if (schema.items)
|
|
174
|
+
return true;
|
|
175
|
+
if (schema.allOf?.length)
|
|
176
|
+
return true;
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
43
179
|
getSchemas(params) {
|
|
44
180
|
const { fieldConfigs } = params;
|
|
45
181
|
const componentSchemas = this.components.schemas;
|
|
@@ -65,7 +201,7 @@ export class Reflector {
|
|
|
65
201
|
for (const schema of schemas) {
|
|
66
202
|
this.schemaMap.set(schema.name, schema);
|
|
67
203
|
}
|
|
68
|
-
console.log(`${schemas.length} schemas
|
|
204
|
+
console.log(`${schemas.length} schemas generated successfully.`);
|
|
69
205
|
return { schemas, propertiesNames };
|
|
70
206
|
}
|
|
71
207
|
getModules() {
|
|
@@ -73,8 +209,8 @@ export class Reflector {
|
|
|
73
209
|
for (const [path, methods] of Object.entries(this.paths)) {
|
|
74
210
|
const rawMethod = Object.values(methods)[0];
|
|
75
211
|
const rawName = rawMethod.operationId?.split("_")[0] ?? "";
|
|
76
|
-
const
|
|
77
|
-
const moduleName =
|
|
212
|
+
const nameParts = splitByUppercase(rawName).filter((x) => x !== "Controller");
|
|
213
|
+
const moduleName = nameParts.join("");
|
|
78
214
|
const baseEndpoint = getEndpoint(path);
|
|
79
215
|
const operations = Object.entries(methods).map(([apiMethod, attributes]) => {
|
|
80
216
|
return {
|
|
@@ -146,7 +282,7 @@ export class Reflector {
|
|
|
146
282
|
export default mockedParams
|
|
147
283
|
`;
|
|
148
284
|
this.mockedParamsFile.changeData(mockedFile);
|
|
149
|
-
//
|
|
285
|
+
// Save all files in parallel, awaiting completion
|
|
150
286
|
await Promise.all([
|
|
151
287
|
...moduleSchemaFiles.map((f) => f.save()),
|
|
152
288
|
this.typesSrc.save(),
|
package/dist/method.d.ts
CHANGED
package/dist/module.js
CHANGED
|
@@ -15,6 +15,8 @@ export declare class PrimitiveProp {
|
|
|
15
15
|
private readonly example;
|
|
16
16
|
private readonly fallbackExample;
|
|
17
17
|
private get effectiveType();
|
|
18
|
+
/** Non-required fields become nullable (| null) instead of optional (?) */
|
|
19
|
+
private get isEffectivelyNullable();
|
|
18
20
|
constructor(params: {
|
|
19
21
|
name: string;
|
|
20
22
|
schemaObject: SchemaObject;
|
|
@@ -14,11 +14,16 @@ export class PrimitiveProp {
|
|
|
14
14
|
get effectiveType() {
|
|
15
15
|
return this.customType ?? this.rawType;
|
|
16
16
|
}
|
|
17
|
+
/** Non-required fields become nullable (| null) instead of optional (?) */
|
|
18
|
+
get isEffectivelyNullable() {
|
|
19
|
+
return this.isNullable || !this.required;
|
|
20
|
+
}
|
|
17
21
|
constructor(params) {
|
|
18
22
|
const { name, schemaObject, required, validator, customType, isParam, isNullable } = params;
|
|
19
23
|
const { type: rawType } = schemaObject;
|
|
20
24
|
this.isNullable = !!isNullable;
|
|
21
|
-
const
|
|
25
|
+
const normalizedRawType = rawType === "integer" ? "number" : rawType;
|
|
26
|
+
const type = normalizedRawType ?? "string";
|
|
22
27
|
const { emptyExample, example } = this.getExampleAndFallback({ schemaObject, type, name });
|
|
23
28
|
this.example = example;
|
|
24
29
|
this.fallbackExample = emptyExample;
|
|
@@ -94,8 +99,15 @@ export class PrimitiveProp {
|
|
|
94
99
|
return "";
|
|
95
100
|
};
|
|
96
101
|
const buildedExample = `params?.empty || isEmpty ? ${this.fallbackExample} : ${this.example}`;
|
|
97
|
-
const
|
|
98
|
-
const
|
|
102
|
+
const effectivelyNullable = this.isEffectivelyNullable;
|
|
103
|
+
const keyExpr = effectivelyNullable ? `params?.data?.${name} ?? null` : `params?.data?.${name}`;
|
|
104
|
+
let typeParam = "";
|
|
105
|
+
if (effectivelyNullable) {
|
|
106
|
+
typeParam = `<${this.effectiveType} | null>`;
|
|
107
|
+
}
|
|
108
|
+
else if (this.customType) {
|
|
109
|
+
typeParam = `<${this.effectiveType}>`;
|
|
110
|
+
}
|
|
99
111
|
return `
|
|
100
112
|
build${typeParam}({ key: ${keyExpr}, placeholder: ${this.example}, example: ${buildedExample}, required: ${required}, ${buildedValidator()}})
|
|
101
113
|
`;
|
|
@@ -107,13 +119,13 @@ export class PrimitiveProp {
|
|
|
107
119
|
return `${this.thisDot()}${this.name} = ${this.buildedConst}`;
|
|
108
120
|
}
|
|
109
121
|
classBuild() {
|
|
110
|
-
const req = (this.required || this.
|
|
111
|
-
const nullable = this.
|
|
122
|
+
const req = (this.required || this.isEffectivelyNullable) ? "" : "?";
|
|
123
|
+
const nullable = this.isEffectivelyNullable ? " | null" : "";
|
|
112
124
|
return `${this.name}${req}: BuildedInput<${this.effectiveType}${nullable}>`;
|
|
113
125
|
}
|
|
114
126
|
interfaceBuild() {
|
|
115
|
-
const req = this.required ? "" : "?";
|
|
116
|
-
const nullable = this.
|
|
127
|
+
const req = (this.required || this.isEffectivelyNullable) ? "" : "?";
|
|
128
|
+
const nullable = this.isEffectivelyNullable ? " | null" : "";
|
|
117
129
|
return `${this.name}${req}: ${this.effectiveType}${nullable}`;
|
|
118
130
|
}
|
|
119
131
|
patchBuild() {
|