svelte-reflector 1.3.3 → 1.3.4
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/api/ApiClassBuilder.d.ts +23 -0
- package/dist/core/api/ApiClassBuilder.js +125 -0
- package/dist/core/api/ApiFileBuilder.d.ts +12 -0
- package/dist/core/api/ApiFileBuilder.js +24 -0
- package/dist/core/api/ApiMethodGenerator.d.ts +17 -0
- package/dist/core/api/ApiMethodGenerator.js +176 -0
- package/dist/core/api/ApiParamProcessor.d.ts +24 -0
- package/dist/core/api/ApiParamProcessor.js +36 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +5 -0
- package/dist/core/module/ModuleClassBuilder.d.ts +1 -0
- package/dist/core/module/ModuleClassBuilder.js +5 -4
- package/dist/core/module/ModuleImports.d.ts +1 -0
- package/dist/core/module/ModuleImports.js +6 -1
- package/dist/generate-doc.js +5 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.js +6 -1
- package/dist/method.d.ts +2 -0
- package/dist/method.js +6 -0
- package/dist/module.d.ts +4 -0
- package/dist/module.js +35 -2
- package/dist/reflector.js +4 -0
- package/package.json +2 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Method } from "../../method.js";
|
|
2
|
+
import type { ModuleImports } from "../module/ModuleImports.js";
|
|
3
|
+
import type { ModuleClassBuilder } from "../module/ModuleClassBuilder.js";
|
|
4
|
+
export interface ApiEndpointBlock {
|
|
5
|
+
paramCode: string;
|
|
6
|
+
classCode: string;
|
|
7
|
+
schemaEntries: Set<string>;
|
|
8
|
+
}
|
|
9
|
+
export declare class ApiClassBuilder {
|
|
10
|
+
private readonly imports;
|
|
11
|
+
private readonly methodGenerator;
|
|
12
|
+
private readonly paramProcessor;
|
|
13
|
+
constructor(params: {
|
|
14
|
+
imports: ModuleImports;
|
|
15
|
+
classBuilder: ModuleClassBuilder;
|
|
16
|
+
});
|
|
17
|
+
build(params: {
|
|
18
|
+
method: Method;
|
|
19
|
+
}): ApiEndpointBlock | null;
|
|
20
|
+
private buildStateProperties;
|
|
21
|
+
private buildResetLines;
|
|
22
|
+
private shouldSkipMethod;
|
|
23
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { capitalizeFirstLetter, createDangerMessage, treatByUppercase } from "../../helpers/helpers.js";
|
|
2
|
+
import { ApiMethodGenerator } from "./ApiMethodGenerator.js";
|
|
3
|
+
import { ApiParamProcessor } from "./ApiParamProcessor.js";
|
|
4
|
+
export class ApiClassBuilder {
|
|
5
|
+
imports;
|
|
6
|
+
methodGenerator = new ApiMethodGenerator();
|
|
7
|
+
paramProcessor;
|
|
8
|
+
constructor(params) {
|
|
9
|
+
this.imports = params.imports;
|
|
10
|
+
this.paramProcessor = new ApiParamProcessor({
|
|
11
|
+
imports: params.imports,
|
|
12
|
+
classBuilder: params.classBuilder,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
build(params) {
|
|
16
|
+
const { method } = params;
|
|
17
|
+
if (this.shouldSkipMethod(method))
|
|
18
|
+
return null;
|
|
19
|
+
const { request, headers, cookies, paths, querys } = method;
|
|
20
|
+
const { bodyType, responseType, attributeType, isPrimitiveResponse } = request;
|
|
21
|
+
// Process per-endpoint params
|
|
22
|
+
const processedParams = this.paramProcessor.process({
|
|
23
|
+
methodName: capitalizeFirstLetter(method.name),
|
|
24
|
+
querys,
|
|
25
|
+
paths,
|
|
26
|
+
headers,
|
|
27
|
+
cookies,
|
|
28
|
+
});
|
|
29
|
+
// Build state properties
|
|
30
|
+
const stateProps = this.buildStateProperties(method);
|
|
31
|
+
// Build the call method
|
|
32
|
+
const callMethod = this.methodGenerator.generate(method);
|
|
33
|
+
// Build reset method
|
|
34
|
+
const resetLines = this.buildResetLines(method, processedParams.paramReset);
|
|
35
|
+
// Collect schema entries
|
|
36
|
+
const schemaEntries = new Set();
|
|
37
|
+
if (bodyType) {
|
|
38
|
+
schemaEntries.add(bodyType);
|
|
39
|
+
}
|
|
40
|
+
if (responseType && responseType !== "response" && !isPrimitiveResponse) {
|
|
41
|
+
schemaEntries.add(`type ${responseType}Interface`);
|
|
42
|
+
schemaEntries.add(responseType);
|
|
43
|
+
}
|
|
44
|
+
// Handle form imports
|
|
45
|
+
if (attributeType === "form" && bodyType) {
|
|
46
|
+
this.imports.addReflectorImport("isFormValid");
|
|
47
|
+
}
|
|
48
|
+
// Handle list imports
|
|
49
|
+
if (attributeType === "list") {
|
|
50
|
+
this.imports.addReflectorImport("genericArrayBundler");
|
|
51
|
+
}
|
|
52
|
+
const className = capitalizeFirstLetter(method.name);
|
|
53
|
+
const classCode = `
|
|
54
|
+
export class ${className} {
|
|
55
|
+
${stateProps.join(";")}
|
|
56
|
+
${processedParams.paramAttributes.map((a) => `${a};`).join("\n ")}
|
|
57
|
+
|
|
58
|
+
${callMethod}
|
|
59
|
+
|
|
60
|
+
reset() {
|
|
61
|
+
${resetLines.join(";")}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const paramCode = processedParams.paramClasses.join("\n");
|
|
66
|
+
return { paramCode, classCode, schemaEntries };
|
|
67
|
+
}
|
|
68
|
+
buildStateProperties(method) {
|
|
69
|
+
const { attributeType, responseType, bodyType, isPrimitiveResponse } = method.request;
|
|
70
|
+
const props = ["loading = $state<boolean>(false)"];
|
|
71
|
+
if (attributeType === "form" && bodyType) {
|
|
72
|
+
// For form endpoints, the form IS the data
|
|
73
|
+
props.push(`form = new ${bodyType}()`);
|
|
74
|
+
}
|
|
75
|
+
else if (attributeType === "list") {
|
|
76
|
+
props.push(`data = $state<${responseType}['data']>([])`);
|
|
77
|
+
props.push("totalPages = $state<number>(1)");
|
|
78
|
+
}
|
|
79
|
+
else if (attributeType === "entity" && responseType && !isPrimitiveResponse) {
|
|
80
|
+
props.push(`data = $state<${responseType} | undefined>()`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
props.push("data = $state<unknown>(undefined)");
|
|
84
|
+
}
|
|
85
|
+
return props;
|
|
86
|
+
}
|
|
87
|
+
buildResetLines(method, paramReset) {
|
|
88
|
+
const { attributeType, bodyType } = method.request;
|
|
89
|
+
const lines = [];
|
|
90
|
+
if (attributeType === "form" && bodyType) {
|
|
91
|
+
lines.push(`this.form = new ${bodyType}()`);
|
|
92
|
+
}
|
|
93
|
+
else if (attributeType === "list") {
|
|
94
|
+
lines.push("this.data = []");
|
|
95
|
+
lines.push("this.totalPages = 1");
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
lines.push("this.data = undefined");
|
|
99
|
+
}
|
|
100
|
+
lines.push(...paramReset);
|
|
101
|
+
return lines;
|
|
102
|
+
}
|
|
103
|
+
shouldSkipMethod(method) {
|
|
104
|
+
const { bodyType, responseType, attributeType } = method.request;
|
|
105
|
+
if (bodyType === "string") {
|
|
106
|
+
createDangerMessage(`Method ${method.name} was skipped because it has an invalid body.`);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
const isNullResponse = !responseType || responseType === "null";
|
|
110
|
+
if ((attributeType === "entity" || attributeType === "list") && isNullResponse) {
|
|
111
|
+
createDangerMessage(`Method ${method.name} was skipped because it has a null response.`);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
const endpointParams = [...method.endpoint.matchAll(/\{(\w+)\}/g)].map((m) => m[1]).filter(Boolean);
|
|
115
|
+
if (endpointParams.length > 0) {
|
|
116
|
+
const declaredPaths = new Set(method.paths.map((p) => p.name));
|
|
117
|
+
const undeclared = endpointParams.filter((p) => !declaredPaths.has(p));
|
|
118
|
+
if (undeclared.length > 0) {
|
|
119
|
+
createDangerMessage(`Method ${method.name} was skipped because it has undeclared path params: ${undeclared.join(", ")}`);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModuleImports } from "../module/ModuleImports.js";
|
|
2
|
+
import type { ApiEndpointBlock } from "./ApiClassBuilder.js";
|
|
3
|
+
export declare class ApiFileBuilder {
|
|
4
|
+
private readonly imports;
|
|
5
|
+
constructor(params: {
|
|
6
|
+
imports: ModuleImports;
|
|
7
|
+
});
|
|
8
|
+
build(params: {
|
|
9
|
+
endpointBlocks: ApiEndpointBlock[];
|
|
10
|
+
classImports: string;
|
|
11
|
+
}): string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class ApiFileBuilder {
|
|
2
|
+
imports;
|
|
3
|
+
constructor(params) {
|
|
4
|
+
this.imports = params.imports;
|
|
5
|
+
}
|
|
6
|
+
build(params) {
|
|
7
|
+
const { endpointBlocks, classImports } = params;
|
|
8
|
+
const reflectorImports = this.imports.buildReflectorImportsLine();
|
|
9
|
+
const enumImports = this.imports.buildEnumImportsLine();
|
|
10
|
+
const mockedImports = this.imports.buildMockedImportsLine();
|
|
11
|
+
const sections = endpointBlocks.map((block) => {
|
|
12
|
+
return `${block.paramCode}\n${block.classCode}`;
|
|
13
|
+
});
|
|
14
|
+
return `
|
|
15
|
+
${this.imports.getImportsArray().join(";")}
|
|
16
|
+
${reflectorImports}
|
|
17
|
+
${mockedImports}
|
|
18
|
+
${enumImports}
|
|
19
|
+
${classImports}
|
|
20
|
+
|
|
21
|
+
${sections.join("\n\n")}
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Method } from "../../method.js";
|
|
2
|
+
export declare class ApiMethodGenerator {
|
|
3
|
+
private readonly endpointBuilder;
|
|
4
|
+
generate(method: Method): string;
|
|
5
|
+
private buildProps;
|
|
6
|
+
private buildPathsInfo;
|
|
7
|
+
private buildApiCall;
|
|
8
|
+
private buildListCall;
|
|
9
|
+
private buildEntityCall;
|
|
10
|
+
private buildFormCall;
|
|
11
|
+
private buildDeleteCall;
|
|
12
|
+
private buildParamsType;
|
|
13
|
+
private buildDescription;
|
|
14
|
+
private buildMethodReturn;
|
|
15
|
+
private joinProps;
|
|
16
|
+
private buildQuerys;
|
|
17
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { MethodEndpointBuilder } from "../method/MethodEndpointBuilder.js";
|
|
2
|
+
export class ApiMethodGenerator {
|
|
3
|
+
endpointBuilder = new MethodEndpointBuilder();
|
|
4
|
+
generate(method) {
|
|
5
|
+
const description = this.buildDescription(method);
|
|
6
|
+
const endpoint = this.endpointBuilder.build(method.endpoint, method.paths);
|
|
7
|
+
const apiCall = this.buildApiCall(method);
|
|
8
|
+
const props = this.buildProps(method);
|
|
9
|
+
const pathsInfo = this.buildPathsInfo(method);
|
|
10
|
+
const paramsType = this.buildParamsType(method, pathsInfo);
|
|
11
|
+
return `
|
|
12
|
+
${description}
|
|
13
|
+
async call(params?: ${paramsType}) {
|
|
14
|
+
|
|
15
|
+
const behavior = params?.behavior ?? new Behavior();
|
|
16
|
+
const { onError, onSuccess } = behavior;
|
|
17
|
+
|
|
18
|
+
this.loading = true;
|
|
19
|
+
${props}
|
|
20
|
+
const endpoint = ${endpoint}
|
|
21
|
+
|
|
22
|
+
${apiCall.outside}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
${apiCall.inside}
|
|
26
|
+
await onSuccess?.(response);
|
|
27
|
+
|
|
28
|
+
return ${this.buildMethodReturn(method)};
|
|
29
|
+
} catch (e) {
|
|
30
|
+
let parsedError: ApiErrorResponse;
|
|
31
|
+
try {
|
|
32
|
+
parsedError = JSON.parse((e as Error).message) as ApiErrorResponse;
|
|
33
|
+
} catch {
|
|
34
|
+
parsedError = { error: 'unknown', message: (e as Error).message ?? String(e) };
|
|
35
|
+
}
|
|
36
|
+
return await onError?.(parsedError);
|
|
37
|
+
} finally {
|
|
38
|
+
this.loading = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
buildProps(method) {
|
|
44
|
+
const { querys, paths, cookies } = method;
|
|
45
|
+
const lines = [];
|
|
46
|
+
if (querys.length > 0) {
|
|
47
|
+
lines.push(`const { ${querys.map((x) => x.name).join(", ")} } = this.querys.bundle()`);
|
|
48
|
+
}
|
|
49
|
+
if (paths.length > 0) {
|
|
50
|
+
lines.push(`const { ${paths.map((x) => x.name).join(", ")} } = params?.paths ?? this.paths`);
|
|
51
|
+
}
|
|
52
|
+
if (cookies.length > 0) {
|
|
53
|
+
lines.push(`const cookies = this.cookies`);
|
|
54
|
+
}
|
|
55
|
+
return lines.join("\n");
|
|
56
|
+
}
|
|
57
|
+
buildPathsInfo(method) {
|
|
58
|
+
const { paths } = method;
|
|
59
|
+
if (paths.length === 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
return `{ ${paths
|
|
62
|
+
.map((p) => {
|
|
63
|
+
const type = p.rawType ?? p.type;
|
|
64
|
+
return `${p.name}: ${type}`;
|
|
65
|
+
})
|
|
66
|
+
.join("; ")} }`;
|
|
67
|
+
}
|
|
68
|
+
buildApiCall(method) {
|
|
69
|
+
const { attributeType, apiType, responseType, bodyType, hasEnumResponse } = method.request;
|
|
70
|
+
const responseTypeStr = hasEnumResponse && responseType ? responseType : method.responseTypeInterface;
|
|
71
|
+
if (attributeType === "list") {
|
|
72
|
+
return this.buildListCall(method, responseTypeStr);
|
|
73
|
+
}
|
|
74
|
+
if (attributeType === "entity") {
|
|
75
|
+
return this.buildEntityCall(method, responseTypeStr);
|
|
76
|
+
}
|
|
77
|
+
if (apiType === "post" || apiType === "put" || apiType === "patch") {
|
|
78
|
+
return this.buildFormCall(method, responseTypeStr, bodyType);
|
|
79
|
+
}
|
|
80
|
+
if (apiType === "delete") {
|
|
81
|
+
return this.buildDeleteCall(responseTypeStr);
|
|
82
|
+
}
|
|
83
|
+
return { inside: "", outside: "" };
|
|
84
|
+
}
|
|
85
|
+
buildListCall(method, responseType) {
|
|
86
|
+
const querys = this.joinProps(method.querys);
|
|
87
|
+
const inside = `
|
|
88
|
+
const response = await api.get<${responseType}, unknown>({
|
|
89
|
+
endpoint,
|
|
90
|
+
queryData: { ${querys} }
|
|
91
|
+
})
|
|
92
|
+
this.data = ${method.request.responseType}.from(response.data);
|
|
93
|
+
this.totalPages = response.totalPages;
|
|
94
|
+
`;
|
|
95
|
+
return { inside, outside: "" };
|
|
96
|
+
}
|
|
97
|
+
buildEntityCall(method, responseType) {
|
|
98
|
+
const rType = method.request.responseType;
|
|
99
|
+
const isPrimitive = method.request.isPrimitiveResponse;
|
|
100
|
+
const querys = this.buildQuerys(method.querys);
|
|
101
|
+
const buildedThisResponseType = rType && !isPrimitive
|
|
102
|
+
? `this.data = new ${rType}({ data: response })`
|
|
103
|
+
: "";
|
|
104
|
+
const inside = `
|
|
105
|
+
const response = await api.get<${responseType}, unknown>({
|
|
106
|
+
endpoint,
|
|
107
|
+
${querys}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
${buildedThisResponseType}
|
|
111
|
+
`;
|
|
112
|
+
return { inside, outside: "" };
|
|
113
|
+
}
|
|
114
|
+
buildFormCall(method, responseType, bodyType) {
|
|
115
|
+
const { apiType } = method.request;
|
|
116
|
+
const hasHeaders = method.headers.length > 0;
|
|
117
|
+
const hasData = !!bodyType;
|
|
118
|
+
const outside = [];
|
|
119
|
+
if (hasData) {
|
|
120
|
+
outside.push(`const data = this.form.bundle()`);
|
|
121
|
+
}
|
|
122
|
+
if (hasHeaders) {
|
|
123
|
+
outside.push(`const headers = this.headers.bundle()`);
|
|
124
|
+
}
|
|
125
|
+
const inside = `
|
|
126
|
+
const response = await api.${apiType}<${responseType}>({
|
|
127
|
+
endpoint,
|
|
128
|
+
${hasData ? "data," : ""}
|
|
129
|
+
${hasHeaders ? "headers," : ""}
|
|
130
|
+
})
|
|
131
|
+
`;
|
|
132
|
+
return { inside, outside: outside.join("\n") };
|
|
133
|
+
}
|
|
134
|
+
buildDeleteCall(responseType) {
|
|
135
|
+
const inside = `
|
|
136
|
+
const response = await api.delete<${responseType}, unknown>({
|
|
137
|
+
endpoint,
|
|
138
|
+
})
|
|
139
|
+
`;
|
|
140
|
+
return { inside, outside: "" };
|
|
141
|
+
}
|
|
142
|
+
buildParamsType(method, paramsPaths) {
|
|
143
|
+
const responseType = method.responseTypeInterface;
|
|
144
|
+
if (paramsPaths) {
|
|
145
|
+
return `ApiCallParams<${responseType}, ${paramsPaths}>`;
|
|
146
|
+
}
|
|
147
|
+
return `ApiCallParams<${responseType}>`;
|
|
148
|
+
}
|
|
149
|
+
buildDescription(method) {
|
|
150
|
+
return `/** ${method.description ?? ""} */`;
|
|
151
|
+
}
|
|
152
|
+
buildMethodReturn(method) {
|
|
153
|
+
const { attributeType, responseType, hasEnumResponse, isPrimitiveResponse } = method.request;
|
|
154
|
+
if (attributeType === "list") {
|
|
155
|
+
return "this.data";
|
|
156
|
+
}
|
|
157
|
+
if (!responseType) {
|
|
158
|
+
return "null";
|
|
159
|
+
}
|
|
160
|
+
if (hasEnumResponse) {
|
|
161
|
+
return "response.data";
|
|
162
|
+
}
|
|
163
|
+
if (isPrimitiveResponse) {
|
|
164
|
+
return "response";
|
|
165
|
+
}
|
|
166
|
+
return `new ${responseType}({ data: response })`;
|
|
167
|
+
}
|
|
168
|
+
joinProps(props) {
|
|
169
|
+
return props.map((x) => x.name).join(",");
|
|
170
|
+
}
|
|
171
|
+
buildQuerys(querys) {
|
|
172
|
+
if (querys.length === 0)
|
|
173
|
+
return "";
|
|
174
|
+
return `queryData: {${querys.map((q) => q.name).join(",")}}`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AttributeProp } from "../../types/types.js";
|
|
2
|
+
import type { PrimitiveProp } from "../../props/primitive.property.js";
|
|
3
|
+
import type { ModuleImports } from "../module/ModuleImports.js";
|
|
4
|
+
import type { ModuleClassBuilder } from "../module/ModuleClassBuilder.js";
|
|
5
|
+
export interface ApiProcessedParams {
|
|
6
|
+
paramClasses: string[];
|
|
7
|
+
paramAttributes: string[];
|
|
8
|
+
paramReset: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class ApiParamProcessor {
|
|
11
|
+
private readonly imports;
|
|
12
|
+
private readonly classBuilder;
|
|
13
|
+
constructor(params: {
|
|
14
|
+
imports: ModuleImports;
|
|
15
|
+
classBuilder: ModuleClassBuilder;
|
|
16
|
+
});
|
|
17
|
+
process(params: {
|
|
18
|
+
methodName: string;
|
|
19
|
+
querys: AttributeProp[];
|
|
20
|
+
paths: PrimitiveProp[];
|
|
21
|
+
headers: PrimitiveProp[];
|
|
22
|
+
cookies: PrimitiveProp[];
|
|
23
|
+
}): ApiProcessedParams;
|
|
24
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { capitalizeFirstLetter } from "../../helpers/helpers.js";
|
|
2
|
+
export class ApiParamProcessor {
|
|
3
|
+
imports;
|
|
4
|
+
classBuilder;
|
|
5
|
+
constructor(params) {
|
|
6
|
+
this.imports = params.imports;
|
|
7
|
+
this.classBuilder = params.classBuilder;
|
|
8
|
+
}
|
|
9
|
+
process(params) {
|
|
10
|
+
const { methodName, cookies, headers, paths, querys } = params;
|
|
11
|
+
const paramClasses = [];
|
|
12
|
+
const paramAttributes = [];
|
|
13
|
+
const paramReset = [];
|
|
14
|
+
const processParam = (paramData) => {
|
|
15
|
+
const { paramType, attrName, props } = paramData;
|
|
16
|
+
const className = `${capitalizeFirstLetter(methodName)}${capitalizeFirstLetter(paramType)}`;
|
|
17
|
+
paramClasses.push(this.classBuilder.buildClassProps({ props, name: paramType, className }));
|
|
18
|
+
paramAttributes.push(`${attrName} = new ${className}()`);
|
|
19
|
+
paramReset.push(`this.${attrName} = new ${className}()`);
|
|
20
|
+
};
|
|
21
|
+
const argEntries = [
|
|
22
|
+
{ attrName: "querys", props: querys },
|
|
23
|
+
{ attrName: "headers", props: headers },
|
|
24
|
+
{ attrName: "paths", props: paths },
|
|
25
|
+
{ attrName: "cookies", props: cookies },
|
|
26
|
+
];
|
|
27
|
+
for (const { attrName, props } of argEntries) {
|
|
28
|
+
if (!props.length)
|
|
29
|
+
continue;
|
|
30
|
+
this.imports.addReflectorImport("QueryBuilder");
|
|
31
|
+
const paramType = capitalizeFirstLetter(attrName);
|
|
32
|
+
processParam({ paramType, attrName, props });
|
|
33
|
+
}
|
|
34
|
+
return { paramClasses, paramAttributes, paramReset };
|
|
35
|
+
}
|
|
36
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ export { ModuleParamProcessor, type ProcessedParams } from "./module/ModuleParam
|
|
|
4
4
|
export { ModuleClassBuilder } from "./module/ModuleClassBuilder.js";
|
|
5
5
|
export { ModuleConstructorBuilder, type Form } from "./module/ModuleConstructorBuilder.js";
|
|
6
6
|
export { ModuleFileBuilder, type FileBuildParams } from "./module/ModuleFileBuilder.js";
|
|
7
|
+
export { ApiFileBuilder } from "./api/ApiFileBuilder.js";
|
|
8
|
+
export { ApiClassBuilder, type ApiEndpointBlock } from "./api/ApiClassBuilder.js";
|
|
9
|
+
export { ApiMethodGenerator } from "./api/ApiMethodGenerator.js";
|
|
10
|
+
export { ApiParamProcessor, type ApiProcessedParams } from "./api/ApiParamProcessor.js";
|
|
7
11
|
export { Method } from "./method/Method.js";
|
|
8
12
|
export { MethodBuilder } from "./method/MethodBuilder.js";
|
|
9
13
|
export { MethodApiTypeAnalyzer } from "./method/MethodApiTypeAnalyzer.js";
|
package/dist/core/index.js
CHANGED
|
@@ -5,6 +5,11 @@ export { ModuleParamProcessor } from "./module/ModuleParamProcessor.js";
|
|
|
5
5
|
export { ModuleClassBuilder } from "./module/ModuleClassBuilder.js";
|
|
6
6
|
export { ModuleConstructorBuilder } from "./module/ModuleConstructorBuilder.js";
|
|
7
7
|
export { ModuleFileBuilder } from "./module/ModuleFileBuilder.js";
|
|
8
|
+
// Api-related exports
|
|
9
|
+
export { ApiFileBuilder } from "./api/ApiFileBuilder.js";
|
|
10
|
+
export { ApiClassBuilder } from "./api/ApiClassBuilder.js";
|
|
11
|
+
export { ApiMethodGenerator } from "./api/ApiMethodGenerator.js";
|
|
12
|
+
export { ApiParamProcessor } from "./api/ApiParamProcessor.js";
|
|
8
13
|
// Method-related exports
|
|
9
14
|
export { Method } from "./method/Method.js";
|
|
10
15
|
export { MethodBuilder } from "./method/MethodBuilder.js";
|
|
@@ -5,7 +5,8 @@ export class ModuleClassBuilder {
|
|
|
5
5
|
this.imports = params.imports;
|
|
6
6
|
}
|
|
7
7
|
buildClassProps(params) {
|
|
8
|
-
const { name, props } = params;
|
|
8
|
+
const { name, props, className } = params;
|
|
9
|
+
const outputName = className ?? name;
|
|
9
10
|
const bundle = [];
|
|
10
11
|
const attributes = [];
|
|
11
12
|
if (name === "Paths") {
|
|
@@ -18,7 +19,7 @@ export class ModuleClassBuilder {
|
|
|
18
19
|
});
|
|
19
20
|
this.imports.addPageStateImport();
|
|
20
21
|
return `
|
|
21
|
-
class ${
|
|
22
|
+
class ${outputName} {
|
|
22
23
|
${attributes.join(";")}
|
|
23
24
|
}
|
|
24
25
|
`;
|
|
@@ -66,7 +67,7 @@ export class ModuleClassBuilder {
|
|
|
66
67
|
}
|
|
67
68
|
`;
|
|
68
69
|
return `
|
|
69
|
-
class ${
|
|
70
|
+
class ${outputName} {
|
|
70
71
|
${attributes.join(";")}
|
|
71
72
|
|
|
72
73
|
${constructorBuild}
|
|
@@ -100,7 +101,7 @@ export class ModuleClassBuilder {
|
|
|
100
101
|
`
|
|
101
102
|
: "";
|
|
102
103
|
return `
|
|
103
|
-
class ${
|
|
104
|
+
class ${outputName} {
|
|
104
105
|
${attributes.join(";")}
|
|
105
106
|
|
|
106
107
|
${bundleBuild}
|
|
@@ -16,6 +16,7 @@ export declare class ModuleImports {
|
|
|
16
16
|
hasEnumImports(): boolean;
|
|
17
17
|
private hasMockedImports;
|
|
18
18
|
getMockedImports(): string[];
|
|
19
|
+
private readonly typeOnlyImports;
|
|
19
20
|
buildReflectorImportsLine(): string;
|
|
20
21
|
buildEnumImportsLine(): string;
|
|
21
22
|
buildMockedImportsLine(): string;
|
|
@@ -50,11 +50,16 @@ export class ModuleImports {
|
|
|
50
50
|
getMockedImports() {
|
|
51
51
|
return Array.from(this.mockedImports);
|
|
52
52
|
}
|
|
53
|
+
typeOnlyImports = new Set(["ApiCallParams"]);
|
|
53
54
|
buildReflectorImportsLine() {
|
|
54
55
|
const imports = this.getReflectorImportsArray();
|
|
55
|
-
const regularImports = imports.filter(i => i !== "setQueryGroup");
|
|
56
|
+
const regularImports = imports.filter(i => i !== "setQueryGroup" && !this.typeOnlyImports.has(i));
|
|
57
|
+
const typeImports = imports.filter(i => this.typeOnlyImports.has(i));
|
|
56
58
|
const hasSetQueryGroup = imports.includes("setQueryGroup");
|
|
57
59
|
let result = `import { ${regularImports.join(", ")}, type ApiErrorResponse`;
|
|
60
|
+
for (const t of typeImports) {
|
|
61
|
+
result += `, type ${t}`;
|
|
62
|
+
}
|
|
58
63
|
if (hasSetQueryGroup) {
|
|
59
64
|
result += `, setQueryGroup`;
|
|
60
65
|
}
|
package/dist/generate-doc.js
CHANGED
|
@@ -84,12 +84,16 @@ export async function reflector(manual = false) {
|
|
|
84
84
|
}
|
|
85
85
|
// Lê reflector.json do projeto consumidor (opcional)
|
|
86
86
|
let apiImport = "$lib/api";
|
|
87
|
+
let experimentalFeatures = false;
|
|
87
88
|
try {
|
|
88
89
|
const reflectorJsonPath = path.resolve(process.cwd(), "reflector.json");
|
|
89
90
|
const reflectorJson = JSON.parse(fs.readFileSync(reflectorJsonPath, "utf8"));
|
|
90
91
|
if (reflectorJson.api) {
|
|
91
92
|
apiImport = reflectorJson.api;
|
|
92
93
|
}
|
|
94
|
+
if (reflectorJson.experimentalFeatures === true) {
|
|
95
|
+
experimentalFeatures = true;
|
|
96
|
+
}
|
|
93
97
|
}
|
|
94
98
|
catch {
|
|
95
99
|
// reflector.json não encontrado ou inválido — usa o padrão
|
|
@@ -99,7 +103,7 @@ export async function reflector(manual = false) {
|
|
|
99
103
|
console.warn("[reflector] OpenAPI sem components; abortando.");
|
|
100
104
|
return breakReflector();
|
|
101
105
|
}
|
|
102
|
-
const r = new Reflector({ components, paths, fieldConfigs, typeImports, apiImport });
|
|
106
|
+
const r = new Reflector({ components, paths, fieldConfigs, typeImports, apiImport, experimentalFeatures });
|
|
103
107
|
await r.build();
|
|
104
108
|
await r.localSave(data);
|
|
105
109
|
return breakReflector();
|
package/dist/main.d.ts
CHANGED
|
@@ -21,12 +21,14 @@ export declare class Reflector {
|
|
|
21
21
|
schemas: Schema[];
|
|
22
22
|
modules: Module[];
|
|
23
23
|
readonly apiImport: string;
|
|
24
|
+
readonly experimentalFeatures: boolean;
|
|
24
25
|
constructor(params: {
|
|
25
26
|
components: ComponentsObject;
|
|
26
27
|
paths: PathsObject;
|
|
27
28
|
fieldConfigs: FieldConfigs;
|
|
28
29
|
typeImports: TypeImports;
|
|
29
30
|
apiImport: string;
|
|
31
|
+
experimentalFeatures?: boolean;
|
|
30
32
|
});
|
|
31
33
|
/**
|
|
32
34
|
* Promotes inline object request-body schemas to named components.
|
package/dist/main.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Schema } from "./schema.js";
|
|
|
6
6
|
import { Module } from "./module.js";
|
|
7
7
|
import { generatedDir } from "./vars.global.js";
|
|
8
8
|
import { ReflectorFile } from "./reflector.js";
|
|
9
|
+
import * as process from "node:process";
|
|
9
10
|
export const enumTypes = new Map();
|
|
10
11
|
export const mockedParams = new Set();
|
|
11
12
|
export class Reflector {
|
|
@@ -24,9 +25,11 @@ export class Reflector {
|
|
|
24
25
|
schemas;
|
|
25
26
|
modules;
|
|
26
27
|
apiImport;
|
|
28
|
+
experimentalFeatures;
|
|
27
29
|
constructor(params) {
|
|
28
|
-
const { components, paths, fieldConfigs, typeImports, apiImport } = params;
|
|
30
|
+
const { components, paths, fieldConfigs, typeImports, apiImport, experimentalFeatures } = params;
|
|
29
31
|
this.apiImport = apiImport;
|
|
32
|
+
this.experimentalFeatures = experimentalFeatures ?? false;
|
|
30
33
|
this.typeImports = typeImports;
|
|
31
34
|
// Clear global state between runs
|
|
32
35
|
enumTypes.clear();
|
|
@@ -237,6 +240,7 @@ export class Reflector {
|
|
|
237
240
|
name,
|
|
238
241
|
...info,
|
|
239
242
|
apiImport: this.apiImport,
|
|
243
|
+
experimentalFeatures: this.experimentalFeatures,
|
|
240
244
|
});
|
|
241
245
|
});
|
|
242
246
|
return modules;
|
|
@@ -290,6 +294,7 @@ export class Reflector {
|
|
|
290
294
|
this.enumFile.save(),
|
|
291
295
|
this.mockedParamsFile.save(),
|
|
292
296
|
...this.modules.filter((m) => m.methods.length > 0).map((m) => m.src.save()),
|
|
297
|
+
...this.modules.filter((m) => m.methods.length > 0 && m.apiSrc).map((m) => m.apiSrc.save()),
|
|
293
298
|
]);
|
|
294
299
|
return {};
|
|
295
300
|
}
|
package/dist/method.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export declare class Method {
|
|
|
17
17
|
hasEnumResponse: boolean;
|
|
18
18
|
isPrimitiveResponse: boolean;
|
|
19
19
|
};
|
|
20
|
+
get analyzers(): import("./core/method/Method.js").MethodAnalyzers;
|
|
21
|
+
get responseTypeInterface(): string;
|
|
20
22
|
get headers(): PrimitiveProp[];
|
|
21
23
|
get cookies(): PrimitiveProp[];
|
|
22
24
|
get paths(): PrimitiveProp[];
|
package/dist/method.js
CHANGED
|
@@ -11,6 +11,12 @@ export class Method {
|
|
|
11
11
|
get request() {
|
|
12
12
|
return this.method.analyzers.request;
|
|
13
13
|
}
|
|
14
|
+
get analyzers() {
|
|
15
|
+
return this.method.analyzers;
|
|
16
|
+
}
|
|
17
|
+
get responseTypeInterface() {
|
|
18
|
+
return this.method.responseTypeInterface;
|
|
19
|
+
}
|
|
14
20
|
get headers() {
|
|
15
21
|
return this.method.analyzers.props.headers;
|
|
16
22
|
}
|
package/dist/module.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class Module {
|
|
|
6
6
|
readonly path: string;
|
|
7
7
|
readonly moduleName: string;
|
|
8
8
|
readonly src: Source;
|
|
9
|
+
readonly apiSrc: Source | null;
|
|
9
10
|
readonly methods: Method[];
|
|
10
11
|
/** Schema class names directly used by this module (for per-module schema generation) */
|
|
11
12
|
readonly schemaClassNames: string[];
|
|
@@ -21,7 +22,10 @@ export declare class Module {
|
|
|
21
22
|
operations: ReflectorOperation[];
|
|
22
23
|
path: string;
|
|
23
24
|
apiImport: string;
|
|
25
|
+
experimentalFeatures?: boolean;
|
|
24
26
|
});
|
|
25
27
|
private buildModuleData;
|
|
28
|
+
private buildApiFile;
|
|
29
|
+
private getApiPath;
|
|
26
30
|
private getPath;
|
|
27
31
|
}
|
package/dist/module.js
CHANGED
|
@@ -4,12 +4,13 @@ import { Source } from "./file.js";
|
|
|
4
4
|
import { capitalizeFirstLetter, toKebabCase } from "./helpers/helpers.js";
|
|
5
5
|
import { Method } from "./method.js";
|
|
6
6
|
import { generatedDir } from "./vars.global.js";
|
|
7
|
-
import { ModuleImports, ModuleMethodProcessor, ModuleParamProcessor, ModuleClassBuilder, ModuleConstructorBuilder, ModuleFileBuilder, } from "./core/index.js";
|
|
7
|
+
import { ModuleImports, ModuleMethodProcessor, ModuleParamProcessor, ModuleClassBuilder, ModuleConstructorBuilder, ModuleFileBuilder, ApiFileBuilder, ApiClassBuilder, } from "./core/index.js";
|
|
8
8
|
export class Module {
|
|
9
9
|
name;
|
|
10
10
|
path;
|
|
11
11
|
moduleName;
|
|
12
12
|
src;
|
|
13
|
+
apiSrc;
|
|
13
14
|
methods;
|
|
14
15
|
/** Schema class names directly used by this module (for per-module schema generation) */
|
|
15
16
|
schemaClassNames;
|
|
@@ -20,7 +21,7 @@ export class Module {
|
|
|
20
21
|
constructorBuilder;
|
|
21
22
|
fileBuilder;
|
|
22
23
|
constructor(params) {
|
|
23
|
-
const { name, operations, moduleName, path: modulePath, apiImport } = params;
|
|
24
|
+
const { name, operations, moduleName, path: modulePath, apiImport, experimentalFeatures } = params;
|
|
24
25
|
this.moduleName = moduleName;
|
|
25
26
|
this.name = capitalizeFirstLetter(name);
|
|
26
27
|
this.path = modulePath;
|
|
@@ -61,6 +62,8 @@ export class Module {
|
|
|
61
62
|
moduleName: this.name,
|
|
62
63
|
}),
|
|
63
64
|
});
|
|
65
|
+
// Cria o arquivo Api (apenas com experimentalFeatures)
|
|
66
|
+
this.apiSrc = experimentalFeatures ? this.buildApiFile(apiImport) : null;
|
|
64
67
|
}
|
|
65
68
|
buildModuleData(processedMethods, processedParams) {
|
|
66
69
|
const moduleAttributes = new Set().add("loading = $state<boolean>(false)");
|
|
@@ -102,6 +105,36 @@ export class Module {
|
|
|
102
105
|
buildedMethods,
|
|
103
106
|
};
|
|
104
107
|
}
|
|
108
|
+
buildApiFile(apiImport) {
|
|
109
|
+
const apiImports = new ModuleImports(apiImport);
|
|
110
|
+
apiImports.addReflectorImport("ApiCallParams");
|
|
111
|
+
const apiClassBuilderDep = new ModuleClassBuilder({ imports: apiImports });
|
|
112
|
+
const apiClassBuilder = new ApiClassBuilder({ imports: apiImports, classBuilder: apiClassBuilderDep });
|
|
113
|
+
const apiFileBuilder = new ApiFileBuilder({ imports: apiImports });
|
|
114
|
+
const endpointBlocks = [];
|
|
115
|
+
const apiSchemaEntries = new Set();
|
|
116
|
+
for (const method of this.methods) {
|
|
117
|
+
const block = apiClassBuilder.build({ method });
|
|
118
|
+
if (!block)
|
|
119
|
+
continue;
|
|
120
|
+
endpointBlocks.push(block);
|
|
121
|
+
block.schemaEntries.forEach((e) => apiSchemaEntries.add(e));
|
|
122
|
+
}
|
|
123
|
+
const cleanEntries = Array.from(apiSchemaEntries).filter((x) => x !== "type any");
|
|
124
|
+
const kebabName = toKebabCase(this.name);
|
|
125
|
+
const classImports = cleanEntries.length > 0
|
|
126
|
+
? `import { ${cleanEntries.join(", ")} } from './${kebabName}.schema.svelte';`
|
|
127
|
+
: "";
|
|
128
|
+
return new Source({
|
|
129
|
+
path: this.getApiPath(),
|
|
130
|
+
data: apiFileBuilder.build({ endpointBlocks, classImports }),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
getApiPath() {
|
|
134
|
+
const kebabName = toKebabCase(this.name);
|
|
135
|
+
const inPath = path.join(generatedDir, "controllers", kebabName);
|
|
136
|
+
return path.join(inPath, `${kebabName}.api.svelte.ts`);
|
|
137
|
+
}
|
|
105
138
|
getPath() {
|
|
106
139
|
const kebabName = toKebabCase(this.name);
|
|
107
140
|
const inPath = path.join(generatedDir, "controllers", kebabName);
|
package/dist/reflector.js
CHANGED
|
@@ -10,6 +10,10 @@ export class ReflectorFile {
|
|
|
10
10
|
"type ValidatorResult = string | null",
|
|
11
11
|
"type ValidatorFn<T> = (v: T) => ValidatorResult",
|
|
12
12
|
"type BundleResult<T> = T extends { bundle: () => infer R } ? R : T;",
|
|
13
|
+
`export type ApiCallParams<TResponse, TPaths = void> =
|
|
14
|
+
TPaths extends void
|
|
15
|
+
? { behavior?: Behavior<TResponse, ApiErrorResponse> }
|
|
16
|
+
: { behavior?: Behavior<TResponse, ApiErrorResponse>; paths?: TPaths }`,
|
|
13
17
|
`type PartialBuildedInput<T> = {
|
|
14
18
|
[K in Exclude<keyof T, 'bundle'>]?: BuildedInput<T[K]>;
|
|
15
19
|
} & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-reflector",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "Reflects zod types from openAPI schemas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dotenv": "^16.4.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^24.12.
|
|
28
|
+
"@types/node": "^24.12.2",
|
|
29
29
|
"typescript": "^5.9.3"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|