@reckona/mreact-router 0.0.65 → 0.0.67

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.
Files changed (59) hide show
  1. package/package.json +13 -12
  2. package/src/actions.ts +1130 -0
  3. package/src/adapters/aws-lambda.ts +993 -0
  4. package/src/adapters/cloudflare.ts +1286 -0
  5. package/src/adapters/devtools.ts +5 -0
  6. package/src/adapters/edge.ts +70 -0
  7. package/src/adapters/node.ts +126 -0
  8. package/src/adapters/static.ts +61 -0
  9. package/src/app-router-globals.ts +19 -0
  10. package/src/assets.ts +113 -0
  11. package/src/build.ts +2948 -0
  12. package/src/bundle-pipeline.ts +496 -0
  13. package/src/cache-config.ts +35 -0
  14. package/src/cache-stats.ts +54 -0
  15. package/src/cache.ts +418 -0
  16. package/src/cli-options.ts +296 -0
  17. package/src/cli.ts +94 -0
  18. package/src/client.ts +3398 -0
  19. package/src/config.ts +146 -0
  20. package/src/cookies.ts +113 -0
  21. package/src/csp.ts +103 -0
  22. package/src/csrf.ts +132 -0
  23. package/src/deferred.ts +52 -0
  24. package/src/dev-server.ts +262 -0
  25. package/src/file-conventions.ts +88 -0
  26. package/src/http.ts +128 -0
  27. package/src/i18n.ts +98 -0
  28. package/src/import-policy.ts +261 -0
  29. package/src/index.ts +221 -0
  30. package/src/link.ts +47 -0
  31. package/src/logger.ts +149 -0
  32. package/src/module-runner.ts +554 -0
  33. package/src/multipart.ts +577 -0
  34. package/src/native-escape.ts +53 -0
  35. package/src/native-route-matcher.ts +173 -0
  36. package/src/navigation-state.ts +98 -0
  37. package/src/navigation.ts +215 -0
  38. package/src/prerender-store.ts +233 -0
  39. package/src/render.ts +5187 -0
  40. package/src/route-path.ts +5 -0
  41. package/src/route-shells.ts +47 -0
  42. package/src/route-source.ts +224 -0
  43. package/src/route-styles.ts +91 -0
  44. package/src/routes.ts +362 -0
  45. package/src/runtime-cache.ts +8 -0
  46. package/src/runtime-state.ts +37 -0
  47. package/src/security-headers.ts +134 -0
  48. package/src/serve.ts +1265 -0
  49. package/src/session.ts +238 -0
  50. package/src/source-jsx.ts +30 -0
  51. package/src/source-modules.ts +69 -0
  52. package/src/stream-list.ts +45 -0
  53. package/src/trace.ts +114 -0
  54. package/src/types.ts +155 -0
  55. package/src/upgrade.ts +8 -0
  56. package/src/vite-config.ts +63 -0
  57. package/src/vite-plugin-cache-key.ts +53 -0
  58. package/src/vite.ts +690 -0
  59. package/src/workspace-packages.ts +67 -0
