@kosmojs/lib 0.0.26

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 ADDED
@@ -0,0 +1,1346 @@
1
+ // src/ast.ts
2
+ import { resolve } from "node:path";
3
+ import { styleText } from "node:util";
4
+ import crc from "crc/crc32";
5
+ import { flattener } from "tfusion";
6
+ import {
7
+ Project,
8
+ SyntaxKind
9
+ } from "ts-morph";
10
+ import {
11
+ HTTPMethods,
12
+ RequestValidationTargets
13
+ } from "@kosmojs/api";
14
+ var astFactory = () => {
15
+ const createProject = (opts) => new Project(opts);
16
+ const resolveRouteSignature = async (route, opts) => {
17
+ const {
18
+ sourceFile = createProject().addSourceFileAtPath(route.fileFullpath)
19
+ } = { ...opts };
20
+ const [typeDeclarations, referencedFiles] = extractTypeDeclarations(
21
+ sourceFile,
22
+ opts
23
+ );
24
+ const defaultExport = extractDefaultExport(sourceFile);
25
+ const paramsRefinements = defaultExport ? extractParamsRefinements(defaultExport) : void 0;
26
+ const methods = defaultExport ? extractRouteMethods(route, defaultExport) : [];
27
+ return {
28
+ typeDeclarations,
29
+ paramsRefinements,
30
+ methods: methods.map((e) => e.method),
31
+ validationDefinitions: methods.flatMap((e) => e.validationDefinitions),
32
+ referencedFiles
33
+ };
34
+ };
35
+ const extractDefaultExport = (sourceFile) => {
36
+ const [defaultExport] = sourceFile.getExportAssignments().flatMap((exportAssignment) => {
37
+ if (exportAssignment.isExportEquals()) {
38
+ return [];
39
+ }
40
+ const callExpression = exportAssignment.getExpression();
41
+ return callExpression.isKind(SyntaxKind.CallExpression) ? [callExpression] : [];
42
+ });
43
+ return defaultExport;
44
+ };
45
+ const extractParamsRefinements = (callExpression) => {
46
+ const [
47
+ _routeName,
48
+ // first generic - the route name
49
+ paramsGeneric
50
+ // second generic - params refinements
51
+ ] = extractGenerics(callExpression);
52
+ if (!paramsGeneric?.isKind(SyntaxKind.TupleType)) {
53
+ return;
54
+ }
55
+ return paramsGeneric.getElements().map((node, index) => {
56
+ return {
57
+ index,
58
+ text: node.getText()
59
+ };
60
+ });
61
+ };
62
+ const extractRouteMethods = (route, callExpression) => {
63
+ const funcDeclaration = callExpression.getFirstChildByKind(SyntaxKind.ArrowFunction) || callExpression.getFirstChildByKind(SyntaxKind.FunctionExpression);
64
+ if (!funcDeclaration) {
65
+ return [];
66
+ }
67
+ const arrayLiteralExpression = funcDeclaration.getFirstChildByKind(
68
+ SyntaxKind.ArrayLiteralExpression
69
+ );
70
+ if (!arrayLiteralExpression) {
71
+ return [];
72
+ }
73
+ const callExpressions = [];
74
+ for (const e of arrayLiteralExpression.getChildrenOfKind(
75
+ SyntaxKind.CallExpression
76
+ )) {
77
+ const name = e.getExpression().getText();
78
+ if (HTTPMethods[name]) {
79
+ callExpressions.push([e, name]);
80
+ }
81
+ }
82
+ const methods = [];
83
+ for (const [callExpression2, method] of callExpressions) {
84
+ const [vDefs, vOpts] = extractGenerics(callExpression2);
85
+ methods.push({
86
+ method,
87
+ validationDefinitions: extractValidationDefinitions(
88
+ route,
89
+ method,
90
+ vDefs,
91
+ vOpts
92
+ )
93
+ });
94
+ }
95
+ return methods;
96
+ };
97
+ const parseRuntimeValidation = (typeNode) => {
98
+ if (typeNode.isKind(SyntaxKind.LiteralType)) {
99
+ const literal = typeNode.getFirstChild();
100
+ if (literal?.isKind(SyntaxKind.TrueKeyword)) {
101
+ return true;
102
+ } else if (literal?.isKind(SyntaxKind.FalseKeyword)) {
103
+ return false;
104
+ }
105
+ }
106
+ return void 0;
107
+ };
108
+ const extractResponseVariant = (typeNode) => {
109
+ if (!typeNode.isKind(SyntaxKind.TupleType)) {
110
+ return;
111
+ }
112
+ let status = 200;
113
+ let contentType;
114
+ let body;
115
+ const [statusNode, contentTypeNode, bodyNode] = typeNode.getElements();
116
+ if (statusNode?.isKind(SyntaxKind.LiteralType)) {
117
+ const literal = statusNode.getFirstChildByKind(SyntaxKind.NumericLiteral);
118
+ if (literal) {
119
+ status = Number(literal.getText());
120
+ }
121
+ }
122
+ if (contentTypeNode) {
123
+ contentType = extractStringLiteral(contentTypeNode);
124
+ }
125
+ if (bodyNode) {
126
+ body = bodyNode.getText();
127
+ if (["object"].includes(body)) {
128
+ body = "{}";
129
+ }
130
+ }
131
+ return { status, contentType, body };
132
+ };
133
+ const parseValidationOptions = (typeNode) => {
134
+ const opts = {};
135
+ if (!typeNode?.isKind(SyntaxKind.TypeLiteral)) {
136
+ return opts;
137
+ }
138
+ for (const prop of typeNode.getMembers()) {
139
+ if (!prop.isKind(SyntaxKind.PropertySignature)) {
140
+ continue;
141
+ }
142
+ const target = prop.getName();
143
+ const typeNode2 = prop.getTypeNodeOrThrow();
144
+ if (!typeNode2.isKind(SyntaxKind.TypeLiteral)) {
145
+ continue;
146
+ }
147
+ let contentType;
148
+ let runtimeValidation;
149
+ const customErrors = {};
150
+ for (const member of typeNode2.getMembers()) {
151
+ if (!member.isKind(SyntaxKind.PropertySignature)) {
152
+ continue;
153
+ }
154
+ const nameNode = member.getNameNode();
155
+ const valueNode = member.getTypeNodeOrThrow();
156
+ const name = nameNode.isKind(SyntaxKind.StringLiteral) ? nameNode.getLiteralText() : nameNode.getText();
157
+ if (name === "contentType") {
158
+ contentType = extractStringLiteral(valueNode);
159
+ } else if (name === "runtimeValidation") {
160
+ runtimeValidation = parseRuntimeValidation(valueNode);
161
+ } else if (name.startsWith("error")) {
162
+ const literal = extractStringLiteral(valueNode);
163
+ if (literal) {
164
+ customErrors[name] = literal;
165
+ }
166
+ }
167
+ }
168
+ opts[target] = {
169
+ contentType,
170
+ runtimeValidation,
171
+ customErrors
172
+ };
173
+ }
174
+ return opts;
175
+ };
176
+ const extractStringLiteral = (typeNode) => {
177
+ const literal = typeNode.isKind(SyntaxKind.LiteralType) ? typeNode.getFirstChildByKind(SyntaxKind.StringLiteral) : void 0;
178
+ return literal ? literal.getLiteralText() : void 0;
179
+ };
180
+ const extractValidationDefinitions = (route, method, defsNode, optsNode) => {
181
+ const definitions = [];
182
+ if (!defsNode?.isKind(SyntaxKind.TypeLiteral)) {
183
+ return definitions;
184
+ }
185
+ const optsMap = parseValidationOptions(optsNode);
186
+ const createId = (target, hash) => {
187
+ return [
188
+ target.replace(/^./, (c) => c.toUpperCase()),
189
+ "T",
190
+ method,
191
+ crc(route.id + hash)
192
+ ].join("");
193
+ };
194
+ for (const prop of defsNode.getMembers()) {
195
+ if (!prop.isKind(SyntaxKind.PropertySignature)) {
196
+ continue;
197
+ }
198
+ const target = prop.getName();
199
+ const typeNode = prop.getTypeNodeOrThrow();
200
+ if (target === "response") {
201
+ const variants = typeNode.isKind(SyntaxKind.UnionType) ? typeNode.getChildrenOfKind(SyntaxKind.TupleType) : [typeNode];
202
+ definitions.push({
203
+ ...optsMap[target],
204
+ method,
205
+ target,
206
+ variants: variants.flatMap((e, i) => {
207
+ const { status, contentType, body } = extractResponseVariant(e) || {};
208
+ if (!status) {
209
+ return [];
210
+ }
211
+ if (contentType && typeof contentType !== "string") {
212
+ console.warn(
213
+ styleText(
214
+ ["bold", "red"],
215
+ `\u2717 The second element of a response variant should specify the Response Content Type`
216
+ )
217
+ );
218
+ console.warn(
219
+ styleText(["blue"], ` Example: [200, "json", Schema]`)
220
+ );
221
+ console.warn(
222
+ ` Route: ${route.name}; Method: ${method}; Response Variant: #${i}`
223
+ );
224
+ console.warn();
225
+ }
226
+ return [
227
+ {
228
+ id: createId(
229
+ target,
230
+ JSON.stringify([status, contentType, body])
231
+ ),
232
+ status,
233
+ contentType,
234
+ body
235
+ }
236
+ ];
237
+ })
238
+ });
239
+ } else if (Object.keys(RequestValidationTargets).includes(target)) {
240
+ definitions.push({
241
+ ...optsMap[target],
242
+ method,
243
+ target,
244
+ schema: {
245
+ id: createId(target),
246
+ text: typeNode.getText()
247
+ }
248
+ });
249
+ }
250
+ }
251
+ return definitions;
252
+ };
253
+ const extractTypeDeclarations = (sourceFile, opts) => {
254
+ const declarations = [];
255
+ const referencedFiles = opts?.withReferencedFiles ? [] : void 0;
256
+ for (const declaration of sourceFile.getImportDeclarations()) {
257
+ const modulePath = declaration.getModuleSpecifierValue();
258
+ const path = /^\.\.?\/?/.test(modulePath) ? opts?.relpathResolver ? opts.relpathResolver(modulePath) : modulePath : modulePath;
259
+ const typeOnlyDeclaration = declaration.isTypeOnly();
260
+ const defaultImport = typeOnlyDeclaration ? declaration.getDefaultImport() : void 0;
261
+ if (defaultImport) {
262
+ const name = defaultImport.getText();
263
+ const text = `import type ${name} from "${path}";`;
264
+ declarations.push({
265
+ importDeclaration: {
266
+ name,
267
+ path
268
+ },
269
+ text
270
+ });
271
+ if (referencedFiles) {
272
+ referencedFiles.push(...getReferencedFiles(defaultImport));
273
+ }
274
+ }
275
+ for (const namedImport of declaration.getNamedImports()) {
276
+ if (namedImport.isTypeOnly() || typeOnlyDeclaration) {
277
+ const nameNode = namedImport.getNameNode();
278
+ const name = nameNode.getText();
279
+ const alias = namedImport.getAliasNode()?.getText();
280
+ const nameText = alias ? `${name} as ${alias}` : name;
281
+ declarations.push({
282
+ importDeclaration: {
283
+ name,
284
+ alias,
285
+ path
286
+ },
287
+ text: `import type { ${nameText} } from "${path}";`
288
+ });
289
+ if (referencedFiles) {
290
+ if (nameNode.isKind(SyntaxKind.Identifier)) {
291
+ referencedFiles.push(...getReferencedFiles(nameNode));
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+ for (const declaration of sourceFile.getTypeAliases()) {
298
+ const name = declaration.getName();
299
+ const text = declaration.getFullText().trim();
300
+ declarations.push({
301
+ typeAliasDeclaration: { name },
302
+ text
303
+ });
304
+ }
305
+ for (const declaration of sourceFile.getInterfaces()) {
306
+ const name = declaration.getName();
307
+ const text = declaration.getFullText().trim();
308
+ declarations.push({
309
+ interfaceDeclaration: { name },
310
+ text
311
+ });
312
+ }
313
+ for (const declaration of sourceFile.getEnums()) {
314
+ const name = declaration.getName();
315
+ const text = declaration.getFullText().trim();
316
+ declarations.push({
317
+ enumDeclaration: { name },
318
+ text
319
+ });
320
+ }
321
+ for (const declaration of sourceFile.getExportDeclarations()) {
322
+ const typeOnlyDeclaration = declaration.isTypeOnly();
323
+ const modulePath = declaration.getModuleSpecifierValue();
324
+ const path = modulePath ? /^\.\.?\/?/.test(modulePath) ? opts?.relpathResolver ? opts.relpathResolver(modulePath) : modulePath : modulePath : void 0;
325
+ for (const namedExport of declaration.getNamedExports()) {
326
+ if (namedExport.isTypeOnly() || typeOnlyDeclaration) {
327
+ const nameNode = namedExport.getNameNode();
328
+ const name = nameNode.getText();
329
+ const alias = namedExport.getAliasNode()?.getText();
330
+ const nameText = alias ? `${name} as ${alias}` : name;
331
+ declarations.push({
332
+ exportDeclaration: {
333
+ name,
334
+ alias: alias ?? name,
335
+ path
336
+ },
337
+ text: path ? `export type { ${nameText} } from "${path}";` : `export type { ${nameText} };`
338
+ });
339
+ if (referencedFiles) {
340
+ if (nameNode.isKind(SyntaxKind.Identifier)) {
341
+ referencedFiles.push(...getReferencedFiles(nameNode));
342
+ }
343
+ }
344
+ }
345
+ }
346
+ }
347
+ return referencedFiles ? [declarations, [...new Set(referencedFiles)]] : [declarations];
348
+ };
349
+ const getReferencedFiles = (importIdentifier) => {
350
+ const declarations = importIdentifier?.getSymbol()?.getAliasedSymbol()?.getDeclarations() || [];
351
+ return declarations.flatMap((e) => {
352
+ const sourceFile = e.getSourceFile();
353
+ return sourceFile ? [sourceFile.getFilePath()] : [];
354
+ });
355
+ };
356
+ const extractGenerics = (callExpression) => {
357
+ return callExpression.getTypeArguments();
358
+ };
359
+ const typeResolverFactory = ({ root }) => {
360
+ const project = createProject({
361
+ tsConfigFilePath: resolve(root, "tsconfig.json"),
362
+ skipAddingFilesFromTsConfig: true
363
+ });
364
+ const literalTypesResolver = (literalTypes, options) => {
365
+ const sourceFile = project.createSourceFile(
366
+ `${crc(literalTypes)}-${Date.now()}.ts`,
367
+ literalTypes,
368
+ { overwrite: true }
369
+ );
370
+ const resolvedTypes = flattener(project, sourceFile, {
371
+ ...options,
372
+ stripComments: true
373
+ });
374
+ project.removeSourceFile(sourceFile);
375
+ return resolvedTypes;
376
+ };
377
+ return {
378
+ getSourceFile: (fileFullpath) => {
379
+ return project.getSourceFile(fileFullpath) || project.addSourceFileAtPath(fileFullpath);
380
+ },
381
+ refreshSourceFile: async (fileFullpath) => {
382
+ const sourceFile = project.getSourceFile(fileFullpath);
383
+ if (sourceFile) {
384
+ await sourceFile.refreshFromFileSystem();
385
+ }
386
+ },
387
+ literalTypesResolver
388
+ };
389
+ };
390
+ return {
391
+ createProject,
392
+ extractDefaultExport,
393
+ extractParamsRefinements,
394
+ extractRouteMethods,
395
+ extractTypeDeclarations,
396
+ resolveRouteSignature,
397
+ typeResolverFactory
398
+ };
399
+ };
400
+
401
+ // src/defaults.ts
402
+ var defaults = {
403
+ appPrefix: "~",
404
+ srcPrefix: "@",
405
+ libPrefix: "_",
406
+ coreDir: "core",
407
+ srcDir: "src",
408
+ libDir: "lib",
409
+ configDir: "config",
410
+ apiDir: "api",
411
+ pagesDir: "pages",
412
+ entryDir: "entry",
413
+ fetchDir: "fetch",
414
+ refineTypeName: "VRefine"
415
+ };
416
+
417
+ // src/generators.ts
418
+ var GENERATOR_META = /* @__PURE__ */ Symbol.for("KOSMO:GENERATOR_META");
419
+ var defineGenerator = (factory, meta) => {
420
+ const value = meta ? deepFreeze(structuredClone(meta)) : void 0;
421
+ const defineProperty = (fn) => {
422
+ Object.defineProperty(fn, GENERATOR_META, {
423
+ value,
424
+ enumerable: false,
425
+ writable: false,
426
+ configurable: false
427
+ });
428
+ return fn;
429
+ };
430
+ return defineProperty((...args) => {
431
+ return defineProperty(factory(...args));
432
+ });
433
+ };
434
+ var isGenerator = (fn) => {
435
+ return GENERATOR_META in fn;
436
+ };
437
+ var getGeneratorMeta = (fn) => {
438
+ return fn[GENERATOR_META];
439
+ };
440
+ var defineGeneratorFactory = (f) => f;
441
+ var deepFreeze = (obj) => {
442
+ if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
443
+ Object.freeze(obj);
444
+ for (const prop of Object.getOwnPropertyNames(obj)) {
445
+ deepFreeze(obj[prop]);
446
+ }
447
+ }
448
+ return obj;
449
+ };
450
+
451
+ // src/paths.ts
452
+ import { access, constants } from "node:fs/promises";
453
+ import { join, resolve as resolve2 } from "node:path";
454
+ var createTsconfigPaths = (prefix) => {
455
+ return {
456
+ [`${defaults.appPrefix}/*`]: [`${prefix}/*`],
457
+ [`${defaults.srcPrefix}/*`]: [`${prefix}/${defaults.srcDir}/*`],
458
+ [`${defaults.libPrefix}/*`]: [
459
+ `${prefix}/${defaults.libDir}/${defaults.srcDir}/*`
460
+ ]
461
+ };
462
+ };
463
+ var pathResolver = (sourceFolder) => {
464
+ const createPath = (...a) => {
465
+ return sourceFolder.root ? resolve2(sourceFolder.root, join(...a)) : join(...a);
466
+ };
467
+ const createImport = {
468
+ src(...a) {
469
+ return join(defaults.srcPrefix, sourceFolder.name, ...a);
470
+ },
471
+ config(...a) {
472
+ return this.src(defaults.configDir, ...a);
473
+ },
474
+ api(...a) {
475
+ return this.src(defaults.apiDir, ...a);
476
+ },
477
+ pages(...a) {
478
+ return this.src(defaults.pagesDir, ...a);
479
+ },
480
+ lib(...a) {
481
+ return join(defaults.libPrefix, sourceFolder.name, ...a);
482
+ },
483
+ libApi(...a) {
484
+ return this.lib(defaults.apiDir, ...a);
485
+ },
486
+ libEntry(...a) {
487
+ return this.lib(defaults.entryDir, ...a);
488
+ }
489
+ };
490
+ return {
491
+ createPath: {
492
+ src(...a) {
493
+ return createPath(defaults.srcDir, sourceFolder.name, ...a);
494
+ },
495
+ api(...a) {
496
+ return this.src(defaults.apiDir, ...a);
497
+ },
498
+ pages(...a) {
499
+ return this.src(defaults.pagesDir, ...a);
500
+ },
501
+ config(...a) {
502
+ return this.src(defaults.configDir, ...a);
503
+ },
504
+ entry(...a) {
505
+ return this.src(defaults.entryDir, ...a);
506
+ },
507
+ lib(...a) {
508
+ return createPath(
509
+ defaults.libDir,
510
+ defaults.srcDir,
511
+ sourceFolder.name,
512
+ ...a
513
+ );
514
+ },
515
+ libApi(...a) {
516
+ return this.lib(defaults.apiDir, ...a);
517
+ },
518
+ libEntry(...a) {
519
+ return this.lib(defaults.entryDir, ...a);
520
+ },
521
+ libPages(...a) {
522
+ return this.lib(defaults.pagesDir, ...a);
523
+ },
524
+ distDir(...a) {
525
+ return createPath(sourceFolder.distDir, sourceFolder.name, ...a);
526
+ }
527
+ },
528
+ createImport,
529
+ createImportHelper: (key, ...a) => {
530
+ return createImport[key](...a.slice(0, -1));
531
+ }
532
+ };
533
+ };
534
+ var pathExists = async (path) => {
535
+ try {
536
+ await access(path, constants.F_OK);
537
+ return true;
538
+ } catch {
539
+ return false;
540
+ }
541
+ };
542
+
543
+ // src/render.ts
544
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
545
+ import { dirname, join as join2 } from "node:path";
546
+ import crc2 from "crc/crc32";
547
+ import handlebars from "handlebars";
548
+ var render = (template, context, options) => {
549
+ const { noEscape = true, renderer = handlebars } = { ...options };
550
+ return renderer.compile(template, { noEscape })(context);
551
+ };
552
+ var renderToFile = async (file, template, context, options) => {
553
+ const content = render(template, context, options);
554
+ if (await pathExists(file)) {
555
+ const { overwrite = true } = { ...options };
556
+ if (overwrite === false) {
557
+ return;
558
+ }
559
+ const fileContent = await readFile(file, "utf8");
560
+ if (typeof overwrite === "function" && !overwrite(fileContent)) {
561
+ return;
562
+ }
563
+ if (crc2(content) === crc2(fileContent)) {
564
+ return;
565
+ }
566
+ }
567
+ await mkdir(dirname(file), { recursive: true });
568
+ await writeFile(file, content, "utf8");
569
+ };
570
+ var renderFactory = (options) => {
571
+ const renderer = handlebars.create();
572
+ renderer.registerPartial({ ...options?.partials });
573
+ renderer.registerHelper({ ...options?.helpers });
574
+ return {
575
+ render(template, context, selfOoptions) {
576
+ return render(template, context, {
577
+ renderer,
578
+ ...options,
579
+ ...selfOoptions
580
+ });
581
+ },
582
+ async renderToFile(file, template, context, selfOoptions) {
583
+ return renderToFile(
584
+ options?.outdir ? join2(options.outdir, file) : file,
585
+ template,
586
+ context,
587
+ { renderer, ...options, ...selfOoptions }
588
+ );
589
+ }
590
+ };
591
+ };
592
+ var renderHelpers = {
593
+ createParamsLiteral: (params) => {
594
+ return params.schema.map((p) => {
595
+ return p.kind === "splat" ? `${p.const}?: Array<string | number>` : p.kind === "optional" ? `${p.const}?: string | number` : `${p.const}: string | number`;
596
+ }).join(", ");
597
+ }
598
+ };
599
+
600
+ // src/routes/base.ts
601
+ import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
602
+ import { styleText as styleText2 } from "node:util";
603
+ import crc3 from "crc/crc32";
604
+ import { parse } from "path-to-regexp";
605
+ import picomatch from "picomatch";
606
+ import { glob } from "tinyglobby";
607
+ var API_INDEX_BASENAME = "index";
608
+ var API_INDEX_PATTERN = `${API_INDEX_BASENAME}.ts`;
609
+ var API_USE_BASENAME = "use";
610
+ var API_USE_PATTERN = `${API_USE_BASENAME}.ts`;
611
+ var PAGE_INDEX_BASENAME = "index";
612
+ var PAGE_INDEX_PATTERN = `${PAGE_INDEX_BASENAME}.{tsx,vue}`;
613
+ var PAGE_LAYOUT_BASENAME = "layout";
614
+ var PAGE_LAYOUT_PATTERN = `${PAGE_LAYOUT_BASENAME}.{tsx,vue}`;
615
+ var ROUTE_FILE_PATTERNS = [
616
+ // match index files in api dir
617
+ `${defaults.apiDir}/**/${API_INDEX_PATTERN}`,
618
+ // match use files in api dir
619
+ `${defaults.apiDir}/**/${API_USE_PATTERN}`,
620
+ // match index files in pages dir
621
+ `${defaults.pagesDir}/**/${PAGE_INDEX_PATTERN}`,
622
+ // match layout files in pages dir
623
+ `${defaults.pagesDir}/**/${PAGE_LAYOUT_PATTERN}`
624
+ ];
625
+ var scanRoutes = async (sourceFolder) => {
626
+ const { createPath } = pathResolver(sourceFolder);
627
+ return glob(ROUTE_FILE_PATTERNS, {
628
+ cwd: createPath.src(),
629
+ absolute: true,
630
+ onlyFiles: true,
631
+ followSymbolicLinks: false,
632
+ ignore: [
633
+ // ignore top-level matches, routes resides in folders, even index route
634
+ `${defaults.apiDir}/${API_INDEX_PATTERN}`,
635
+ `${defaults.apiDir}/${API_USE_PATTERN}`,
636
+ `${defaults.pagesDir}/${PAGE_INDEX_PATTERN}`,
637
+ `${defaults.pagesDir}/${PAGE_LAYOUT_PATTERN}`
638
+ ]
639
+ });
640
+ };
641
+ var isRouteFile = (file, sourceFolder) => {
642
+ const [_sourceFolder, _folder, ...rest] = resolve3(sourceFolder.root, file).replace(`${sourceFolder.root}/${defaults.srcDir}/`, "").split("/");
643
+ if (!_folder || _sourceFolder !== sourceFolder.name || rest.length < 2) {
644
+ return false;
645
+ }
646
+ return picomatch.isMatch(join3(_folder, ...rest), ROUTE_FILE_PATTERNS) ? [_folder, rest.join("/")] : false;
647
+ };
648
+ var isApiRoute = (file) => {
649
+ return picomatch.matchBase(file, `**/${API_INDEX_PATTERN}`);
650
+ };
651
+ var isApiUse = (file) => {
652
+ return picomatch.matchBase(file, `**/${API_USE_PATTERN}`);
653
+ };
654
+ var isPageRoute = (file) => {
655
+ return picomatch.matchBase(file, `**/${PAGE_INDEX_PATTERN}`);
656
+ };
657
+ var isPageLayout = (file) => {
658
+ return picomatch.matchBase(file, `**/${PAGE_LAYOUT_PATTERN}`);
659
+ };
660
+ var createRouteEntry = (fileFullpath, sourceFolder) => {
661
+ const resolvedPaths = isRouteFile(fileFullpath, sourceFolder);
662
+ if (!resolvedPaths) {
663
+ return;
664
+ }
665
+ const [folder, file] = resolvedPaths;
666
+ const id = `${file.replace(/\W+/g, "_")}_${crc3(file)}`;
667
+ const name = dirname2(file);
668
+ try {
669
+ const [pathTokens, pathPattern] = pathTokensFactory(dirname2(file));
670
+ return { id, name, folder, file, fileFullpath, pathTokens, pathPattern };
671
+ } catch (error) {
672
+ console.error(
673
+ `\u2757${styleText2("red", "ERROR")}: Failed parsing path for "${styleText2("cyan", file)}"`
674
+ );
675
+ console.error(error);
676
+ return;
677
+ }
678
+ };
679
+ var pathTokensFactory = (path, {
680
+ transformStaticValue = normalizeStaticValue
681
+ } = {}) => {
682
+ const extractParts = (tokens2, createConst, insideGroup = false) => {
683
+ const parts = [];
684
+ for (const token of tokens2) {
685
+ switch (token.type) {
686
+ case "text":
687
+ if (token.value !== "/") {
688
+ parts.push({
689
+ type: "static",
690
+ value: transformStaticValue(token.value)
691
+ });
692
+ }
693
+ break;
694
+ case "param":
695
+ parts.push({
696
+ type: "param",
697
+ kind: insideGroup ? "optional" : "required",
698
+ name: token.name,
699
+ const: createConst(token.name)
700
+ });
701
+ break;
702
+ case "wildcard":
703
+ parts.push({
704
+ type: "param",
705
+ kind: "splat",
706
+ name: token.name,
707
+ const: createConst(token.name)
708
+ });
709
+ break;
710
+ case "group":
711
+ parts.push(...extractParts(token.tokens, createConst, true));
712
+ break;
713
+ }
714
+ }
715
+ return parts;
716
+ };
717
+ const patternTransforms = [
718
+ // Transform required params: [id] => :id
719
+ // Only pure \w param names,
720
+ // [some-id] used as is, not treated as param,
721
+ // use [some_id] instead.
722
+ (s) => s.replace(/\[(\w+)\]/g, ":$1"),
723
+ // Transform optional params: {id} => {:id}
724
+ // Only pure \w param names,
725
+ // anything else treated as a path-to-regexp pattern and used as is.
726
+ // {some-id} treated as an optional static segment.
727
+ // use {some_id} for simple param syntax
728
+ // or {:some-id} pattern where :some is the param name and -id is a static segment.
729
+ (s) => s.replace(/\{(\w+)\}/g, "{:$1}"),
730
+ // Transform splat params: {...param} => {*param}
731
+ (s) => s.replace(/\{\.\.\./g, "{*"),
732
+ // Insert leading slash inside optional/splat groups.
733
+ // {:name} => {/:name}
734
+ // {*name} => {/*name}
735
+ (s) => {
736
+ return s.startsWith("{") ? s.replace(/^\{/, "{/") : s;
737
+ }
738
+ ];
739
+ const detectBareParams = (s) => {
740
+ let depth = 0;
741
+ for (const [i, ch] of [...s].entries()) {
742
+ if (ch === "{") {
743
+ depth += 1;
744
+ } else if (ch === "}") {
745
+ depth -= 1;
746
+ } else if (ch === ":" && depth === 0) {
747
+ const match = s.slice(i + 1).match(/^\w+/);
748
+ return match?.[0] || ":";
749
+ }
750
+ }
751
+ return;
752
+ };
753
+ const tokens = path.replace(/^index\/?/, "").split("/").flatMap((orig) => {
754
+ if (!orig.length) {
755
+ return [];
756
+ }
757
+ const bareParam = detectBareParams(orig);
758
+ if (bareParam === ":") {
759
+ throw new Error(
760
+ `${path} contains colons outside braces, use : only within {}`
761
+ );
762
+ } else if (bareParam) {
763
+ throw new Error(
764
+ `${path} contains bare params, use [${bareParam}] instead of :${bareParam}`
765
+ );
766
+ }
767
+ const pattern = patternTransforms.reduce((src, fn) => fn(src), orig);
768
+ const { tokens: tokens2 } = parse(pattern);
769
+ const parts = extractParts(tokens2, (val) => {
770
+ return /\W/.test(val) || /^\d/.test(val) ? [val.replace(/^\d+|\W/g, "_"), crc3(orig)].join("_") : val;
771
+ });
772
+ const isStatic = parts.length === 1 ? parts[0].type === "static" : false;
773
+ const isParam = parts.length === 1 ? parts[0].type === "param" : false;
774
+ const kind = isStatic ? "static" : isParam ? "param" : "mixed";
775
+ return [
776
+ {
777
+ kind,
778
+ orig,
779
+ pattern,
780
+ parts
781
+ }
782
+ ];
783
+ });
784
+ return [
785
+ tokens,
786
+ tokens.map(({ pattern }, i) => {
787
+ const next = tokens[i + 1];
788
+ if (!next || next.pattern.includes("/")) {
789
+ return pattern;
790
+ }
791
+ const slashRequired = tokens.slice(i + 1).some((e) => {
792
+ return e.parts.some((e2) => {
793
+ return e2.type === "static" || e2.kind === "required";
794
+ });
795
+ });
796
+ return slashRequired ? `${pattern}/` : pattern;
797
+ }).join("")
798
+ ];
799
+ };
800
+ var normalizeStaticValue = (value) => {
801
+ return value.replace(/\+/g, "\\\\+");
802
+ };
803
+ var sortRoutes = (a, b) => {
804
+ const aSpecificity = routeSpecificity(a.pathTokens);
805
+ const bSpecificity = routeSpecificity(b.pathTokens);
806
+ if (aSpecificity !== bSpecificity) {
807
+ return bSpecificity - aSpecificity;
808
+ }
809
+ if (a.pathTokens.length !== b.pathTokens.length) {
810
+ return a.pathTokens.length - b.pathTokens.length;
811
+ }
812
+ return a.name.localeCompare(b.name);
813
+ };
814
+ var paramWeight = (part) => {
815
+ return {
816
+ required: 2,
817
+ optional: 1,
818
+ splat: 0
819
+ }[part.kind];
820
+ };
821
+ var mixedSegmentWeight = (parts) => {
822
+ const hasSplat = parts.some((p) => {
823
+ return p.type === "param" ? p.kind === "splat" : false;
824
+ });
825
+ return hasSplat ? 0.5 : 3;
826
+ };
827
+ var segmentWeight = (token) => {
828
+ return {
829
+ static: 4,
830
+ mixed: mixedSegmentWeight(token.parts),
831
+ param: paramWeight(token.parts[0])
832
+ }[token.kind];
833
+ };
834
+ var routeSpecificity = (pathTokens) => {
835
+ return pathTokens.reduce((sum, token) => sum + segmentWeight(token), 0);
836
+ };
837
+
838
+ // src/routes/resolve.ts
839
+ import { dirname as dirname3, join as join4, resolve as resolve4 } from "node:path";
840
+ import { styleText as styleText3 } from "node:util";
841
+ import crc4 from "crc/crc32";
842
+ import mimeTypes from "mime-types";
843
+
844
+ // src/routes/templates/resolved-types.hbs
845
+ var resolved_types_default = "{{#each resolvedTypes}}\nexport type {{name}} = {{text}};\n{{/each}}\n";
846
+
847
+ // src/routes/templates/types.hbs
848
+ 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';
849
+
850
+ // src/routes/resolve.ts
851
+ var resolverFactory = (sourceFolder, cacheFactory) => {
852
+ const {
853
+ //
854
+ generators = [],
855
+ refineTypeName = defaults.refineTypeName
856
+ } = sourceFolder.config;
857
+ const { typeResolverFactory, resolveRouteSignature } = astFactory();
858
+ const resolveTypes = generators.some(
859
+ (e) => getGeneratorMeta(e)?.resolveTypes
860
+ );
861
+ const {
862
+ //
863
+ literalTypesResolver,
864
+ getSourceFile,
865
+ refreshSourceFile
866
+ } = typeResolverFactory(sourceFolder);
867
+ return {
868
+ pageLayoutResolver(entry) {
869
+ const { name } = entry;
870
+ const handler = async () => {
871
+ return {
872
+ kind: "pageLayout",
873
+ entry
874
+ };
875
+ };
876
+ return { name, handler };
877
+ },
878
+ pageRouteResolver(entry) {
879
+ const { id, name, folder, file, fileFullpath, pathTokens, pathPattern } = entry;
880
+ const handler = async () => {
881
+ const entry2 = {
882
+ id,
883
+ name,
884
+ pathTokens,
885
+ pathPattern,
886
+ params: {
887
+ schema: pathTokens.flatMap((e) => {
888
+ return e.parts.filter((p) => p.type === "param");
889
+ })
890
+ },
891
+ folder,
892
+ file,
893
+ fileFullpath
894
+ };
895
+ return {
896
+ kind: "pageRoute",
897
+ entry: entry2
898
+ };
899
+ };
900
+ return { name, handler };
901
+ },
902
+ apiUseResolver(entry) {
903
+ const { name } = entry;
904
+ const handler = async () => {
905
+ return {
906
+ kind: "apiUse",
907
+ entry
908
+ };
909
+ };
910
+ return { name, handler };
911
+ },
912
+ apiRouteResolver(entry) {
913
+ const {
914
+ //
915
+ id,
916
+ name,
917
+ file,
918
+ folder,
919
+ fileFullpath,
920
+ pathTokens,
921
+ pathPattern
922
+ } = entry;
923
+ const handler = async (updatedFile) => {
924
+ const paramsSchema = pathTokens.flatMap(
925
+ (e) => {
926
+ return e.parts.flatMap((p) => {
927
+ return p.type === "param" ? [p] : [];
928
+ });
929
+ }
930
+ );
931
+ const optionalParams = paramsSchema.length ? paramsSchema.filter((e) => e.kind === "required").length === 0 : true;
932
+ const cacheStore = cacheFactory ? cacheFactory({ id, file, fileFullpath }, sourceFolder, {
933
+ resolveTypes
934
+ }) : void 0;
935
+ let cache = cacheStore ? await cacheStore.get({ validate: true }) : void 0;
936
+ if (!cache) {
937
+ if (updatedFile === fileFullpath) {
938
+ await refreshSourceFile(fileFullpath);
939
+ }
940
+ const {
941
+ typeDeclarations,
942
+ paramsRefinements,
943
+ methods,
944
+ validationDefinitions: validationDefinitions2,
945
+ referencedFiles = []
946
+ } = await resolveRouteSignature(
947
+ { id, name, fileFullpath, optionalParams },
948
+ {
949
+ withReferencedFiles: true,
950
+ sourceFile: getSourceFile(fileFullpath),
951
+ relpathResolver(path) {
952
+ return join4(
953
+ sourceFolder.name,
954
+ defaults.apiDir,
955
+ dirname3(file),
956
+ path
957
+ );
958
+ }
959
+ }
960
+ );
961
+ const validationTypes = validationDefinitions2.flatMap((def) => {
962
+ return def.target === "response" ? def.variants.flatMap(({ id: id2, body }) => {
963
+ return body ? [{ id: id2, text: body }] : [];
964
+ }) : [def.schema];
965
+ });
966
+ const numericParams = paramsRefinements ? paramsRefinements.flatMap(({ text, index }) => {
967
+ if (text === "number") {
968
+ const param = paramsSchema.at(index);
969
+ return param ? [param.name] : [];
970
+ }
971
+ return [];
972
+ }) : [];
973
+ const typesFile = pathResolver(sourceFolder).createPath.libApi(
974
+ dirname3(file),
975
+ "types.ts"
976
+ );
977
+ const params = {
978
+ id: ["ParamsT", crc4(name)].join(""),
979
+ schema: paramsSchema,
980
+ resolvedType: void 0
981
+ };
982
+ const typesFileContent = render(types_default, {
983
+ params,
984
+ paramsSchema: paramsSchema.map((param, index) => {
985
+ return {
986
+ ...param,
987
+ isRequired: param.kind === "required",
988
+ isSplat: param.kind === "splat",
989
+ refinement: paramsRefinements?.at(index)
990
+ };
991
+ }),
992
+ typeDeclarations,
993
+ validationTypes
994
+ });
995
+ const resolvedTypes = resolveTypes ? literalTypesResolver(typesFileContent, {
996
+ stripComments: true,
997
+ overrides: { [refineTypeName]: refineTypeName },
998
+ withProperties: [
999
+ params.id,
1000
+ ...validationTypes.flatMap(({ id: id2 }) => id2)
1001
+ ]
1002
+ }) : void 0;
1003
+ await renderToFile(
1004
+ typesFile,
1005
+ resolvedTypes ? resolved_types_default : typesFileContent,
1006
+ { resolvedTypes }
1007
+ );
1008
+ params.resolvedType = resolvedTypes?.find(
1009
+ (e) => e.name === params.id
1010
+ );
1011
+ cache = {
1012
+ // placeholder values, if cacheFactory given,
1013
+ // they will be replaced by persistCache
1014
+ hash: 0,
1015
+ referencedFiles: {},
1016
+ // real values
1017
+ params,
1018
+ methods,
1019
+ typeDeclarations,
1020
+ numericParams,
1021
+ validationDefinitions: validationDefinitions2.map((def) => {
1022
+ return {
1023
+ ...def,
1024
+ ...def.target === "response" ? {
1025
+ variants: def.variants.map((variant) => {
1026
+ return {
1027
+ ...variant,
1028
+ resolvedType: resolvedTypes?.find(
1029
+ (e) => e.name === variant.id
1030
+ )
1031
+ };
1032
+ })
1033
+ } : {
1034
+ schema: {
1035
+ ...def.schema,
1036
+ resolvedType: resolvedTypes?.find(
1037
+ (e) => e.name === def.schema.id
1038
+ )
1039
+ }
1040
+ }
1041
+ };
1042
+ })
1043
+ };
1044
+ if (cacheStore) {
1045
+ const { referencedFiles: _void, ...rest } = cache;
1046
+ cache = await cacheStore.set({ ...rest, referencedFiles });
1047
+ }
1048
+ }
1049
+ const validationDefinitions = cache.validationDefinitions.flatMap(
1050
+ (def) => {
1051
+ let augmentedDef = def;
1052
+ if (def.target === "response") {
1053
+ augmentedDef = {
1054
+ ...def,
1055
+ variants: def.variants.flatMap((variant, i) => {
1056
+ if (typeof variant.contentType !== "string") {
1057
+ return [variant];
1058
+ }
1059
+ if (variant.contentType.includes("/")) {
1060
+ return [variant];
1061
+ }
1062
+ const contentType = mimeTypes.lookup(variant.contentType);
1063
+ if (contentType === false) {
1064
+ console.warn(
1065
+ styleText3(
1066
+ ["bold", "red"],
1067
+ "\u2717 Failed resolving Response Content Type"
1068
+ )
1069
+ );
1070
+ console.warn(
1071
+ ` Invalid value provided for mime-types lookup - ${variant.contentType}`
1072
+ );
1073
+ console.warn(
1074
+ styleText3(
1075
+ ["cyan"],
1076
+ ` Response variant #${i} excluded from route schemas`
1077
+ )
1078
+ );
1079
+ console.warn(` Route: ${name}; Method: ${def.method}`);
1080
+ console.warn();
1081
+ return [];
1082
+ }
1083
+ return [{ ...variant, contentType }];
1084
+ })
1085
+ };
1086
+ } else if (def.contentType && !def.contentType.includes("/")) {
1087
+ const contentType = mimeTypes.lookup(def.contentType);
1088
+ if (contentType === false) {
1089
+ console.warn(
1090
+ styleText3(
1091
+ ["bold", "red"],
1092
+ "\u2717 Failed resolving Response Content Type"
1093
+ )
1094
+ );
1095
+ console.warn(
1096
+ ` Invalid value provided for mime-types lookup - ${def.contentType}`
1097
+ );
1098
+ console.warn(` Route: ${name}; Method: ${def.method}`);
1099
+ console.warn();
1100
+ } else {
1101
+ augmentedDef = { ...def, contentType };
1102
+ }
1103
+ }
1104
+ return augmentedDef ? [augmentedDef] : [];
1105
+ }
1106
+ );
1107
+ const entry2 = {
1108
+ id,
1109
+ name,
1110
+ pathTokens,
1111
+ pathPattern,
1112
+ params: cache.params,
1113
+ numericParams: cache.numericParams,
1114
+ optionalParams,
1115
+ folder,
1116
+ file,
1117
+ fileFullpath,
1118
+ methods: cache.methods,
1119
+ typeDeclarations: cache.typeDeclarations,
1120
+ validationDefinitions,
1121
+ referencedFiles: Object.keys(cache.referencedFiles).map(
1122
+ // expand referenced files path,
1123
+ // they are stored as relative in cache
1124
+ (e) => resolve4(sourceFolder.root, e)
1125
+ )
1126
+ };
1127
+ return {
1128
+ kind: "apiRoute",
1129
+ entry: entry2
1130
+ };
1131
+ };
1132
+ return { name, handler };
1133
+ }
1134
+ };
1135
+ };
1136
+
1137
+ // src/routes/nesting.ts
1138
+ import { basename } from "node:path";
1139
+ var nestedRoutesFactory = (routeEntries) => {
1140
+ const entryStack = structuredClone(routeEntries).sort(sortRoutes);
1141
+ const transformEntries = (entries, parent) => {
1142
+ return [...new Set(entries.map((e) => e.name))].flatMap((name) => {
1143
+ const nameEntries = entryStack.flatMap(({ fileFullpath, ...entry }) => {
1144
+ return entry.name === name ? [entry] : [];
1145
+ });
1146
+ const index = nameEntries.find(
1147
+ (e) => basename(e.file).startsWith(PAGE_INDEX_BASENAME)
1148
+ );
1149
+ const layout = nameEntries.find(
1150
+ (e) => basename(e.file).startsWith(PAGE_LAYOUT_BASENAME)
1151
+ );
1152
+ if (index || layout) {
1153
+ return [
1154
+ {
1155
+ index: index ? {
1156
+ ...index,
1157
+ pathTokens: index.pathTokens.slice(
1158
+ parent?.pathTokens.length || 0
1159
+ )
1160
+ } : void 0,
1161
+ layout: layout ? {
1162
+ ...layout,
1163
+ pathTokens: layout.pathTokens.slice(
1164
+ parent?.pathTokens.length || 0
1165
+ )
1166
+ } : void 0,
1167
+ parent: parent?.name,
1168
+ children: transformEntries(
1169
+ findDescendantEntries(index || layout),
1170
+ index || layout
1171
+ )
1172
+ }
1173
+ ];
1174
+ }
1175
+ return [];
1176
+ });
1177
+ };
1178
+ const findDescendantEntries = ({
1179
+ name,
1180
+ pathTokens
1181
+ }) => {
1182
+ const potentialChildren = entryStack.filter((entry) => {
1183
+ if (entry.pathTokens.length <= pathTokens.length) {
1184
+ return false;
1185
+ }
1186
+ if (!entry.name.startsWith(`${name}/`)) {
1187
+ return false;
1188
+ }
1189
+ return true;
1190
+ });
1191
+ return potentialChildren.filter((child) => {
1192
+ const hasIntermediateRoute = potentialChildren.some((intermediate) => {
1193
+ if (intermediate === child) {
1194
+ return false;
1195
+ }
1196
+ if (intermediate.pathTokens.length <= pathTokens.length) {
1197
+ return false;
1198
+ }
1199
+ if (intermediate.pathTokens.length >= child.pathTokens.length) {
1200
+ return false;
1201
+ }
1202
+ return child.name.startsWith(`${intermediate.name}/`);
1203
+ });
1204
+ return !hasIntermediateRoute;
1205
+ });
1206
+ };
1207
+ const rootEntries = entryStack.filter((entry) => {
1208
+ const hasParent = entryStack.some((potential) => {
1209
+ if (potential === entry) {
1210
+ return false;
1211
+ }
1212
+ if (potential.pathTokens.length >= entry.pathTokens.length) {
1213
+ return false;
1214
+ }
1215
+ return entry.name.startsWith(`${potential.name}/`);
1216
+ });
1217
+ return !hasParent;
1218
+ });
1219
+ return transformEntries(rootEntries);
1220
+ };
1221
+
1222
+ // src/routes/index.ts
1223
+ var routesFactory = async (sourceFolder, cacheFactory) => {
1224
+ const {
1225
+ apiRouteResolver,
1226
+ apiUseResolver,
1227
+ pageRouteResolver,
1228
+ pageLayoutResolver
1229
+ } = resolverFactory(sourceFolder, cacheFactory);
1230
+ const resolversFactory = (routeFiles2) => {
1231
+ const resolvers = /* @__PURE__ */ new Map();
1232
+ const entries = routeFiles2.flatMap((file) => {
1233
+ const entry = createRouteEntry(file, sourceFolder);
1234
+ return entry ? [entry] : [];
1235
+ });
1236
+ for (const entry of entries) {
1237
+ if (entry.folder === defaults.apiDir) {
1238
+ if (isApiRoute(entry.file)) {
1239
+ resolvers.set(entry.fileFullpath, apiRouteResolver(entry));
1240
+ } else if (isApiUse(entry.file)) {
1241
+ resolvers.set(entry.fileFullpath, apiUseResolver(entry));
1242
+ }
1243
+ } else if (entry.folder === defaults.pagesDir) {
1244
+ if (isPageRoute(entry.file)) {
1245
+ resolvers.set(entry.fileFullpath, pageRouteResolver(entry));
1246
+ } else if (isPageLayout(entry.file)) {
1247
+ resolvers.set(entry.fileFullpath, pageLayoutResolver(entry));
1248
+ }
1249
+ }
1250
+ }
1251
+ return resolvers;
1252
+ };
1253
+ const routeFiles = await scanRoutes(sourceFolder);
1254
+ return {
1255
+ resolvers: resolversFactory(routeFiles),
1256
+ resolversFactory
1257
+ };
1258
+ };
1259
+
1260
+ // src/spinner.ts
1261
+ import { Spinner } from "@topcli/spinner";
1262
+ var spinnerFactory = (startText) => {
1263
+ const spinner = new Spinner().start(startText);
1264
+ let _text = startText;
1265
+ return {
1266
+ text(text) {
1267
+ _text = text;
1268
+ spinner.text = text;
1269
+ },
1270
+ append(text) {
1271
+ spinner.text = `${_text} \u203A ${text}`;
1272
+ },
1273
+ succeed(text) {
1274
+ if (text) {
1275
+ this.append(text);
1276
+ } else {
1277
+ this.text(_text);
1278
+ }
1279
+ spinner.succeed();
1280
+ },
1281
+ failed(text) {
1282
+ if (text) {
1283
+ this.text([_text, text].join("\n"));
1284
+ }
1285
+ spinner.failed();
1286
+ }
1287
+ };
1288
+ };
1289
+ var withSpinner = (text, pipe, spinner) => pipe(spinner || spinnerFactory(text));
1290
+
1291
+ // src/typebox.ts
1292
+ var typeboxLiteralText = (text, sourceFolder) => {
1293
+ const { refineTypeName = defaults.refineTypeName } = sourceFolder.config;
1294
+ return [
1295
+ // Escape backticks for safe use in template literals
1296
+ [/(?<!\\)`/g, "\\`"],
1297
+ // Escape $ for safe use in template literals
1298
+ [/(?<!\\)\$\{/g, "\\${"],
1299
+ /**
1300
+ * TypeBox's built-in `Options` type is not configurable.
1301
+ * To allow a custom type name, exposing `refineTypeName` option,
1302
+ * defaulted to VRefine, then renaming it to `Options`.
1303
+ * */
1304
+ [new RegExp(`\\b${refineTypeName}\\s*<`, "g"), "Options<"]
1305
+ ].reduce((text2, [a, b]) => text2.replace(a, b), text);
1306
+ };
1307
+ export {
1308
+ API_INDEX_BASENAME,
1309
+ API_INDEX_PATTERN,
1310
+ API_USE_BASENAME,
1311
+ API_USE_PATTERN,
1312
+ GENERATOR_META,
1313
+ PAGE_INDEX_BASENAME,
1314
+ PAGE_INDEX_PATTERN,
1315
+ PAGE_LAYOUT_BASENAME,
1316
+ PAGE_LAYOUT_PATTERN,
1317
+ astFactory,
1318
+ createRouteEntry,
1319
+ createTsconfigPaths,
1320
+ defaults,
1321
+ defineGenerator,
1322
+ defineGeneratorFactory,
1323
+ getGeneratorMeta,
1324
+ isApiRoute,
1325
+ isApiUse,
1326
+ isGenerator,
1327
+ isPageLayout,
1328
+ isPageRoute,
1329
+ isRouteFile,
1330
+ nestedRoutesFactory,
1331
+ normalizeStaticValue,
1332
+ pathExists,
1333
+ pathResolver,
1334
+ pathTokensFactory,
1335
+ render,
1336
+ renderFactory,
1337
+ renderHelpers,
1338
+ renderToFile,
1339
+ routesFactory,
1340
+ scanRoutes,
1341
+ sortRoutes,
1342
+ spinnerFactory,
1343
+ typeboxLiteralText,
1344
+ withSpinner
1345
+ };
1346
+ //# sourceMappingURL=index.js.map