@kosmojs/lib 0.1.8 → 0.2.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/package.json +8 -12
- package/pkg/ast.d.ts +5 -4
- package/pkg/generators.d.ts +1 -2
- package/pkg/generic.d.ts +3 -0
- package/pkg/index.d.ts +1 -1
- package/pkg/index.js +361 -179
- package/pkg/index.js.map +1 -1
- package/pkg/paths.d.ts +1 -1
- package/pkg/routes/base.d.ts +2 -2
- package/pkg/vite.d.ts +58 -1
- package/pkg/typebox.d.ts +0 -2
- /package/pkg/{routes/templates.d.ts → templates.d.ts} +0 -0
package/pkg/index.js
CHANGED
|
@@ -1,18 +1,173 @@
|
|
|
1
1
|
import { builtinModules } from "node:module";
|
|
2
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
3
2
|
import { styleText } from "node:util";
|
|
4
3
|
import crc from "crc/crc32";
|
|
5
|
-
import
|
|
6
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
4
|
+
import flattener from "tfusion";
|
|
5
|
+
import { ModuleResolutionKind, Project, SyntaxKind } from "ts-morph";
|
|
7
6
|
import { RequestValidationTargets, defaults } from "@kosmojs/core";
|
|
8
7
|
import { HTTPMethods } from "@kosmojs/core/api";
|
|
9
|
-
import
|
|
10
|
-
import { access, constants, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { mergeConfig } from "vite";
|
|
9
|
+
import { access, constants, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
10
|
+
import { basename, dirname, isAbsolute, join, parse, resolve } from "node:path";
|
|
11
11
|
import handlebars from "handlebars";
|
|
12
|
+
import picomatch from "picomatch";
|
|
12
13
|
import { glob } from "tinyglobby";
|
|
13
|
-
import { compile, parse } from "path-to-regexp";
|
|
14
|
-
import
|
|
14
|
+
import { compile, parse as parse$1 } from "path-to-regexp";
|
|
15
|
+
import { lookup } from "mrmime";
|
|
15
16
|
import { Spinner } from "@topcli/spinner";
|
|
17
|
+
//#region src/generic.ts
|
|
18
|
+
var mergeConfigs = (...configs) => {
|
|
19
|
+
return [{
|
|
20
|
+
configFile: false,
|
|
21
|
+
define: {
|
|
22
|
+
KOSMO_PRODUCTION_BUILD: "false",
|
|
23
|
+
KOSMO_SERVERSIDE_FETCH: "false"
|
|
24
|
+
}
|
|
25
|
+
}, ...configs].reduce((config, prev) => mergeConfig(config || {}, prev || {}), {});
|
|
26
|
+
};
|
|
27
|
+
var escapeTemplateLiterals = (origin) => {
|
|
28
|
+
return [[/(?<!\\)`/g, "\\`"], [/(?<!\\)\$\{/g, "\\${"]].reduce((text, [a, b]) => text.replace(a, b), origin);
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/paths.ts
|
|
32
|
+
var pathResolver = (sourceFolder) => {
|
|
33
|
+
const createPath = (...a) => {
|
|
34
|
+
return sourceFolder.root ? resolve(sourceFolder.root, join(...a)) : join(...a);
|
|
35
|
+
};
|
|
36
|
+
const createImport = {
|
|
37
|
+
src(a, { origin }) {
|
|
38
|
+
return origin === "src" ? join(defaults.srcPrefix, ...a) : join(defaults.appPrefix, defaults.srcDir, sourceFolder.name, ...a);
|
|
39
|
+
},
|
|
40
|
+
api(a, o) {
|
|
41
|
+
return this.src([defaults.apiDir, ...a], o);
|
|
42
|
+
},
|
|
43
|
+
entry(a, o) {
|
|
44
|
+
return this.src([defaults.entryDir, ...a], o);
|
|
45
|
+
},
|
|
46
|
+
pages(a, o) {
|
|
47
|
+
return this.src([defaults.pagesDir, ...a], o);
|
|
48
|
+
},
|
|
49
|
+
lib(a, { origin }) {
|
|
50
|
+
return origin === "src" ? join(defaults.libPrefix, ...a) : join(defaults.appPrefix, defaults.libDir, sourceFolder.name, ...a);
|
|
51
|
+
},
|
|
52
|
+
libCore(a, o) {
|
|
53
|
+
return this.lib(["core", ...a], o);
|
|
54
|
+
},
|
|
55
|
+
libApi(a, o) {
|
|
56
|
+
return this.lib([defaults.apiDir, ...a], o);
|
|
57
|
+
},
|
|
58
|
+
libEntry(a, o) {
|
|
59
|
+
return this.lib([defaults.entryDir, ...a], o);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
createPath: {
|
|
64
|
+
src(...a) {
|
|
65
|
+
return createPath(defaults.srcDir, sourceFolder.name, ...a);
|
|
66
|
+
},
|
|
67
|
+
api(...a) {
|
|
68
|
+
return this.src(defaults.apiDir, ...a);
|
|
69
|
+
},
|
|
70
|
+
pages(...a) {
|
|
71
|
+
return this.src(defaults.pagesDir, ...a);
|
|
72
|
+
},
|
|
73
|
+
entry(...a) {
|
|
74
|
+
return this.src(defaults.entryDir, ...a);
|
|
75
|
+
},
|
|
76
|
+
lib(...a) {
|
|
77
|
+
return createPath(defaults.libDir, sourceFolder.name, ...a);
|
|
78
|
+
},
|
|
79
|
+
libCore(...a) {
|
|
80
|
+
return this.lib("core", ...a);
|
|
81
|
+
},
|
|
82
|
+
libApi(...a) {
|
|
83
|
+
return this.lib(defaults.apiDir, ...a);
|
|
84
|
+
},
|
|
85
|
+
libEntry(...a) {
|
|
86
|
+
return this.lib(defaults.entryDir, ...a);
|
|
87
|
+
},
|
|
88
|
+
libPages(...a) {
|
|
89
|
+
return this.lib(defaults.pagesDir, ...a);
|
|
90
|
+
},
|
|
91
|
+
distDir(...a) {
|
|
92
|
+
return createPath(sourceFolder.distDir, sourceFolder.name, ...a);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
createImport,
|
|
96
|
+
createImportHelpers(o) {
|
|
97
|
+
if (!["src", "lib"].includes(o?.origin)) throw new Error(`createImportHelpers: required exactly one argument of shape { origin: "src|lib" }`);
|
|
98
|
+
return { createImport(key, ...a) {
|
|
99
|
+
return createImport[key](a.slice(0, -1), o);
|
|
100
|
+
} };
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
var pathExists = async (path) => {
|
|
105
|
+
try {
|
|
106
|
+
await access(path, constants.F_OK);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/render.ts
|
|
114
|
+
var render = (template, context, options) => {
|
|
115
|
+
const { noEscape = true, renderer = handlebars } = { ...options };
|
|
116
|
+
return renderer.compile(template, { noEscape })(context);
|
|
117
|
+
};
|
|
118
|
+
var renderToFile = async (file, template, context, options) => {
|
|
119
|
+
const content = render(template, context, options);
|
|
120
|
+
/**
|
|
121
|
+
* Two fs calls (exists + read) are worth it to avoid touching the file
|
|
122
|
+
* and triggering watchers unnecessarily.
|
|
123
|
+
* */
|
|
124
|
+
if (await pathExists(file)) {
|
|
125
|
+
const { overwrite = true } = { ...options };
|
|
126
|
+
if (overwrite === false) return;
|
|
127
|
+
const fileContent = await readFile(file, "utf8");
|
|
128
|
+
if (typeof overwrite === "function" && !overwrite(fileContent)) return;
|
|
129
|
+
if (crc(content) === crc(fileContent)) return;
|
|
130
|
+
}
|
|
131
|
+
await mkdir(dirname(file), { recursive: true });
|
|
132
|
+
await writeFile(file, content, "utf8");
|
|
133
|
+
};
|
|
134
|
+
var renderFactory = (options) => {
|
|
135
|
+
const createRenderer = (selfOoptions) => {
|
|
136
|
+
const renderer = handlebars.create();
|
|
137
|
+
renderer.registerPartial({
|
|
138
|
+
...options?.partials,
|
|
139
|
+
...selfOoptions?.partials
|
|
140
|
+
});
|
|
141
|
+
renderer.registerHelper({
|
|
142
|
+
...options?.helpers,
|
|
143
|
+
...selfOoptions?.helpers
|
|
144
|
+
});
|
|
145
|
+
return renderer;
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
render(template, context, selfOoptions) {
|
|
149
|
+
return render(template, context, {
|
|
150
|
+
...options,
|
|
151
|
+
...selfOoptions,
|
|
152
|
+
renderer: createRenderer(selfOoptions)
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
async renderToFile(file, template, context, selfOoptions) {
|
|
156
|
+
return renderToFile(options?.outdir ? join(options.outdir, file) : file, template, context, {
|
|
157
|
+
...options,
|
|
158
|
+
...selfOoptions,
|
|
159
|
+
renderer: createRenderer(selfOoptions)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/templates/resolved-types.hbs?raw
|
|
166
|
+
var resolved_types_default = "{{#each resolvedTypes}}\nexport type {{name}} = {{text}};\n{{/each}}\n";
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/templates/types.hbs?raw
|
|
169
|
+
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";
|
|
170
|
+
//#endregion
|
|
16
171
|
//#region src/ast.ts
|
|
17
172
|
var astFactory = () => {
|
|
18
173
|
const createProject = (opts) => new Project(opts);
|
|
@@ -160,7 +315,7 @@ var astFactory = () => {
|
|
|
160
315
|
method,
|
|
161
316
|
target,
|
|
162
317
|
variants: variants.flatMap((e, i) => {
|
|
163
|
-
const { status, contentType, body } = extractResponseVariant(e)
|
|
318
|
+
const { status, contentType, body } = { ...extractResponseVariant(e) };
|
|
164
319
|
if (!status) return [];
|
|
165
320
|
if (contentType && typeof contentType !== "string") {
|
|
166
321
|
console.warn(styleText(["bold", "red"], `✗ The second element of a response variant should specify the Response Content Type`));
|
|
@@ -300,11 +455,42 @@ var astFactory = () => {
|
|
|
300
455
|
const extractGenerics = (callExpression) => {
|
|
301
456
|
return callExpression.getTypeArguments();
|
|
302
457
|
};
|
|
303
|
-
const typeResolverFactory = (
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
458
|
+
const typeResolverFactory = (sourceFolder) => {
|
|
459
|
+
const { createPath } = pathResolver(sourceFolder);
|
|
460
|
+
const project = createProject({ compilerOptions: {
|
|
461
|
+
moduleResolution: ModuleResolutionKind.Bundler,
|
|
462
|
+
types: [],
|
|
463
|
+
noLib: true,
|
|
464
|
+
skipLibCheck: true,
|
|
465
|
+
paths: {
|
|
466
|
+
[`${defaults.appPrefix}/*`]: [`${sourceFolder.root}/*`],
|
|
467
|
+
[`${defaults.srcPrefix}/*`]: [createPath.src("*")],
|
|
468
|
+
[`${defaults.libPrefix}/*`]: [createPath.lib("*")]
|
|
469
|
+
}
|
|
470
|
+
} });
|
|
471
|
+
const withTypeboxSchema = (resolvedTypes) => {
|
|
472
|
+
const materializedTypes = render(resolved_types_default, { resolvedTypes });
|
|
473
|
+
const sourceFile = project.createSourceFile(`${crc(materializedTypes)}-${Date.now()}.ts`, materializedTypes, { overwrite: true });
|
|
474
|
+
const types = resolvedTypes.map((type) => {
|
|
475
|
+
const typeNode = sourceFile.getTypeAlias(type.name)?.getTypeNode();
|
|
476
|
+
if (!typeNode) return type;
|
|
477
|
+
const properties = type.properties ? type.properties.map((prop) => {
|
|
478
|
+
const propNode = typeNode.isKind(SyntaxKind.TypeLiteral) ? typeNode.getProperty(prop.name)?.getTypeNode() : void 0;
|
|
479
|
+
if (!propNode) return prop;
|
|
480
|
+
return {
|
|
481
|
+
...prop,
|
|
482
|
+
typeboxSchema: renderTypeboxSchema(propNode)
|
|
483
|
+
};
|
|
484
|
+
}) : void 0;
|
|
485
|
+
return {
|
|
486
|
+
...type,
|
|
487
|
+
...properties ? { properties } : {},
|
|
488
|
+
typeboxSchema: renderTypeboxSchema(typeNode)
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
project.removeSourceFile(sourceFile);
|
|
492
|
+
return types;
|
|
493
|
+
};
|
|
308
494
|
const literalTypesResolver = (literalTypes, options) => {
|
|
309
495
|
const sourceFile = project.createSourceFile(`${crc(literalTypes)}-${Date.now()}.ts`, literalTypes, { overwrite: true });
|
|
310
496
|
const resolvedTypes = flattener(project, sourceFile, {
|
|
@@ -312,7 +498,7 @@ var astFactory = () => {
|
|
|
312
498
|
stripComments: true
|
|
313
499
|
});
|
|
314
500
|
project.removeSourceFile(sourceFile);
|
|
315
|
-
return resolvedTypes;
|
|
501
|
+
return withTypeboxSchema(resolvedTypes);
|
|
316
502
|
};
|
|
317
503
|
return {
|
|
318
504
|
getSourceFile: (fileFullpath) => {
|
|
@@ -335,150 +521,61 @@ var astFactory = () => {
|
|
|
335
521
|
typeResolverFactory
|
|
336
522
|
};
|
|
337
523
|
};
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return
|
|
362
|
-
},
|
|
363
|
-
pages(a, o) {
|
|
364
|
-
return this.src([defaults.pagesDir, ...a], o);
|
|
365
|
-
},
|
|
366
|
-
lib(a, { origin }) {
|
|
367
|
-
return origin === "src" ? join(defaults.libPrefix, ...a) : join(defaults.appPrefix, defaults.libDir, sourceFolder.name, ...a);
|
|
368
|
-
},
|
|
369
|
-
libCore(a, o) {
|
|
370
|
-
return this.lib(["core", ...a], o);
|
|
371
|
-
},
|
|
372
|
-
libApi(a, o) {
|
|
373
|
-
return this.lib([defaults.apiDir, ...a], o);
|
|
374
|
-
},
|
|
375
|
-
libEntry(a, o) {
|
|
376
|
-
return this.lib([defaults.entryDir, ...a], o);
|
|
524
|
+
/**
|
|
525
|
+
* Render a ts-morph TypeNode to TypeBox Script text, replacing every VRefine
|
|
526
|
+
* occurrence (at any depth) with the infix `with` form.
|
|
527
|
+
*
|
|
528
|
+
* VRefine<string, { format: "email" }> -> (string with { format: "email" })
|
|
529
|
+
* VRefine<string, { format: "email" }>[] -> (string with { format: "email" })[]
|
|
530
|
+
* VRefine<Array<string>, { minItems: 1 }> -> (Array<string> with { minItems: 1 })
|
|
531
|
+
* VRefine<Record<string, string>, { maxProperties: 20 }>
|
|
532
|
+
* -> (Record<string, string> with { maxProperties: 20 })
|
|
533
|
+
*
|
|
534
|
+
* The parentheses matter: for an array element the `with` clause must bind to
|
|
535
|
+
* the element, not the array, so a VRefine that is the element of an array is
|
|
536
|
+
* wrapped. We wrap unconditionally - harmless when not nested, required when it
|
|
537
|
+
* is.
|
|
538
|
+
*
|
|
539
|
+
* @param typeNode the node to render
|
|
540
|
+
* @param refineTypeName the configured refine type name
|
|
541
|
+
* */
|
|
542
|
+
var renderTypeboxSchema = (typeNode, refineTypeName = defaults.refineTypeName) => {
|
|
543
|
+
const traverse = (typeNode) => {
|
|
544
|
+
if (typeNode.isKind(SyntaxKind.TypeReference) && typeNode.getTypeName().getText() === refineTypeName) {
|
|
545
|
+
const [inner, opts] = typeNode.getTypeArguments();
|
|
546
|
+
if (!inner || !opts) return typeNode.getText();
|
|
547
|
+
return `(${traverse(inner)} with ${opts.getText()})`;
|
|
377
548
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
},
|
|
390
|
-
entry(...a) {
|
|
391
|
-
return this.src(defaults.entryDir, ...a);
|
|
392
|
-
},
|
|
393
|
-
lib(...a) {
|
|
394
|
-
return createPath(defaults.libDir, sourceFolder.name, ...a);
|
|
395
|
-
},
|
|
396
|
-
libCore(...a) {
|
|
397
|
-
return this.lib("core", ...a);
|
|
398
|
-
},
|
|
399
|
-
libApi(...a) {
|
|
400
|
-
return this.lib(defaults.apiDir, ...a);
|
|
401
|
-
},
|
|
402
|
-
libEntry(...a) {
|
|
403
|
-
return this.lib(defaults.entryDir, ...a);
|
|
404
|
-
},
|
|
405
|
-
libPages(...a) {
|
|
406
|
-
return this.lib(defaults.pagesDir, ...a);
|
|
407
|
-
},
|
|
408
|
-
distDir(...a) {
|
|
409
|
-
return createPath(sourceFolder.distDir, sourceFolder.name, ...a);
|
|
410
|
-
}
|
|
411
|
-
},
|
|
412
|
-
createImport,
|
|
413
|
-
createImportHelpers(o) {
|
|
414
|
-
if (!["src", "lib"].includes(o?.origin)) throw new Error(`createImportHelpers: required exactly one argument of shape { origin: "src|lib" }`);
|
|
415
|
-
return { createImport(key, ...a) {
|
|
416
|
-
return createImport[key](a.slice(0, -1), o);
|
|
417
|
-
} };
|
|
549
|
+
if (typeNode.isKind(SyntaxKind.ArrayType)) return `${traverse(typeNode.getElementTypeNode())}[]`;
|
|
550
|
+
if (typeNode.isKind(SyntaxKind.TypeLiteral)) {
|
|
551
|
+
const members = [];
|
|
552
|
+
for (const member of typeNode.getMembers()) if (member.isKind(SyntaxKind.PropertySignature)) {
|
|
553
|
+
const name = member.getName();
|
|
554
|
+
const optional = member.hasQuestionToken() ? "?" : "";
|
|
555
|
+
const t = member.getTypeNode();
|
|
556
|
+
const rendered = t ? traverse(t) : "unknown";
|
|
557
|
+
members.push(`${name}${optional}: ${rendered}`);
|
|
558
|
+
} else members.push(member.getText());
|
|
559
|
+
return `{ ${members.join(", ")} }`;
|
|
418
560
|
}
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
//#endregion
|
|
430
|
-
//#region src/render.ts
|
|
431
|
-
var render = (template, context, options) => {
|
|
432
|
-
const { noEscape = true, renderer = handlebars } = { ...options };
|
|
433
|
-
return renderer.compile(template, { noEscape })(context);
|
|
434
|
-
};
|
|
435
|
-
var renderToFile = async (file, template, context, options) => {
|
|
436
|
-
const content = render(template, context, options);
|
|
437
|
-
/**
|
|
438
|
-
* Two fs calls (exists + read) are worth it to avoid touching the file
|
|
439
|
-
* and triggering watchers unnecessarily.
|
|
440
|
-
* */
|
|
441
|
-
if (await pathExists(file)) {
|
|
442
|
-
const { overwrite = true } = { ...options };
|
|
443
|
-
if (overwrite === false) return;
|
|
444
|
-
const fileContent = await readFile(file, "utf8");
|
|
445
|
-
if (typeof overwrite === "function" && !overwrite(fileContent)) return;
|
|
446
|
-
if (crc(content) === crc(fileContent)) return;
|
|
447
|
-
}
|
|
448
|
-
await mkdir(dirname(file), { recursive: true });
|
|
449
|
-
await writeFile(file, content, "utf8");
|
|
450
|
-
};
|
|
451
|
-
var renderFactory = (options) => {
|
|
452
|
-
const createRenderer = (selfOoptions) => {
|
|
453
|
-
const renderer = handlebars.create();
|
|
454
|
-
renderer.registerPartial({
|
|
455
|
-
...options?.partials,
|
|
456
|
-
...selfOoptions?.partials
|
|
457
|
-
});
|
|
458
|
-
renderer.registerHelper({
|
|
459
|
-
...options?.helpers,
|
|
460
|
-
...selfOoptions?.helpers
|
|
461
|
-
});
|
|
462
|
-
return renderer;
|
|
463
|
-
};
|
|
464
|
-
return {
|
|
465
|
-
render(template, context, selfOoptions) {
|
|
466
|
-
return render(template, context, {
|
|
467
|
-
...options,
|
|
468
|
-
...selfOoptions,
|
|
469
|
-
renderer: createRenderer(selfOoptions)
|
|
470
|
-
});
|
|
471
|
-
},
|
|
472
|
-
async renderToFile(file, template, context, selfOoptions) {
|
|
473
|
-
return renderToFile(options?.outdir ? join(options.outdir, file) : file, template, context, {
|
|
474
|
-
...options,
|
|
475
|
-
...selfOoptions,
|
|
476
|
-
renderer: createRenderer(selfOoptions)
|
|
477
|
-
});
|
|
561
|
+
if (typeNode.isKind(SyntaxKind.TupleType)) return `[${typeNode.getElements().map((el) => traverse(el)).join(", ")}]`;
|
|
562
|
+
if (typeNode.isKind(SyntaxKind.ParenthesizedType)) return `(${traverse(typeNode.getTypeNode())})`;
|
|
563
|
+
if (typeNode.isKind(SyntaxKind.UnionType)) return typeNode.getTypeNodes().map((t) => traverse(t)).join(" | ");
|
|
564
|
+
if (typeNode.isKind(SyntaxKind.IntersectionType)) return typeNode.getTypeNodes().map((t) => traverse(t)).join(" & ");
|
|
565
|
+
if (typeNode.isKind(SyntaxKind.TypeReference)) {
|
|
566
|
+
const args = typeNode.getTypeArguments();
|
|
567
|
+
if (args.length === 0) return typeNode.getText();
|
|
568
|
+
return `${typeNode.getTypeName().getText()}<${args.map((a) => traverse(a)).join(", ")}>`;
|
|
478
569
|
}
|
|
570
|
+
return typeNode.getText();
|
|
479
571
|
};
|
|
572
|
+
return escapeTemplateLiterals(traverse(typeNode));
|
|
480
573
|
};
|
|
481
574
|
//#endregion
|
|
575
|
+
//#region src/generators.ts
|
|
576
|
+
var defineGenerator = (f) => f;
|
|
577
|
+
var defineGeneratorFactory = (f) => f;
|
|
578
|
+
//#endregion
|
|
482
579
|
//#region src/routes/paths.ts
|
|
483
580
|
/**
|
|
484
581
|
* Parse a filesystem route path into structured PathToken array.
|
|
@@ -558,7 +655,7 @@ var pathTokensFactory = (path) => {
|
|
|
558
655
|
if (bareParam === ":") throw new Error(`${path} contains colons outside braces, use : only within {}`);
|
|
559
656
|
else if (bareParam) throw new Error(`${path} contains bare params, use [${bareParam}] instead of :${bareParam}`);
|
|
560
657
|
const pattern = patternTransforms.reduce((src, fn) => fn(src), orig);
|
|
561
|
-
const { tokens } = parse(pattern.replace(/\{[^}]*\}|(\+)/g, (m, p1) => p1 ? "\\+" : m));
|
|
658
|
+
const { tokens } = parse$1(pattern.replace(/\{[^}]*\}|(\+)/g, (m, p1) => p1 ? "\\+" : m));
|
|
562
659
|
const parts = extractParts(tokens, (val) => {
|
|
563
660
|
return /\W/.test(val) || /^\d/.test(val) ? [val.replace(/^\d+|\W/g, "_"), crc(orig)].join("_") : val;
|
|
564
661
|
});
|
|
@@ -604,7 +701,7 @@ var createHonoPattern = (tokens) => {
|
|
|
604
701
|
return tokens.flatMap((token, i) => {
|
|
605
702
|
if (token.kind === "static") return [token.parts[0].value];
|
|
606
703
|
if (token.kind === "param") return [paramValue(token.parts[0])];
|
|
607
|
-
const { tokens } = parse(token.pattern.replace(/\//g, ""));
|
|
704
|
+
const { tokens } = parse$1(token.pattern.replace(/\//g, ""));
|
|
608
705
|
return [`:_${i}{${tokensToRegex(tokens)}}`];
|
|
609
706
|
}).join("/");
|
|
610
707
|
};
|
|
@@ -615,9 +712,9 @@ var API_INDEX_PATTERN = `${API_INDEX_BASENAME}.ts`;
|
|
|
615
712
|
var API_USE_BASENAME = "use";
|
|
616
713
|
var API_USE_PATTERN = `use.ts`;
|
|
617
714
|
var PAGE_INDEX_BASENAME = "index";
|
|
618
|
-
var PAGE_INDEX_PATTERN = `${PAGE_INDEX_BASENAME}.{tsx,vue,mdx,md}`;
|
|
715
|
+
var PAGE_INDEX_PATTERN = `${PAGE_INDEX_BASENAME}.{tsx,vue,svelte,mdx,md}`;
|
|
619
716
|
var PAGE_LAYOUT_BASENAME = "layout";
|
|
620
|
-
var PAGE_LAYOUT_PATTERN = `${PAGE_LAYOUT_BASENAME}.{tsx,vue,mdx}`;
|
|
717
|
+
var PAGE_LAYOUT_PATTERN = `${PAGE_LAYOUT_BASENAME}.{tsx,vue,svelte,mdx}`;
|
|
621
718
|
var ROUTE_FILE_PATTERNS = [
|
|
622
719
|
`${defaults.apiDir}/**/${API_INDEX_PATTERN}`,
|
|
623
720
|
`${defaults.apiDir}/**/${API_USE_PATTERN}`,
|
|
@@ -741,12 +838,6 @@ var sortRoutes = (a, b) => {
|
|
|
741
838
|
return a.pathPattern.localeCompare(b.pathPattern);
|
|
742
839
|
};
|
|
743
840
|
//#endregion
|
|
744
|
-
//#region src/templates/resolved-types.hbs?raw
|
|
745
|
-
var resolved_types_default = "{{#each resolvedTypes}}\nexport type {{name}} = {{text}};\n{{/each}}\n";
|
|
746
|
-
//#endregion
|
|
747
|
-
//#region src/templates/types.hbs?raw
|
|
748
|
-
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";
|
|
749
|
-
//#endregion
|
|
750
841
|
//#region src/routes/resolve.ts
|
|
751
842
|
var resolverFactory = (sourceFolder, cacheFactory) => {
|
|
752
843
|
const { generators = [], refineTypeName = defaults.refineTypeName } = sourceFolder.config;
|
|
@@ -831,7 +922,7 @@ var resolverFactory = (sourceFolder, cacheFactory) => {
|
|
|
831
922
|
withReferencedFiles: true,
|
|
832
923
|
sourceFile: getSourceFile(fileFullpath),
|
|
833
924
|
relpathResolver(path) {
|
|
834
|
-
return join(
|
|
925
|
+
return join(defaults.srcPrefix, defaults.apiDir, dirname(file), path);
|
|
835
926
|
}
|
|
836
927
|
});
|
|
837
928
|
const validationTypes = validationDefinitions.flatMap((def) => {
|
|
@@ -917,8 +1008,8 @@ var resolverFactory = (sourceFolder, cacheFactory) => {
|
|
|
917
1008
|
variants: def.variants.flatMap((variant, i) => {
|
|
918
1009
|
if (typeof variant.contentType !== "string") return [variant];
|
|
919
1010
|
if (variant.contentType.includes("/")) return [variant];
|
|
920
|
-
const contentType =
|
|
921
|
-
if (contentType
|
|
1011
|
+
const contentType = lookup(variant.contentType);
|
|
1012
|
+
if (!contentType) {
|
|
922
1013
|
console.warn(styleText(["bold", "red"], "✗ Failed resolving Response Content Type"));
|
|
923
1014
|
console.warn(` Invalid value provided for mime-types lookup - ${variant.contentType}`);
|
|
924
1015
|
console.warn(styleText(["cyan"], ` Response variant #${i} excluded from route schemas`));
|
|
@@ -933,8 +1024,8 @@ var resolverFactory = (sourceFolder, cacheFactory) => {
|
|
|
933
1024
|
})
|
|
934
1025
|
};
|
|
935
1026
|
else if (def.contentType && !def.contentType.includes("/")) {
|
|
936
|
-
const contentType =
|
|
937
|
-
if (contentType
|
|
1027
|
+
const contentType = lookup(def.contentType);
|
|
1028
|
+
if (!contentType) {
|
|
938
1029
|
console.warn(styleText(["bold", "red"], "✗ Failed resolving Response Content Type"));
|
|
939
1030
|
console.warn(` Invalid value provided for mime-types lookup - ${def.contentType}`);
|
|
940
1031
|
console.warn(` Route: ${name}; Method: ${def.method}`);
|
|
@@ -1091,17 +1182,102 @@ var spinnerFactory = (startText) => {
|
|
|
1091
1182
|
};
|
|
1092
1183
|
var withSpinner = (text, pipe, spinner) => pipe(spinner || spinnerFactory(text));
|
|
1093
1184
|
//#endregion
|
|
1094
|
-
//#region src/typebox.ts
|
|
1095
|
-
var typeboxLiteralText = (text, sourceFolder) => {
|
|
1096
|
-
const { refineTypeName = defaults.refineTypeName } = sourceFolder.config;
|
|
1097
|
-
return [
|
|
1098
|
-
[/(?<!\\)`/g, "\\`"],
|
|
1099
|
-
[/(?<!\\)\$\{/g, "\\${"],
|
|
1100
|
-
[new RegExp(`\\b${refineTypeName}\\s*<`, "g"), "Options<"]
|
|
1101
|
-
].reduce((text, [a, b]) => text.replace(a, b), text);
|
|
1102
|
-
};
|
|
1103
|
-
//#endregion
|
|
1104
1185
|
//#region src/vite.ts
|
|
1186
|
+
/**
|
|
1187
|
+
* Resolves Kosmo's generated import prefixes to absolute file paths.
|
|
1188
|
+
*
|
|
1189
|
+
* WHY THIS EXISTS
|
|
1190
|
+
* ---------------
|
|
1191
|
+
* Every source folder is scaffolded with three tsconfig `paths` prefixes
|
|
1192
|
+
* that let generated and user code import across
|
|
1193
|
+
* the folder's layout without long relative chains:
|
|
1194
|
+
*
|
|
1195
|
+
* appPrefix "@" -> the project root (<root>/*)
|
|
1196
|
+
* srcPrefix "~" -> the folder's src dir (<root>/src/<name>/*)
|
|
1197
|
+
* libPrefix "_" -> the folder's lib dir (<root>/lib/<name>/*)
|
|
1198
|
+
*
|
|
1199
|
+
* These are the backbone of the generated code: `defineRoute` and friends are
|
|
1200
|
+
* emitted with `_/...` imports, pages import layouts and components via `~/...`,
|
|
1201
|
+
* and `@/...` reaches shared app-level modules. If a bundler cannot resolve
|
|
1202
|
+
* them, nothing the framework generates can be loaded.
|
|
1203
|
+
*
|
|
1204
|
+
* Historically Vite resolved these through its built-in `resolve.tsconfigPaths: true`,
|
|
1205
|
+
* reading the `paths` straight from the generated tsconfig. That option
|
|
1206
|
+
* regressed in Vite 8.1.0 (still broken as of 8.1.3): it stops resolving
|
|
1207
|
+
* `paths` mappings, especially those defined in an extended / referenced base
|
|
1208
|
+
* config - which is exactly Kosmo's structure, where a source folder's
|
|
1209
|
+
* tsconfig `extends` `lib/<name>/tsconfig.base.json` and the paths use the
|
|
1210
|
+
* TypeScript `${configDir}` variable. The failure surfaces as:
|
|
1211
|
+
*
|
|
1212
|
+
* [vite] Failed to resolve import "_/pageSamples/welcome.vue" from
|
|
1213
|
+
* "src/<name>/pages/index/index.vue". Does the file exist?
|
|
1214
|
+
*
|
|
1215
|
+
* (Upstream: vitejs/vite #22139, #22047, #22371, #21889, #21856.)
|
|
1216
|
+
*
|
|
1217
|
+
* Rather than pin Vite to 8.0.x indefinitely, or depend on the general-purpose
|
|
1218
|
+
* `vite-tsconfig-paths` plugin, this internal plugin resolves ONLY
|
|
1219
|
+
* known prefixes against dirs derived from the source folder. That makes it:
|
|
1220
|
+
*
|
|
1221
|
+
* - Correct on 8.1.x, where native `tsconfigPaths` is broken.
|
|
1222
|
+
* - Faster: no tsconfig discovery, no `extends` chain walking, no glob
|
|
1223
|
+
* matching - just a prefix check and a handful of `stat` probes,
|
|
1224
|
+
* cached by Vite per specifier.
|
|
1225
|
+
* - Non-blocking: probes via async `fs/promises` `stat`, so `resolveId` never
|
|
1226
|
+
* stalls the event loop.
|
|
1227
|
+
* - `.vue` / `.mdx`-aware: `resolveId` sees every specifier regardless of the
|
|
1228
|
+
* importer's type, so imports from non-JS files resolve without the
|
|
1229
|
+
* `loose` / `allowJs` escape hatches `vite-tsconfig-paths` needs.
|
|
1230
|
+
*
|
|
1231
|
+
* A project with custom `paths` beyond these three prefixes should install
|
|
1232
|
+
* `vite-tsconfig-paths`; this plugin is internal and intentionally narrow.
|
|
1233
|
+
*
|
|
1234
|
+
* The prefix -> dir bases come from `pathResolver`, the same source of truth the
|
|
1235
|
+
* tsconfig generator uses, so alias resolution here can never drift from the
|
|
1236
|
+
* paths that were written into the generated tsconfig.
|
|
1237
|
+
* */
|
|
1238
|
+
var tsconfigPaths = (sourceFolder) => {
|
|
1239
|
+
/**
|
|
1240
|
+
* Candidate extensions for an extensionless specifier, in resolution priority order.
|
|
1241
|
+
* Mirrors the renderable file types the generators emit;
|
|
1242
|
+
* a new page type (e.g. `.svelte`) must be added here or its aliased imports won't resolve.
|
|
1243
|
+
* */
|
|
1244
|
+
const EXTENSIONS = [
|
|
1245
|
+
".ts",
|
|
1246
|
+
".tsx",
|
|
1247
|
+
".js",
|
|
1248
|
+
".jsx",
|
|
1249
|
+
".mjs",
|
|
1250
|
+
".vue",
|
|
1251
|
+
".svelte",
|
|
1252
|
+
".md",
|
|
1253
|
+
".mdx",
|
|
1254
|
+
".json"
|
|
1255
|
+
];
|
|
1256
|
+
const { createPath } = pathResolver(sourceFolder);
|
|
1257
|
+
const table = [
|
|
1258
|
+
[`${defaults.srcPrefix}/`, createPath.src()],
|
|
1259
|
+
[`${defaults.libPrefix}/`, createPath.lib()],
|
|
1260
|
+
[`${defaults.appPrefix}/`, sourceFolder.root]
|
|
1261
|
+
];
|
|
1262
|
+
const resolveWithin = async (baseDir, rest) => {
|
|
1263
|
+
const target = join(baseDir, rest);
|
|
1264
|
+
const { ext } = parse(rest);
|
|
1265
|
+
if (EXTENSIONS.includes(ext)) {
|
|
1266
|
+
for (const candidate of [target, `${target}/index${ext}`]) if (await isFile(candidate)) return candidate;
|
|
1267
|
+
return target;
|
|
1268
|
+
}
|
|
1269
|
+
for (const e of EXTENSIONS) for (const candidate of [`${target}${e}`, `${target}/index${e}`]) if (await isFile(candidate)) return candidate;
|
|
1270
|
+
return target;
|
|
1271
|
+
};
|
|
1272
|
+
return {
|
|
1273
|
+
name: "kosmo:tsconfigPaths",
|
|
1274
|
+
enforce: "pre",
|
|
1275
|
+
async resolveId(source) {
|
|
1276
|
+
if (isAbsolute(source) || source.startsWith(".")) return;
|
|
1277
|
+
for (const [prefix, baseDir] of table) if (source.startsWith(prefix)) return resolveWithin(baseDir, source.replace(prefix, ""));
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
};
|
|
1105
1281
|
var nodePrefix = () => {
|
|
1106
1282
|
return {
|
|
1107
1283
|
name: "kosmojs:node-prefix",
|
|
@@ -1114,8 +1290,14 @@ var nodePrefix = () => {
|
|
|
1114
1290
|
}
|
|
1115
1291
|
};
|
|
1116
1292
|
};
|
|
1117
|
-
var vitePlugins = {
|
|
1293
|
+
var vitePlugins = {
|
|
1294
|
+
tsconfigPaths,
|
|
1295
|
+
nodePrefix
|
|
1296
|
+
};
|
|
1297
|
+
var isFile = async (path) => {
|
|
1298
|
+
return stat(path).then((e) => e.isFile()).catch(() => false);
|
|
1299
|
+
};
|
|
1118
1300
|
//#endregion
|
|
1119
|
-
export { API_INDEX_BASENAME, API_INDEX_PATTERN, API_USE_BASENAME, API_USE_PATTERN, PAGE_INDEX_BASENAME, PAGE_INDEX_PATTERN, PAGE_LAYOUT_BASENAME, PAGE_LAYOUT_PATTERN, astFactory, createHonoPattern, createPathPattern, createRouteEntry,
|
|
1301
|
+
export { API_INDEX_BASENAME, API_INDEX_PATTERN, API_USE_BASENAME, API_USE_PATTERN, PAGE_INDEX_BASENAME, PAGE_INDEX_PATTERN, PAGE_LAYOUT_BASENAME, PAGE_LAYOUT_PATTERN, astFactory, createHonoPattern, createPathPattern, createRouteEntry, defineGenerator, defineGeneratorFactory, escapeTemplateLiterals, isApiRoute, isApiUse, isPageLayout, isPageRoute, isRouteFile, mergeConfigs, nestedRoutesFactory, pathExists, pathResolver, pathTokensFactory, render, renderFactory, renderToFile, routesFactory, scanRoutes, sortRoutes, spinnerFactory, tsconfigPaths, vitePlugins, withSpinner };
|
|
1120
1302
|
|
|
1121
1303
|
//# sourceMappingURL=index.js.map
|