@kosmojs/dev 0.0.25 → 0.0.27
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/package.json +21 -19
- package/pkg/cache.d.ts +2 -20
- package/pkg/chassis.d.ts +3 -0
- package/pkg/chassis.js +636 -0
- package/pkg/chassis.js.map +7 -0
- package/pkg/{stub-generator → core-generator}/index.d.ts +1 -2
- package/pkg/core-generator/templates.d.ts +3 -0
- package/pkg/index.d.ts +13 -9
- package/pkg/index.js +26 -1777
- package/pkg/index.js.map +4 -4
- package/pkg/alias-plugin/index.d.ts +0 -5
- package/pkg/ast.d.ts +0 -44
- package/pkg/base-plugin/api-handler.d.ts +0 -10
- package/pkg/base-plugin/index.d.ts +0 -4
- package/pkg/base-plugin/spinner.d.ts +0 -8
- package/pkg/base-plugin/worker.d.ts +0 -16
- package/pkg/base-plugin/worker.js +0 -1321
- package/pkg/base-plugin/worker.js.map +0 -7
- package/pkg/defaults.d.ts +0 -13
- package/pkg/define-plugin/index.d.ts +0 -9
- package/pkg/fs.d.ts +0 -1
- package/pkg/paths.d.ts +0 -13
- package/pkg/render.d.ts +0 -28
- package/pkg/routes-factory/base.d.ts +0 -47
- package/pkg/routes-factory/index.d.ts +0 -9
- package/pkg/routes-factory/nesting.d.ts +0 -3
- package/pkg/routes-factory/resolve.d.ts +0 -26
- package/pkg/stub-generator/index.js +0 -183
- package/pkg/stub-generator/index.js.map +0 -7
- package/pkg/typebox.d.ts +0 -2
- package/pkg/types.d.ts +0 -305
package/pkg/index.js
CHANGED
|
@@ -1,1781 +1,30 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
configDir: "config",
|
|
16
|
-
apiDir: "api",
|
|
17
|
-
pagesDir: "pages",
|
|
18
|
-
entryDir: "entry",
|
|
19
|
-
fetchDir: "fetch"
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// src/paths.ts
|
|
23
|
-
var createTsconfigPaths = (prefix) => {
|
|
24
|
-
return {
|
|
25
|
-
[`${defaults.appPrefix}/*`]: [`${prefix}/*`],
|
|
26
|
-
[`${defaults.srcPrefix}/*`]: [`${prefix}/${defaults.srcDir}/*`],
|
|
27
|
-
[`${defaults.libPrefix}/*`]: [
|
|
28
|
-
`${prefix}/${defaults.libDir}/${defaults.srcDir}/*`
|
|
29
|
-
]
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
var pathResolver = ({
|
|
33
|
-
appRoot,
|
|
34
|
-
sourceFolder
|
|
35
|
-
}) => {
|
|
36
|
-
const createPath = (...a) => {
|
|
37
|
-
return appRoot ? join(appRoot, ...a) : join(...a);
|
|
38
|
-
};
|
|
39
|
-
const createImport = {
|
|
40
|
-
coreApi(...a) {
|
|
41
|
-
return join(defaults.appPrefix, defaults.coreDir, defaults.apiDir, ...a);
|
|
42
|
-
},
|
|
43
|
-
src(...a) {
|
|
44
|
-
return join(defaults.srcPrefix, sourceFolder, ...a);
|
|
45
|
-
},
|
|
46
|
-
config(...a) {
|
|
47
|
-
return this.src(defaults.configDir, ...a);
|
|
48
|
-
},
|
|
49
|
-
api(...a) {
|
|
50
|
-
return this.src(defaults.apiDir, ...a);
|
|
51
|
-
},
|
|
52
|
-
pages(...a) {
|
|
53
|
-
return this.src(defaults.pagesDir, ...a);
|
|
54
|
-
},
|
|
55
|
-
lib(...a) {
|
|
56
|
-
return join(defaults.libPrefix, sourceFolder, ...a);
|
|
57
|
-
},
|
|
58
|
-
libApi(...a) {
|
|
59
|
-
return this.lib(defaults.apiDir, ...a);
|
|
60
|
-
},
|
|
61
|
-
libEntry(...a) {
|
|
62
|
-
return this.lib(defaults.entryDir, ...a);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
return {
|
|
66
|
-
createPath: {
|
|
67
|
-
coreApi(...a) {
|
|
68
|
-
return createPath(defaults.coreDir, defaults.apiDir, ...a);
|
|
69
|
-
},
|
|
70
|
-
src(...a) {
|
|
71
|
-
return createPath(defaults.srcDir, sourceFolder, ...a);
|
|
72
|
-
},
|
|
73
|
-
api(...a) {
|
|
74
|
-
return this.src(defaults.apiDir, ...a);
|
|
75
|
-
},
|
|
76
|
-
pages(...a) {
|
|
77
|
-
return this.src(defaults.pagesDir, ...a);
|
|
78
|
-
},
|
|
79
|
-
config(...a) {
|
|
80
|
-
return this.src(defaults.configDir, ...a);
|
|
81
|
-
},
|
|
82
|
-
entry(...a) {
|
|
83
|
-
return this.src(defaults.entryDir, ...a);
|
|
84
|
-
},
|
|
85
|
-
lib(...a) {
|
|
86
|
-
return createPath(defaults.libDir, defaults.srcDir, sourceFolder, ...a);
|
|
87
|
-
},
|
|
88
|
-
libApi(...a) {
|
|
89
|
-
return this.lib(defaults.apiDir, ...a);
|
|
90
|
-
},
|
|
91
|
-
libEntry(...a) {
|
|
92
|
-
return this.lib(defaults.entryDir, ...a);
|
|
93
|
-
},
|
|
94
|
-
libPages(...a) {
|
|
95
|
-
return this.lib(defaults.pagesDir, ...a);
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
createImport,
|
|
99
|
-
createImportHelper: (key, ...a) => {
|
|
100
|
-
return createImport[key](...a.slice(0, -1));
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// src/alias-plugin/index.ts
|
|
106
|
-
var alias_plugin_default = (appRoot, opt) => {
|
|
107
|
-
return {
|
|
108
|
-
name: "@kosmojs:aliasPlugin",
|
|
109
|
-
async config() {
|
|
110
|
-
const paths = await import(`${appRoot}/tsconfig.json`, { with: { type: "json" } }).then((e) => {
|
|
111
|
-
return {
|
|
112
|
-
...e.default.compilerOptions?.paths,
|
|
113
|
-
...createTsconfigPaths(".")
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
const aliasmap = [];
|
|
117
|
-
const pathEntries = Object.entries(paths);
|
|
118
|
-
for (const [aliasPattern, pathPatterns] of pathEntries) {
|
|
119
|
-
const alias = aliasPattern.replace("/*", "");
|
|
120
|
-
const paths2 = pathPatterns.map((e) => e.replace("/*", "")).sort((a, b) => a.split(/\/+/).length - b.split(/\/+/).length);
|
|
121
|
-
if (paths2.length === 1) {
|
|
122
|
-
aliasmap.push({
|
|
123
|
-
find: new RegExp(`^${alias}/`),
|
|
124
|
-
replacement: `${appRoot}/${paths2[0]}/`
|
|
125
|
-
});
|
|
126
|
-
} else if (paths2.length > 1) {
|
|
127
|
-
aliasmap.push({
|
|
128
|
-
find: new RegExp(`^${alias}/`),
|
|
129
|
-
replacement: "",
|
|
130
|
-
async customResolver(_src) {
|
|
131
|
-
const src = _src.replace(/(\$|\^|\+|\(|\)|\[|\])/g, "\\$1");
|
|
132
|
-
const patterns = paths2.flatMap((path) => [
|
|
133
|
-
// Case 1: Extension is explicitly provided
|
|
134
|
-
// e.g. import styles from "@admin/{solid}/styles.module.css"
|
|
135
|
-
`${path}/${src}*`,
|
|
136
|
-
// Case 2: No extension provided
|
|
137
|
-
// Match any extension and return the first match
|
|
138
|
-
`${path}/${src}.*`,
|
|
139
|
-
// Case 3: Folder containing an index file of any extension
|
|
140
|
-
`${path}/${src}/index.*`
|
|
141
|
-
]);
|
|
142
|
-
const [file] = await glob(patterns, {
|
|
143
|
-
cwd: appRoot,
|
|
144
|
-
onlyFiles: true,
|
|
145
|
-
absolute: true,
|
|
146
|
-
dot: true,
|
|
147
|
-
followSymbolicLinks: false,
|
|
148
|
-
braceExpansion: false,
|
|
149
|
-
globstar: false,
|
|
150
|
-
ignore: opt?.ignore || [
|
|
151
|
-
"**/.git/**",
|
|
152
|
-
"**/node_modules/**",
|
|
153
|
-
"**/public/**",
|
|
154
|
-
"**/var/**"
|
|
155
|
-
]
|
|
156
|
-
});
|
|
157
|
-
return file;
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
resolve: {
|
|
164
|
-
alias: aliasmap
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// src/base-plugin/index.ts
|
|
172
|
-
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
173
|
-
import { basename as basename2, join as join5, resolve as resolve5 } from "node:path";
|
|
174
|
-
import { styleText as styleText4 } from "node:util";
|
|
175
|
-
import { Worker } from "node:worker_threads";
|
|
176
|
-
import stubGenerator from "@kosmojs/dev/stub-generator";
|
|
177
|
-
|
|
178
|
-
// src/routes-factory/resolve.ts
|
|
179
|
-
import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
|
|
180
|
-
import { styleText as styleText2 } from "node:util";
|
|
181
|
-
import crc5 from "crc/crc32";
|
|
182
|
-
import mimeTypes from "mime-types";
|
|
183
|
-
import picomatch from "picomatch";
|
|
184
|
-
import { glob as glob2 } from "tinyglobby";
|
|
185
|
-
|
|
186
|
-
// src/ast.ts
|
|
187
|
-
import { resolve } from "node:path";
|
|
188
|
-
import { styleText } from "node:util";
|
|
189
|
-
import crc from "crc/crc32";
|
|
190
|
-
import { flattener } from "tfusion";
|
|
191
|
-
import {
|
|
192
|
-
Project,
|
|
193
|
-
SyntaxKind
|
|
194
|
-
} from "ts-morph";
|
|
195
|
-
import {
|
|
196
|
-
HTTPMethods,
|
|
197
|
-
RequestValidationTargets
|
|
198
|
-
} from "@kosmojs/api";
|
|
199
|
-
var createProject = (opts) => new Project(opts);
|
|
200
|
-
var resolveRouteSignature = async (route, opts) => {
|
|
201
|
-
const {
|
|
202
|
-
sourceFile = createProject().addSourceFileAtPath(route.fileFullpath)
|
|
203
|
-
} = { ...opts };
|
|
204
|
-
const [typeDeclarations, referencedFiles] = extractTypeDeclarations(
|
|
205
|
-
sourceFile,
|
|
206
|
-
opts
|
|
207
|
-
);
|
|
208
|
-
const defaultExport = extractDefaultExport(sourceFile);
|
|
209
|
-
const paramsRefinements = defaultExport ? extractParamsRefinements(defaultExport) : void 0;
|
|
210
|
-
const methods = defaultExport ? extractRouteMethods(route, defaultExport) : [];
|
|
211
|
-
return {
|
|
212
|
-
typeDeclarations,
|
|
213
|
-
paramsRefinements,
|
|
214
|
-
methods: methods.map((e) => e.method),
|
|
215
|
-
validationDefinitions: methods.flatMap((e) => e.validationDefinitions),
|
|
216
|
-
referencedFiles
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
var extractDefaultExport = (sourceFile) => {
|
|
220
|
-
const [defaultExport] = sourceFile.getExportAssignments().flatMap((exportAssignment) => {
|
|
221
|
-
if (exportAssignment.isExportEquals()) {
|
|
222
|
-
return [];
|
|
223
|
-
}
|
|
224
|
-
const callExpression = exportAssignment.getExpression();
|
|
225
|
-
return callExpression.isKind(SyntaxKind.CallExpression) ? [callExpression] : [];
|
|
226
|
-
});
|
|
227
|
-
return defaultExport;
|
|
228
|
-
};
|
|
229
|
-
var extractParamsRefinements = (callExpression) => {
|
|
230
|
-
const [
|
|
231
|
-
_routeName,
|
|
232
|
-
// first generic - the route name
|
|
233
|
-
paramsGeneric
|
|
234
|
-
// second generic - params refinements
|
|
235
|
-
] = extractGenerics(callExpression);
|
|
236
|
-
if (!paramsGeneric?.isKind(SyntaxKind.TupleType)) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
return paramsGeneric.getElements().map((node, index) => {
|
|
240
|
-
return {
|
|
241
|
-
index,
|
|
242
|
-
text: node.getText()
|
|
243
|
-
};
|
|
244
|
-
});
|
|
245
|
-
};
|
|
246
|
-
var extractRouteMethods = (route, callExpression) => {
|
|
247
|
-
const funcDeclaration = callExpression.getFirstChildByKind(SyntaxKind.ArrowFunction) || callExpression.getFirstChildByKind(SyntaxKind.FunctionExpression);
|
|
248
|
-
if (!funcDeclaration) {
|
|
249
|
-
return [];
|
|
250
|
-
}
|
|
251
|
-
const arrayLiteralExpression = funcDeclaration.getFirstChildByKind(
|
|
252
|
-
SyntaxKind.ArrayLiteralExpression
|
|
253
|
-
);
|
|
254
|
-
if (!arrayLiteralExpression) {
|
|
255
|
-
return [];
|
|
256
|
-
}
|
|
257
|
-
const callExpressions = [];
|
|
258
|
-
for (const e of arrayLiteralExpression.getChildrenOfKind(
|
|
259
|
-
SyntaxKind.CallExpression
|
|
260
|
-
)) {
|
|
261
|
-
const name = e.getExpression().getText();
|
|
262
|
-
if (HTTPMethods[name]) {
|
|
263
|
-
callExpressions.push([e, name]);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const methods = [];
|
|
267
|
-
for (const [callExpression2, method] of callExpressions) {
|
|
268
|
-
const [vDefs, vOpts] = extractGenerics(callExpression2);
|
|
269
|
-
methods.push({
|
|
270
|
-
method,
|
|
271
|
-
validationDefinitions: extractValidationDefinitions(
|
|
272
|
-
route,
|
|
273
|
-
method,
|
|
274
|
-
vDefs,
|
|
275
|
-
vOpts
|
|
276
|
-
)
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
return methods;
|
|
280
|
-
};
|
|
281
|
-
var parseRuntimeValidation = (typeNode) => {
|
|
282
|
-
if (typeNode.isKind(SyntaxKind.LiteralType)) {
|
|
283
|
-
const literal = typeNode.getFirstChild();
|
|
284
|
-
if (literal?.isKind(SyntaxKind.TrueKeyword)) {
|
|
285
|
-
return true;
|
|
286
|
-
} else if (literal?.isKind(SyntaxKind.FalseKeyword)) {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return void 0;
|
|
291
|
-
};
|
|
292
|
-
var extractResponseVariant = (typeNode) => {
|
|
293
|
-
if (!typeNode.isKind(SyntaxKind.TupleType)) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
let status = 200;
|
|
297
|
-
let contentType;
|
|
298
|
-
let body;
|
|
299
|
-
const [statusNode, contentTypeNode, bodyNode] = typeNode.getElements();
|
|
300
|
-
if (statusNode?.isKind(SyntaxKind.LiteralType)) {
|
|
301
|
-
const literal = statusNode.getFirstChildByKind(SyntaxKind.NumericLiteral);
|
|
302
|
-
if (literal) {
|
|
303
|
-
status = Number(literal.getText());
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (contentTypeNode) {
|
|
307
|
-
contentType = extractStringLiteral(contentTypeNode);
|
|
308
|
-
}
|
|
309
|
-
if (bodyNode) {
|
|
310
|
-
body = bodyNode.getText();
|
|
311
|
-
if (["object"].includes(body)) {
|
|
312
|
-
body = "{}";
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
return { status, contentType, body };
|
|
316
|
-
};
|
|
317
|
-
var parseValidationOptions = (typeNode) => {
|
|
318
|
-
const opts = {};
|
|
319
|
-
if (!typeNode?.isKind(SyntaxKind.TypeLiteral)) {
|
|
320
|
-
return opts;
|
|
321
|
-
}
|
|
322
|
-
for (const prop of typeNode.getMembers()) {
|
|
323
|
-
if (!prop.isKind(SyntaxKind.PropertySignature)) {
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
const target = prop.getName();
|
|
327
|
-
const typeNode2 = prop.getTypeNodeOrThrow();
|
|
328
|
-
if (!typeNode2.isKind(SyntaxKind.TypeLiteral)) {
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
let contentType;
|
|
332
|
-
let runtimeValidation;
|
|
333
|
-
const customErrors = {};
|
|
334
|
-
for (const member of typeNode2.getMembers()) {
|
|
335
|
-
if (!member.isKind(SyntaxKind.PropertySignature)) {
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
const nameNode = member.getNameNode();
|
|
339
|
-
const valueNode = member.getTypeNodeOrThrow();
|
|
340
|
-
const name = nameNode.isKind(SyntaxKind.StringLiteral) ? nameNode.getLiteralText() : nameNode.getText();
|
|
341
|
-
if (name === "contentType") {
|
|
342
|
-
contentType = extractStringLiteral(valueNode);
|
|
343
|
-
} else if (name === "runtimeValidation") {
|
|
344
|
-
runtimeValidation = parseRuntimeValidation(valueNode);
|
|
345
|
-
} else if (name.startsWith("error")) {
|
|
346
|
-
const literal = extractStringLiteral(valueNode);
|
|
347
|
-
if (literal) {
|
|
348
|
-
customErrors[name] = literal;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
opts[target] = {
|
|
353
|
-
contentType,
|
|
354
|
-
runtimeValidation,
|
|
355
|
-
customErrors
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
return opts;
|
|
359
|
-
};
|
|
360
|
-
var extractStringLiteral = (typeNode) => {
|
|
361
|
-
const literal = typeNode.isKind(SyntaxKind.LiteralType) ? typeNode.getFirstChildByKind(SyntaxKind.StringLiteral) : void 0;
|
|
362
|
-
return literal ? literal.getLiteralText() : void 0;
|
|
363
|
-
};
|
|
364
|
-
var extractValidationDefinitions = (route, method, defsNode, optsNode) => {
|
|
365
|
-
const definitions = [];
|
|
366
|
-
if (!defsNode?.isKind(SyntaxKind.TypeLiteral)) {
|
|
367
|
-
return definitions;
|
|
368
|
-
}
|
|
369
|
-
const optsMap = parseValidationOptions(optsNode);
|
|
370
|
-
const createId = (target, hash) => {
|
|
371
|
-
return [
|
|
372
|
-
target.replace(/^./, (c) => c.toUpperCase()),
|
|
373
|
-
"T",
|
|
374
|
-
method,
|
|
375
|
-
crc(route.id + hash)
|
|
376
|
-
].join("");
|
|
377
|
-
};
|
|
378
|
-
for (const prop of defsNode.getMembers()) {
|
|
379
|
-
if (!prop.isKind(SyntaxKind.PropertySignature)) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
const target = prop.getName();
|
|
383
|
-
const typeNode = prop.getTypeNodeOrThrow();
|
|
384
|
-
if (target === "response") {
|
|
385
|
-
const variants = typeNode.isKind(SyntaxKind.UnionType) ? typeNode.getChildrenOfKind(SyntaxKind.TupleType) : [typeNode];
|
|
386
|
-
definitions.push({
|
|
387
|
-
...optsMap[target],
|
|
388
|
-
method,
|
|
389
|
-
target,
|
|
390
|
-
variants: variants.flatMap((e, i) => {
|
|
391
|
-
const { status, contentType, body } = extractResponseVariant(e) || {};
|
|
392
|
-
if (!status) {
|
|
393
|
-
return [];
|
|
394
|
-
}
|
|
395
|
-
if (contentType && typeof contentType !== "string") {
|
|
396
|
-
console.warn(
|
|
397
|
-
styleText(
|
|
398
|
-
["bold", "red"],
|
|
399
|
-
`\u2717 The second element of a response variant should specify the Response Content Type`
|
|
400
|
-
)
|
|
401
|
-
);
|
|
402
|
-
console.warn(
|
|
403
|
-
styleText(["blue"], ` Example: [200, "json", Schema]`)
|
|
404
|
-
);
|
|
405
|
-
console.warn(
|
|
406
|
-
` Route: ${route.name}; Method: ${method}; Response Variant: #${i}`
|
|
407
|
-
);
|
|
408
|
-
console.warn();
|
|
409
|
-
}
|
|
410
|
-
return [
|
|
411
|
-
{
|
|
412
|
-
id: createId(target, JSON.stringify([status, contentType, body])),
|
|
413
|
-
status,
|
|
414
|
-
contentType,
|
|
415
|
-
body
|
|
416
|
-
}
|
|
417
|
-
];
|
|
418
|
-
})
|
|
419
|
-
});
|
|
420
|
-
} else if (Object.keys(RequestValidationTargets).includes(target)) {
|
|
421
|
-
definitions.push({
|
|
422
|
-
...optsMap[target],
|
|
423
|
-
method,
|
|
424
|
-
target,
|
|
425
|
-
schema: {
|
|
426
|
-
id: createId(target),
|
|
427
|
-
text: typeNode.getText()
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return definitions;
|
|
433
|
-
};
|
|
434
|
-
var extractTypeDeclarations = (sourceFile, opts) => {
|
|
435
|
-
const declarations = [];
|
|
436
|
-
const referencedFiles = opts?.withReferencedFiles ? [] : void 0;
|
|
437
|
-
for (const declaration of sourceFile.getImportDeclarations()) {
|
|
438
|
-
const modulePath = declaration.getModuleSpecifierValue();
|
|
439
|
-
const path = /^\.\.?\/?/.test(modulePath) ? opts?.relpathResolver ? opts.relpathResolver(modulePath) : modulePath : modulePath;
|
|
440
|
-
const typeOnlyDeclaration = declaration.isTypeOnly();
|
|
441
|
-
const defaultImport = typeOnlyDeclaration ? declaration.getDefaultImport() : void 0;
|
|
442
|
-
if (defaultImport) {
|
|
443
|
-
const name = defaultImport.getText();
|
|
444
|
-
const text = `import type ${name} from "${path}";`;
|
|
445
|
-
declarations.push({
|
|
446
|
-
importDeclaration: {
|
|
447
|
-
name,
|
|
448
|
-
path
|
|
449
|
-
},
|
|
450
|
-
text
|
|
451
|
-
});
|
|
452
|
-
if (referencedFiles) {
|
|
453
|
-
referencedFiles.push(...getReferencedFiles(defaultImport));
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
for (const namedImport of declaration.getNamedImports()) {
|
|
457
|
-
if (namedImport.isTypeOnly() || typeOnlyDeclaration) {
|
|
458
|
-
const nameNode = namedImport.getNameNode();
|
|
459
|
-
const name = nameNode.getText();
|
|
460
|
-
const alias = namedImport.getAliasNode()?.getText();
|
|
461
|
-
const nameText = alias ? `${name} as ${alias}` : name;
|
|
462
|
-
declarations.push({
|
|
463
|
-
importDeclaration: {
|
|
464
|
-
name,
|
|
465
|
-
alias,
|
|
466
|
-
path
|
|
467
|
-
},
|
|
468
|
-
text: `import type { ${nameText} } from "${path}";`
|
|
469
|
-
});
|
|
470
|
-
if (referencedFiles) {
|
|
471
|
-
if (nameNode.isKind(SyntaxKind.Identifier)) {
|
|
472
|
-
referencedFiles.push(...getReferencedFiles(nameNode));
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
for (const declaration of sourceFile.getTypeAliases()) {
|
|
479
|
-
const name = declaration.getName();
|
|
480
|
-
const text = declaration.getFullText().trim();
|
|
481
|
-
declarations.push({
|
|
482
|
-
typeAliasDeclaration: { name },
|
|
483
|
-
text
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
for (const declaration of sourceFile.getInterfaces()) {
|
|
487
|
-
const name = declaration.getName();
|
|
488
|
-
const text = declaration.getFullText().trim();
|
|
489
|
-
declarations.push({
|
|
490
|
-
interfaceDeclaration: { name },
|
|
491
|
-
text
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
for (const declaration of sourceFile.getEnums()) {
|
|
495
|
-
const name = declaration.getName();
|
|
496
|
-
const text = declaration.getFullText().trim();
|
|
497
|
-
declarations.push({
|
|
498
|
-
enumDeclaration: { name },
|
|
499
|
-
text
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
for (const declaration of sourceFile.getExportDeclarations()) {
|
|
503
|
-
const typeOnlyDeclaration = declaration.isTypeOnly();
|
|
504
|
-
const modulePath = declaration.getModuleSpecifierValue();
|
|
505
|
-
const path = modulePath ? /^\.\.?\/?/.test(modulePath) ? opts?.relpathResolver ? opts.relpathResolver(modulePath) : modulePath : modulePath : void 0;
|
|
506
|
-
for (const namedExport of declaration.getNamedExports()) {
|
|
507
|
-
if (namedExport.isTypeOnly() || typeOnlyDeclaration) {
|
|
508
|
-
const nameNode = namedExport.getNameNode();
|
|
509
|
-
const name = nameNode.getText();
|
|
510
|
-
const alias = namedExport.getAliasNode()?.getText();
|
|
511
|
-
const nameText = alias ? `${name} as ${alias}` : name;
|
|
512
|
-
declarations.push({
|
|
513
|
-
exportDeclaration: {
|
|
514
|
-
name,
|
|
515
|
-
alias: alias ?? name,
|
|
516
|
-
path
|
|
517
|
-
},
|
|
518
|
-
text: path ? `export type { ${nameText} } from "${path}";` : `export type { ${nameText} };`
|
|
519
|
-
});
|
|
520
|
-
if (referencedFiles) {
|
|
521
|
-
if (nameNode.isKind(SyntaxKind.Identifier)) {
|
|
522
|
-
referencedFiles.push(...getReferencedFiles(nameNode));
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return referencedFiles ? [declarations, [...new Set(referencedFiles)]] : [declarations];
|
|
529
|
-
};
|
|
530
|
-
var getReferencedFiles = (importIdentifier) => {
|
|
531
|
-
const declarations = importIdentifier?.getSymbol()?.getAliasedSymbol()?.getDeclarations() || [];
|
|
532
|
-
return declarations.flatMap((e) => {
|
|
533
|
-
const sourceFile = e.getSourceFile();
|
|
534
|
-
return sourceFile ? [sourceFile.getFilePath()] : [];
|
|
535
|
-
});
|
|
536
|
-
};
|
|
537
|
-
var extractGenerics = (callExpression) => {
|
|
538
|
-
return callExpression.getTypeArguments();
|
|
539
|
-
};
|
|
540
|
-
var typeResolverFactory = ({ appRoot }) => {
|
|
541
|
-
const project = createProject({
|
|
542
|
-
tsConfigFilePath: resolve(appRoot, "tsconfig.json"),
|
|
543
|
-
skipAddingFilesFromTsConfig: true
|
|
544
|
-
});
|
|
545
|
-
const literalTypesResolver = (literalTypes, options) => {
|
|
546
|
-
const sourceFile = project.createSourceFile(
|
|
547
|
-
`${crc(literalTypes)}-${Date.now()}.ts`,
|
|
548
|
-
literalTypes,
|
|
549
|
-
{ overwrite: true }
|
|
550
|
-
);
|
|
551
|
-
const resolvedTypes = flattener(project, sourceFile, {
|
|
552
|
-
...options,
|
|
553
|
-
stripComments: true
|
|
554
|
-
});
|
|
555
|
-
project.removeSourceFile(sourceFile);
|
|
556
|
-
return resolvedTypes;
|
|
557
|
-
};
|
|
558
|
-
return {
|
|
559
|
-
getSourceFile: (fileFullpath) => {
|
|
560
|
-
return project.getSourceFile(fileFullpath) || project.addSourceFileAtPath(fileFullpath);
|
|
561
|
-
},
|
|
562
|
-
refreshSourceFile: async (fileFullpath) => {
|
|
563
|
-
const sourceFile = project.getSourceFile(fileFullpath);
|
|
564
|
-
if (sourceFile) {
|
|
565
|
-
await sourceFile.refreshFromFileSystem();
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
literalTypesResolver
|
|
569
|
-
};
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
// src/cache.ts
|
|
573
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
574
|
-
import { dirname, resolve as resolve2 } from "node:path";
|
|
575
|
-
import crc2 from "crc/crc32";
|
|
576
|
-
import self from "@kosmojs/dev/package.json" with { type: "json" };
|
|
577
|
-
|
|
578
|
-
// src/fs.ts
|
|
579
|
-
import { access, constants } from "node:fs/promises";
|
|
580
|
-
var pathExists = async (path) => {
|
|
581
|
-
try {
|
|
582
|
-
await access(path, constants.F_OK);
|
|
583
|
-
return true;
|
|
584
|
-
} catch {
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
// src/cache.ts
|
|
590
|
-
var cacheFactory = (route, {
|
|
591
|
-
appRoot,
|
|
592
|
-
sourceFolder,
|
|
593
|
-
extraContext
|
|
594
|
-
}) => {
|
|
595
|
-
const cacheFile = pathResolver({
|
|
596
|
-
appRoot,
|
|
597
|
-
sourceFolder
|
|
598
|
-
}).createPath.libApi(dirname(route.file), "cache.json");
|
|
599
|
-
const getCache = async (opt) => {
|
|
600
|
-
if (await pathExists(cacheFile)) {
|
|
601
|
-
try {
|
|
602
|
-
const cache = JSON.parse(await readFile(cacheFile, "utf8"));
|
|
603
|
-
return opt?.validate ? validateCache(cache) : cache;
|
|
604
|
-
} catch (_e) {
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return void 0;
|
|
608
|
-
};
|
|
609
|
-
const persistCache = async ({
|
|
610
|
-
referencedFiles: _referencedFiles,
|
|
611
|
-
...rest
|
|
612
|
-
}) => {
|
|
613
|
-
const hash = await generateFileHash(route.fileFullpath, {
|
|
614
|
-
...extraContext
|
|
615
|
-
});
|
|
616
|
-
const referencedFiles = {};
|
|
617
|
-
for (const file of _referencedFiles) {
|
|
618
|
-
referencedFiles[
|
|
619
|
-
// Strip project root to ensure cached paths are relative
|
|
620
|
-
// and portable across environments (CI, local, etc.)
|
|
621
|
-
file.replace(`${appRoot}/`, "")
|
|
622
|
-
] = await generateFileHash(file);
|
|
623
|
-
}
|
|
624
|
-
const cache = { ...rest, hash, referencedFiles };
|
|
625
|
-
await mkdir(dirname(cacheFile), { recursive: true });
|
|
626
|
-
await writeFile(cacheFile, JSON.stringify(cache, null, 2), "utf8");
|
|
627
|
-
return cache;
|
|
628
|
-
};
|
|
629
|
-
const validateCache = async (cache) => {
|
|
630
|
-
if (!cache?.hash) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
if (!cache.typeDeclarations || !cache.referencedFiles) {
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
const hash = await generateFileHash(route.fileFullpath, {
|
|
637
|
-
...extraContext
|
|
638
|
-
});
|
|
639
|
-
if (!identicalHashSum(cache.hash, hash)) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
for (const [file, hash2] of Object.entries(cache.referencedFiles)) {
|
|
643
|
-
if (!identicalHashSum(hash2, await generateFileHash(resolve2(appRoot, file)))) {
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
return cache;
|
|
648
|
-
};
|
|
649
|
-
return {
|
|
650
|
-
getCache,
|
|
651
|
-
validateCache,
|
|
652
|
-
persistCache
|
|
653
|
-
};
|
|
654
|
-
};
|
|
655
|
-
var generateFileHash = async (file, extraContext) => {
|
|
656
|
-
let fileContent;
|
|
657
|
-
try {
|
|
658
|
-
fileContent = await readFile(file, "utf8");
|
|
659
|
-
} catch (_e) {
|
|
660
|
-
return 0;
|
|
661
|
-
}
|
|
662
|
-
return fileContent ? crc2(
|
|
663
|
-
JSON.stringify({
|
|
664
|
-
...extraContext,
|
|
665
|
-
[self.cacheVersion]: fileContent
|
|
666
|
-
})
|
|
667
|
-
) : 0;
|
|
668
|
-
};
|
|
669
|
-
var identicalHashSum = (a, b) => {
|
|
670
|
-
return a === b;
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
// src/render.ts
|
|
674
|
-
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
675
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
676
|
-
import crc3 from "crc/crc32";
|
|
677
|
-
import handlebars from "handlebars";
|
|
678
|
-
var render = (template, context2, options) => {
|
|
679
|
-
const { noEscape = true, renderer = handlebars } = { ...options };
|
|
680
|
-
return renderer.compile(template, { noEscape })(context2);
|
|
681
|
-
};
|
|
682
|
-
var renderToFile = async (file, template, context2, options) => {
|
|
683
|
-
const content = render(template, context2, options);
|
|
684
|
-
if (await pathExists(file)) {
|
|
685
|
-
const { overwrite = true } = { ...options };
|
|
686
|
-
if (overwrite === false) {
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
const fileContent = await readFile2(file, "utf8");
|
|
690
|
-
if (typeof overwrite === "function" && !overwrite(fileContent)) {
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
if (crc3(content) === crc3(fileContent)) {
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
await mkdir2(dirname2(file), { recursive: true });
|
|
698
|
-
await writeFile2(file, content, "utf8");
|
|
699
|
-
};
|
|
700
|
-
var renderFactory = (options) => {
|
|
701
|
-
const renderer = handlebars.create();
|
|
702
|
-
renderer.registerPartial({ ...options?.partials });
|
|
703
|
-
renderer.registerHelper({ ...options?.helpers });
|
|
704
|
-
return {
|
|
705
|
-
render(template, context2, selfOoptions) {
|
|
706
|
-
return render(template, context2, {
|
|
707
|
-
renderer,
|
|
708
|
-
...options,
|
|
709
|
-
...selfOoptions
|
|
710
|
-
});
|
|
711
|
-
},
|
|
712
|
-
async renderToFile(file, template, context2, selfOoptions) {
|
|
713
|
-
return renderToFile(
|
|
714
|
-
options?.outdir ? join2(options.outdir, file) : file,
|
|
715
|
-
template,
|
|
716
|
-
context2,
|
|
717
|
-
{ renderer, ...options, ...selfOoptions }
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
};
|
|
722
|
-
var renderHelpers = {
|
|
723
|
-
createParamsLiteral: (params) => {
|
|
724
|
-
return params.schema.map((p) => {
|
|
725
|
-
return p.kind === "splat" ? `${p.const}?: Array<string | number>` : p.kind === "optional" ? `${p.const}?: string | number` : `${p.const}: string | number`;
|
|
726
|
-
}).join(", ");
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
// src/routes-factory/base.ts
|
|
731
|
-
import crc4 from "crc/crc32";
|
|
732
|
-
import { parse } from "path-to-regexp";
|
|
733
|
-
var pathTokensFactory = (path, {
|
|
734
|
-
transformStaticValue = normalizeStaticValue
|
|
735
|
-
} = {}) => {
|
|
736
|
-
const extractParts = (tokens2, createConst, insideGroup = false) => {
|
|
737
|
-
const parts = [];
|
|
738
|
-
for (const token of tokens2) {
|
|
739
|
-
switch (token.type) {
|
|
740
|
-
case "text":
|
|
741
|
-
if (token.value !== "/") {
|
|
742
|
-
parts.push({
|
|
743
|
-
type: "static",
|
|
744
|
-
value: transformStaticValue(token.value)
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
break;
|
|
748
|
-
case "param":
|
|
749
|
-
parts.push({
|
|
750
|
-
type: "param",
|
|
751
|
-
kind: insideGroup ? "optional" : "required",
|
|
752
|
-
name: token.name,
|
|
753
|
-
const: createConst(token.name)
|
|
754
|
-
});
|
|
755
|
-
break;
|
|
756
|
-
case "wildcard":
|
|
757
|
-
parts.push({
|
|
758
|
-
type: "param",
|
|
759
|
-
kind: "splat",
|
|
760
|
-
name: token.name,
|
|
761
|
-
const: createConst(token.name)
|
|
762
|
-
});
|
|
763
|
-
break;
|
|
764
|
-
case "group":
|
|
765
|
-
parts.push(...extractParts(token.tokens, createConst, true));
|
|
766
|
-
break;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return parts;
|
|
770
|
-
};
|
|
771
|
-
const patternTransforms = [
|
|
772
|
-
// Transform required params: [id] => :id
|
|
773
|
-
// Only pure \w param names,
|
|
774
|
-
// [some-id] used as is, not treated as param,
|
|
775
|
-
// use [some_id] instead.
|
|
776
|
-
(s) => s.replace(/\[(\w+)\]/g, ":$1"),
|
|
777
|
-
// Transform optional params: {id} => {:id}
|
|
778
|
-
// Only pure \w param names,
|
|
779
|
-
// anything else treated as a path-to-regexp pattern and used as is.
|
|
780
|
-
// {some-id} treated as an optional static segment.
|
|
781
|
-
// use {some_id} for simple param syntax
|
|
782
|
-
// or {:some-id} pattern where :some is the param name and -id is a static segment.
|
|
783
|
-
(s) => s.replace(/\{(\w+)\}/g, "{:$1}"),
|
|
784
|
-
// Transform splat params: {...param} => {*param}
|
|
785
|
-
(s) => s.replace(/\{\.\.\./g, "{*"),
|
|
786
|
-
// Insert leading slash inside optional/splat groups.
|
|
787
|
-
// {:name} => {/:name}
|
|
788
|
-
// {*name} => {/*name}
|
|
789
|
-
(s) => {
|
|
790
|
-
return s.startsWith("{") ? s.replace(/^\{/, "{/") : s;
|
|
791
|
-
}
|
|
792
|
-
];
|
|
793
|
-
const detectBareParams = (s) => {
|
|
794
|
-
let depth = 0;
|
|
795
|
-
for (const [i, ch] of [...s].entries()) {
|
|
796
|
-
if (ch === "{") {
|
|
797
|
-
depth += 1;
|
|
798
|
-
} else if (ch === "}") {
|
|
799
|
-
depth -= 1;
|
|
800
|
-
} else if (ch === ":" && depth === 0) {
|
|
801
|
-
const match = s.slice(i + 1).match(/^\w+/);
|
|
802
|
-
return match?.[0] || ":";
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
return;
|
|
806
|
-
};
|
|
807
|
-
const tokens = path.replace(/^index\/?/, "").split("/").flatMap((orig) => {
|
|
808
|
-
if (!orig.length) {
|
|
809
|
-
return [];
|
|
810
|
-
}
|
|
811
|
-
const bareParam = detectBareParams(orig);
|
|
812
|
-
if (bareParam === ":") {
|
|
813
|
-
throw new Error(
|
|
814
|
-
`${path} contains colons outside braces, use : only within {}`
|
|
815
|
-
);
|
|
816
|
-
} else if (bareParam) {
|
|
817
|
-
throw new Error(
|
|
818
|
-
`${path} contains bare params, use [${bareParam}] instead of :${bareParam}`
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
const pattern = patternTransforms.reduce((src, fn) => fn(src), orig);
|
|
822
|
-
const { tokens: tokens2 } = parse(pattern);
|
|
823
|
-
const parts = extractParts(tokens2, (val) => {
|
|
824
|
-
return /\W/.test(val) || /^\d/.test(val) ? [val.replace(/^\d+|\W/g, "_"), crc4(orig)].join("_") : val;
|
|
825
|
-
});
|
|
826
|
-
const isStatic = parts.length === 1 ? parts[0].type === "static" : false;
|
|
827
|
-
const isParam = parts.length === 1 ? parts[0].type === "param" : false;
|
|
828
|
-
const kind = isStatic ? "static" : isParam ? "param" : "mixed";
|
|
829
|
-
return [
|
|
830
|
-
{
|
|
831
|
-
kind,
|
|
832
|
-
orig,
|
|
833
|
-
pattern,
|
|
834
|
-
parts
|
|
835
|
-
}
|
|
836
|
-
];
|
|
837
|
-
});
|
|
838
|
-
return [
|
|
839
|
-
tokens,
|
|
840
|
-
tokens.map(({ pattern }, i) => {
|
|
841
|
-
const next = tokens[i + 1];
|
|
842
|
-
if (!next || next.pattern.includes("/")) {
|
|
843
|
-
return pattern;
|
|
844
|
-
}
|
|
845
|
-
const slashRequired = tokens.slice(i + 1).some((e) => {
|
|
846
|
-
return e.parts.some((e2) => {
|
|
847
|
-
return e2.type === "static" || e2.kind === "required";
|
|
848
|
-
});
|
|
849
|
-
});
|
|
850
|
-
return slashRequired ? `${pattern}/` : pattern;
|
|
851
|
-
}).join("")
|
|
852
|
-
];
|
|
853
|
-
};
|
|
854
|
-
var normalizeStaticValue = (value) => {
|
|
855
|
-
return value.replace(/\+/g, "\\\\+");
|
|
856
|
-
};
|
|
857
|
-
var sortRoutes = (a, b) => {
|
|
858
|
-
const aSpecificity = routeSpecificity(a.pathTokens);
|
|
859
|
-
const bSpecificity = routeSpecificity(b.pathTokens);
|
|
860
|
-
if (aSpecificity !== bSpecificity) {
|
|
861
|
-
return bSpecificity - aSpecificity;
|
|
862
|
-
}
|
|
863
|
-
if (a.pathTokens.length !== b.pathTokens.length) {
|
|
864
|
-
return a.pathTokens.length - b.pathTokens.length;
|
|
865
|
-
}
|
|
866
|
-
return a.name.localeCompare(b.name);
|
|
867
|
-
};
|
|
868
|
-
var paramWeight = (part) => {
|
|
869
|
-
return {
|
|
870
|
-
required: 2,
|
|
871
|
-
optional: 1,
|
|
872
|
-
splat: 0
|
|
873
|
-
}[part.kind];
|
|
874
|
-
};
|
|
875
|
-
var mixedSegmentWeight = (parts) => {
|
|
876
|
-
const hasSplat = parts.some((p) => {
|
|
877
|
-
return p.type === "param" ? p.kind === "splat" : false;
|
|
878
|
-
});
|
|
879
|
-
return hasSplat ? 0.5 : 3;
|
|
880
|
-
};
|
|
881
|
-
var segmentWeight = (token) => {
|
|
882
|
-
return {
|
|
883
|
-
static: 4,
|
|
884
|
-
mixed: mixedSegmentWeight(token.parts),
|
|
885
|
-
param: paramWeight(token.parts[0])
|
|
886
|
-
}[token.kind];
|
|
887
|
-
};
|
|
888
|
-
var routeSpecificity = (pathTokens) => {
|
|
889
|
-
return pathTokens.reduce((sum, token) => sum + segmentWeight(token), 0);
|
|
890
|
-
};
|
|
891
|
-
|
|
892
|
-
// src/routes-factory/templates/resolved-types.hbs
|
|
893
|
-
var resolved_types_default = "{{#each resolvedTypes}}\nexport type {{name}} = {{text}};\n{{/each}}\n";
|
|
894
|
-
|
|
895
|
-
// src/routes-factory/templates/types.hbs
|
|
896
|
-
var types_default = '{{#each typeDeclarations}}{{text}}\n{{/each}}\n\nexport type {{params.id}} = {\n {{#each paramsSchema}}\n "{{name}}"{{#unless isRequired}}?{{/unless}}: {{#if refinement}}\n {{refinement.text}},\n {{else}}\n {{#if isSplat}}Array<string>{{else}}string{{/if}},\n {{/if}}\n {{/each}}\n};\n\n{{#each validationTypes}}export type {{id}} = {{text}};\n{{/each}}\n';
|
|
897
|
-
|
|
898
|
-
// src/routes-factory/resolve.ts
|
|
899
|
-
var API_INDEX_BASENAME = "index";
|
|
900
|
-
var API_INDEX_PATTERN = `${API_INDEX_BASENAME}.ts`;
|
|
901
|
-
var API_USE_BASENAME = "use";
|
|
902
|
-
var API_USE_PATTERN = `${API_USE_BASENAME}.ts`;
|
|
903
|
-
var PAGE_INDEX_BASENAME = "index";
|
|
904
|
-
var PAGE_INDEX_PATTERN = `${PAGE_INDEX_BASENAME}.{tsx,vue}`;
|
|
905
|
-
var PAGE_LAYOUT_BASENAME = "layout";
|
|
906
|
-
var PAGE_LAYOUT_PATTERN = `${PAGE_LAYOUT_BASENAME}.{tsx,vue}`;
|
|
907
|
-
var ROUTE_FILE_PATTERNS = [
|
|
908
|
-
// match index files in api dir
|
|
909
|
-
`${defaults.apiDir}/**/${API_INDEX_PATTERN}`,
|
|
910
|
-
// match use files in api dir
|
|
911
|
-
`${defaults.apiDir}/**/${API_USE_PATTERN}`,
|
|
912
|
-
// match index files in pages dir
|
|
913
|
-
`${defaults.pagesDir}/**/${PAGE_INDEX_PATTERN}`,
|
|
914
|
-
// match layout files in pages dir
|
|
915
|
-
`${defaults.pagesDir}/**/${PAGE_LAYOUT_PATTERN}`
|
|
916
|
-
];
|
|
917
|
-
var scanRoutes = async ({
|
|
918
|
-
appRoot,
|
|
919
|
-
sourceFolder
|
|
920
|
-
}) => {
|
|
921
|
-
const { createPath } = pathResolver({ appRoot, sourceFolder });
|
|
922
|
-
return glob2(ROUTE_FILE_PATTERNS, {
|
|
923
|
-
cwd: createPath.src(),
|
|
924
|
-
absolute: true,
|
|
925
|
-
onlyFiles: true,
|
|
926
|
-
followSymbolicLinks: false,
|
|
927
|
-
ignore: [
|
|
928
|
-
// ignore top-level matches, routes resides in folders, even index route
|
|
929
|
-
`${defaults.apiDir}/${API_INDEX_PATTERN}`,
|
|
930
|
-
`${defaults.apiDir}/${API_USE_PATTERN}`,
|
|
931
|
-
`${defaults.pagesDir}/${PAGE_INDEX_PATTERN}`,
|
|
932
|
-
`${defaults.pagesDir}/${PAGE_LAYOUT_PATTERN}`
|
|
933
|
-
]
|
|
934
|
-
});
|
|
935
|
-
};
|
|
936
|
-
var isRouteFile = (file, {
|
|
937
|
-
appRoot,
|
|
938
|
-
sourceFolder
|
|
939
|
-
}) => {
|
|
940
|
-
const [_sourceFolder, folder, ...rest] = resolve3(appRoot, file).replace(`${appRoot}/${defaults.srcDir}/`, "").split("/");
|
|
941
|
-
if (!folder || _sourceFolder !== sourceFolder || rest.length < 2) {
|
|
942
|
-
return false;
|
|
943
|
-
}
|
|
944
|
-
return picomatch.isMatch(join3(folder, ...rest), ROUTE_FILE_PATTERNS) ? [folder, rest.join("/")] : false;
|
|
945
|
-
};
|
|
946
|
-
var isApiRoute = (file) => {
|
|
947
|
-
return picomatch.matchBase(file, `**/${API_INDEX_PATTERN}`);
|
|
948
|
-
};
|
|
949
|
-
var isApiUse = (file) => {
|
|
950
|
-
return picomatch.matchBase(file, `**/${API_USE_PATTERN}`);
|
|
951
|
-
};
|
|
952
|
-
var isPageRoute = (file) => {
|
|
953
|
-
return picomatch.matchBase(file, `**/${PAGE_INDEX_PATTERN}`);
|
|
954
|
-
};
|
|
955
|
-
var isPageLayout = (file) => {
|
|
956
|
-
return picomatch.matchBase(file, `**/${PAGE_LAYOUT_PATTERN}`);
|
|
957
|
-
};
|
|
958
|
-
var createRouteEntry = (fileFullpath, {
|
|
959
|
-
appRoot,
|
|
960
|
-
sourceFolder
|
|
961
|
-
}) => {
|
|
962
|
-
const resolvedPaths = isRouteFile(fileFullpath, { appRoot, sourceFolder });
|
|
963
|
-
if (!resolvedPaths) {
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
const [folder, file] = resolvedPaths;
|
|
967
|
-
const id = `${file.replace(/\W+/g, "_")}_${crc5(file)}`;
|
|
968
|
-
const name = dirname3(file);
|
|
969
|
-
try {
|
|
970
|
-
const [pathTokens, pathPattern] = pathTokensFactory(dirname3(file));
|
|
971
|
-
return { id, name, folder, file, fileFullpath, pathTokens, pathPattern };
|
|
972
|
-
} catch (error) {
|
|
973
|
-
console.error(
|
|
974
|
-
`\u2757${styleText2("red", "ERROR")}: Failed parsing path for "${styleText2("cyan", file)}"`
|
|
975
|
-
);
|
|
976
|
-
console.error(error);
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
var pageLayoutResolverFactory = () => {
|
|
981
|
-
return (entry) => {
|
|
982
|
-
const { name } = entry;
|
|
983
|
-
const handler = async () => {
|
|
984
|
-
return {
|
|
985
|
-
kind: "pageLayout",
|
|
986
|
-
entry
|
|
987
|
-
};
|
|
988
|
-
};
|
|
989
|
-
return { name, handler };
|
|
990
|
-
};
|
|
991
|
-
};
|
|
992
|
-
var pageRouteResolverFactory = () => {
|
|
993
|
-
return (entry) => {
|
|
994
|
-
const { id, name, folder, file, fileFullpath, pathTokens, pathPattern } = entry;
|
|
995
|
-
const handler = async () => {
|
|
996
|
-
const entry2 = {
|
|
997
|
-
id,
|
|
998
|
-
name,
|
|
999
|
-
pathTokens,
|
|
1000
|
-
pathPattern,
|
|
1001
|
-
params: {
|
|
1002
|
-
schema: pathTokens.flatMap((e) => {
|
|
1003
|
-
return e.parts.filter((p) => p.type === "param");
|
|
1004
|
-
})
|
|
1005
|
-
},
|
|
1006
|
-
folder,
|
|
1007
|
-
file,
|
|
1008
|
-
fileFullpath
|
|
1009
|
-
};
|
|
1010
|
-
return {
|
|
1011
|
-
kind: "pageRoute",
|
|
1012
|
-
entry: entry2
|
|
1013
|
-
};
|
|
1014
|
-
};
|
|
1015
|
-
return { name, handler };
|
|
1016
|
-
};
|
|
1017
|
-
};
|
|
1018
|
-
var apiUseResolverFactory = () => {
|
|
1019
|
-
return (entry) => {
|
|
1020
|
-
const { name } = entry;
|
|
1021
|
-
const handler = async () => {
|
|
1022
|
-
return {
|
|
1023
|
-
kind: "apiUse",
|
|
1024
|
-
entry
|
|
1025
|
-
};
|
|
1026
|
-
};
|
|
1027
|
-
return { name, handler };
|
|
1028
|
-
};
|
|
1029
|
-
};
|
|
1030
|
-
var apiRouteResolverFactory = (pluginOptions) => {
|
|
1031
|
-
const {
|
|
1032
|
-
appRoot,
|
|
1033
|
-
sourceFolder,
|
|
1034
|
-
generators = [],
|
|
1035
|
-
refineTypeName
|
|
1036
|
-
} = pluginOptions;
|
|
1037
|
-
const resolveTypes = generators.some((e) => e.options?.resolveTypes);
|
|
1038
|
-
const {
|
|
1039
|
-
//
|
|
1040
|
-
literalTypesResolver,
|
|
1041
|
-
getSourceFile,
|
|
1042
|
-
refreshSourceFile
|
|
1043
|
-
} = typeResolverFactory(pluginOptions);
|
|
1044
|
-
return ({
|
|
1045
|
-
id,
|
|
1046
|
-
name,
|
|
1047
|
-
file,
|
|
1048
|
-
folder,
|
|
1049
|
-
fileFullpath,
|
|
1050
|
-
pathTokens,
|
|
1051
|
-
pathPattern
|
|
1052
|
-
}) => {
|
|
1053
|
-
const handler = async (updatedFile) => {
|
|
1054
|
-
const paramsSchema = pathTokens.flatMap(
|
|
1055
|
-
(e) => {
|
|
1056
|
-
return e.parts.flatMap((p) => {
|
|
1057
|
-
return p.type === "param" ? [p] : [];
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
);
|
|
1061
|
-
const optionalParams = paramsSchema.length ? paramsSchema.filter((e) => e.kind === "required").length === 0 : true;
|
|
1062
|
-
const { getCache, persistCache } = cacheFactory(
|
|
1063
|
-
{ id, file, fileFullpath },
|
|
1064
|
-
{
|
|
1065
|
-
appRoot,
|
|
1066
|
-
sourceFolder,
|
|
1067
|
-
extraContext: { resolveTypes }
|
|
1068
|
-
}
|
|
1069
|
-
);
|
|
1070
|
-
let cache = await getCache({ validate: true });
|
|
1071
|
-
if (!cache) {
|
|
1072
|
-
if (updatedFile === fileFullpath) {
|
|
1073
|
-
await refreshSourceFile(fileFullpath);
|
|
1074
|
-
}
|
|
1075
|
-
const {
|
|
1076
|
-
typeDeclarations,
|
|
1077
|
-
paramsRefinements,
|
|
1078
|
-
methods,
|
|
1079
|
-
validationDefinitions: validationDefinitions2,
|
|
1080
|
-
referencedFiles = []
|
|
1081
|
-
} = await resolveRouteSignature(
|
|
1082
|
-
{ id, name, fileFullpath, optionalParams },
|
|
1083
|
-
{
|
|
1084
|
-
withReferencedFiles: true,
|
|
1085
|
-
sourceFile: getSourceFile(fileFullpath),
|
|
1086
|
-
relpathResolver(path) {
|
|
1087
|
-
return join3(sourceFolder, defaults.apiDir, dirname3(file), path);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
);
|
|
1091
|
-
const validationTypes = validationDefinitions2.flatMap((def) => {
|
|
1092
|
-
return def.target === "response" ? def.variants.flatMap(({ id: id2, body }) => {
|
|
1093
|
-
return body ? [{ id: id2, text: body }] : [];
|
|
1094
|
-
}) : [def.schema];
|
|
1095
|
-
});
|
|
1096
|
-
const numericParams = paramsRefinements ? paramsRefinements.flatMap(({ text, index }) => {
|
|
1097
|
-
if (text === "number") {
|
|
1098
|
-
const param = paramsSchema.at(index);
|
|
1099
|
-
return param ? [param.name] : [];
|
|
1100
|
-
}
|
|
1101
|
-
return [];
|
|
1102
|
-
}) : [];
|
|
1103
|
-
const typesFile = pathResolver({
|
|
1104
|
-
appRoot,
|
|
1105
|
-
sourceFolder
|
|
1106
|
-
}).createPath.libApi(dirname3(file), "types.ts");
|
|
1107
|
-
const params = {
|
|
1108
|
-
id: ["ParamsT", crc5(name)].join(""),
|
|
1109
|
-
schema: paramsSchema,
|
|
1110
|
-
resolvedType: void 0
|
|
1111
|
-
};
|
|
1112
|
-
const typesFileContent = render(types_default, {
|
|
1113
|
-
params,
|
|
1114
|
-
paramsSchema: paramsSchema.map((param, index) => {
|
|
1115
|
-
return {
|
|
1116
|
-
...param,
|
|
1117
|
-
isRequired: param.kind === "required",
|
|
1118
|
-
isSplat: param.kind === "splat",
|
|
1119
|
-
refinement: paramsRefinements?.at(index)
|
|
1120
|
-
};
|
|
1121
|
-
}),
|
|
1122
|
-
typeDeclarations,
|
|
1123
|
-
validationTypes
|
|
1124
|
-
});
|
|
1125
|
-
const resolvedTypes = resolveTypes ? literalTypesResolver(typesFileContent, {
|
|
1126
|
-
stripComments: true,
|
|
1127
|
-
overrides: { [refineTypeName]: refineTypeName },
|
|
1128
|
-
withProperties: [
|
|
1129
|
-
params.id,
|
|
1130
|
-
...validationTypes.flatMap(({ id: id2 }) => id2)
|
|
1131
|
-
]
|
|
1132
|
-
}) : void 0;
|
|
1133
|
-
await renderToFile(
|
|
1134
|
-
typesFile,
|
|
1135
|
-
resolvedTypes ? resolved_types_default : typesFileContent,
|
|
1136
|
-
{ resolvedTypes }
|
|
1137
|
-
);
|
|
1138
|
-
params.resolvedType = resolvedTypes?.find((e) => e.name === params.id);
|
|
1139
|
-
cache = await persistCache({
|
|
1140
|
-
params,
|
|
1141
|
-
methods,
|
|
1142
|
-
typeDeclarations,
|
|
1143
|
-
numericParams,
|
|
1144
|
-
referencedFiles,
|
|
1145
|
-
validationDefinitions: validationDefinitions2.map((def) => {
|
|
1146
|
-
return {
|
|
1147
|
-
...def,
|
|
1148
|
-
...def.target === "response" ? {
|
|
1149
|
-
variants: def.variants.map((variant) => {
|
|
1150
|
-
return {
|
|
1151
|
-
...variant,
|
|
1152
|
-
resolvedType: resolvedTypes?.find(
|
|
1153
|
-
(e) => e.name === variant.id
|
|
1154
|
-
)
|
|
1155
|
-
};
|
|
1156
|
-
})
|
|
1157
|
-
} : {
|
|
1158
|
-
schema: {
|
|
1159
|
-
...def.schema,
|
|
1160
|
-
resolvedType: resolvedTypes?.find(
|
|
1161
|
-
(e) => e.name === def.schema.id
|
|
1162
|
-
)
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
})
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
const validationDefinitions = cache.validationDefinitions.flatMap(
|
|
1170
|
-
(def) => {
|
|
1171
|
-
let augmentedDef = def;
|
|
1172
|
-
if (def.target === "response") {
|
|
1173
|
-
augmentedDef = {
|
|
1174
|
-
...def,
|
|
1175
|
-
variants: def.variants.flatMap((variant, i) => {
|
|
1176
|
-
if (typeof variant.contentType !== "string") {
|
|
1177
|
-
return [variant];
|
|
1178
|
-
}
|
|
1179
|
-
if (variant.contentType.includes("/")) {
|
|
1180
|
-
return [variant];
|
|
1181
|
-
}
|
|
1182
|
-
const contentType = mimeTypes.lookup(variant.contentType);
|
|
1183
|
-
if (contentType === false) {
|
|
1184
|
-
console.warn(
|
|
1185
|
-
styleText2(
|
|
1186
|
-
["bold", "red"],
|
|
1187
|
-
"\u2717 Failed resolving Response Content Type"
|
|
1188
|
-
)
|
|
1189
|
-
);
|
|
1190
|
-
console.warn(
|
|
1191
|
-
` Invalid value provided for mime-types lookup - ${variant.contentType}`
|
|
1192
|
-
);
|
|
1193
|
-
console.warn(
|
|
1194
|
-
styleText2(
|
|
1195
|
-
["cyan"],
|
|
1196
|
-
` Response variant #${i} excluded from route schemas`
|
|
1197
|
-
)
|
|
1198
|
-
);
|
|
1199
|
-
console.warn(` Route: ${name}; Method: ${def.method}`);
|
|
1200
|
-
console.warn();
|
|
1201
|
-
return [];
|
|
1202
|
-
}
|
|
1203
|
-
return [{ ...variant, contentType }];
|
|
1204
|
-
})
|
|
1205
|
-
};
|
|
1206
|
-
} else if (def.contentType && !def.contentType.includes("/")) {
|
|
1207
|
-
const contentType = mimeTypes.lookup(def.contentType);
|
|
1208
|
-
if (contentType === false) {
|
|
1209
|
-
console.warn(
|
|
1210
|
-
styleText2(
|
|
1211
|
-
["bold", "red"],
|
|
1212
|
-
"\u2717 Failed resolving Response Content Type"
|
|
1213
|
-
)
|
|
1214
|
-
);
|
|
1215
|
-
console.warn(
|
|
1216
|
-
` Invalid value provided for mime-types lookup - ${def.contentType}`
|
|
1217
|
-
);
|
|
1218
|
-
console.warn(` Route: ${name}; Method: ${def.method}`);
|
|
1219
|
-
console.warn();
|
|
1220
|
-
} else {
|
|
1221
|
-
augmentedDef = { ...def, contentType };
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return augmentedDef ? [augmentedDef] : [];
|
|
1225
|
-
}
|
|
1226
|
-
);
|
|
1227
|
-
const entry = {
|
|
1228
|
-
id,
|
|
1229
|
-
name,
|
|
1230
|
-
pathTokens,
|
|
1231
|
-
pathPattern,
|
|
1232
|
-
params: cache.params,
|
|
1233
|
-
numericParams: cache.numericParams,
|
|
1234
|
-
optionalParams,
|
|
1235
|
-
folder,
|
|
1236
|
-
file,
|
|
1237
|
-
fileFullpath,
|
|
1238
|
-
methods: cache.methods,
|
|
1239
|
-
typeDeclarations: cache.typeDeclarations,
|
|
1240
|
-
validationDefinitions,
|
|
1241
|
-
referencedFiles: Object.keys(cache.referencedFiles).map(
|
|
1242
|
-
// expand referenced files path,
|
|
1243
|
-
// they are stored as relative in cache
|
|
1244
|
-
(e) => resolve3(appRoot, e)
|
|
1245
|
-
)
|
|
1246
|
-
};
|
|
1247
|
-
return {
|
|
1248
|
-
kind: "apiRoute",
|
|
1249
|
-
entry
|
|
1250
|
-
};
|
|
1251
|
-
};
|
|
1252
|
-
return { name, handler };
|
|
1253
|
-
};
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
// src/routes-factory/nesting.ts
|
|
1257
|
-
import { basename } from "node:path";
|
|
1258
|
-
var nestedRoutesFactory = (routeEntries) => {
|
|
1259
|
-
const entryStack = structuredClone(routeEntries).sort(sortRoutes);
|
|
1260
|
-
const transformEntries = (entries, parent) => {
|
|
1261
|
-
return [...new Set(entries.map((e) => e.name))].flatMap((name) => {
|
|
1262
|
-
const nameEntries = entryStack.flatMap(({ fileFullpath, ...entry }) => {
|
|
1263
|
-
return entry.name === name ? [entry] : [];
|
|
1264
|
-
});
|
|
1265
|
-
const index = nameEntries.find(
|
|
1266
|
-
(e) => basename(e.file).startsWith(PAGE_INDEX_BASENAME)
|
|
1267
|
-
);
|
|
1268
|
-
const layout = nameEntries.find(
|
|
1269
|
-
(e) => basename(e.file).startsWith(PAGE_LAYOUT_BASENAME)
|
|
1270
|
-
);
|
|
1271
|
-
if (index || layout) {
|
|
1272
|
-
return [
|
|
1273
|
-
{
|
|
1274
|
-
index: index ? {
|
|
1275
|
-
...index,
|
|
1276
|
-
pathTokens: index.pathTokens.slice(
|
|
1277
|
-
parent?.pathTokens.length || 0
|
|
1278
|
-
)
|
|
1279
|
-
} : void 0,
|
|
1280
|
-
layout: layout ? {
|
|
1281
|
-
...layout,
|
|
1282
|
-
pathTokens: layout.pathTokens.slice(
|
|
1283
|
-
parent?.pathTokens.length || 0
|
|
1284
|
-
)
|
|
1285
|
-
} : void 0,
|
|
1286
|
-
parent: parent?.name,
|
|
1287
|
-
children: transformEntries(
|
|
1288
|
-
findDescendantEntries(index || layout),
|
|
1289
|
-
index || layout
|
|
1290
|
-
)
|
|
1291
|
-
}
|
|
1292
|
-
];
|
|
1293
|
-
}
|
|
1294
|
-
return [];
|
|
1295
|
-
});
|
|
1296
|
-
};
|
|
1297
|
-
const findDescendantEntries = ({
|
|
1298
|
-
name,
|
|
1299
|
-
pathTokens
|
|
1300
|
-
}) => {
|
|
1301
|
-
const potentialChildren = entryStack.filter((entry) => {
|
|
1302
|
-
if (entry.pathTokens.length <= pathTokens.length) {
|
|
1303
|
-
return false;
|
|
1304
|
-
}
|
|
1305
|
-
if (!entry.name.startsWith(`${name}/`)) {
|
|
1306
|
-
return false;
|
|
1307
|
-
}
|
|
1308
|
-
return true;
|
|
1309
|
-
});
|
|
1310
|
-
return potentialChildren.filter((child) => {
|
|
1311
|
-
const hasIntermediateRoute = potentialChildren.some((intermediate) => {
|
|
1312
|
-
if (intermediate === child) {
|
|
1313
|
-
return false;
|
|
1314
|
-
}
|
|
1315
|
-
if (intermediate.pathTokens.length <= pathTokens.length) {
|
|
1316
|
-
return false;
|
|
1317
|
-
}
|
|
1318
|
-
if (intermediate.pathTokens.length >= child.pathTokens.length) {
|
|
1319
|
-
return false;
|
|
1320
|
-
}
|
|
1321
|
-
return child.name.startsWith(`${intermediate.name}/`);
|
|
1322
|
-
});
|
|
1323
|
-
return !hasIntermediateRoute;
|
|
1324
|
-
});
|
|
1325
|
-
};
|
|
1326
|
-
const rootEntries = entryStack.filter((entry) => {
|
|
1327
|
-
const hasParent = entryStack.some((potential) => {
|
|
1328
|
-
if (potential === entry) {
|
|
1329
|
-
return false;
|
|
1330
|
-
}
|
|
1331
|
-
if (potential.pathTokens.length >= entry.pathTokens.length) {
|
|
1332
|
-
return false;
|
|
1333
|
-
}
|
|
1334
|
-
return entry.name.startsWith(`${potential.name}/`);
|
|
1335
|
-
});
|
|
1336
|
-
return !hasParent;
|
|
1337
|
-
});
|
|
1338
|
-
return transformEntries(rootEntries);
|
|
1339
|
-
};
|
|
1340
|
-
|
|
1341
|
-
// src/routes-factory/index.ts
|
|
1342
|
-
var routesFactory = async (pluginOptions) => {
|
|
1343
|
-
const { appRoot, sourceFolder } = pluginOptions;
|
|
1344
|
-
const apiRouteResolver = apiRouteResolverFactory(pluginOptions);
|
|
1345
|
-
const apiUseResolver = apiUseResolverFactory(pluginOptions);
|
|
1346
|
-
const pageRouteResolver = pageRouteResolverFactory(pluginOptions);
|
|
1347
|
-
const pageLayoutResolver = pageLayoutResolverFactory(pluginOptions);
|
|
1348
|
-
const resolversFactory = (routeFiles2) => {
|
|
1349
|
-
const resolvers = /* @__PURE__ */ new Map();
|
|
1350
|
-
const entries = routeFiles2.flatMap((file) => {
|
|
1351
|
-
const entry = createRouteEntry(file, pluginOptions);
|
|
1352
|
-
return entry ? [entry] : [];
|
|
1353
|
-
});
|
|
1354
|
-
for (const entry of entries) {
|
|
1355
|
-
if (entry.folder === defaults.apiDir) {
|
|
1356
|
-
if (isApiRoute(entry.file)) {
|
|
1357
|
-
resolvers.set(entry.fileFullpath, apiRouteResolver(entry));
|
|
1358
|
-
} else if (isApiUse(entry.file)) {
|
|
1359
|
-
resolvers.set(entry.fileFullpath, apiUseResolver(entry));
|
|
1360
|
-
}
|
|
1361
|
-
} else if (entry.folder === defaults.pagesDir) {
|
|
1362
|
-
if (isPageRoute(entry.file)) {
|
|
1363
|
-
resolvers.set(entry.fileFullpath, pageRouteResolver(entry));
|
|
1364
|
-
} else if (isPageLayout(entry.file)) {
|
|
1365
|
-
resolvers.set(entry.fileFullpath, pageLayoutResolver(entry));
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
return resolvers;
|
|
1370
|
-
};
|
|
1371
|
-
const routeFiles = await scanRoutes({ appRoot, sourceFolder });
|
|
1372
|
-
return {
|
|
1373
|
-
resolvers: resolversFactory(routeFiles),
|
|
1374
|
-
resolversFactory
|
|
1375
|
-
};
|
|
1376
|
-
};
|
|
1377
|
-
|
|
1378
|
-
// src/base-plugin/api-handler.ts
|
|
1379
|
-
import { join as join4, resolve as resolve4 } from "node:path";
|
|
1380
|
-
import { styleText as styleText3 } from "node:util";
|
|
1381
|
-
import { context } from "esbuild";
|
|
1382
|
-
var api_handler_default = async (options) => {
|
|
1383
|
-
const { appRoot, sourceFolder, baseurl, apiurl } = options;
|
|
1384
|
-
const { createPath } = pathResolver({ appRoot, sourceFolder });
|
|
1385
|
-
const outDir = join4(options.outDir, defaults.apiDir);
|
|
1386
|
-
const esbuildOptions = await import(resolve4(appRoot, "esbuild.json"), { with: { type: "json" } }).then((e) => e.default);
|
|
1387
|
-
let devSetup;
|
|
1388
|
-
const watcher = async () => {
|
|
1389
|
-
const rebuildPlugin = {
|
|
1390
|
-
name: "rebuild",
|
|
1391
|
-
setup(build) {
|
|
1392
|
-
build.onEnd(async () => {
|
|
1393
|
-
if (devSetup) {
|
|
1394
|
-
await devSetup.teardownHandler?.();
|
|
1395
|
-
}
|
|
1396
|
-
try {
|
|
1397
|
-
await import(`${outDir}/dev.js?${Date.now()}`).then((e) => {
|
|
1398
|
-
devSetup = e.default;
|
|
1399
|
-
});
|
|
1400
|
-
console.debug(`${styleText3("green", "\u279C")} Api handler ready`);
|
|
1401
|
-
} catch (error) {
|
|
1402
|
-
console.error(`${styleText3("red", "\u2717")} Api handler error`);
|
|
1403
|
-
console.error(error);
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
};
|
|
1408
|
-
const ctx = await context({
|
|
1409
|
-
...esbuildOptions,
|
|
1410
|
-
define: {
|
|
1411
|
-
...esbuildOptions.define,
|
|
1412
|
-
PRODUCTION_BUILD: "false"
|
|
1413
|
-
},
|
|
1414
|
-
logLevel: "error",
|
|
1415
|
-
bundle: true,
|
|
1416
|
-
entryPoints: [createPath.api("dev.ts")],
|
|
1417
|
-
plugins: [rebuildPlugin],
|
|
1418
|
-
outdir: outDir
|
|
1419
|
-
});
|
|
1420
|
-
return {
|
|
1421
|
-
async start() {
|
|
1422
|
-
await ctx.watch({
|
|
1423
|
-
// waits this many milliseconds before rebuilding after a change is detected
|
|
1424
|
-
delay: options.watcher.delay
|
|
1425
|
-
});
|
|
1426
|
-
},
|
|
1427
|
-
async stop() {
|
|
1428
|
-
await ctx.dispose();
|
|
1429
|
-
}
|
|
1430
|
-
};
|
|
1431
|
-
};
|
|
1432
|
-
const devMiddleware = async (req, res, next) => {
|
|
1433
|
-
const {
|
|
1434
|
-
requestMatcher = () => {
|
|
1435
|
-
return new RegExp(`^${join4(baseurl, apiurl)}($|/)`).test(
|
|
1436
|
-
req.url
|
|
1437
|
-
);
|
|
1438
|
-
},
|
|
1439
|
-
requestHandler
|
|
1440
|
-
} = { ...devSetup };
|
|
1441
|
-
if (!requestMatcher(req)) {
|
|
1442
|
-
return next();
|
|
1443
|
-
}
|
|
1444
|
-
const handler = requestHandler?.();
|
|
1445
|
-
return handler ? handler(req, res) : next();
|
|
1446
|
-
};
|
|
1447
|
-
return {
|
|
1448
|
-
watcher,
|
|
1449
|
-
devMiddleware
|
|
1450
|
-
};
|
|
1451
|
-
};
|
|
1452
|
-
|
|
1453
|
-
// src/base-plugin/spinner.ts
|
|
1454
|
-
import { Spinner } from "@topcli/spinner";
|
|
1455
|
-
var spinnerFactory = (startText) => {
|
|
1456
|
-
const spinner = new Spinner().start(startText);
|
|
1457
|
-
let _text = startText;
|
|
1458
|
-
return {
|
|
1459
|
-
text(text) {
|
|
1460
|
-
_text = text;
|
|
1461
|
-
spinner.text = text;
|
|
1462
|
-
},
|
|
1463
|
-
append(text) {
|
|
1464
|
-
spinner.text = `${_text} \u203A ${text}`;
|
|
1465
|
-
},
|
|
1466
|
-
succeed(text) {
|
|
1467
|
-
if (text) {
|
|
1468
|
-
this.append(text);
|
|
1469
|
-
} else {
|
|
1470
|
-
this.text(_text);
|
|
1471
|
-
}
|
|
1472
|
-
spinner.succeed();
|
|
1473
|
-
},
|
|
1474
|
-
failed(text) {
|
|
1475
|
-
if (text) {
|
|
1476
|
-
this.text([_text, text].join("\n"));
|
|
1477
|
-
}
|
|
1478
|
-
spinner.failed();
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
};
|
|
1482
|
-
var withSpinner = (text, pipe, spinner) => pipe(spinner || spinnerFactory(text));
|
|
1483
|
-
|
|
1484
|
-
// src/base-plugin/index.ts
|
|
1485
|
-
var base_plugin_default = (apiurl, pluginOptions) => {
|
|
1486
|
-
const outDirSuffix = "client";
|
|
1487
|
-
const store = {};
|
|
1488
|
-
const createWorker = () => {
|
|
1489
|
-
const { generators = [], ...restOptions } = store.resolvedOptions;
|
|
1490
|
-
const generatorModules = generators.map(
|
|
1491
|
-
(e) => [e.moduleImport, e.moduleConfig]
|
|
1492
|
-
);
|
|
1493
|
-
const workerData = {
|
|
1494
|
-
...restOptions,
|
|
1495
|
-
generatorModules
|
|
1496
|
-
};
|
|
1497
|
-
return new Worker(resolve5(import.meta.dirname, "base-plugin/worker.js"), {
|
|
1498
|
-
workerData,
|
|
1499
|
-
env: {
|
|
1500
|
-
...process.env,
|
|
1501
|
-
FORCE_COLOR: "1"
|
|
1502
|
-
}
|
|
1503
|
-
});
|
|
1504
|
-
};
|
|
1505
|
-
const workerHandler = (onReady, onExit) => {
|
|
1506
|
-
const worker = createWorker();
|
|
1507
|
-
const spinnerMap = /* @__PURE__ */ new Map();
|
|
1508
|
-
worker.on("error", async (error) => {
|
|
1509
|
-
console.error(error);
|
|
1510
|
-
});
|
|
1511
|
-
worker.on("exit", async () => {
|
|
1512
|
-
await onExit?.();
|
|
1513
|
-
});
|
|
1514
|
-
worker.on(
|
|
1515
|
-
"message",
|
|
1516
|
-
async (msg) => {
|
|
1517
|
-
if (msg?.spinner) {
|
|
1518
|
-
const { id, startText, method, text } = msg.spinner;
|
|
1519
|
-
withSpinner(
|
|
1520
|
-
startText,
|
|
1521
|
-
(spinner) => {
|
|
1522
|
-
spinnerMap.set(id, spinner);
|
|
1523
|
-
spinner[method](text || "");
|
|
1524
|
-
if (method === "succeed" || method === "failed") {
|
|
1525
|
-
spinnerMap.delete(id);
|
|
1526
|
-
}
|
|
1527
|
-
},
|
|
1528
|
-
spinnerMap.get(id)
|
|
1529
|
-
);
|
|
1530
|
-
} else if (msg?.error) {
|
|
1531
|
-
const { error } = msg;
|
|
1532
|
-
if (error.stack) {
|
|
1533
|
-
const [message, ...stack] = error.stack.split("\n");
|
|
1534
|
-
console.error(styleText4("red", message));
|
|
1535
|
-
console.error(stack.join("\n"));
|
|
1536
|
-
} else if (error?.message) {
|
|
1537
|
-
console.error(`${styleText4("red", error?.name)}: ${error.message}`);
|
|
1538
|
-
} else {
|
|
1539
|
-
console.error(error);
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
);
|
|
1544
|
-
const readyHandler = async (msg) => {
|
|
1545
|
-
if (msg === "ready") {
|
|
1546
|
-
worker.off("message", readyHandler);
|
|
1547
|
-
await onReady?.();
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
worker.on("message", readyHandler);
|
|
1551
|
-
return async () => {
|
|
1552
|
-
await worker.terminate();
|
|
1553
|
-
};
|
|
1554
|
-
};
|
|
1555
|
-
return {
|
|
1556
|
-
name: "@kosmojs:basePlugin",
|
|
1557
|
-
config(config) {
|
|
1558
|
-
if (!config.build?.outDir) {
|
|
1559
|
-
throw new Error("Incomplete config, missing build.outDir");
|
|
1560
|
-
}
|
|
1561
|
-
return {
|
|
1562
|
-
build: {
|
|
1563
|
-
outDir: join5(config.build.outDir, outDirSuffix),
|
|
1564
|
-
manifest: true
|
|
1565
|
-
}
|
|
1566
|
-
};
|
|
1567
|
-
},
|
|
1568
|
-
async configResolved(_config) {
|
|
1569
|
-
store.config = _config;
|
|
1570
|
-
const appRoot = resolve5(store.config.root, "../..");
|
|
1571
|
-
const sourceFolder = basename2(store.config.root);
|
|
1572
|
-
const outDir = resolve5(appRoot, resolve5(store.config.build.outDir, ".."));
|
|
1573
|
-
const { stabilityThreshold = 1e3 } = typeof store.config.server.watch?.awaitWriteFinish === "object" ? store.config.server.watch.awaitWriteFinish : {};
|
|
1574
|
-
const watcher = {
|
|
1575
|
-
delay: stabilityThreshold,
|
|
1576
|
-
...store.config.server.watch ? { options: store.config.server.watch } : {}
|
|
1577
|
-
};
|
|
1578
|
-
{
|
|
1579
|
-
const { generators = [], refineTypeName = "TRefine" } = {
|
|
1580
|
-
...pluginOptions
|
|
1581
|
-
};
|
|
1582
|
-
const apiGenerator = generators.find((e) => e.slot === "api");
|
|
1583
|
-
const fetchGenerator = generators.find((e) => e.slot === "fetch");
|
|
1584
|
-
const ssrGenerator = generators.find((e) => e.slot === "ssr");
|
|
1585
|
-
store.resolvedOptions = {
|
|
1586
|
-
...pluginOptions,
|
|
1587
|
-
command: store.config.command,
|
|
1588
|
-
watcher,
|
|
1589
|
-
generators: [
|
|
1590
|
-
// 1. stub generator should run first
|
|
1591
|
-
stubGenerator(),
|
|
1592
|
-
// 2. then api generator
|
|
1593
|
-
...apiGenerator ? [apiGenerator] : [],
|
|
1594
|
-
// 3. then fetch generator, only if api generator also enabled
|
|
1595
|
-
...fetchGenerator && apiGenerator ? [fetchGenerator] : [],
|
|
1596
|
-
// 4. user generators in the order they were added
|
|
1597
|
-
...generators.filter((e) => {
|
|
1598
|
-
return e.slot ? !["api", "fetch", "ssr"].includes(e.slot) : true;
|
|
1599
|
-
}),
|
|
1600
|
-
// 5. ssr generator should run last
|
|
1601
|
-
...ssrGenerator ? [ssrGenerator] : []
|
|
1602
|
-
],
|
|
1603
|
-
refineTypeName,
|
|
1604
|
-
baseurl: store.config.base,
|
|
1605
|
-
apiurl,
|
|
1606
|
-
appRoot,
|
|
1607
|
-
sourceFolder,
|
|
1608
|
-
outDir
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
const packageJsonFile = resolve5(appRoot, "package.json");
|
|
1612
|
-
const packageJson = await import(packageJsonFile, {
|
|
1613
|
-
with: { type: "json" }
|
|
1614
|
-
}).then((e) => e.default);
|
|
1615
|
-
const newDependencies = [];
|
|
1616
|
-
for (const generator of store.resolvedOptions.generators) {
|
|
1617
|
-
for (const key of ["dependencies", "devDependencies"]) {
|
|
1618
|
-
for (const [pkg, ver] of Object.entries(generator[key] || {})) {
|
|
1619
|
-
if (!packageJson[key]?.[pkg]) {
|
|
1620
|
-
newDependencies.push([key, pkg, ver]);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
if (newDependencies.length) {
|
|
1626
|
-
console.warn();
|
|
1627
|
-
console.warn(
|
|
1628
|
-
[
|
|
1629
|
-
"\u{1F4A1} ",
|
|
1630
|
-
styleText4(["bold", "italic", "red"], "New dependencies added: "),
|
|
1631
|
-
styleText4("dim", newDependencies.map(([, pkg]) => pkg).join(", "))
|
|
1632
|
-
].join("")
|
|
1633
|
-
);
|
|
1634
|
-
console.warn(
|
|
1635
|
-
"\u{1F4E6}",
|
|
1636
|
-
[
|
|
1637
|
-
styleText4(
|
|
1638
|
-
["bold", "blueBright"],
|
|
1639
|
-
store.config.command === "build" ? "Install them and run a new build: " : "Install them and restart dev server: "
|
|
1640
|
-
),
|
|
1641
|
-
styleText4(
|
|
1642
|
-
"dim",
|
|
1643
|
-
["npm", "pnpm", "yarn"].map((e) => `\`${e} install\``).join(" / ")
|
|
1644
|
-
)
|
|
1645
|
-
].join("")
|
|
1646
|
-
);
|
|
1647
|
-
console.warn();
|
|
1648
|
-
for (const [key, pkg, ver] of newDependencies) {
|
|
1649
|
-
packageJson[key] = { ...packageJson[key], [pkg]: ver };
|
|
1650
|
-
}
|
|
1651
|
-
await writeFile3(
|
|
1652
|
-
packageJsonFile,
|
|
1653
|
-
JSON.stringify(packageJson, null, 2),
|
|
1654
|
-
"utf8"
|
|
1655
|
-
);
|
|
1656
|
-
}
|
|
1657
|
-
if (store.config.command === "build") {
|
|
1658
|
-
const { resolvers } = await routesFactory(store.resolvedOptions);
|
|
1659
|
-
const resolvedEntries = [];
|
|
1660
|
-
{
|
|
1661
|
-
const spinner = spinnerFactory("Resolving Routes");
|
|
1662
|
-
for (const { name, handler } of resolvers.values()) {
|
|
1663
|
-
spinner.append(
|
|
1664
|
-
`[ ${resolvedEntries.length + 1} of ${resolvers.size} ] ${name}`
|
|
1665
|
-
);
|
|
1666
|
-
resolvedEntries.push(await handler());
|
|
1667
|
-
}
|
|
1668
|
-
spinner.succeed();
|
|
1669
|
-
}
|
|
1670
|
-
{
|
|
1671
|
-
const spinner = spinnerFactory("Running Generators");
|
|
1672
|
-
for (const { name, factory } of store.resolvedOptions.generators) {
|
|
1673
|
-
spinner.append(name);
|
|
1674
|
-
const { build } = await factory(store.resolvedOptions);
|
|
1675
|
-
await build(resolvedEntries);
|
|
1676
|
-
}
|
|
1677
|
-
spinner.succeed();
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
},
|
|
1681
|
-
async configureServer(server) {
|
|
1682
|
-
if (store.config.command !== "serve") {
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
if (!store.resolvedOptions.generators.find((e) => e.slot === "api")) {
|
|
1686
|
-
const stopWorker2 = workerHandler(() => stopWorker2());
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
const apiHandler = await api_handler_default(store.resolvedOptions);
|
|
1690
|
-
const apiWatcher = await apiHandler.watcher();
|
|
1691
|
-
const stopWorker = workerHandler(
|
|
1692
|
-
async () => {
|
|
1693
|
-
await apiWatcher.start();
|
|
1694
|
-
},
|
|
1695
|
-
async () => {
|
|
1696
|
-
await apiWatcher.stop();
|
|
1697
|
-
}
|
|
1698
|
-
);
|
|
1699
|
-
server.middlewares.use(apiHandler.devMiddleware);
|
|
1700
|
-
server.httpServer?.on("close", stopWorker);
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
|
-
};
|
|
1704
|
-
|
|
1705
|
-
// src/define-plugin/index.ts
|
|
1706
|
-
import { readFile as readFile3 } from "node:fs/promises";
|
|
1707
|
-
import { parseEnv } from "node:util";
|
|
1708
|
-
var define_plugin_default = (entries) => {
|
|
1709
|
-
return {
|
|
1710
|
-
name: "@kosmojs:definePlugin",
|
|
1711
|
-
async config() {
|
|
1712
|
-
const define = {};
|
|
1713
|
-
for (const { keys, file, defineOn = "process.env", use } of entries) {
|
|
1714
|
-
define[defineOn] = {};
|
|
1715
|
-
const fileExists = file ? await pathExists(file) : false;
|
|
1716
|
-
const env = fileExists ? parseEnv(await readFile3(file, "utf8")) : process.env;
|
|
1717
|
-
for (const [key, val] of Object.entries(env)) {
|
|
1718
|
-
if (keys.includes(key)) {
|
|
1719
|
-
define[`${defineOn}.${key}`] = JSON.stringify(val);
|
|
1720
|
-
}
|
|
1721
|
-
use?.(key, val);
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
return { define };
|
|
1725
|
-
}
|
|
1726
|
-
};
|
|
1727
|
-
};
|
|
1728
|
-
|
|
1729
|
-
// src/typebox.ts
|
|
1730
|
-
var typeboxLiteralText = (text, options) => {
|
|
1731
|
-
return [
|
|
1732
|
-
// Escape backticks for safe use in template literals
|
|
1733
|
-
[/(?<!\\)`/g, "\\`"],
|
|
1734
|
-
// Escape $ for safe use in template literals
|
|
1735
|
-
[/(?<!\\)\$\{/g, "\\${"],
|
|
1736
|
-
/**
|
|
1737
|
-
* TypeBox's built-in `Options` type is not configurable.
|
|
1738
|
-
* To allow a custom type name, exposing `refineTypeName` option,
|
|
1739
|
-
* defaulted to TRefine, then renaming it to `Options`.
|
|
1740
|
-
* */
|
|
1741
|
-
[new RegExp(`\\b${options.refineTypeName}\\s*<`, "g"), "Options<"]
|
|
1742
|
-
].reduce((text2, [a, b]) => text2.replace(a, b), text);
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { default as default2 } from "@kosmojs/fetch-generator";
|
|
3
|
+
import { default as default3 } from "@kosmojs/hono-generator";
|
|
4
|
+
import { default as default4 } from "@kosmojs/koa-generator";
|
|
5
|
+
import { default as default5 } from "@kosmojs/mdx-generator";
|
|
6
|
+
import { default as default6 } from "@kosmojs/openapi-generator";
|
|
7
|
+
import { default as default7 } from "@kosmojs/react-generator";
|
|
8
|
+
import { default as default8 } from "@kosmojs/solid-generator";
|
|
9
|
+
import { default as default9 } from "@kosmojs/ssg-generator";
|
|
10
|
+
import { default as default10 } from "@kosmojs/ssr-generator";
|
|
11
|
+
import { default as default11 } from "@kosmojs/typebox-generator";
|
|
12
|
+
import { default as default12 } from "@kosmojs/vue-generator";
|
|
13
|
+
var defineConfig = (config) => {
|
|
14
|
+
return config;
|
|
1743
15
|
};
|
|
1744
16
|
export {
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
createTsconfigPaths,
|
|
1758
|
-
base_plugin_default as default,
|
|
1759
|
-
defaults,
|
|
1760
|
-
define_plugin_default as definePlugin,
|
|
1761
|
-
isApiRoute,
|
|
1762
|
-
isApiUse,
|
|
1763
|
-
isPageLayout,
|
|
1764
|
-
isPageRoute,
|
|
1765
|
-
isRouteFile,
|
|
1766
|
-
nestedRoutesFactory,
|
|
1767
|
-
normalizeStaticValue,
|
|
1768
|
-
pageLayoutResolverFactory,
|
|
1769
|
-
pageRouteResolverFactory,
|
|
1770
|
-
pathResolver,
|
|
1771
|
-
pathTokensFactory,
|
|
1772
|
-
render,
|
|
1773
|
-
renderFactory,
|
|
1774
|
-
renderHelpers,
|
|
1775
|
-
renderToFile,
|
|
1776
|
-
routesFactory,
|
|
1777
|
-
scanRoutes,
|
|
1778
|
-
sortRoutes,
|
|
1779
|
-
typeboxLiteralText
|
|
17
|
+
defineConfig,
|
|
18
|
+
default2 as fetchGenerator,
|
|
19
|
+
default3 as honoGenerator,
|
|
20
|
+
default4 as koaGenerator,
|
|
21
|
+
default5 as mdxGenerator,
|
|
22
|
+
default6 as openapiGenerator,
|
|
23
|
+
default7 as reactGenerator,
|
|
24
|
+
default8 as solidGenerator,
|
|
25
|
+
default9 as ssgGenerator,
|
|
26
|
+
default10 as ssrGenerator,
|
|
27
|
+
default11 as typeboxGenerator,
|
|
28
|
+
default12 as vueGenerator
|
|
1780
29
|
};
|
|
1781
30
|
//# sourceMappingURL=index.js.map
|