@@ -0,0 +1,554 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, join, sep } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { formatDiagnostic, type ServerOutputMode } from "@reckona/mreact-compiler";
6
+ import { transformCompilerModuleContext } from "@reckona/mreact-compiler/internal";
7
+ import { runnerImport, type InlineConfig, type PluginOption } from "vite";
8
+ import { resolveWorkspacePackageFile } from "./workspace-packages.js";
9
+ import {
10
+ bundleRouterModule,
11
+ type RouterCompatBuildApi,
12
+ type RouterCompatPlugin,
13
+ } from "./bundle-pipeline.js";
14
+ import { resolveRouterCacheLimit } from "./cache-config.js";
15
+ import type { BuiltServerModuleArtifact } from "./build.js";
16
+ import {
17
+ createRouterRuntimeCacheCounters,
18
+ readRouterRuntimeCacheEntry,
19
+ routerRuntimeCacheStat,
20
+ type RouterRuntimeCacheCounters,
21
+ type RouterRuntimeCacheStat,
22
+ } from "./cache-stats.js";
23
+ import {
24
+ compilerModuleContextForSource,
25
+ createClientRouteInferenceCache,
26
+ formatClientRouteInferenceDiagnostic,
27
+ inferClientRouteModule,
28
+ type ClientRouteInferenceCache,
29
+ } from "./client.js";
30
+ import { vitePluginsCacheKey } from "./vite-plugin-cache-key.js";
31
+
32
+ const runnerConfig = {
33
+ configFile: false,
34
+ logLevel: "silent",
35
+ } satisfies InlineConfig;
36
+ const nativeEscapeTransform = {
37
+ batchImportName: "escapeHtmlBatch",
38
+ batchImportSource: "@reckona/mreact-router/native-escape",
39
+ } as const;
40
+ const sourceModuleCache = new Map<string, Promise<unknown>>();
41
+ const maxSourceModuleCacheEntries = resolveRouterCacheLimit("SOURCE_MODULE", 512);
42
+ const sourceModuleCacheCounters = createRouterRuntimeCacheCounters();
43
+ const serverSourceTransformCache = new Map<string, string>();
44
+ const maxServerSourceTransformCacheEntries = resolveRouterCacheLimit(
45
+ "SERVER_SOURCE_TRANSFORM",
46
+ 512,
47
+ );
48
+ const serverSourceTransformCacheCounters = createRouterRuntimeCacheCounters();
49
+ let fileImportVersion = 0;
50
+
51
+ export function routerModuleRunnerRuntimeCacheStats(): RouterRuntimeCacheStat[] {
52
+ return [
53
+ routerRuntimeCacheStat(
54
+ "source-module",
55
+ sourceModuleCache,
56
+ maxSourceModuleCacheEntries,
57
+ sourceModuleCacheCounters,
58
+ ),
59
+ routerRuntimeCacheStat(
60
+ "server-source-transform",
61
+ serverSourceTransformCache,
62
+ maxServerSourceTransformCacheEntries,
63
+ serverSourceTransformCacheCounters,
64
+ ),
65
+ ];
66
+ }
67
+
68
+ export async function importAppRouterSourceModule<T>(options: {
69
+ cacheKey?: string | undefined;
70
+ code: string;
71
+ label: string;
72
+ resolveDir?: string | undefined;
73
+ serverSourceTransform?: ServerSourceTransformOptions | undefined;
74
+ sourcefile?: string | undefined;
75
+ vitePlugins?: readonly PluginOption[] | undefined;
76
+ }): Promise<T> {
77
+ if (options.cacheKey !== undefined) {
78
+ const cacheKey = options.cacheKey;
79
+ const cached = readRouterRuntimeCacheEntry(
80
+ sourceModuleCache,
81
+ cacheKey,
82
+ sourceModuleCacheCounters,
83
+ ) as Promise<T> | undefined;
84
+
85
+ if (cached !== undefined) {
86
+ return cached;
87
+ }
88
+
89
+ const loaded = importAppRouterSourceModuleWithoutCache<T>(options).catch((error) => {
90
+ sourceModuleCache.delete(cacheKey);
91
+ throw error;
92
+ });
93
+ setBoundedCacheEntry(
94
+ sourceModuleCache,
95
+ cacheKey,
96
+ loaded,
97
+ maxSourceModuleCacheEntries,
98
+ sourceModuleCacheCounters,
99
+ );
100
+
101
+ return loaded;
102
+ }
103
+
104
+ return importAppRouterSourceModuleWithoutCache(options);
105
+ }
106
+
107
+ async function importAppRouterSourceModuleWithoutCache<T>(options: {
108
+ code: string;
109
+ label: string;
110
+ resolveDir?: string | undefined;
111
+ serverSourceTransform?: ServerSourceTransformOptions | undefined;
112
+ sourcefile?: string | undefined;
113
+ vitePlugins?: readonly PluginOption[] | undefined;
114
+ }): Promise<T> {
115
+ const code =
116
+ options.resolveDir === undefined ? options.code : await bundleAppRouterSourceModule(options);
117
+ const executableCode = withNodeRequireShimForEsmBundle({
118
+ code,
119
+ requireBaseDir:
120
+ options.resolveDir ??
121
+ (options.sourcefile === undefined ? undefined : dirname(options.sourcefile)),
122
+ });
123
+ const encodedLabel = encodeURIComponent(options.label.replace(/[^A-Za-z0-9_$.-]/g, "-"));
124
+ const url = `data:text/javascript;base64,${Buffer.from(executableCode).toString(
125
+ "base64",
126
+ )}#${encodedLabel}-${Date.now()}-${Math.random()}`;
127
+ const result = await runnerImport<T>(url, runnerConfig);
128
+
129
+ return result.module;
130
+ }
131
+
132
+ export async function importAppRouterFileModule<T>(file: string): Promise<T> {
133
+ fileImportVersion += 1;
134
+ const url = `${pathToFileURL(file).href}?t=${Date.now()}${fileImportVersion}`;
135
+ const result = await runnerImport<T>(url, runnerConfig);
136
+
137
+ return result.module;
138
+ }
139
+
140
+ export async function importAppRouterBuiltFileModule<T>(options: {
141
+ cacheKey?: string | undefined;
142
+ file: string;
143
+ }): Promise<T> {
144
+ if (options.cacheKey !== undefined) {
145
+ const cacheKey = options.cacheKey;
146
+ const cached = readRouterRuntimeCacheEntry(
147
+ sourceModuleCache,
148
+ cacheKey,
149
+ sourceModuleCacheCounters,
150
+ ) as Promise<T> | undefined;
151
+
152
+ if (cached !== undefined) {
153
+ return cached;
154
+ }
155
+
156
+ const loaded = import(pathToFileURL(options.file).href).catch((error) => {
157
+ sourceModuleCache.delete(cacheKey);
158
+ throw error;
159
+ }) as Promise<T>;
160
+ setBoundedCacheEntry(
161
+ sourceModuleCache,
162
+ cacheKey,
163
+ loaded,
164
+ maxSourceModuleCacheEntries,
165
+ sourceModuleCacheCounters,
166
+ );
167
+
168
+ return loaded;
169
+ }
170
+
171
+ return (await import(pathToFileURL(options.file).href)) as T;
172
+ }
173
+
174
+ export async function bundleAppRouterSourceModule(options: {
175
+ code: string;
176
+ label: string;
177
+ resolveDir?: string | undefined;
178
+ serverSourceTransform?: ServerSourceTransformOptions | undefined;
179
+ sourcefile?: string | undefined;
180
+ vitePlugins?: readonly PluginOption[] | undefined;
181
+ }): Promise<string> {
182
+ const output = await bundleRouterModule({
183
+ code: options.code,
184
+ filename: options.sourcefile ?? join(options.resolveDir ?? process.cwd(), "module.js"),
185
+ platform: "node",
186
+ vitePlugins: options.vitePlugins,
187
+ plugins: [
188
+ workspacePackageResolutionPlugin(),
189
+ ...(options.serverSourceTransform === undefined
190
+ ? []
191
+ : [serverSourceTransformPlugin(options.serverSourceTransform)]),
192
+ ],
193
+ });
194
+ const code = output.code;
195
+
196
+ if (code === undefined) {
197
+ throw new Error(`Failed to bundle ${options.label} for Vite runner.`);
198
+ }
199
+
200
+ return code;
201
+ }
202
+
203
+ export function fileImportMetaUrlPlugin(): RouterCompatPlugin {
204
+ const workspacePackagesDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
205
+
206
+ return {
207
+ name: "mreact-router-file-import-meta-url",
208
+ setup(buildApi) {
209
+ buildApi.onLoad({ filter: /(?:\.mreact)?\.[cm]?[jt]sx?$/ }, async (args) => {
210
+ if (
211
+ args.path.includes(`${sep}node_modules${sep}`) ||
212
+ args.path.startsWith(`${workspacePackagesDir}${sep}`)
213
+ ) {
214
+ return undefined;
215
+ }
216
+
217
+ const source = await readFile(args.path, "utf8");
218
+
219
+ return {
220
+ contents: withFileImportMetaUrl(source, args.path),
221
+ loader: "js",
222
+ resolveDir: dirname(args.path),
223
+ };
224
+ });
225
+ },
226
+ };
227
+ }
228
+
229
+ interface ServerSourceTransformOptions {
230
+ clientRouteInferenceCache?: ClientRouteInferenceCache | undefined;
231
+ dev: boolean;
232
+ serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
233
+ serverOutput: ServerOutputMode;
234
+ vitePlugins?: readonly PluginOption[] | undefined;
235
+ }
236
+
237
+ function serverSourceTransformPlugin(options: ServerSourceTransformOptions): RouterCompatPlugin {
238
+ const clientRouteInferenceCache =
239
+ options.clientRouteInferenceCache ?? createClientRouteInferenceCache();
240
+ const workspacePackagesDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
241
+
242
+ return {
243
+ name: "mreact-router-server-source-transform",
244
+ setup(buildApi) {
245
+ buildApi.onLoad({ filter: /(?:\.mreact)?\.[cm]?[jt]sx$/ }, async (args) => {
246
+ if (args.path.includes(`${sep}node_modules${sep}`)) {
247
+ return undefined;
248
+ }
249
+
250
+ if (args.path.startsWith(`${workspacePackagesDir}${sep}`)) {
251
+ return undefined;
252
+ }
253
+
254
+ const source = await readFile(args.path, "utf8");
255
+ const contents = await transformServerSourceFile({
256
+ ...options,
257
+ clientRouteInferenceCache,
258
+ filename: args.path,
259
+ source,
260
+ });
261
+
262
+ return {
263
+ contents,
264
+ loader: "js",
265
+ resolveDir: dirname(args.path),
266
+ };
267
+ });
268
+ },
269
+ };
270
+ }
271
+
272
+ async function transformServerSourceFile(
273
+ options: ServerSourceTransformOptions & {
274
+ filename: string;
275
+ source: string;
276
+ },
277
+ ): Promise<string> {
278
+ const sourceHash = hashText(options.source);
279
+ const artifact = options.serverModules?.get(options.filename)?.[options.serverOutput];
280
+
281
+ if (artifact !== undefined && artifact.sourceHash === sourceHash) {
282
+ return artifact.code;
283
+ }
284
+
285
+ const source = options.dev
286
+ ? withFileImportMetaUrl(options.source, options.filename)
287
+ : options.source;
288
+ const transformedSourceHash = source === options.source ? sourceHash : hashText(source);
289
+ const clientRouteInferenceCache =
290
+ options.clientRouteInferenceCache ?? createClientRouteInferenceCache();
291
+ const moduleContext = await compilerModuleContextForSource({
292
+ cache: clientRouteInferenceCache,
293
+ code: source,
294
+ filename: options.filename,
295
+ });
296
+ const clientInference = await inferClientRouteModule({
297
+ cache: clientRouteInferenceCache,
298
+ code: source,
299
+ filename: options.filename,
300
+ moduleContext,
301
+ vitePlugins: options.vitePlugins,
302
+ });
303
+
304
+ for (const diagnostic of clientInference.diagnostics) {
305
+ console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
306
+ }
307
+
308
+ const cacheKey = `${options.serverOutput}\0${options.dev ? "dev" : "prod"}\0${options.filename}\0${transformedSourceHash}\0${clientInference.clientBoundaryImports.join("\0")}\0${vitePluginsCacheKey(options.vitePlugins)}`;
309
+ const cached = readRouterRuntimeCacheEntry(
310
+ serverSourceTransformCache,
311
+ cacheKey,
312
+ serverSourceTransformCacheCounters,
313
+ );
314
+
315
+ if (cached !== undefined) {
316
+ return cached;
317
+ }
318
+
319
+ const output = transformCompilerModuleContext({
320
+ code: source,
321
+ clientBoundaryImports: clientInference.clientBoundaryImports,
322
+ dev: options.dev,
323
+ filename: options.filename,
324
+ moduleContext,
325
+ serverEscape: nativeEscapeTransform,
326
+ serverOutput: options.serverOutput,
327
+ target: "server",
328
+ });
329
+ const fatalDiagnostics = output.diagnostics.filter(
330
+ (diagnostic) => diagnostic.code !== "MR_UNSUPPORTED_SERVER_EVENT_HANDLER",
331
+ );
332
+
333
+ if (fatalDiagnostics.length > 0) {
334
+ throw new Error(
335
+ fatalDiagnostics
336
+ .map((diagnostic) => formatDiagnostic(options.filename, diagnostic))
337
+ .join("\n"),
338
+ );
339
+ }
340
+
341
+ setBoundedCacheEntry(
342
+ serverSourceTransformCache,
343
+ cacheKey,
344
+ output.code,
345
+ maxServerSourceTransformCacheEntries,
346
+ serverSourceTransformCacheCounters,
347
+ );
348
+
349
+ return output.code;
350
+ }
351
+
352
+ function withFileImportMetaUrl(source: string, filename: string): string {
353
+ if (!source.includes("import.meta.url")) {
354
+ return source;
355
+ }
356
+
357
+ return source.replaceAll("import.meta.url", JSON.stringify(pathToFileURL(filename).href));
358
+ }
359
+
360
+ function withNodeRequireShimForEsmBundle(options: {
361
+ code: string;
362
+ requireBaseDir?: string | undefined;
363
+ }): string {
364
+ const requireBaseUrl = pathToFileURL(
365
+ join(options.requireBaseDir ?? process.cwd(), "__mreact_require_shim.cjs"),
366
+ ).href;
367
+ const code = options.code.replaceAll(
368
+ "createRequire(import.meta.url)",
369
+ `createRequire(${JSON.stringify(requireBaseUrl)})`,
370
+ );
371
+
372
+ if (!needsNodeRequireShim(options.code)) {
373
+ return code;
374
+ }
375
+
376
+ return `import { createRequire as __mreactCreateRequire } from "node:module";
377
+ const require = __mreactCreateRequire(${JSON.stringify(requireBaseUrl)});
378
+ ${code}`;
379
+ }
380
+
381
+ function needsNodeRequireShim(code: string): boolean {
382
+ return code.includes("Dynamic require of") && /\b__require\s*=/.test(code);
383
+ }
384
+
385
+ function workspacePackageResolutionPlugin() {
386
+ const currentDir = dirname(fileURLToPath(import.meta.url));
387
+ const packageRoot = dirname(currentDir);
388
+ const sourceOrDist = currentDir.endsWith(`${sep}dist`) ? "dist/index.js" : "src/index.ts";
389
+ const appRouterGlobalsSourceOrDist = currentDir.endsWith(`${sep}dist`)
390
+ ? "dist/app-router-globals.js"
391
+ : "src/app-router-globals.ts";
392
+ const linkSourceOrDist = currentDir.endsWith(`${sep}dist`) ? "dist/link.js" : "src/link.ts";
393
+ const nativeEscapeSourceOrDist = currentDir.endsWith(`${sep}dist`)
394
+ ? "dist/native-escape.js"
395
+ : "src/native-escape.ts";
396
+ const navigationStateSourceOrDist = currentDir.endsWith(`${sep}dist`)
397
+ ? "dist/navigation-state.js"
398
+ : "src/navigation-state.ts";
399
+ const sessionSourceOrDist = currentDir.endsWith(`${sep}dist`)
400
+ ? "dist/session.js"
401
+ : "src/session.ts";
402
+ const streamListSourceOrDist = currentDir.endsWith(`${sep}dist`)
403
+ ? "dist/stream-list.js"
404
+ : "src/stream-list.ts";
405
+ const packageFile = (
406
+ monorepoDir: string,
407
+ packageName: string,
408
+ entry: string,
409
+ specifier: string,
410
+ resolveDir?: string | undefined,
411
+ ): string =>
412
+ resolveWorkspacePackageFile({
413
+ currentFileUrl: import.meta.url,
414
+ entry,
415
+ monorepoDir,
416
+ packageName,
417
+ resolveDir,
418
+ specifier,
419
+ });
420
+ const entries = new Map<string, { entry: string; monorepoDir: string; packageName: string }>([
421
+ ["@reckona/mreact", { entry: "index", monorepoDir: "react", packageName: "@reckona/mreact" }],
422
+ [
423
+ "@reckona/mreact/jsx-dev-runtime",
424
+ { entry: "jsx-dev-runtime", monorepoDir: "react", packageName: "@reckona/mreact" },
425
+ ],
426
+ [
427
+ "@reckona/mreact/jsx-runtime",
428
+ { entry: "jsx-runtime", monorepoDir: "react", packageName: "@reckona/mreact" },
429
+ ],
430
+ [
431
+ "@reckona/mreact-auth",
432
+ { entry: "index", monorepoDir: "auth", packageName: "@reckona/mreact-auth" },
433
+ ],
434
+ [
435
+ "@reckona/mreact-compat",
436
+ { entry: "index", monorepoDir: "react-compat", packageName: "@reckona/mreact-compat" },
437
+ ],
438
+ [
439
+ "@reckona/mreact-compat/event-priority",
440
+ {
441
+ entry: "event-priority",
442
+ monorepoDir: "react-compat",
443
+ packageName: "@reckona/mreact-compat",
444
+ },
445
+ ],
446
+ [
447
+ "@reckona/mreact-compat/flight",
448
+ { entry: "flight", monorepoDir: "react-compat", packageName: "@reckona/mreact-compat" },
449
+ ],
450
+ [
451
+ "@reckona/mreact-compat/internal",
452
+ { entry: "internal", monorepoDir: "react-compat", packageName: "@reckona/mreact-compat" },
453
+ ],
454
+ [
455
+ "@reckona/mreact-compat/jsx-dev-runtime",
456
+ {
457
+ entry: "jsx-dev-runtime",
458
+ monorepoDir: "react-compat",
459
+ packageName: "@reckona/mreact-compat",
460
+ },
461
+ ],
462
+ [
463
+ "@reckona/mreact-compat/jsx-runtime",
464
+ { entry: "jsx-runtime", monorepoDir: "react-compat", packageName: "@reckona/mreact-compat" },
465
+ ],
466
+ [
467
+ "@reckona/mreact-compat/scheduler",
468
+ { entry: "scheduler", monorepoDir: "react-compat", packageName: "@reckona/mreact-compat" },
469
+ ],
470
+ [
471
+ "@reckona/mreact-reactive-core",
472
+ {
473
+ entry: "index",
474
+ monorepoDir: "reactive-core",
475
+ packageName: "@reckona/mreact-reactive-core",
476
+ },
477
+ ],
478
+ [
479
+ "@reckona/mreact-query",
480
+ { entry: "index", monorepoDir: "query", packageName: "@reckona/mreact-query" },
481
+ ],
482
+ [
483
+ "@reckona/mreact-server",
484
+ { entry: "index", monorepoDir: "server", packageName: "@reckona/mreact-server" },
485
+ ],
486
+ ]);
487
+ const routerEntries = new Map([
488
+ ["@reckona/mreact-router", join(packageRoot, sourceOrDist)],
489
+ ["@reckona/mreact-router/app-router-globals", join(packageRoot, appRouterGlobalsSourceOrDist)],
490
+ ["@reckona/mreact-router/link", join(packageRoot, linkSourceOrDist)],
491
+ ["@reckona/mreact-router/native-escape", join(packageRoot, nativeEscapeSourceOrDist)],
492
+ ["@reckona/mreact-router/navigation-state", join(packageRoot, navigationStateSourceOrDist)],
493
+ ["@reckona/mreact-router/session", join(packageRoot, sessionSourceOrDist)],
494
+ ["@reckona/mreact-router/stream-list", join(packageRoot, streamListSourceOrDist)],
495
+ ["@reckona/mreact-router/internal/native-escape", join(packageRoot, nativeEscapeSourceOrDist)],
496
+ ["@reckona/mreact-router/internal/session", join(packageRoot, sessionSourceOrDist)],
497
+ ]);
498
+
499
+ return {
500
+ name: "mreact-router-workspace-packages",
501
+ setup(buildApi: Pick<RouterCompatBuildApi, "onResolve">) {
502
+ buildApi.onResolve(
503
+ {
504
+ filter:
505
+ /^@reckona\/(?:mreact(?:\/(?:jsx-dev-runtime|jsx-runtime))?|mreact-(?:auth|query|reactive-core|server|router|compat)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler|app-router-globals|link|native-escape|navigation-state|session|stream-list|internal\/native-escape|internal\/session))?)$/,
506
+ },
507
+ (args) => {
508
+ const routerPath = routerEntries.get(args.path);
509
+
510
+ if (routerPath !== undefined) {
511
+ return { path: routerPath };
512
+ }
513
+
514
+ const entry = entries.get(args.path);
515
+
516
+ return entry === undefined
517
+ ? undefined
518
+ : {
519
+ path: packageFile(
520
+ entry.monorepoDir,
521
+ entry.packageName,
522
+ entry.entry,
523
+ args.path,
524
+ args.resolveDir,
525
+ ),
526
+ };
527
+ },
528
+ );
529
+ },
530
+ };
531
+ }
532
+
533
+ function setBoundedCacheEntry<K, V>(
534
+ cache: Map<K, V>,
535
+ key: K,
536
+ value: V,
537
+ maxEntries: number,
538
+ counters: RouterRuntimeCacheCounters,
539
+ ): void {
540
+ if (cache.size >= maxEntries) {
541
+ const oldestKey = cache.keys().next().value as K | undefined;
542
+
543
+ if (oldestKey !== undefined) {
544
+ cache.delete(oldestKey);
545
+ counters.evictions += 1;
546
+ }
547
+ }
548
+
549
+ cache.set(key, value);
550
+ }
551
+
552
+ function hashText(text: string): string {
553
+ return createHash("sha256").update(text).digest("hex").slice(0, 16);
554
+ }