@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/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 { flattener } from "tfusion";
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 picomatch from "picomatch";
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 mimeTypes from "mime-types";
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 = ({ root, name }) => {
304
- const project = createProject({
305
- tsConfigFilePath: resolve(root, join(defaults.srcDir, name, "tsconfig.json")),
306
- skipAddingFilesFromTsConfig: true
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
- //#endregion
339
- //#region src/generators.ts
340
- var defineGenerator = (f) => f;
341
- var defineGeneratorFactory = (f) => f;
342
- var createTemplateResolver = (customTemplates, defaultTemplate) => {
343
- const templates = Object.entries({ ...customTemplates }).map(([pattern, template]) => [picomatch(pattern), template]);
344
- return (pattern, entry) => {
345
- const match = templates.find(([isMatch]) => isMatch(pattern));
346
- if (!match) return defaultTemplate;
347
- return typeof match[1] === "function" ? match[1](entry) : match[1];
348
- };
349
- };
350
- //#endregion
351
- //#region src/paths.ts
352
- var pathResolver = (sourceFolder) => {
353
- const createPath = (...a) => {
354
- return sourceFolder.root ? resolve(sourceFolder.root, join(...a)) : join(...a);
355
- };
356
- const createImport = {
357
- src(a, { origin }) {
358
- return origin === "src" ? join(defaults.srcPrefix, ...a) : join(defaults.appPrefix, defaults.srcDir, sourceFolder.name, ...a);
359
- },
360
- api(a, o) {
361
- return this.src([defaults.apiDir, ...a], o);
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
- return {
380
- createPath: {
381
- src(...a) {
382
- return createPath(defaults.srcDir, sourceFolder.name, ...a);
383
- },
384
- api(...a) {
385
- return this.src(defaults.apiDir, ...a);
386
- },
387
- pages(...a) {
388
- return this.src(defaults.pagesDir, ...a);
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
- var pathExists = async (path) => {
422
- try {
423
- await access(path, constants.F_OK);
424
- return true;
425
- } catch {
426
- return false;
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(sourceFolder.name, defaults.apiDir, dirname(file), path);
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 = mimeTypes.lookup(variant.contentType);
921
- if (contentType === false) {
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 = mimeTypes.lookup(def.contentType);
937
- if (contentType === false) {
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 = { nodePrefix };
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, createTemplateResolver, defineGenerator, defineGeneratorFactory, isApiRoute, isApiUse, isPageLayout, isPageRoute, isRouteFile, nestedRoutesFactory, pathExists, pathResolver, pathTokensFactory, render, renderFactory, renderToFile, routesFactory, scanRoutes, sortRoutes, spinnerFactory, typeboxLiteralText, vitePlugins, withSpinner };
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