@reckona/mreact-router 0.0.87 → 0.0.88

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.
@@ -1,11 +1,19 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync } from "node:fs";
2
3
  import { readFile } from "node:fs/promises";
3
- import { dirname, join, sep } from "node:path";
4
+ import { dirname, extname, isAbsolute, join, sep } from "node:path";
4
5
  import { fileURLToPath, pathToFileURL } from "node:url";
5
6
  import { formatDiagnostic } from "@reckona/mreact-compiler";
6
7
  import type { ServerOutputMode } from "@reckona/mreact-shared/compiler-contract";
7
8
  import { transformCompilerModuleContext } from "@reckona/mreact-compiler/internal";
8
- import { runnerImport, type InlineConfig, type PluginOption } from "vite";
9
+ import {
10
+ createRunnableDevEnvironment,
11
+ mergeConfig,
12
+ resolveConfig,
13
+ type InlineConfig,
14
+ type PluginOption,
15
+ type RunnableDevEnvironment,
16
+ } from "vite";
9
17
  import { resolveWorkspacePackageFile } from "./workspace-packages.js";
10
18
  import {
11
19
  bundleRouterModule,
@@ -47,6 +55,10 @@ const maxServerSourceTransformCacheEntries = resolveRouterCacheLimit(
47
55
  512,
48
56
  );
49
57
  const serverSourceTransformCacheCounters = createRouterRuntimeCacheCounters();
58
+ const packageTypeCache = new Map<string, string | undefined>();
59
+ const runnerVirtualModulePrefix = "virtual:mreact-router-source/";
60
+ const runnerVirtualModules = new Map<string, string>();
61
+ let sharedRunnerEnvironment: Promise<RunnableDevEnvironment> | undefined;
50
62
  let fileImportVersion = 0;
51
63
 
52
64
  export function routerModuleRunnerRuntimeCacheStats(): RouterRuntimeCacheStat[] {
@@ -71,6 +83,7 @@ export async function importAppRouterSourceModule<T>(options: {
71
83
  code: string;
72
84
  externalizeAppSourceModuleDirs?: readonly string[] | undefined;
73
85
  label: string;
86
+ plugins?: readonly RouterCompatPlugin[] | undefined;
74
87
  resolveDir?: string | undefined;
75
88
  serverSourceTransform?: ServerSourceTransformOptions | undefined;
76
89
  sourcefile?: string | undefined;
@@ -110,6 +123,7 @@ async function importAppRouterSourceModuleWithoutCache<T>(options: {
110
123
  code: string;
111
124
  externalizeAppSourceModuleDirs?: readonly string[] | undefined;
112
125
  label: string;
126
+ plugins?: readonly RouterCompatPlugin[] | undefined;
113
127
  resolveDir?: string | undefined;
114
128
  serverSourceTransform?: ServerSourceTransformOptions | undefined;
115
129
  sourcefile?: string | undefined;
@@ -117,30 +131,108 @@ async function importAppRouterSourceModuleWithoutCache<T>(options: {
117
131
  }): Promise<T> {
118
132
  const code =
119
133
  options.resolveDir === undefined ? options.code : await bundleAppRouterSourceModule(options);
134
+ const sourcefile = options.sourcefile ?? join(options.resolveDir ?? process.cwd(), "module.js");
120
135
  const executableCode = withNodeRequireShimForEsmBundle({
121
- code: withFileImportMetaUrl(
122
- code,
123
- options.sourcefile ?? join(options.resolveDir ?? process.cwd(), "module.js"),
124
- ),
136
+ code: withFileImportMetaUrl(code, sourcefile),
137
+ filename: sourcefile,
125
138
  requireBaseDir:
126
139
  options.resolveDir ??
127
140
  (options.sourcefile === undefined ? undefined : dirname(options.sourcefile)),
128
141
  });
129
142
  const encodedLabel = encodeURIComponent(options.label.replace(/[^A-Za-z0-9_$.-]/g, "-"));
130
- const url = `data:text/javascript;base64,${Buffer.from(executableCode).toString(
131
- "base64",
132
- )}#${encodedLabel}-${Date.now()}-${Math.random()}`;
133
- const result = await runnerImport<T>(url, runnerConfig);
143
+ const publicId = `${runnerVirtualModulePrefix}${encodedLabel}-${Date.now()}-${Math.random()}.mjs`;
144
+ runnerVirtualModules.set(publicId, executableCode);
134
145
 
135
- return result.module;
146
+ try {
147
+ return await importWithSharedRunner<T>(publicId, { invalidateEntry: true });
148
+ } finally {
149
+ runnerVirtualModules.delete(publicId);
150
+ }
136
151
  }
137
152
 
138
153
  export async function importAppRouterFileModule<T>(file: string): Promise<T> {
139
154
  fileImportVersion += 1;
140
155
  const url = `${pathToFileURL(file).href}?t=${Date.now()}${fileImportVersion}`;
141
- const result = await runnerImport<T>(url, runnerConfig);
142
156
 
143
- return result.module;
157
+ return await importWithSharedRunner<T>(url, { invalidateEntry: true });
158
+ }
159
+
160
+ async function importWithSharedRunner<T>(
161
+ moduleId: string,
162
+ options?: { invalidateEntry?: boolean | undefined },
163
+ ): Promise<T> {
164
+ const environment = await getSharedRunnerEnvironment();
165
+ const module = (await environment.runner.import(moduleId)) as T;
166
+
167
+ if (options?.invalidateEntry === true) {
168
+ const evaluatedModule =
169
+ environment.runner.evaluatedModules.getModuleByUrl(moduleId) ??
170
+ environment.runner.evaluatedModules.getModuleById(moduleId) ??
171
+ environment.runner.evaluatedModules.getModuleById(`\0${moduleId}`);
172
+
173
+ if (evaluatedModule !== undefined) {
174
+ environment.runner.evaluatedModules.invalidateModule(evaluatedModule);
175
+ }
176
+ }
177
+
178
+ return module;
179
+ }
180
+
181
+ async function getSharedRunnerEnvironment(): Promise<RunnableDevEnvironment> {
182
+ if (sharedRunnerEnvironment !== undefined) {
183
+ return await sharedRunnerEnvironment;
184
+ }
185
+
186
+ sharedRunnerEnvironment = createSharedRunnerEnvironment().catch((error) => {
187
+ sharedRunnerEnvironment = undefined;
188
+ throw error;
189
+ });
190
+
191
+ return await sharedRunnerEnvironment;
192
+ }
193
+
194
+ async function createSharedRunnerEnvironment(): Promise<RunnableDevEnvironment> {
195
+ const config = await resolveConfig(
196
+ mergeConfig(runnerConfig, {
197
+ cacheDir: process.cwd(),
198
+ configFile: false,
199
+ envDir: false,
200
+ environments: {
201
+ mreact_router: {
202
+ consumer: "server",
203
+ dev: { moduleRunnerTransform: true },
204
+ resolve: {
205
+ conditions: ["node"],
206
+ external: true,
207
+ mainFields: [],
208
+ },
209
+ },
210
+ },
211
+ plugins: [
212
+ {
213
+ name: "mreact-router-source-module",
214
+ resolveId(source) {
215
+ return runnerVirtualModules.has(source) ? `\0${source}` : undefined;
216
+ },
217
+ load(id) {
218
+ const source = id.startsWith("\0") ? id.slice(1) : id;
219
+
220
+ return source.startsWith(runnerVirtualModulePrefix)
221
+ ? runnerVirtualModules.get(source)
222
+ : undefined;
223
+ },
224
+ },
225
+ ],
226
+ } satisfies InlineConfig),
227
+ "serve",
228
+ );
229
+ const environment = createRunnableDevEnvironment("mreact_router", config, {
230
+ hot: false,
231
+ runnerOptions: { hmr: { logger: false } },
232
+ });
233
+ await environment.init();
234
+
235
+ return environment;
144
236
  }
145
237
 
146
238
  export async function importAppRouterBuiltFileModule<T>(options: {
@@ -181,6 +273,7 @@ export async function bundleAppRouterSourceModule(options: {
181
273
  code: string;
182
274
  externalizeAppSourceModuleDirs?: readonly string[] | undefined;
183
275
  label: string;
276
+ plugins?: readonly RouterCompatPlugin[] | undefined;
184
277
  resolveDir?: string | undefined;
185
278
  serverSourceTransform?: ServerSourceTransformOptions | undefined;
186
279
  sourcefile?: string | undefined;
@@ -197,6 +290,7 @@ export async function bundleAppRouterSourceModule(options: {
197
290
  ...(options.serverSourceTransform === undefined
198
291
  ? []
199
292
  : [serverSourceTransformPlugin(options.serverSourceTransform)]),
293
+ ...(options.plugins ?? []),
200
294
  ],
201
295
  });
202
296
  const code = output.code;
@@ -367,29 +461,471 @@ function withFileImportMetaUrl(source: string, filename: string): string {
367
461
 
368
462
  function withNodeRequireShimForEsmBundle(options: {
369
463
  code: string;
464
+ filename: string;
370
465
  requireBaseDir?: string | undefined;
371
466
  }): string {
372
467
  const requireBaseUrl = pathToFileURL(
373
468
  join(options.requireBaseDir ?? process.cwd(), "__mreact_require_shim.cjs"),
374
469
  ).href;
375
- const code = options.code.replaceAll(
470
+ const rewritten = rewriteNodeModulesExternalImports(options.code);
471
+ const code = rewritten.code.replaceAll(
376
472
  "createRequire(import.meta.url)",
377
473
  `createRequire(${JSON.stringify(requireBaseUrl)})`,
378
474
  );
475
+ const needsFilenameGlobalShim = needsCommonJsFilenameGlobalShim(options.code);
476
+ const needsRequireShim = needsNodeRequireShim(options.code);
379
477
 
380
- if (!needsNodeRequireShim(options.code)) {
478
+ if (
479
+ !rewritten.needsNativeImport &&
480
+ !rewritten.needsRequire &&
481
+ !needsFilenameGlobalShim &&
482
+ !needsRequireShim
483
+ ) {
381
484
  return code;
382
485
  }
383
486
 
384
- return `import { createRequire as __mreactCreateRequire } from "node:module";
385
- const require = __mreactCreateRequire(${JSON.stringify(requireBaseUrl)});
386
- ${code}`;
487
+ return `${rewritten.needsNativeImport ? 'const __mreactNativeImport = Function("specifier", "return import(specifier)");\n' : ""}${needsFilenameGlobalShim ? `const __filename = ${JSON.stringify(options.filename)};
488
+ const __dirname = ${JSON.stringify(dirname(options.filename))};
489
+ ` : ""}${rewritten.needsRequire || needsRequireShim ? `import { createRequire as __mreactCreateRequire } from "node:module";
490
+ const __mreactRequire = __mreactCreateRequire(${JSON.stringify(requireBaseUrl)});
491
+ const require = __mreactRequire;
492
+ ` : ""}${code}`;
493
+ }
494
+
495
+ function needsCommonJsFilenameGlobalShim(code: string): boolean {
496
+ return /\b__(?:filename|dirname)\b/.test(code);
387
497
  }
388
498
 
389
499
  function needsNodeRequireShim(code: string): boolean {
390
500
  return code.includes("Dynamic require of") && /\b__require\s*=/.test(code);
391
501
  }
392
502
 
503
+ function rewriteNodeModulesExternalImports(code: string): {
504
+ code: string;
505
+ needsNativeImport: boolean;
506
+ needsRequire: boolean;
507
+ } {
508
+ const nativeImportBindings = new Map<string, string>();
509
+ let needsNativeImport = false;
510
+ let needsRequire = false;
511
+ let importIndex = 0;
512
+ const importFromPattern = /^import\s+([^;\n]+?)\s+from\s+(["'])([^"']+)\2;?$/gm;
513
+ const sideEffectImportPattern = /^import\s+(["'])([^"']+)\1;?$/gm;
514
+ const withRewrittenImports = code.replace(
515
+ importFromPattern,
516
+ (statement: string, clause: string, _quote: string, specifier: string) => {
517
+ const file = nodeModulesExternalImportPath(specifier);
518
+
519
+ if (file === undefined || !isNodeImportableModuleFile(file)) {
520
+ return statement;
521
+ }
522
+
523
+ if (isNodeCommonJsModuleFile(file)) {
524
+ needsRequire = true;
525
+ const requireExpression = `__mreactRequire(${JSON.stringify(file)})`;
526
+ return commonJsImportClauseToRequireStatements(
527
+ clause.trim(),
528
+ requireExpression,
529
+ importIndex++,
530
+ );
531
+ }
532
+
533
+ needsNativeImport = true;
534
+ const rewritten = esmImportClauseToNativeImportStatements(clause.trim(), specifier, importIndex++);
535
+ for (const [name, replacement] of rewritten.bindings) {
536
+ nativeImportBindings.set(name, replacement);
537
+ }
538
+
539
+ return rewritten.code;
540
+ },
541
+ );
542
+ const rewrittenCode = withRewrittenImports.replace(
543
+ sideEffectImportPattern,
544
+ (statement: string, _quote: string, specifier: string) => {
545
+ const file = nodeModulesExternalImportPath(specifier);
546
+
547
+ if (file === undefined || !isNodeImportableModuleFile(file)) {
548
+ return statement;
549
+ }
550
+
551
+ if (isNodeCommonJsModuleFile(file)) {
552
+ needsRequire = true;
553
+ return `__mreactRequire(${JSON.stringify(file)});`;
554
+ }
555
+
556
+ needsNativeImport = true;
557
+ return `await __mreactNativeImport(${JSON.stringify(specifier)});`;
558
+ },
559
+ );
560
+
561
+ return {
562
+ code:
563
+ nativeImportBindings.size === 0
564
+ ? rewrittenCode
565
+ : replaceImportedIdentifiers(rewrittenCode, nativeImportBindings),
566
+ needsNativeImport,
567
+ needsRequire,
568
+ };
569
+ }
570
+
571
+ function nodeModulesExternalImportPath(specifier: string): string | undefined {
572
+ const file = externalImportFilePath(specifier);
573
+
574
+ if (file === undefined || !isNodeModulesPath(file)) {
575
+ return undefined;
576
+ }
577
+
578
+ return file;
579
+ }
580
+
581
+ function externalImportFilePath(specifier: string): string | undefined {
582
+ if (specifier.startsWith("file://")) {
583
+ try {
584
+ return fileURLToPath(specifier);
585
+ } catch {
586
+ return undefined;
587
+ }
588
+ }
589
+
590
+ return isAbsolute(specifier) ? specifier : undefined;
591
+ }
592
+
593
+ function isNodeModulesPath(file: string): boolean {
594
+ return file.split(sep).includes("node_modules");
595
+ }
596
+
597
+ function isNodeImportableModuleFile(file: string): boolean {
598
+ const extension = extname(file);
599
+
600
+ return extension === ".cjs" || extension === ".mjs" || extension === ".js" || extension === ".node";
601
+ }
602
+
603
+ function isNodeCommonJsModuleFile(file: string): boolean {
604
+ const extension = extname(file);
605
+
606
+ if (extension === ".cjs" || extension === ".cts" || extension === ".node") {
607
+ return true;
608
+ }
609
+
610
+ if (extension === ".mjs" || extension === ".mts" || extension !== ".js") {
611
+ return false;
612
+ }
613
+
614
+ return nearestPackageType(file) !== "module";
615
+ }
616
+
617
+ function nearestPackageType(file: string): string | undefined {
618
+ let directory = dirname(file);
619
+
620
+ while (true) {
621
+ const packageJson = join(directory, "package.json");
622
+
623
+ if (packageTypeCache.has(packageJson)) {
624
+ return packageTypeCache.get(packageJson);
625
+ }
626
+
627
+ if (existsSync(packageJson)) {
628
+ const type = readPackageType(packageJson);
629
+ packageTypeCache.set(packageJson, type);
630
+ return type;
631
+ }
632
+
633
+ const parent = dirname(directory);
634
+
635
+ if (parent === directory) {
636
+ return undefined;
637
+ }
638
+
639
+ directory = parent;
640
+ }
641
+ }
642
+
643
+ function readPackageType(packageJson: string): string | undefined {
644
+ try {
645
+ const parsed = JSON.parse(readFileSync(packageJson, "utf8")) as { type?: unknown };
646
+
647
+ return typeof parsed.type === "string" ? parsed.type : undefined;
648
+ } catch {
649
+ return undefined;
650
+ }
651
+ }
652
+
653
+ function commonJsImportClauseToRequireStatements(
654
+ clause: string,
655
+ requireExpression: string,
656
+ importIndex: number,
657
+ ): string {
658
+ if (clause.startsWith("* as ")) {
659
+ return `const ${clause.slice(5).trim()} = ${requireExpression};`;
660
+ }
661
+
662
+ if (clause.startsWith("{")) {
663
+ return namedCommonJsImportsToRequireStatements(clause, requireExpression);
664
+ }
665
+
666
+ const commaIndex = clause.indexOf(",");
667
+
668
+ if (commaIndex === -1) {
669
+ return `const ${clause} = ${requireExpression};`;
670
+ }
671
+
672
+ const defaultName = clause.slice(0, commaIndex).trim();
673
+ const namedOrNamespace = clause.slice(commaIndex + 1).trim();
674
+ const temporaryName = `__mreactExternalCommonJs${importIndex}`;
675
+ const statements = [`const ${temporaryName} = ${requireExpression};`, `const ${defaultName} = ${temporaryName};`];
676
+
677
+ if (namedOrNamespace.startsWith("* as ")) {
678
+ statements.push(`const ${namedOrNamespace.slice(5).trim()} = ${temporaryName};`);
679
+ } else if (namedOrNamespace.startsWith("{")) {
680
+ statements.push(namedCommonJsImportsToRequireStatements(namedOrNamespace, temporaryName));
681
+ }
682
+
683
+ return statements.join("\n");
684
+ }
685
+
686
+ function esmImportClauseToNativeImportStatements(
687
+ clause: string,
688
+ specifier: string,
689
+ importIndex: number,
690
+ ): { bindings: Map<string, string>; code: string } {
691
+ const bindings = new Map<string, string>();
692
+ const temporaryName = `__mreactExternalModule${importIndex}`;
693
+ const statements = [
694
+ `const ${temporaryName} = await __mreactNativeImport(${JSON.stringify(specifier)});`,
695
+ ];
696
+
697
+ if (clause.startsWith("* as ")) {
698
+ statements.push(`const ${clause.slice(5).trim()} = ${temporaryName};`);
699
+ } else if (clause.startsWith("{")) {
700
+ collectNamedEsmImportBindings(clause, temporaryName, bindings);
701
+ } else {
702
+ const commaIndex = clause.indexOf(",");
703
+
704
+ if (commaIndex === -1) {
705
+ bindings.set(clause, `${temporaryName}.default`);
706
+ } else {
707
+ const defaultName = clause.slice(0, commaIndex).trim();
708
+ const namedOrNamespace = clause.slice(commaIndex + 1).trim();
709
+ bindings.set(defaultName, `${temporaryName}.default`);
710
+
711
+ if (namedOrNamespace.startsWith("* as ")) {
712
+ statements.push(`const ${namedOrNamespace.slice(5).trim()} = ${temporaryName};`);
713
+ } else if (namedOrNamespace.startsWith("{")) {
714
+ collectNamedEsmImportBindings(namedOrNamespace, temporaryName, bindings);
715
+ }
716
+ }
717
+ }
718
+
719
+ return { bindings, code: statements.join("\n") };
720
+ }
721
+
722
+ function collectNamedEsmImportBindings(
723
+ clause: string,
724
+ moduleName: string,
725
+ bindings: Map<string, string>,
726
+ ): void {
727
+ for (const binding of namedImportBindings(clause)) {
728
+ const replacement =
729
+ binding.imported === "default"
730
+ ? `${moduleName}.default`
731
+ : `${moduleName}[${JSON.stringify(binding.imported)}]`;
732
+ bindings.set(binding.local, replacement);
733
+ }
734
+ }
735
+
736
+ function namedCommonJsImportsToRequireStatements(
737
+ clause: string,
738
+ sourceExpression: string,
739
+ ): string {
740
+ const objectBindings: string[] = [];
741
+ const statements: string[] = [];
742
+
743
+ for (const binding of namedImportBindings(clause)) {
744
+ if (binding.imported === "default") {
745
+ statements.push(`const ${binding.local} = ${sourceExpression};`);
746
+ } else if (binding.imported === binding.local) {
747
+ objectBindings.push(binding.imported);
748
+ } else {
749
+ objectBindings.push(`${JSON.stringify(binding.imported)}: ${binding.local}`);
750
+ }
751
+ }
752
+
753
+ if (objectBindings.length > 0) {
754
+ statements.unshift(`const { ${objectBindings.join(", ")} } = ${sourceExpression};`);
755
+ }
756
+
757
+ return statements.join("\n");
758
+ }
759
+
760
+ function namedImportBindings(clause: string): Array<{ imported: string; local: string }> {
761
+ return clause
762
+ .slice(1, -1)
763
+ .split(",")
764
+ .map((binding) => binding.trim())
765
+ .filter((binding) => binding.length > 0)
766
+ .map((binding) => {
767
+ const alias = binding.match(/^(.+?)\s+as\s+(.+)$/);
768
+ const imported = alias?.[1]?.trim() ?? binding;
769
+ const local = alias?.[2]?.trim() ?? binding;
770
+
771
+ return { imported, local };
772
+ });
773
+ }
774
+
775
+ function replaceImportedIdentifiers(code: string, bindings: ReadonlyMap<string, string>): string {
776
+ let rewritten = "";
777
+ let index = 0;
778
+
779
+ while (index < code.length) {
780
+ const char = code[index];
781
+ const next = code[index + 1];
782
+
783
+ if (char === '"' || char === "'") {
784
+ const end = quotedStringEnd(code, index, char);
785
+ rewritten += code.slice(index, end);
786
+ index = end;
787
+ continue;
788
+ }
789
+
790
+ if (char === "`") {
791
+ const end = templateLiteralEnd(code, index);
792
+ rewritten += code.slice(index, end);
793
+ index = end;
794
+ continue;
795
+ }
796
+
797
+ if (char === "/" && next === "/") {
798
+ const end = lineCommentEnd(code, index);
799
+ rewritten += code.slice(index, end);
800
+ index = end;
801
+ continue;
802
+ }
803
+
804
+ if (char === "/" && next === "*") {
805
+ const end = blockCommentEnd(code, index);
806
+ rewritten += code.slice(index, end);
807
+ index = end;
808
+ continue;
809
+ }
810
+
811
+ if (isIdentifierStart(char)) {
812
+ const end = identifierEnd(code, index);
813
+ const identifier = code.slice(index, end);
814
+ const replacement = bindings.get(identifier);
815
+
816
+ if (replacement !== undefined && shouldReplaceImportedIdentifier(code, index, end)) {
817
+ rewritten += importedIdentifierReplacement(code, index, end, identifier, replacement);
818
+ } else {
819
+ rewritten += identifier;
820
+ }
821
+
822
+ index = end;
823
+ continue;
824
+ }
825
+
826
+ rewritten += char;
827
+ index += 1;
828
+ }
829
+
830
+ return rewritten;
831
+ }
832
+
833
+ function shouldReplaceImportedIdentifier(code: string, start: number, end: number): boolean {
834
+ const previous = previousNonWhitespace(code, start);
835
+ const next = nextNonWhitespace(code, end);
836
+
837
+ return previous !== "." && previous !== "?" && next !== ":";
838
+ }
839
+
840
+ function importedIdentifierReplacement(
841
+ code: string,
842
+ start: number,
843
+ end: number,
844
+ identifier: string,
845
+ replacement: string,
846
+ ): string {
847
+ const previous = previousNonWhitespace(code, start);
848
+ const next = nextNonWhitespace(code, end);
849
+
850
+ return (previous === "{" || previous === ",") && (next === "," || next === "}")
851
+ ? `${identifier}: ${replacement}`
852
+ : replacement;
853
+ }
854
+
855
+ function previousNonWhitespace(code: string, index: number): string | undefined {
856
+ for (let position = index - 1; position >= 0; position -= 1) {
857
+ if (!/\s/u.test(code[position] ?? "")) {
858
+ return code[position];
859
+ }
860
+ }
861
+
862
+ return undefined;
863
+ }
864
+
865
+ function nextNonWhitespace(code: string, index: number): string | undefined {
866
+ for (let position = index; position < code.length; position += 1) {
867
+ if (!/\s/u.test(code[position] ?? "")) {
868
+ return code[position];
869
+ }
870
+ }
871
+
872
+ return undefined;
873
+ }
874
+
875
+ function quotedStringEnd(code: string, start: number, quote: string): number {
876
+ for (let index = start + 1; index < code.length; index += 1) {
877
+ if (code[index] === "\\") {
878
+ index += 1;
879
+ } else if (code[index] === quote) {
880
+ return index + 1;
881
+ }
882
+ }
883
+
884
+ return code.length;
885
+ }
886
+
887
+ function templateLiteralEnd(code: string, start: number): number {
888
+ for (let index = start + 1; index < code.length; index += 1) {
889
+ if (code[index] === "\\") {
890
+ index += 1;
891
+ } else if (code[index] === "`") {
892
+ return index + 1;
893
+ }
894
+ }
895
+
896
+ return code.length;
897
+ }
898
+
899
+ function lineCommentEnd(code: string, start: number): number {
900
+ const end = code.indexOf("\n", start + 2);
901
+
902
+ return end === -1 ? code.length : end;
903
+ }
904
+
905
+ function blockCommentEnd(code: string, start: number): number {
906
+ const end = code.indexOf("*/", start + 2);
907
+
908
+ return end === -1 ? code.length : end + 2;
909
+ }
910
+
911
+ function identifierEnd(code: string, start: number): number {
912
+ let end = start + 1;
913
+
914
+ while (end < code.length && isIdentifierPart(code[end] ?? "")) {
915
+ end += 1;
916
+ }
917
+
918
+ return end;
919
+ }
920
+
921
+ function isIdentifierStart(char: string | undefined): boolean {
922
+ return char !== undefined && /[A-Za-z_$]/u.test(char);
923
+ }
924
+
925
+ function isIdentifierPart(char: string): boolean {
926
+ return /[A-Za-z0-9_$]/u.test(char);
927
+ }
928
+
393
929
  function workspacePackageResolutionPlugin() {
394
930
  const currentDir = dirname(fileURLToPath(import.meta.url));
395
931
  const packageRoot = dirname(currentDir);