svelte-reflector 1.3.3 → 1.3.5
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/CodegenContext.d.ts +15 -0
- package/dist/core/CodegenContext.js +15 -0
- package/dist/core/Reflector.d.ts +25 -0
- package/dist/core/Reflector.js +66 -0
- package/dist/core/api/ApiClassBuilder.d.ts +23 -0
- package/dist/core/api/ApiClassBuilder.js +106 -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/config/ReflectorConfig.d.ts +17 -0
- package/dist/core/config/ReflectorConfig.js +9 -0
- package/dist/core/emit/RuntimeFilesEmitter.d.ts +13 -0
- package/dist/core/emit/RuntimeFilesEmitter.js +55 -0
- package/dist/core/generators/ApiCallStrategy.d.ts +9 -0
- package/dist/core/generators/ApiCallStrategy.js +32 -0
- package/dist/core/generators/CallMethodGenerator.d.ts +14 -0
- package/dist/core/generators/CallMethodGenerator.js +154 -0
- package/dist/core/generators/CallStrategy.d.ts +23 -0
- package/dist/core/generators/CallStrategy.js +1 -0
- package/dist/core/generators/ModuleCallStrategy.d.ts +9 -0
- package/dist/core/generators/ModuleCallStrategy.js +41 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +8 -0
- package/dist/core/method/Method.d.ts +25 -9
- package/dist/core/method/Method.js +24 -1
- package/dist/core/method/MethodApiTypeAnalyzer.js +3 -0
- package/dist/core/method/MethodBuilder.d.ts +2 -1
- package/dist/core/method/MethodBuilder.js +2 -2
- package/dist/core/method/MethodRequestAnalyzer.d.ts +2 -1
- package/dist/core/method/MethodRequestAnalyzer.js +5 -4
- package/dist/core/method/MethodValidator.d.ts +5 -0
- package/dist/core/method/MethodValidator.js +26 -0
- package/dist/core/method/generators/MethodGenerator.d.ts +2 -6
- package/dist/core/method/generators/MethodGenerator.js +5 -76
- package/dist/core/module/Module.d.ts +36 -0
- package/dist/core/module/Module.js +140 -0
- package/dist/core/module/ModuleClassBuilder.d.ts +4 -0
- package/dist/core/module/ModuleClassBuilder.js +8 -6
- package/dist/core/module/ModuleConstructorBuilder.d.ts +3 -0
- package/dist/core/module/ModuleConstructorBuilder.js +17 -13
- package/dist/core/module/ModuleFactory.d.ts +13 -0
- package/dist/core/module/ModuleFactory.js +44 -0
- package/dist/core/module/ModuleImports.d.ts +4 -1
- package/dist/core/module/ModuleImports.js +13 -6
- package/dist/core/module/ModuleMethodProcessor.d.ts +1 -2
- package/dist/core/module/ModuleMethodProcessor.js +3 -25
- package/dist/core/module/ModuleSchemaFileBuilder.d.ts +15 -0
- package/dist/core/module/ModuleSchemaFileBuilder.js +37 -0
- package/dist/core/openapi/InlineSchemaPromoter.d.ts +33 -0
- package/dist/core/openapi/InlineSchemaPromoter.js +140 -0
- package/dist/core/schema/ReflectorInterface.d.ts +14 -0
- package/dist/core/schema/ReflectorInterface.js +24 -0
- package/dist/core/schema/Schema.d.ts +30 -0
- package/dist/core/schema/Schema.js +64 -0
- package/dist/core/schema/SchemaClassRenderer.d.ts +16 -0
- package/dist/core/schema/SchemaClassRenderer.js +54 -0
- package/dist/core/schema/SchemaDependencyCollector.d.ts +17 -0
- package/dist/core/schema/SchemaDependencyCollector.js +33 -0
- package/dist/core/schema/SchemaPropertyClassifier.d.ts +19 -0
- package/dist/core/schema/SchemaPropertyClassifier.js +106 -0
- package/dist/core/schema/SchemaRegistry.d.ts +16 -0
- package/dist/core/schema/SchemaRegistry.js +51 -0
- package/dist/generate-doc.js +9 -72
- package/dist/helpers/codegen.d.ts +19 -0
- package/dist/helpers/codegen.js +38 -0
- package/dist/helpers/prop-name.d.ts +10 -0
- package/dist/helpers/prop-name.js +13 -0
- package/dist/loadTemplate.d.ts +2 -0
- package/dist/loadTemplate.js +8 -0
- package/dist/loaders/ConfigLoader.d.ts +20 -0
- package/dist/loaders/ConfigLoader.js +70 -0
- package/dist/loaders/OpenAPILoader.d.ts +10 -0
- package/dist/loaders/OpenAPILoader.js +31 -0
- package/dist/main.d.ts +12 -54
- package/dist/main.js +39 -323
- package/dist/method.d.ts +2 -0
- package/dist/method.js +6 -0
- package/dist/module.d.ts +11 -2
- package/dist/module.js +42 -14
- package/dist/props/array.property.d.ts +3 -1
- package/dist/props/array.property.js +6 -9
- package/dist/props/enum.property.d.ts +2 -0
- package/dist/props/enum.property.js +6 -7
- package/dist/props/primitive.property.d.ts +0 -1
- package/dist/props/primitive.property.js +4 -9
- package/dist/reflector.js +4 -0
- package/dist/runtime/reflector.svelte.ts +210 -0
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +36 -132
- package/package.json +8 -4
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run codegen state. Replaces module-level mutable globals so two
|
|
3
|
+
* concurrent Reflector instances don't corrupt each other's output.
|
|
4
|
+
*
|
|
5
|
+
* - `enumTypes`: maps the joined enum literal string (e.g. `'a','b','c'`) to
|
|
6
|
+
* its generated type name. `EnumProp` writes here on first sighting and
|
|
7
|
+
* reads back the stable name on repeat sightings.
|
|
8
|
+
* - `mockedParams`: set of path-param names that need `$state` fallbacks in
|
|
9
|
+
* the generated `MockedParams` class. Written by `ModuleClassBuilder` when
|
|
10
|
+
* it sees path params.
|
|
11
|
+
*/
|
|
12
|
+
export declare class CodegenContext {
|
|
13
|
+
readonly enumTypes: Map<string, string>;
|
|
14
|
+
readonly mockedParams: Set<string>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run codegen state. Replaces module-level mutable globals so two
|
|
3
|
+
* concurrent Reflector instances don't corrupt each other's output.
|
|
4
|
+
*
|
|
5
|
+
* - `enumTypes`: maps the joined enum literal string (e.g. `'a','b','c'`) to
|
|
6
|
+
* its generated type name. `EnumProp` writes here on first sighting and
|
|
7
|
+
* reads back the stable name on repeat sightings.
|
|
8
|
+
* - `mockedParams`: set of path-param names that need `$state` fallbacks in
|
|
9
|
+
* the generated `MockedParams` class. Written by `ModuleClassBuilder` when
|
|
10
|
+
* it sees path params.
|
|
11
|
+
*/
|
|
12
|
+
export class CodegenContext {
|
|
13
|
+
enumTypes = new Map();
|
|
14
|
+
mockedParams = new Set();
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Module } from "./module/Module.js";
|
|
2
|
+
import { Schema } from "./schema/Schema.js";
|
|
3
|
+
import type { ComponentsObject, PathsObject } from "../types/open-api-spec.interface.js";
|
|
4
|
+
import type { FieldConfigs, TypeImports } from "../types/types.js";
|
|
5
|
+
import type { ReflectorConfig } from "./config/ReflectorConfig.js";
|
|
6
|
+
export declare class Reflector {
|
|
7
|
+
readonly schemas: Schema[];
|
|
8
|
+
readonly modules: Module[];
|
|
9
|
+
readonly propertiesNames: Set<string>;
|
|
10
|
+
private readonly registry;
|
|
11
|
+
private readonly typeImports;
|
|
12
|
+
private readonly config;
|
|
13
|
+
private readonly context;
|
|
14
|
+
private readonly srcDir;
|
|
15
|
+
constructor(params: {
|
|
16
|
+
components: ComponentsObject;
|
|
17
|
+
paths: PathsObject;
|
|
18
|
+
fieldConfigs: FieldConfigs;
|
|
19
|
+
typeImports: TypeImports;
|
|
20
|
+
apiImport: string;
|
|
21
|
+
experimentalFeatures?: boolean;
|
|
22
|
+
config?: Partial<ReflectorConfig>;
|
|
23
|
+
});
|
|
24
|
+
build(): Promise<{}>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as process from "node:process";
|
|
4
|
+
import { Source } from "../file.js";
|
|
5
|
+
import { Module } from "./module/Module.js";
|
|
6
|
+
import { Schema } from "./schema/Schema.js";
|
|
7
|
+
import { CodegenContext } from "./CodegenContext.js";
|
|
8
|
+
import { InlineSchemaPromoter } from "./openapi/InlineSchemaPromoter.js";
|
|
9
|
+
import { SchemaRegistry } from "./schema/SchemaRegistry.js";
|
|
10
|
+
import { ModuleFactory } from "./module/ModuleFactory.js";
|
|
11
|
+
import { ModuleSchemaFileBuilder } from "./module/ModuleSchemaFileBuilder.js";
|
|
12
|
+
import { RuntimeFilesEmitter } from "./emit/RuntimeFilesEmitter.js";
|
|
13
|
+
import { resolveReflectorConfig } from "./config/ReflectorConfig.js";
|
|
14
|
+
import { generatedDir } from "../vars.global.js";
|
|
15
|
+
export class Reflector {
|
|
16
|
+
schemas;
|
|
17
|
+
modules;
|
|
18
|
+
propertiesNames;
|
|
19
|
+
registry;
|
|
20
|
+
typeImports;
|
|
21
|
+
config;
|
|
22
|
+
context = new CodegenContext();
|
|
23
|
+
srcDir = path.resolve(process.cwd(), `${generatedDir}/controllers`);
|
|
24
|
+
constructor(params) {
|
|
25
|
+
const { components, paths, fieldConfigs, typeImports, apiImport, experimentalFeatures, config } = params;
|
|
26
|
+
this.typeImports = typeImports;
|
|
27
|
+
this.config = resolveReflectorConfig(config);
|
|
28
|
+
InlineSchemaPromoter.promote(components, paths);
|
|
29
|
+
this.modules = ModuleFactory.build({
|
|
30
|
+
paths,
|
|
31
|
+
apiImport,
|
|
32
|
+
experimentalFeatures: experimentalFeatures ?? false,
|
|
33
|
+
context: this.context,
|
|
34
|
+
config: this.config,
|
|
35
|
+
});
|
|
36
|
+
this.registry = new SchemaRegistry({ components, fieldConfigs, context: this.context });
|
|
37
|
+
this.schemas = this.registry.schemas;
|
|
38
|
+
this.propertiesNames = this.registry.propertiesNames;
|
|
39
|
+
}
|
|
40
|
+
async build() {
|
|
41
|
+
fs.rmSync(this.srcDir, { recursive: true, force: true });
|
|
42
|
+
fs.mkdirSync(this.srcDir, { recursive: true });
|
|
43
|
+
const moduleSchemaFiles = [];
|
|
44
|
+
for (const module of this.modules) {
|
|
45
|
+
if (module.methods.length === 0 || module.schemaClassNames.length === 0)
|
|
46
|
+
continue;
|
|
47
|
+
const neededSchemas = this.registry.resolveTransitiveDeps(module.schemaClassNames);
|
|
48
|
+
if (neededSchemas.length === 0)
|
|
49
|
+
continue;
|
|
50
|
+
const data = ModuleSchemaFileBuilder.build({ schemas: neededSchemas, typeImports: this.typeImports, config: this.config });
|
|
51
|
+
const schemaFilePath = module.src.path.replace(".module.svelte.ts", ".schema.svelte.ts");
|
|
52
|
+
moduleSchemaFiles.push(new Source({ path: schemaFilePath, data }));
|
|
53
|
+
}
|
|
54
|
+
const runtimeFiles = RuntimeFilesEmitter.build({
|
|
55
|
+
propertiesNames: this.propertiesNames,
|
|
56
|
+
context: this.context,
|
|
57
|
+
});
|
|
58
|
+
await Promise.all([
|
|
59
|
+
...moduleSchemaFiles.map((f) => f.save()),
|
|
60
|
+
...runtimeFiles.map((f) => f.save()),
|
|
61
|
+
...this.modules.filter((m) => m.methods.length > 0).map((m) => m.src.save()),
|
|
62
|
+
...this.modules.filter((m) => m.methods.length > 0 && m.apiSrc).map((m) => m.apiSrc.save()),
|
|
63
|
+
]);
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Method } from "../method/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 callStrategy;
|
|
13
|
+
private readonly paramProcessor;
|
|
14
|
+
constructor(params: {
|
|
15
|
+
imports: ModuleImports;
|
|
16
|
+
classBuilder: ModuleClassBuilder;
|
|
17
|
+
});
|
|
18
|
+
build(params: {
|
|
19
|
+
method: Method;
|
|
20
|
+
}): ApiEndpointBlock | null;
|
|
21
|
+
private buildStateProperties;
|
|
22
|
+
private buildResetLines;
|
|
23
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { capitalizeFirstLetter } from "../../helpers/helpers.js";
|
|
2
|
+
import { MethodValidator } from "../method/MethodValidator.js";
|
|
3
|
+
import { CallMethodGenerator } from "../generators/CallMethodGenerator.js";
|
|
4
|
+
import { ApiCallStrategy } from "../generators/ApiCallStrategy.js";
|
|
5
|
+
import { ApiParamProcessor } from "./ApiParamProcessor.js";
|
|
6
|
+
export class ApiClassBuilder {
|
|
7
|
+
imports;
|
|
8
|
+
methodGenerator = new CallMethodGenerator();
|
|
9
|
+
callStrategy = new ApiCallStrategy();
|
|
10
|
+
paramProcessor;
|
|
11
|
+
constructor(params) {
|
|
12
|
+
this.imports = params.imports;
|
|
13
|
+
this.paramProcessor = new ApiParamProcessor({
|
|
14
|
+
imports: params.imports,
|
|
15
|
+
classBuilder: params.classBuilder,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
build(params) {
|
|
19
|
+
const { method } = params;
|
|
20
|
+
if (MethodValidator.isSkippable(method))
|
|
21
|
+
return null;
|
|
22
|
+
const { request, headers, cookies, paths, querys } = method;
|
|
23
|
+
const { bodyType, responseType, attributeType, isPrimitiveResponse } = request;
|
|
24
|
+
// Process per-endpoint params
|
|
25
|
+
const processedParams = this.paramProcessor.process({
|
|
26
|
+
methodName: capitalizeFirstLetter(method.name),
|
|
27
|
+
querys,
|
|
28
|
+
paths,
|
|
29
|
+
headers,
|
|
30
|
+
cookies,
|
|
31
|
+
});
|
|
32
|
+
// Build state properties
|
|
33
|
+
const stateProps = this.buildStateProperties(method);
|
|
34
|
+
// Build the call method
|
|
35
|
+
const callMethod = this.methodGenerator.generate(method, this.callStrategy);
|
|
36
|
+
// Build reset method
|
|
37
|
+
const resetLines = this.buildResetLines(method, processedParams.paramReset);
|
|
38
|
+
// Collect schema entries
|
|
39
|
+
const schemaEntries = new Set();
|
|
40
|
+
if (bodyType) {
|
|
41
|
+
schemaEntries.add(bodyType);
|
|
42
|
+
}
|
|
43
|
+
if (responseType && responseType !== "response" && !isPrimitiveResponse) {
|
|
44
|
+
schemaEntries.add(`type ${responseType}Interface`);
|
|
45
|
+
schemaEntries.add(responseType);
|
|
46
|
+
}
|
|
47
|
+
// Handle form imports
|
|
48
|
+
if (attributeType === "form" && bodyType) {
|
|
49
|
+
this.imports.addReflectorImport("isFormValid");
|
|
50
|
+
}
|
|
51
|
+
// Handle list imports
|
|
52
|
+
if (attributeType === "list") {
|
|
53
|
+
this.imports.addReflectorImport("genericArrayBundler");
|
|
54
|
+
}
|
|
55
|
+
const className = capitalizeFirstLetter(method.name);
|
|
56
|
+
const classCode = `
|
|
57
|
+
export class ${className} {
|
|
58
|
+
${stateProps.join(";")}
|
|
59
|
+
${processedParams.paramAttributes.map((a) => `${a};`).join("\n ")}
|
|
60
|
+
|
|
61
|
+
${callMethod}
|
|
62
|
+
|
|
63
|
+
reset() {
|
|
64
|
+
${resetLines.join(";")}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
const paramCode = processedParams.paramClasses.join("\n");
|
|
69
|
+
return { paramCode, classCode, schemaEntries };
|
|
70
|
+
}
|
|
71
|
+
buildStateProperties(method) {
|
|
72
|
+
const { attributeType, responseType, bodyType, isPrimitiveResponse } = method.request;
|
|
73
|
+
const props = ["loading = $state<boolean>(false)"];
|
|
74
|
+
if (attributeType === "form" && bodyType) {
|
|
75
|
+
// For form endpoints, the form IS the data
|
|
76
|
+
props.push(`form = new ${bodyType}()`);
|
|
77
|
+
}
|
|
78
|
+
else if (attributeType === "list") {
|
|
79
|
+
props.push(`data = $state<${responseType}['data']>([])`);
|
|
80
|
+
props.push("totalPages = $state<number>(1)");
|
|
81
|
+
}
|
|
82
|
+
else if (attributeType === "entity" && responseType && !isPrimitiveResponse) {
|
|
83
|
+
props.push(`data = $state<${responseType} | undefined>()`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
props.push("data = $state<unknown>(undefined)");
|
|
87
|
+
}
|
|
88
|
+
return props;
|
|
89
|
+
}
|
|
90
|
+
buildResetLines(method, paramReset) {
|
|
91
|
+
const { attributeType, bodyType } = method.request;
|
|
92
|
+
const lines = [];
|
|
93
|
+
if (attributeType === "form" && bodyType) {
|
|
94
|
+
lines.push(`this.form = new ${bodyType}()`);
|
|
95
|
+
}
|
|
96
|
+
else if (attributeType === "list") {
|
|
97
|
+
lines.push("this.data = []");
|
|
98
|
+
lines.push("this.totalPages = 1");
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
lines.push("this.data = undefined");
|
|
102
|
+
}
|
|
103
|
+
lines.push(...paramReset);
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolates the consumer's framework conventions (import aliases, env flag
|
|
3
|
+
* name) from the codegen core. Defaults reproduce the previous hard-coded
|
|
4
|
+
* SvelteKit paths so existing consumers don't need to change anything.
|
|
5
|
+
*/
|
|
6
|
+
export interface ReflectorConfig {
|
|
7
|
+
/** Alias that resolves to the generated reflector folder (e.g. `$reflector`). */
|
|
8
|
+
reflectorAlias: string;
|
|
9
|
+
/** Full import path to the validators/sanitizers module. */
|
|
10
|
+
validatorsImport: string;
|
|
11
|
+
/** Module path for the environment flag (e.g. `$env/static/public`). */
|
|
12
|
+
environmentImport: string;
|
|
13
|
+
/** Name of the exported environment flag — values other than `DEV` are treated as prod. */
|
|
14
|
+
environmentFlag: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const DEFAULT_REFLECTOR_CONFIG: ReflectorConfig;
|
|
17
|
+
export declare function resolveReflectorConfig(partial?: Partial<ReflectorConfig>): ReflectorConfig;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const DEFAULT_REFLECTOR_CONFIG = {
|
|
2
|
+
reflectorAlias: "$reflector",
|
|
3
|
+
validatorsImport: "$lib/sanitizers/validateFormats",
|
|
4
|
+
environmentImport: "$env/static/public",
|
|
5
|
+
environmentFlag: "PUBLIC_ENVIRONMENT",
|
|
6
|
+
};
|
|
7
|
+
export function resolveReflectorConfig(partial) {
|
|
8
|
+
return { ...DEFAULT_REFLECTOR_CONFIG, ...(partial ?? {}) };
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Source } from "../../file.js";
|
|
2
|
+
import type { CodegenContext } from "../CodegenContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Emits the shared runtime-support files that sit alongside the generated
|
|
5
|
+
* module files: the reflector runtime template, the FIELD_NAMES list, the
|
|
6
|
+
* enum unions, and the MockedParams class used by path-param stubs.
|
|
7
|
+
*/
|
|
8
|
+
export declare class RuntimeFilesEmitter {
|
|
9
|
+
static build(params: {
|
|
10
|
+
propertiesNames: Set<string>;
|
|
11
|
+
context: CodegenContext;
|
|
12
|
+
}): Source[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import * as process from "node:process";
|
|
3
|
+
import { Source } from "../../file.js";
|
|
4
|
+
import { loadReflectorTemplate } from "../../loadTemplate.js";
|
|
5
|
+
import { generatedDir } from "../../vars.global.js";
|
|
6
|
+
import { dedent } from "../../helpers/codegen.js";
|
|
7
|
+
function generated(relPath) {
|
|
8
|
+
return path.resolve(process.cwd(), `${generatedDir}/${relPath}`);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Emits the shared runtime-support files that sit alongside the generated
|
|
12
|
+
* module files: the reflector runtime template, the FIELD_NAMES list, the
|
|
13
|
+
* enum unions, and the MockedParams class used by path-param stubs.
|
|
14
|
+
*/
|
|
15
|
+
export class RuntimeFilesEmitter {
|
|
16
|
+
static build(params) {
|
|
17
|
+
const { propertiesNames, context } = params;
|
|
18
|
+
const typesSrc = new Source({
|
|
19
|
+
path: generated("reflector.svelte.ts"),
|
|
20
|
+
data: loadReflectorTemplate(),
|
|
21
|
+
});
|
|
22
|
+
const fieldLiterals = Array.from(propertiesNames).map((p) => `'${p}'`);
|
|
23
|
+
const fieldsFile = new Source({
|
|
24
|
+
path: generated("fields.ts"),
|
|
25
|
+
data: dedent `
|
|
26
|
+
export const FIELD_NAMES = [
|
|
27
|
+
${fieldLiterals}
|
|
28
|
+
] as const;
|
|
29
|
+
export type FieldName = (typeof FIELD_NAMES)[number]
|
|
30
|
+
`,
|
|
31
|
+
});
|
|
32
|
+
const enumss = Array.from(context.enumTypes)
|
|
33
|
+
.map(([types, key]) => `export const ${key} = [ ${types} ] as const; export type ${key} = typeof ${key}[number] `)
|
|
34
|
+
.join(";");
|
|
35
|
+
const enumFile = new Source({
|
|
36
|
+
path: generated("enums.ts"),
|
|
37
|
+
data: enumss,
|
|
38
|
+
});
|
|
39
|
+
const mockedFields = Array.from(context.mockedParams)
|
|
40
|
+
.map((paramName) => `${paramName} = $state<string | null>(null)`)
|
|
41
|
+
.join(";");
|
|
42
|
+
const mockedFile = new Source({
|
|
43
|
+
path: generated("mocked-params.svelte.ts"),
|
|
44
|
+
data: dedent `
|
|
45
|
+
class MockedParams {
|
|
46
|
+
${mockedFields}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const mockedParams = new MockedParams()
|
|
50
|
+
export default mockedParams
|
|
51
|
+
`,
|
|
52
|
+
});
|
|
53
|
+
return [typesSrc, fieldsFile, enumFile, mockedFile];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CallMethodInput, CallStrategy } from "./CallStrategy.js";
|
|
2
|
+
export declare class ApiCallStrategy implements CallStrategy {
|
|
3
|
+
readonly listStateAccess = "this.data";
|
|
4
|
+
buildSignature(method: CallMethodInput): string;
|
|
5
|
+
entityStateAccess(_method: CallMethodInput): string;
|
|
6
|
+
formStateAccess(_method: CallMethodInput): string;
|
|
7
|
+
private buildParamsType;
|
|
8
|
+
private buildPathsInfo;
|
|
9
|
+
}
|