@kithinji/pod 1.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/build.js +22 -0
- package/dist/main.js +4464 -0
- package/dist/main.js.map +7 -0
- package/dist/types/add/component/component.d.ts +5 -0
- package/dist/types/add/component/component.d.ts.map +1 -0
- package/dist/types/add/component/index.d.ts +2 -0
- package/dist/types/add/component/index.d.ts.map +1 -0
- package/dist/types/add/index.d.ts +4 -0
- package/dist/types/add/index.d.ts.map +1 -0
- package/dist/types/add/module/index.d.ts +2 -0
- package/dist/types/add/module/index.d.ts.map +1 -0
- package/dist/types/add/module/module.d.ts +3 -0
- package/dist/types/add/module/module.d.ts.map +1 -0
- package/dist/types/add/new/index.d.ts +2 -0
- package/dist/types/add/new/index.d.ts.map +1 -0
- package/dist/types/config/config.d.ts +18 -0
- package/dist/types/config/config.d.ts.map +1 -0
- package/dist/types/config/index.d.ts +2 -0
- package/dist/types/config/index.d.ts.map +1 -0
- package/dist/types/dev/index.d.ts +2 -0
- package/dist/types/dev/index.d.ts.map +1 -0
- package/dist/types/dev/project.d.ts +9 -0
- package/dist/types/dev/project.d.ts.map +1 -0
- package/dist/types/dev/server.d.ts +2 -0
- package/dist/types/dev/server.d.ts.map +1 -0
- package/dist/types/docker/docker.d.ts +2 -0
- package/dist/types/docker/docker.d.ts.map +1 -0
- package/dist/types/docker/index.d.ts +2 -0
- package/dist/types/docker/index.d.ts.map +1 -0
- package/dist/types/macros/expand_macros.d.ts +48 -0
- package/dist/types/macros/expand_macros.d.ts.map +1 -0
- package/dist/types/macros/index.d.ts +3 -0
- package/dist/types/macros/index.d.ts.map +1 -0
- package/dist/types/macros/macro_executer.d.ts +12 -0
- package/dist/types/macros/macro_executer.d.ts.map +1 -0
- package/dist/types/main.d.ts +13 -0
- package/dist/types/main.d.ts.map +1 -0
- package/dist/types/plugins/analyzers/graph.d.ts +25 -0
- package/dist/types/plugins/analyzers/graph.d.ts.map +1 -0
- package/dist/types/plugins/css/index.d.ts +7 -0
- package/dist/types/plugins/css/index.d.ts.map +1 -0
- package/dist/types/plugins/generators/generate_controller.d.ts +2 -0
- package/dist/types/plugins/generators/generate_controller.d.ts.map +1 -0
- package/dist/types/plugins/generators/generate_rsc.d.ts +2 -0
- package/dist/types/plugins/generators/generate_rsc.d.ts.map +1 -0
- package/dist/types/plugins/generators/generate_server_component.d.ts +2 -0
- package/dist/types/plugins/generators/generate_server_component.d.ts.map +1 -0
- package/dist/types/plugins/generators/tsx_server_stub.d.ts +2 -0
- package/dist/types/plugins/generators/tsx_server_stub.d.ts.map +1 -0
- package/dist/types/plugins/index.d.ts +4 -0
- package/dist/types/plugins/index.d.ts.map +1 -0
- package/dist/types/plugins/my.d.ts +10 -0
- package/dist/types/plugins/my.d.ts.map +1 -0
- package/dist/types/plugins/transformers/j2d.d.ts +11 -0
- package/dist/types/plugins/transformers/j2d.d.ts.map +1 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/index.d.ts.map +1 -0
- package/dist/types/store/store.d.ts +14 -0
- package/dist/types/store/store.d.ts.map +1 -0
- package/dist/types/utils/cases.d.ts +4 -0
- package/dist/types/utils/cases.d.ts.map +1 -0
- package/dist/types/utils/create.d.ts +12 -0
- package/dist/types/utils/create.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/package.json +44 -0
- package/src/add/component/component.ts +496 -0
- package/src/add/component/index.ts +1 -0
- package/src/add/index.ts +3 -0
- package/src/add/module/index.ts +1 -0
- package/src/add/module/module.ts +521 -0
- package/src/add/new/index.ts +135 -0
- package/src/config/config.ts +141 -0
- package/src/config/index.ts +1 -0
- package/src/dev/index.ts +1 -0
- package/src/dev/project.ts +45 -0
- package/src/dev/server.ts +190 -0
- package/src/docker/docker.ts +452 -0
- package/src/docker/index.ts +1 -0
- package/src/macros/expand_macros.ts +791 -0
- package/src/macros/index.ts +2 -0
- package/src/macros/macro_executer.ts +189 -0
- package/src/main.ts +95 -0
- package/src/plugins/analyzers/graph.ts +291 -0
- package/src/plugins/css/index.ts +25 -0
- package/src/plugins/generators/generate_controller.ts +308 -0
- package/src/plugins/generators/generate_rsc.ts +274 -0
- package/src/plugins/generators/generate_server_component.ts +279 -0
- package/src/plugins/generators/tsx_server_stub.ts +295 -0
- package/src/plugins/index.ts +3 -0
- package/src/plugins/my.ts +274 -0
- package/src/plugins/transformers/j2d.ts +1014 -0
- package/src/store/index.ts +1 -0
- package/src/store/store.ts +44 -0
- package/src/utils/cases.ts +15 -0
- package/src/utils/create.ts +26 -0
- package/src/utils/index.ts +2 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { parseSync } from "@swc/core";
|
|
2
|
+
import type { ClassDeclaration, Decorator } from "@swc/core";
|
|
3
|
+
|
|
4
|
+
interface MethodParam {
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ComponentMethod {
|
|
10
|
+
name: string;
|
|
11
|
+
params: MethodParam[];
|
|
12
|
+
returnType: string;
|
|
13
|
+
isAsync: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ComponentInfo {
|
|
17
|
+
className: string;
|
|
18
|
+
methods: ComponentMethod[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function generateServerComponent(
|
|
22
|
+
filePath: string,
|
|
23
|
+
code: string
|
|
24
|
+
): string {
|
|
25
|
+
const ast = parseSync(code, {
|
|
26
|
+
syntax: "typescript",
|
|
27
|
+
tsx: filePath.endsWith("x"),
|
|
28
|
+
decorators: true,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const componentInfo = extractComponentInfo(ast);
|
|
32
|
+
|
|
33
|
+
return generateStubCode(componentInfo);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function extractComponentInfo(ast: any): ComponentInfo {
|
|
37
|
+
let componentClass: ClassDeclaration | null = null;
|
|
38
|
+
|
|
39
|
+
for (const item of ast.body) {
|
|
40
|
+
if (
|
|
41
|
+
item.type === "ExportDeclaration" &&
|
|
42
|
+
item.declaration.type === "ClassDeclaration"
|
|
43
|
+
) {
|
|
44
|
+
const classDecl = item.declaration as ClassDeclaration;
|
|
45
|
+
|
|
46
|
+
if (hasComponentDecorator(classDecl.decorators)) {
|
|
47
|
+
componentClass = classDecl;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!componentClass || !componentClass.identifier) {
|
|
54
|
+
throw new Error("Component class is undefined");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const className = componentClass.identifier.value;
|
|
58
|
+
const methods = extractMethods(componentClass);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
className,
|
|
62
|
+
methods,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function hasComponentDecorator(decorators?: Decorator[]): boolean {
|
|
67
|
+
if (!decorators) return false;
|
|
68
|
+
|
|
69
|
+
return decorators.some((decorator) => {
|
|
70
|
+
const expr = decorator.expression;
|
|
71
|
+
|
|
72
|
+
if (expr.type === "CallExpression") {
|
|
73
|
+
if (
|
|
74
|
+
expr.callee.type === "Identifier" &&
|
|
75
|
+
expr.callee.value === "Component"
|
|
76
|
+
) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (expr.type === "Identifier" && expr.value === "Component") {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return false;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function extractMethods(classDecl: ClassDeclaration): ComponentMethod[] {
|
|
90
|
+
const methods: ComponentMethod[] = [];
|
|
91
|
+
|
|
92
|
+
for (const member of classDecl.body) {
|
|
93
|
+
if (member.type === "ClassMethod") {
|
|
94
|
+
const method = member as any;
|
|
95
|
+
|
|
96
|
+
const methodName =
|
|
97
|
+
method.key.type === "Identifier" ? method.key.value : "";
|
|
98
|
+
|
|
99
|
+
if (!methodName) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const params = extractMethodParams(method.function.params || []);
|
|
104
|
+
const returnType = extractReturnType(method.function.returnType);
|
|
105
|
+
const isAsync = method.function.async || false;
|
|
106
|
+
|
|
107
|
+
methods.push({
|
|
108
|
+
name: methodName,
|
|
109
|
+
params,
|
|
110
|
+
returnType,
|
|
111
|
+
isAsync,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return methods;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function extractMethodParams(params: any[]): MethodParam[] {
|
|
120
|
+
const result: MethodParam[] = [];
|
|
121
|
+
|
|
122
|
+
for (const param of params) {
|
|
123
|
+
if (param.type === "Parameter") {
|
|
124
|
+
const pat = param.pat as any;
|
|
125
|
+
|
|
126
|
+
if (pat.type === "Identifier") {
|
|
127
|
+
const name = pat.value;
|
|
128
|
+
const type = pat.typeAnnotation?.typeAnnotation
|
|
129
|
+
? stringifyType(pat.typeAnnotation.typeAnnotation)
|
|
130
|
+
: "any";
|
|
131
|
+
|
|
132
|
+
result.push({
|
|
133
|
+
name,
|
|
134
|
+
type,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function extractReturnType(returnType?: any): string {
|
|
144
|
+
if (!returnType || !returnType.typeAnnotation) {
|
|
145
|
+
return "any";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const type = returnType.typeAnnotation;
|
|
149
|
+
|
|
150
|
+
if (type.type === "TsTypeReference") {
|
|
151
|
+
const typeName = type.typeName;
|
|
152
|
+
if (typeName.type === "Identifier" && typeName.value === "Promise") {
|
|
153
|
+
if (type.typeParams && type.typeParams.params.length > 0) {
|
|
154
|
+
return stringifyType(type.typeParams.params[0]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return stringifyType(type);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function stringifyType(typeNode: any): string {
|
|
163
|
+
if (!typeNode) return "any";
|
|
164
|
+
|
|
165
|
+
switch (typeNode.type) {
|
|
166
|
+
case "TsKeywordType":
|
|
167
|
+
return typeNode.kind;
|
|
168
|
+
|
|
169
|
+
case "TsTypeReference":
|
|
170
|
+
if (typeNode.typeName.type === "Identifier") {
|
|
171
|
+
const baseName = typeNode.typeName.value;
|
|
172
|
+
if (typeNode.typeParams && typeNode.typeParams.params.length > 0) {
|
|
173
|
+
const params = typeNode.typeParams.params
|
|
174
|
+
.map(stringifyType)
|
|
175
|
+
.join(", ");
|
|
176
|
+
return `${baseName}<${params}>`;
|
|
177
|
+
}
|
|
178
|
+
return baseName;
|
|
179
|
+
}
|
|
180
|
+
return "any";
|
|
181
|
+
|
|
182
|
+
case "TsArrayType":
|
|
183
|
+
return `${stringifyType(typeNode.elemType)}[]`;
|
|
184
|
+
|
|
185
|
+
case "TsUnionType":
|
|
186
|
+
return typeNode.types.map(stringifyType).join(" | ");
|
|
187
|
+
|
|
188
|
+
case "TsIntersectionType":
|
|
189
|
+
return typeNode.types.map(stringifyType).join(" & ");
|
|
190
|
+
|
|
191
|
+
case "TsTypeLiteral":
|
|
192
|
+
const props = typeNode.members
|
|
193
|
+
.map((member: any) => {
|
|
194
|
+
if (member.type === "TsPropertySignature") {
|
|
195
|
+
const key =
|
|
196
|
+
member.key.type === "Identifier" ? member.key.value : "";
|
|
197
|
+
const type = member.typeAnnotation
|
|
198
|
+
? stringifyType(member.typeAnnotation.typeAnnotation)
|
|
199
|
+
: "any";
|
|
200
|
+
return `${key}: ${type}`;
|
|
201
|
+
}
|
|
202
|
+
return "";
|
|
203
|
+
})
|
|
204
|
+
.filter(Boolean);
|
|
205
|
+
return `{ ${props.join("; ")} }`;
|
|
206
|
+
|
|
207
|
+
default:
|
|
208
|
+
return "any";
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function generateStubCode(componentInfo: ComponentInfo): string {
|
|
213
|
+
const className = componentInfo.className;
|
|
214
|
+
|
|
215
|
+
const build = componentInfo.methods.find((p) => p.name == "build");
|
|
216
|
+
|
|
217
|
+
if (build == undefined) {
|
|
218
|
+
throw new Error("Component has no build function");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return `import {
|
|
222
|
+
Component,
|
|
223
|
+
Inject,
|
|
224
|
+
getCurrentInjector,
|
|
225
|
+
OrcaComponent,
|
|
226
|
+
JSX,
|
|
227
|
+
OSC,
|
|
228
|
+
HttpClient,
|
|
229
|
+
} from "@kithinji/orca";
|
|
230
|
+
|
|
231
|
+
@Component()
|
|
232
|
+
export class ${className} extends OrcaComponent {
|
|
233
|
+
props!: any;
|
|
234
|
+
|
|
235
|
+
constructor(
|
|
236
|
+
@Inject("OSC_URL", { maybe: true }) private oscUrl?: string,
|
|
237
|
+
private readonly http: HttpClient,
|
|
238
|
+
) {
|
|
239
|
+
super();
|
|
240
|
+
|
|
241
|
+
if(this.oscUrl === undefined) {
|
|
242
|
+
throw new Error("Server component requires osc url be defined");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
build() {
|
|
247
|
+
const root = document.createElement("div");
|
|
248
|
+
root.textContent = "loading...";
|
|
249
|
+
|
|
250
|
+
const injector = getCurrentInjector();
|
|
251
|
+
|
|
252
|
+
if(injector == null) {
|
|
253
|
+
throw new Error("Injector is null");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const osc = new OSC(root);
|
|
257
|
+
|
|
258
|
+
const subscription = this.http.post<JSX.Element>(
|
|
259
|
+
\`\${this.oscUrl}?c=${className}\`, {
|
|
260
|
+
body: this.props
|
|
261
|
+
}
|
|
262
|
+
).subscribe((jsx: JSX.Element) => {
|
|
263
|
+
const action = jsx.action || "insert";
|
|
264
|
+
|
|
265
|
+
if (action === "insert") {
|
|
266
|
+
osc.handleInsert(jsx);
|
|
267
|
+
} else if (action === "update") {
|
|
268
|
+
osc.handleUpdate(jsx);
|
|
269
|
+
} else {
|
|
270
|
+
console.warn(\`Unknown action: \${action}\`);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
this.pushDrop(() => subscription.unsubscribe());
|
|
275
|
+
|
|
276
|
+
return root;
|
|
277
|
+
}
|
|
278
|
+
}`;
|
|
279
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { parseSync, printSync } from "@swc/core";
|
|
4
|
+
import type {
|
|
5
|
+
ModuleItem,
|
|
6
|
+
ClassDeclaration,
|
|
7
|
+
Decorator,
|
|
8
|
+
ImportDeclaration,
|
|
9
|
+
} from "@swc/core";
|
|
10
|
+
|
|
11
|
+
interface ClassStub {
|
|
12
|
+
name: string;
|
|
13
|
+
propsType: string;
|
|
14
|
+
decorators: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ImportMap {
|
|
18
|
+
[localName: string]: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function generateServerStub(filePath: string, code: string): string {
|
|
22
|
+
const hash = createHash("md5").update(filePath).digest("hex").slice(0, 8);
|
|
23
|
+
const relativeFromSrc = filePath.split("/src/")[1];
|
|
24
|
+
const parsed = path.parse(relativeFromSrc);
|
|
25
|
+
|
|
26
|
+
const fileName = path.join("src", parsed.dir, parsed.name);
|
|
27
|
+
|
|
28
|
+
const ast = parseSync(code, {
|
|
29
|
+
syntax: "typescript",
|
|
30
|
+
tsx: filePath.endsWith("x"),
|
|
31
|
+
decorators: true,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const importMap: ImportMap = {};
|
|
35
|
+
for (const item of ast.body) {
|
|
36
|
+
if (item.type === "ImportDeclaration") {
|
|
37
|
+
const decl = item as ImportDeclaration;
|
|
38
|
+
for (const specifier of decl.specifiers ?? []) {
|
|
39
|
+
let localName: string;
|
|
40
|
+
if (specifier.type === "ImportSpecifier") {
|
|
41
|
+
localName = specifier.local.value;
|
|
42
|
+
} else if (specifier.type === "ImportDefaultSpecifier") {
|
|
43
|
+
localName = specifier.local.value;
|
|
44
|
+
} else {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
importMap[localName] = decl.source.value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const preservedNodes: ModuleItem[] = [];
|
|
53
|
+
const stubbedClasses: ClassStub[] = [];
|
|
54
|
+
|
|
55
|
+
for (const item of ast.body) {
|
|
56
|
+
let shouldStub = false;
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
item.type === "ExportDeclaration" &&
|
|
60
|
+
item.declaration?.type === "ClassDeclaration"
|
|
61
|
+
) {
|
|
62
|
+
const classDecl = item.declaration as ClassDeclaration;
|
|
63
|
+
|
|
64
|
+
if (hasComponentDecorator(classDecl.decorators)) {
|
|
65
|
+
shouldStub = true;
|
|
66
|
+
const stub = extractClassStub(classDecl);
|
|
67
|
+
if (stub) {
|
|
68
|
+
stubbedClasses.push(stub);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!shouldStub) {
|
|
74
|
+
preservedNodes.push(item);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const preservedCode =
|
|
79
|
+
preservedNodes.length > 0
|
|
80
|
+
? printSync({
|
|
81
|
+
type: "Module",
|
|
82
|
+
span: ast.span,
|
|
83
|
+
body: preservedNodes,
|
|
84
|
+
interpreter: ast.interpreter,
|
|
85
|
+
}).code
|
|
86
|
+
: "";
|
|
87
|
+
|
|
88
|
+
const stubCode = stubbedClasses
|
|
89
|
+
.map((stub) => generateClassCode(stub, hash, fileName))
|
|
90
|
+
.join("\n\n");
|
|
91
|
+
|
|
92
|
+
return `
|
|
93
|
+
${preservedCode}
|
|
94
|
+
${stubCode}
|
|
95
|
+
`.trim();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasComponentDecorator(decorators?: Decorator[]): boolean {
|
|
99
|
+
if (!decorators) return false;
|
|
100
|
+
return decorators.some((decorator) => {
|
|
101
|
+
const expr = decorator.expression;
|
|
102
|
+
if (expr.type === "Identifier" && expr.value === "Component") {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (
|
|
106
|
+
expr.type === "CallExpression" &&
|
|
107
|
+
expr.callee.type === "Identifier" &&
|
|
108
|
+
expr.callee.value === "Component"
|
|
109
|
+
) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractClassStub(classDecl: ClassDeclaration): ClassStub | null {
|
|
117
|
+
const className = classDecl.identifier?.value;
|
|
118
|
+
if (!className) return null;
|
|
119
|
+
|
|
120
|
+
let propsType = "{}";
|
|
121
|
+
const decorators: string[] = [];
|
|
122
|
+
|
|
123
|
+
if (classDecl.decorators) {
|
|
124
|
+
for (const dec of classDecl.decorators) {
|
|
125
|
+
const str = stringifyDecorator(dec);
|
|
126
|
+
if (str) decorators.push(str);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const member of classDecl.body) {
|
|
131
|
+
if (member.type === "ClassProperty") {
|
|
132
|
+
if (member.key.type === "Identifier" && member.key.value === "props") {
|
|
133
|
+
propsType = extractPropsType(member);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
name: className,
|
|
140
|
+
propsType,
|
|
141
|
+
decorators,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function stringifyDecorator(decorator: Decorator): string {
|
|
146
|
+
const expr = decorator.expression;
|
|
147
|
+
if (expr.type === "CallExpression" && expr.callee.type === "Identifier") {
|
|
148
|
+
return `@${expr.callee.value}()`;
|
|
149
|
+
}
|
|
150
|
+
if (expr.type === "Identifier") {
|
|
151
|
+
return `@${expr.value}`;
|
|
152
|
+
}
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function extractPropsType(member: any): string {
|
|
157
|
+
const typeAnn = member.typeAnnotation?.typeAnnotation;
|
|
158
|
+
if (!typeAnn) return "{}";
|
|
159
|
+
|
|
160
|
+
if (typeAnn.type === "TsTypeLiteral") {
|
|
161
|
+
const props: string[] = [];
|
|
162
|
+
for (const m of typeAnn.members) {
|
|
163
|
+
if (m.type === "TsPropertySignature") {
|
|
164
|
+
const key = m.key.type === "Identifier" ? m.key.value : "?";
|
|
165
|
+
const t = m.typeAnnotation
|
|
166
|
+
? stringifyType(m.typeAnnotation.typeAnnotation)
|
|
167
|
+
: "any";
|
|
168
|
+
props.push(`${key}: ${t}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return `{ ${props.join("; ")} }`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return stringifyType(typeAnn);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function stringifyParam(param: any): string {
|
|
178
|
+
let decorators: string[] = [];
|
|
179
|
+
if (param.decorators) {
|
|
180
|
+
for (const d of param.decorators) {
|
|
181
|
+
const str = stringifyDecorator(d);
|
|
182
|
+
if (str) decorators.push(str);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const decoratorPrefix = decorators.length ? decorators.join(" ") + " " : "";
|
|
186
|
+
|
|
187
|
+
let typeName = "any";
|
|
188
|
+
let paramName = "";
|
|
189
|
+
let accessibility = "";
|
|
190
|
+
|
|
191
|
+
if (param.type === "TsParameterProperty") {
|
|
192
|
+
accessibility = param.accessibility || "";
|
|
193
|
+
const inner = param.param;
|
|
194
|
+
if (inner.type !== "Identifier") return "";
|
|
195
|
+
|
|
196
|
+
paramName = inner.value;
|
|
197
|
+
if (inner.typeAnnotation?.typeAnnotation) {
|
|
198
|
+
typeName = extractTypeName(inner.typeAnnotation.typeAnnotation);
|
|
199
|
+
}
|
|
200
|
+
} else if (param.type === "Parameter") {
|
|
201
|
+
const pat = param.pat;
|
|
202
|
+
if (pat.type !== "Identifier") return "";
|
|
203
|
+
|
|
204
|
+
paramName = pat.value;
|
|
205
|
+
if (pat.typeAnnotation?.typeAnnotation) {
|
|
206
|
+
typeName = extractTypeName(pat.typeAnnotation.typeAnnotation);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
return "";
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const accessPrefix = accessibility ? `${accessibility} ` : "";
|
|
213
|
+
const result = `${decoratorPrefix}${accessPrefix}${paramName}: ${typeName}`;
|
|
214
|
+
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function extractTypeName(typeNode: any): string {
|
|
219
|
+
if (
|
|
220
|
+
typeNode.type === "TsTypeReference" &&
|
|
221
|
+
typeNode.typeName.type === "Identifier"
|
|
222
|
+
) {
|
|
223
|
+
return typeNode.typeName.value;
|
|
224
|
+
}
|
|
225
|
+
return stringifyType(typeNode);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function stringifyType(typeNode: any): string {
|
|
229
|
+
if (!typeNode) return "any";
|
|
230
|
+
|
|
231
|
+
switch (typeNode.type) {
|
|
232
|
+
case "TsKeywordType":
|
|
233
|
+
return typeNode.kind;
|
|
234
|
+
case "TsTypeReference":
|
|
235
|
+
if (typeNode.typeName.type === "Identifier")
|
|
236
|
+
return typeNode.typeName.value;
|
|
237
|
+
if (typeNode.typeName.type === "TsQualifiedName") {
|
|
238
|
+
return `${stringifyQualifiedName(typeNode.typeName.left)}.${
|
|
239
|
+
typeNode.typeName.right.value
|
|
240
|
+
}`;
|
|
241
|
+
}
|
|
242
|
+
return "any";
|
|
243
|
+
case "TsArrayType":
|
|
244
|
+
return `${stringifyType(typeNode.elemType)}[]`;
|
|
245
|
+
case "TsUnionType":
|
|
246
|
+
return typeNode.types.map(stringifyType).join(" | ");
|
|
247
|
+
case "TsIntersectionType":
|
|
248
|
+
return typeNode.types.map(stringifyType).join(" & ");
|
|
249
|
+
default:
|
|
250
|
+
return "any";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function stringifyQualifiedName(node: any): string {
|
|
255
|
+
if (node.type === "Identifier") return node.value;
|
|
256
|
+
if (node.type === "TsQualifiedName") {
|
|
257
|
+
return `${stringifyQualifiedName(node.left)}.${node.right.value}`;
|
|
258
|
+
}
|
|
259
|
+
return "any";
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function generateClassCode(
|
|
263
|
+
stub: ClassStub,
|
|
264
|
+
hash: string,
|
|
265
|
+
fileName: string
|
|
266
|
+
): string {
|
|
267
|
+
const clientId = `${stub.name}_${hash}`;
|
|
268
|
+
const clientPath = `/${fileName}.js`;
|
|
269
|
+
const decoratorsStr =
|
|
270
|
+
stub.decorators.length > 0 ? stub.decorators.join("\n") + "\n" : "";
|
|
271
|
+
|
|
272
|
+
return `
|
|
273
|
+
${decoratorsStr}export class ${stub.name} {
|
|
274
|
+
props!: ${stub.propsType};
|
|
275
|
+
constructor() {}
|
|
276
|
+
build() {
|
|
277
|
+
const inputProps = { ...this.props };
|
|
278
|
+
return {
|
|
279
|
+
$$typeof: Symbol.for("orca.client.component"),
|
|
280
|
+
id: "${clientId}_" + Math.random().toString(36).slice(2, 9),
|
|
281
|
+
type: "${stub.name}",
|
|
282
|
+
props: {
|
|
283
|
+
...inputProps,
|
|
284
|
+
__clientComponent: {
|
|
285
|
+
id: "${clientId}",
|
|
286
|
+
path: "${clientPath}",
|
|
287
|
+
name: "${stub.name}",
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
key: null
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
`.trim();
|
|
295
|
+
}
|