@oh-my-pi/pi-coding-agent 17.0.8 → 17.0.9
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/CHANGELOG.md +33 -1
- package/dist/cli.js +4850 -4774
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +27 -0
- package/dist/types/config/settings-schema.d.ts +34 -3
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +1 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/settings-defs.d.ts +1 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
- package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +8 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +13 -12
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +1 -1
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +40 -0
- package/src/config/model-resolver.ts +0 -1
- package/src/config/settings-schema.ts +41 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +851 -310
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/lsp/client.ts +23 -0
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +25 -7
- package/src/main.ts +1 -0
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/render.ts +94 -35
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/modes/components/agent-hub.ts +26 -9
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/settings-defs.ts +5 -2
- package/src/modes/components/settings-selector.ts +9 -5
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/controllers/command-controller.ts +14 -7
- package/src/modes/controllers/mcp-command-controller.ts +70 -40
- package/src/modes/interactive-mode.ts +3 -0
- package/src/modes/rpc/rpc-client.ts +94 -3
- package/src/modes/rpc/rpc-frame.ts +164 -4
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +79 -7
- package/src/modes/rpc/rpc-types.ts +34 -2
- package/src/modes/setup-wizard/scenes/model.ts +5 -7
- package/src/modes/setup-wizard/scenes/providers.ts +3 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
- package/src/modes/setup-wizard/scenes/theme.ts +13 -3
- package/src/modes/setup-wizard/scenes/types.ts +9 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +6 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/tools/task.md +12 -2
- package/src/sdk.ts +45 -3
- package/src/session/agent-session.ts +63 -32
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +41 -2
- package/src/session/session-loader.ts +10 -5
- package/src/slash-commands/helpers/usage-report.ts +3 -1
- package/src/task/executor.ts +3 -0
- package/src/task/index.ts +52 -8
- package/src/task/structured-subagent.ts +3 -1
- package/src/task/types.ts +16 -0
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/index.ts +3 -0
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/types.ts +5 -1
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
/// <reference path="./legacy-pi-virtual-modules.d.ts" />
|
|
2
|
+
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import { createRequire, isBuiltin } from "node:module";
|
|
4
5
|
import * as path from "node:path";
|
|
5
6
|
import * as url from "node:url";
|
|
7
|
+
import type { ParseResult, ParserPlugin } from "@babel/parser";
|
|
8
|
+
import { parse as parseBabel } from "@babel/parser";
|
|
9
|
+
import * as traverseModule from "@babel/traverse";
|
|
6
10
|
import { isCompiledBinary, stripWindowsExtendedLengthPathPrefix } from "@oh-my-pi/pi-utils";
|
|
7
11
|
import { registerPluginCacheInvalidator } from "../../discovery/helpers";
|
|
8
12
|
|
|
9
13
|
const IS_COMPILED_BINARY = isCompiledBinary();
|
|
10
14
|
|
|
15
|
+
function isBabelTraverse(value: unknown): value is typeof traverseModule.default {
|
|
16
|
+
return typeof value === "function";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Bun's compiled CJS interop wraps Babel traverse's default one level deeper.
|
|
20
|
+
const traverseDefault: unknown = traverseModule.default;
|
|
21
|
+
const nestedTraverse =
|
|
22
|
+
traverseDefault !== null && typeof traverseDefault === "object" && "default" in traverseDefault
|
|
23
|
+
? traverseDefault.default
|
|
24
|
+
: undefined;
|
|
25
|
+
const traverseCandidate = isBabelTraverse(traverseDefault) ? traverseDefault : nestedTraverse;
|
|
26
|
+
if (!isBabelTraverse(traverseCandidate)) {
|
|
27
|
+
throw new TypeError(
|
|
28
|
+
`Invalid @babel/traverse export: expected function, got default=${typeof traverseDefault}, nested=${typeof nestedTraverse}`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const traverseAst = traverseCandidate;
|
|
32
|
+
|
|
11
33
|
// === Bundled host modules (issue #3423) ===
|
|
12
34
|
//
|
|
13
35
|
// Bun 1.3.14 stopped exposing `--compile` extras through any filesystem-style
|
|
@@ -42,6 +64,104 @@ interface BundledVirtualResolveResult {
|
|
|
42
64
|
namespace: typeof BUNDLED_VIRTUAL_NAMESPACE;
|
|
43
65
|
}
|
|
44
66
|
|
|
67
|
+
interface ExtensionSpecifierReference {
|
|
68
|
+
readonly kind: "import" | "require";
|
|
69
|
+
readonly specifier: string;
|
|
70
|
+
readonly start: number;
|
|
71
|
+
readonly end: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseExtensionSource(source: string, importerPath: string): ParseResult {
|
|
75
|
+
const extension = path.extname(importerPath).toLowerCase();
|
|
76
|
+
const plugins: ParserPlugin[] = ["decorators-legacy", "explicitResourceManagement"];
|
|
77
|
+
if (extension === ".ts" || extension === ".mts" || extension === ".cts" || extension === ".tsx") {
|
|
78
|
+
plugins.push("typescript");
|
|
79
|
+
}
|
|
80
|
+
if (extension === ".jsx" || extension === ".tsx") {
|
|
81
|
+
plugins.push("jsx");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return parseBabel(source, {
|
|
86
|
+
sourceType: "unambiguous",
|
|
87
|
+
allowAwaitOutsideFunction: true,
|
|
88
|
+
allowReturnOutsideFunction: true,
|
|
89
|
+
allowImportExportEverywhere: true,
|
|
90
|
+
allowNewTargetOutsideFunction: true,
|
|
91
|
+
allowSuperOutsideMethod: true,
|
|
92
|
+
allowUndeclaredExports: true,
|
|
93
|
+
errorRecovery: true,
|
|
94
|
+
plugins,
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Failed to parse extension source for dependency rewriting: ${importerPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
99
|
+
{ cause: error },
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function collectExtensionSpecifierReferences(
|
|
105
|
+
source: string,
|
|
106
|
+
importerPath: string,
|
|
107
|
+
ast: ParseResult = parseExtensionSource(source, importerPath),
|
|
108
|
+
): ExtensionSpecifierReference[] {
|
|
109
|
+
const references: ExtensionSpecifierReference[] = [];
|
|
110
|
+
const record = (kind: ExtensionSpecifierReference["kind"], literal: unknown): void => {
|
|
111
|
+
if (!literal || typeof literal !== "object") return;
|
|
112
|
+
const node = literal as { type?: string; value?: unknown; start?: number | null; end?: number | null };
|
|
113
|
+
if (
|
|
114
|
+
node.type === "StringLiteral" &&
|
|
115
|
+
typeof node.value === "string" &&
|
|
116
|
+
typeof node.start === "number" &&
|
|
117
|
+
typeof node.end === "number"
|
|
118
|
+
) {
|
|
119
|
+
references.push({ kind, specifier: node.value, start: node.start, end: node.end });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
traverseAst(ast, {
|
|
123
|
+
enter(nodePath) {
|
|
124
|
+
const node = nodePath.node;
|
|
125
|
+
if (
|
|
126
|
+
node.type === "ImportDeclaration" ||
|
|
127
|
+
node.type === "ExportNamedDeclaration" ||
|
|
128
|
+
node.type === "ExportAllDeclaration"
|
|
129
|
+
) {
|
|
130
|
+
record("import", node.source);
|
|
131
|
+
} else if (node.type === "ImportExpression") {
|
|
132
|
+
record("import", node.source);
|
|
133
|
+
} else if (
|
|
134
|
+
node.type === "TSImportEqualsDeclaration" &&
|
|
135
|
+
node.moduleReference.type === "TSExternalModuleReference"
|
|
136
|
+
) {
|
|
137
|
+
record("require", node.moduleReference.expression);
|
|
138
|
+
} else if (node.type === "CallExpression") {
|
|
139
|
+
if (node.callee.type === "Import") {
|
|
140
|
+
record("import", node.arguments[0]);
|
|
141
|
+
} else if (
|
|
142
|
+
node.callee.type === "Identifier" &&
|
|
143
|
+
node.callee.name === "require" &&
|
|
144
|
+
!nodePath.scope.hasBinding("require", true)
|
|
145
|
+
) {
|
|
146
|
+
record("require", node.arguments[0]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
return references;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function applySpecifierReplacements(
|
|
155
|
+
source: string,
|
|
156
|
+
replacements: ReadonlyArray<ExtensionSpecifierReference & { readonly replacement: string }>,
|
|
157
|
+
): string {
|
|
158
|
+
let rewritten = source;
|
|
159
|
+
for (const reference of [...replacements].sort((left, right) => right.start - left.start)) {
|
|
160
|
+
rewritten = `${rewritten.slice(0, reference.start)}${JSON.stringify(reference.replacement)}${rewritten.slice(reference.end)}`;
|
|
161
|
+
}
|
|
162
|
+
return rewritten;
|
|
163
|
+
}
|
|
164
|
+
|
|
45
165
|
const loadedBundledModules: Record<string, BundledModule> = {};
|
|
46
166
|
let bundledModuleLoadersPromise: Promise<BundledModuleLoaders> | null = null;
|
|
47
167
|
|
|
@@ -180,6 +300,7 @@ const PI_PACKAGE_ALTERNATION = PI_PACKAGE_NAMES.join("|");
|
|
|
180
300
|
const PI_SUBPATH_REMAPS: ReadonlyMap<string, string> = new Map<string, string>([
|
|
181
301
|
["pi-ai/utils/oauth", "pi-ai/oauth"],
|
|
182
302
|
["pi-ai/utils/oauth/", "pi-ai/oauth/"],
|
|
303
|
+
["pi-ai/compat", "pi-ai"],
|
|
183
304
|
]);
|
|
184
305
|
|
|
185
306
|
function remapLegacyPiSubpath(rest: string): string {
|
|
@@ -198,18 +319,16 @@ function remapLegacyPiSubpath(rest: string): string {
|
|
|
198
319
|
}
|
|
199
320
|
|
|
200
321
|
const LEGACY_PI_SPECIFIER_FILTER = new RegExp(`^@(?:${PI_SCOPE_ALTERNATION})/(?:${PI_PACKAGE_ALTERNATION})(?:/.*)?$`);
|
|
201
|
-
const LEGACY_PI_IMPORT_SPECIFIER_REGEX = new RegExp(
|
|
202
|
-
`((?:from\\s+|import\\s+|import\\s*\\(\\s*)["'])(@(?:${PI_SCOPE_ALTERNATION})/(?:${PI_PACKAGE_ALTERNATION})(?:/[^"'()\\s]+)?)(["'])`,
|
|
203
|
-
"g",
|
|
204
|
-
);
|
|
205
322
|
const resolvedSpecifierFallbacks = new Map<string, string>();
|
|
206
323
|
const SOURCE_MODULE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"] as const;
|
|
207
324
|
const SUPPORTED_PACKAGE_IMPORT_CONDITIONS = new Set(["bun", "node", "import", "default"]);
|
|
325
|
+
const SUPPORTED_PACKAGE_REQUIRE_CONDITIONS = new Set(["bun", "node", "require", "default"]);
|
|
208
326
|
const packageRootCache = new Map<string, string | null>();
|
|
209
327
|
const packageImportsCache = new Map<string, Record<string, unknown> | null>();
|
|
210
328
|
const nodePackageRootCache = new Map<string, Promise<string | null>>();
|
|
211
329
|
const packageManifestCache = new Map<string, Promise<Record<string, unknown> | null>>();
|
|
212
330
|
const bareDependencyResolutionCache = new Map<string, Promise<string | null>>();
|
|
331
|
+
const bareRequireResolutionCache = new Map<string, Promise<string | null>>();
|
|
213
332
|
const realpathCache = new Map<string, Promise<string>>();
|
|
214
333
|
const nativeAddonResolutionCache = new Map<string, Promise<string | null>>();
|
|
215
334
|
const nativeAddonRequireScanCache = new Map<string, Promise<boolean>>();
|
|
@@ -222,6 +341,7 @@ function clearLegacyPiResolutionCaches(): void {
|
|
|
222
341
|
nodePackageRootCache.clear();
|
|
223
342
|
packageManifestCache.clear();
|
|
224
343
|
bareDependencyResolutionCache.clear();
|
|
344
|
+
bareRequireResolutionCache.clear();
|
|
225
345
|
nativeAddonResolutionCache.clear();
|
|
226
346
|
nativeAddonRequireScanCache.clear();
|
|
227
347
|
nativeAddonLoaderModulePaths.clear();
|
|
@@ -338,14 +458,21 @@ const LEGACY_PI_CODING_AGENT_SHIM_PATH = IS_COMPILED_BINARY
|
|
|
338
458
|
? bundledModuleVirtualSpecifier(`${CANONICAL_PI_SCOPE}/pi-coding-agent`)
|
|
339
459
|
: sourceShimPath("legacy-pi-coding-agent-shim.ts");
|
|
340
460
|
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
//
|
|
461
|
+
// Legacy pi-tui exported `decodeKittyPrintable` from its package root. The
|
|
462
|
+
// canonical TUI replaced it with the broader `decodePrintableKey`; route only
|
|
463
|
+
// legacy root imports through a sibling shim that preserves the old name.
|
|
464
|
+
const LEGACY_PI_TUI_SHIM_PATH = IS_COMPILED_BINARY
|
|
465
|
+
? bundledModuleVirtualSpecifier(`${CANONICAL_PI_SCOPE}/pi-tui`)
|
|
466
|
+
: sourceShimPath("legacy-pi-tui-shim.ts");
|
|
467
|
+
|
|
468
|
+
// Package-root overrides. Shim entries (`pi-ai`, `pi-coding-agent`, `pi-tui`)
|
|
469
|
+
// always replace the canonical surface so legacy helpers stay reachable. The
|
|
470
|
+
// other bundled host packages (`pi-agent-core`, `pi-natives`, `pi-utils`) are
|
|
471
|
+
// added only in compiled-binary mode to route extensions onto the in-process
|
|
472
|
+
// module instance — in dev / source-link / installed-package mode the canonical
|
|
473
|
+
// specifier resolves cleanly through `Bun.resolveSync` and hardcoding a
|
|
474
|
+
// source-tree path would miss installs where bundled packages live at
|
|
475
|
+
// `node_modules/@oh-my-pi/pi-*`.
|
|
349
476
|
//
|
|
350
477
|
// Compiled-binary entries are `omp-legacy-pi-bundled:<key>` specifiers handed
|
|
351
478
|
// to the synthetic onLoad in `installLegacyPiSpecifierShim()` — bunfs paths
|
|
@@ -390,6 +517,7 @@ export function __buildLegacyPiPackageRootOverrides(
|
|
|
390
517
|
const candidates: Record<string, string> = {
|
|
391
518
|
[`${CANONICAL_PI_SCOPE}/pi-ai`]: LEGACY_PI_AI_SHIM_PATH,
|
|
392
519
|
[`${CANONICAL_PI_SCOPE}/pi-coding-agent`]: LEGACY_PI_CODING_AGENT_SHIM_PATH,
|
|
520
|
+
[`${CANONICAL_PI_SCOPE}/pi-tui`]: LEGACY_PI_TUI_SHIM_PATH,
|
|
393
521
|
};
|
|
394
522
|
if (isCompiled) {
|
|
395
523
|
for (const key of bundledModuleKeys) {
|
|
@@ -473,33 +601,6 @@ function toImportSpecifier(resolvedPath: string): string {
|
|
|
473
601
|
return url.pathToFileURL(stripWindowsExtendedLengthPathPrefix(resolvedPath)).href;
|
|
474
602
|
}
|
|
475
603
|
|
|
476
|
-
function rewriteLegacyPiImports(source: string): string {
|
|
477
|
-
return source.replace(
|
|
478
|
-
LEGACY_PI_IMPORT_SPECIFIER_REGEX,
|
|
479
|
-
(match, prefix: string, specifier: string, suffix: string) => {
|
|
480
|
-
const remappedSpecifier = remapLegacyPiSpecifier(specifier);
|
|
481
|
-
if (!remappedSpecifier) {
|
|
482
|
-
return match;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
try {
|
|
486
|
-
return `${prefix}${toImportSpecifier(resolveCanonicalPiSpecifier(remappedSpecifier))}${suffix}`;
|
|
487
|
-
} catch {
|
|
488
|
-
// Resolution failed — typically in compiled binary mode where
|
|
489
|
-
// Bun.resolveSync cannot walk up from /$bunfs/root to find the
|
|
490
|
-
// bundled node_modules. Leave the specifier unchanged so Bun
|
|
491
|
-
// resolves it natively against the extension's own peer deps.
|
|
492
|
-
return match;
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Match the bare TypeBox import specifiers (static + dynamic). Subpath imports
|
|
499
|
-
// like `@sinclair/typebox/compiler` are intentionally excluded — they expose
|
|
500
|
-
// TypeBox-only APIs the Zod-backed shim does not provide.
|
|
501
|
-
const TYPEBOX_IMPORT_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])(@sinclair\/typebox|typebox)(["'])/g;
|
|
502
|
-
|
|
503
604
|
/**
|
|
504
605
|
* Rewrite the extension-owned specifiers OMP must host-resolve — legacy
|
|
505
606
|
* `@(scope)/pi-*`, bare TypeBox packages, package `imports` aliases like
|
|
@@ -508,43 +609,61 @@ const TYPEBOX_IMPORT_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'
|
|
|
508
609
|
* are left untouched so Bun resolves them from the extension's real on-disk
|
|
509
610
|
* location.
|
|
510
611
|
*
|
|
511
|
-
* When `mtimeTag` is provided, extension-owned graph specifiers
|
|
512
|
-
*
|
|
513
|
-
* `?mtime=<tag>` cache-bust so Bun rekeys them on
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
612
|
+
* When `mtimeTag` is provided, extension-owned relative graph specifiers
|
|
613
|
+
* (`./`/`../`) and, by default, resolved package `#alias/*` and extension-local
|
|
614
|
+
* bare deps also carry a `?mtime=<tag>` cache-bust so Bun rekeys them on
|
|
615
|
+
* same-process reloads. `resolvedImportMtimeTag` can disable the tag for
|
|
616
|
+
* resolved package and bare ESM imports inside third-party dependencies, whose
|
|
617
|
+
* transitive ESM imports retain a query-free importer path for Bun's runtime
|
|
618
|
+
* `node_modules` resolution. Host package rewrites (legacy
|
|
619
|
+
* `@(scope)/pi-*`, TypeBox shim) always emit `file://` URLs because they resolve
|
|
620
|
+
* to in-process host code that never changes between reloads.
|
|
517
621
|
*/
|
|
518
622
|
async function rewriteLegacyExtensionSource(
|
|
519
623
|
source: string,
|
|
520
624
|
importerPath: string,
|
|
521
625
|
mtimeTag: string | null = null,
|
|
626
|
+
resolvedImportMtimeTag: string | null = mtimeTag,
|
|
522
627
|
): Promise<string> {
|
|
523
628
|
// Compiled mode completes the override map from the build-supplied module
|
|
524
629
|
// keys on first use; every rewrite path must see the full map.
|
|
525
630
|
await ensureLegacyPiOverridesReady();
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
631
|
+
const references = collectExtensionSpecifierReferences(source, importerPath);
|
|
632
|
+
const replacements: Array<ExtensionSpecifierReference & { replacement: string }> = [];
|
|
633
|
+
for (const reference of references) {
|
|
634
|
+
if (reference.kind !== "import") continue;
|
|
635
|
+
|
|
636
|
+
const specifier = reference.specifier;
|
|
637
|
+
let replacement: string | null = null;
|
|
638
|
+
const remappedSpecifier = remapLegacyPiSpecifier(specifier);
|
|
639
|
+
if (remappedSpecifier) {
|
|
640
|
+
try {
|
|
641
|
+
replacement = toImportSpecifier(resolveCanonicalPiSpecifier(remappedSpecifier));
|
|
642
|
+
} catch {
|
|
643
|
+
// Compiled fallback may be absent from a malformed build. Continue to
|
|
644
|
+
// the extension's on-disk peer dependency resolution below.
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (!replacement && TYPEBOX_SHIM_PATH && (specifier === "typebox" || specifier === "@sinclair/typebox")) {
|
|
648
|
+
replacement = toImportSpecifier(TYPEBOX_SHIM_PATH);
|
|
649
|
+
}
|
|
650
|
+
if (!replacement && specifier.startsWith("#")) {
|
|
651
|
+
const resolved = await resolvePackageImportSpecifier(specifier, importerPath);
|
|
652
|
+
if (resolved) replacement = toGraphImportSpecifier(resolved, resolvedImportMtimeTag);
|
|
653
|
+
}
|
|
654
|
+
if (!replacement && isBareExtensionDependencySpecifier(specifier)) {
|
|
655
|
+
const resolved = await resolveExtensionBareDependency(specifier, importerPath);
|
|
656
|
+
if (resolved) replacement = toGraphImportSpecifier(resolved, resolvedImportMtimeTag);
|
|
657
|
+
}
|
|
658
|
+
if (!replacement && mtimeTag && /^\.\.?\//.test(specifier) && !specifier.includes("?")) {
|
|
659
|
+
replacement = `${specifier}?mtime=${mtimeTag}`;
|
|
660
|
+
}
|
|
661
|
+
if (replacement && replacement !== specifier) {
|
|
662
|
+
replacements.push({ ...reference, replacement });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const withImports = applySpecifierReplacements(source, replacements);
|
|
666
|
+
return rewriteExtensionSpecifiers(withImports, importerPath);
|
|
548
667
|
}
|
|
549
668
|
|
|
550
669
|
/** Test seam for compiled-binary legacy extension source rewriting. */
|
|
@@ -552,15 +671,11 @@ export async function __rewriteLegacyExtensionSourceForTests(
|
|
|
552
671
|
source: string,
|
|
553
672
|
importerPath: string,
|
|
554
673
|
mtimeTag: string | null = null,
|
|
674
|
+
resolvedImportMtimeTag: string | null = mtimeTag,
|
|
555
675
|
): Promise<string> {
|
|
556
|
-
return rewriteLegacyExtensionSource(source, importerPath, mtimeTag);
|
|
676
|
+
return rewriteLegacyExtensionSource(source, importerPath, mtimeTag, resolvedImportMtimeTag);
|
|
557
677
|
}
|
|
558
678
|
|
|
559
|
-
// Match relative graph specifiers so their `./foo.ts` /`../foo` targets get a
|
|
560
|
-
// `?mtime=<tag>` cache-bust suffix without disturbing already-rewritten
|
|
561
|
-
// `file://` URLs or bare/host specifiers.
|
|
562
|
-
const RELATIVE_GRAPH_IMPORT_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])(\.\.?\/[^"'?\s]*)(["'])/g;
|
|
563
|
-
|
|
564
679
|
/**
|
|
565
680
|
* Build the import specifier for a graph-resolved absolute path. POSIX
|
|
566
681
|
* emits a bare filesystem path with an optional `?mtime=<tag>` (Bun keys
|
|
@@ -628,6 +743,33 @@ async function resolveSourceModuleFile(basePath: string): Promise<string | null>
|
|
|
628
743
|
return null;
|
|
629
744
|
}
|
|
630
745
|
|
|
746
|
+
function isPathInsideRoot(rootPath: string, candidatePath: string): boolean {
|
|
747
|
+
const relative = path.relative(rootPath, candidatePath);
|
|
748
|
+
return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async function resolvePackageSourceTarget(packageRoot: string, targetPath: string): Promise<string | null> {
|
|
752
|
+
const candidate = path.resolve(targetPath);
|
|
753
|
+
if (!isPathInsideRoot(path.resolve(packageRoot), candidate)) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
const resolved = await resolveSourceModuleFile(candidate);
|
|
757
|
+
if (!resolved) {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
const realPackageRoot = await realpathOrSelf(packageRoot);
|
|
761
|
+
return isPathInsideRoot(realPackageRoot, resolved) ? resolved : null;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
async function resolvePackageFileTarget(packageRoot: string, targetPath: string): Promise<string | null> {
|
|
765
|
+
const candidate = path.resolve(targetPath);
|
|
766
|
+
if (!isPathInsideRoot(path.resolve(packageRoot), candidate) || !(await pathExists(candidate))) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
const [realPackageRoot, resolved] = await Promise.all([realpathOrSelf(packageRoot), realpathOrSelf(candidate)]);
|
|
770
|
+
return isPathInsideRoot(realPackageRoot, resolved) ? resolved : null;
|
|
771
|
+
}
|
|
772
|
+
|
|
631
773
|
async function findPackageRoot(importerPath: string): Promise<string | null> {
|
|
632
774
|
let dir = path.dirname(importerPath);
|
|
633
775
|
while (true) {
|
|
@@ -672,7 +814,10 @@ async function readPackageImports(packageRoot: string): Promise<Record<string, u
|
|
|
672
814
|
type PackageImportTargetSelection = string | typeof PACKAGE_IMPORT_EXCLUDED | null;
|
|
673
815
|
type ResolvedPackageImportTargetSelection = string | typeof PACKAGE_IMPORT_EXCLUDED;
|
|
674
816
|
|
|
675
|
-
function selectPackageImportTarget(
|
|
817
|
+
function selectPackageImportTarget(
|
|
818
|
+
entry: unknown,
|
|
819
|
+
conditions: ReadonlySet<string> = SUPPORTED_PACKAGE_IMPORT_CONDITIONS,
|
|
820
|
+
): PackageImportTargetSelection {
|
|
676
821
|
if (entry === null) {
|
|
677
822
|
return PACKAGE_IMPORT_EXCLUDED;
|
|
678
823
|
}
|
|
@@ -681,7 +826,7 @@ function selectPackageImportTarget(entry: unknown): PackageImportTargetSelection
|
|
|
681
826
|
}
|
|
682
827
|
if (Array.isArray(entry)) {
|
|
683
828
|
for (const item of entry) {
|
|
684
|
-
const target = selectPackageImportTarget(item);
|
|
829
|
+
const target = selectPackageImportTarget(item, conditions);
|
|
685
830
|
if (target !== null) return target;
|
|
686
831
|
}
|
|
687
832
|
return null;
|
|
@@ -690,10 +835,10 @@ function selectPackageImportTarget(entry: unknown): PackageImportTargetSelection
|
|
|
690
835
|
return null;
|
|
691
836
|
}
|
|
692
837
|
for (const [condition, value] of Object.entries(entry)) {
|
|
693
|
-
if (!
|
|
838
|
+
if (!conditions.has(condition)) {
|
|
694
839
|
continue;
|
|
695
840
|
}
|
|
696
|
-
const target = selectPackageImportTarget(value);
|
|
841
|
+
const target = selectPackageImportTarget(value, conditions);
|
|
697
842
|
if (target !== null) return target;
|
|
698
843
|
}
|
|
699
844
|
return null;
|
|
@@ -708,7 +853,7 @@ async function resolvePackageImportTarget(
|
|
|
708
853
|
return null;
|
|
709
854
|
}
|
|
710
855
|
const substituted = wildcard === null ? target : target.replaceAll("*", wildcard);
|
|
711
|
-
return
|
|
856
|
+
return resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, substituted));
|
|
712
857
|
}
|
|
713
858
|
|
|
714
859
|
async function resolvePackageImportSpecifier(specifier: string, importerPath: string): Promise<string | null> {
|
|
@@ -765,38 +910,6 @@ async function resolvePackageImportSpecifier(specifier: string, importerPath: st
|
|
|
765
910
|
return resolvePackageImportTarget(packageRoot, bestMatch.target, bestMatch.wildcard);
|
|
766
911
|
}
|
|
767
912
|
|
|
768
|
-
const PACKAGE_IMPORT_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])(#[^"'()\s]+)(["'])/g;
|
|
769
|
-
|
|
770
|
-
async function rewriteExtensionPackageImports(
|
|
771
|
-
source: string,
|
|
772
|
-
importerPath: string,
|
|
773
|
-
mtimeTag: string | null = null,
|
|
774
|
-
): Promise<string> {
|
|
775
|
-
let rewritten = "";
|
|
776
|
-
let lastIndex = 0;
|
|
777
|
-
for (const match of source.matchAll(PACKAGE_IMPORT_SPECIFIER_REGEX)) {
|
|
778
|
-
const matchIndex = match.index;
|
|
779
|
-
if (matchIndex === undefined) continue;
|
|
780
|
-
|
|
781
|
-
const [fullMatch, prefix, specifier, suffix] = match;
|
|
782
|
-
if (!prefix || !specifier || !suffix) continue;
|
|
783
|
-
|
|
784
|
-
const resolved = await resolvePackageImportSpecifier(specifier, importerPath);
|
|
785
|
-
if (!resolved) continue;
|
|
786
|
-
|
|
787
|
-
rewritten += source.slice(lastIndex, matchIndex);
|
|
788
|
-
rewritten += `${prefix}${toGraphImportSpecifier(resolved, mtimeTag)}${suffix}`;
|
|
789
|
-
lastIndex = matchIndex + fullMatch.length;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if (lastIndex === 0) {
|
|
793
|
-
return source;
|
|
794
|
-
}
|
|
795
|
-
return `${rewritten}${source.slice(lastIndex)}`;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
const BARE_EXTENSION_IMPORT_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])([^"'()\s]+)(["'])/g;
|
|
799
|
-
|
|
800
913
|
function isBareExtensionDependencySpecifier(specifier: string): boolean {
|
|
801
914
|
if (
|
|
802
915
|
specifier.startsWith(".") ||
|
|
@@ -872,6 +985,58 @@ async function readPackageManifestUncached(packageRoot: string): Promise<Record<
|
|
|
872
985
|
}
|
|
873
986
|
}
|
|
874
987
|
|
|
988
|
+
type ExtensionModuleKind = "commonjs" | "esm";
|
|
989
|
+
class ExtensionModuleKindConflictError extends Error {}
|
|
990
|
+
|
|
991
|
+
async function isCommonJsModulePath(
|
|
992
|
+
modulePath: string,
|
|
993
|
+
sourceType?: "script" | "module",
|
|
994
|
+
inheritedKind?: ExtensionModuleKind,
|
|
995
|
+
): Promise<boolean> {
|
|
996
|
+
const extension = path.extname(modulePath).toLowerCase();
|
|
997
|
+
if (extension === ".cjs" || extension === ".cts") {
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
if (extension !== ".js" && extension !== ".jsx") {
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const packageRoot = await findPackageRoot(modulePath);
|
|
1005
|
+
const manifest = packageRoot ? await readPackageManifest(packageRoot) : null;
|
|
1006
|
+
if (manifest?.type === "module") {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
if (manifest?.type === "commonjs") {
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
const parsedSourceType =
|
|
1013
|
+
sourceType ?? parseExtensionSource(await Bun.file(modulePath).text(), modulePath).program.sourceType;
|
|
1014
|
+
if (parsedSourceType === "module") {
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
if (inheritedKind) {
|
|
1018
|
+
return inheritedKind === "commonjs";
|
|
1019
|
+
}
|
|
1020
|
+
const declaredModuleEntry =
|
|
1021
|
+
packageRoot && typeof manifest?.module === "string"
|
|
1022
|
+
? await resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, manifest.module))
|
|
1023
|
+
: null;
|
|
1024
|
+
return !declaredModuleEntry || path.resolve(modulePath) !== path.resolve(declaredModuleEntry);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
async function isGraphOwnedCommonJsModule(
|
|
1028
|
+
modulePath: string,
|
|
1029
|
+
entryRealPath: string,
|
|
1030
|
+
sourceType?: "script" | "module",
|
|
1031
|
+
inheritedKind?: ExtensionModuleKind,
|
|
1032
|
+
): Promise<boolean> {
|
|
1033
|
+
const extension = path.extname(modulePath).toLowerCase();
|
|
1034
|
+
if (modulePath === entryRealPath && extension !== ".cjs" && extension !== ".cts") {
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
return isCommonJsModulePath(modulePath, sourceType, inheritedKind);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
875
1040
|
async function resolvePackageExportTarget(
|
|
876
1041
|
packageRoot: string,
|
|
877
1042
|
target: string,
|
|
@@ -881,16 +1046,17 @@ async function resolvePackageExportTarget(
|
|
|
881
1046
|
return null;
|
|
882
1047
|
}
|
|
883
1048
|
const substituted = wildcard === null ? target : target.replaceAll("*", wildcard);
|
|
884
|
-
return
|
|
1049
|
+
return resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, substituted));
|
|
885
1050
|
}
|
|
886
1051
|
|
|
887
1052
|
async function resolveNodePackageExport(
|
|
888
1053
|
packageRoot: string,
|
|
889
1054
|
subpath: string | null,
|
|
890
1055
|
manifest: Record<string, unknown>,
|
|
1056
|
+
conditions: ReadonlySet<string> = SUPPORTED_PACKAGE_IMPORT_CONDITIONS,
|
|
891
1057
|
): Promise<string | null> {
|
|
892
1058
|
const exportsField = manifest.exports;
|
|
893
|
-
const rootTarget = subpath === null ? selectPackageImportTarget(exportsField) : null;
|
|
1059
|
+
const rootTarget = subpath === null ? selectPackageImportTarget(exportsField, conditions) : null;
|
|
894
1060
|
if (rootTarget !== null && rootTarget !== PACKAGE_IMPORT_EXCLUDED) {
|
|
895
1061
|
return resolvePackageExportTarget(packageRoot, rootTarget, null);
|
|
896
1062
|
}
|
|
@@ -899,30 +1065,41 @@ async function resolveNodePackageExport(
|
|
|
899
1065
|
}
|
|
900
1066
|
|
|
901
1067
|
const exactKey = subpath === null ? "." : `./${subpath}`;
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
return
|
|
905
|
-
|
|
906
|
-
|
|
1068
|
+
if (Object.hasOwn(exportsField, exactKey)) {
|
|
1069
|
+
const exactTarget = selectPackageImportTarget(exportsField[exactKey], conditions);
|
|
1070
|
+
return exactTarget !== null && exactTarget !== PACKAGE_IMPORT_EXCLUDED
|
|
1071
|
+
? resolvePackageExportTarget(packageRoot, exactTarget, null)
|
|
1072
|
+
: null;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
let bestMatch: {
|
|
1076
|
+
keyLength: number;
|
|
1077
|
+
prefixLength: number;
|
|
1078
|
+
target: PackageImportTargetSelection;
|
|
1079
|
+
wildcard: string;
|
|
1080
|
+
} | null = null;
|
|
907
1081
|
for (const [key, entry] of Object.entries(exportsField)) {
|
|
908
1082
|
const starIndex = key.indexOf("*");
|
|
909
|
-
if (starIndex === -1 || subpath === null) continue;
|
|
1083
|
+
if (starIndex === -1 || subpath === null || !key.startsWith("./")) continue;
|
|
910
1084
|
const prefix = key.slice(2, starIndex);
|
|
911
1085
|
const suffix = key.slice(starIndex + 1);
|
|
912
|
-
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1086
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix)) continue;
|
|
1087
|
+
if (
|
|
1088
|
+
!bestMatch ||
|
|
1089
|
+
prefix.length > bestMatch.prefixLength ||
|
|
1090
|
+
(prefix.length === bestMatch.prefixLength && key.length > bestMatch.keyLength)
|
|
1091
|
+
) {
|
|
1092
|
+
bestMatch = {
|
|
1093
|
+
keyLength: key.length,
|
|
1094
|
+
prefixLength: prefix.length,
|
|
1095
|
+
target: selectPackageImportTarget(entry, conditions),
|
|
1096
|
+
wildcard: subpath.slice(prefix.length, subpath.length - suffix.length),
|
|
1097
|
+
};
|
|
918
1098
|
}
|
|
919
|
-
return resolvePackageExportTarget(
|
|
920
|
-
packageRoot,
|
|
921
|
-
target,
|
|
922
|
-
subpath.slice(prefix.length, subpath.length - suffix.length),
|
|
923
|
-
);
|
|
924
1099
|
}
|
|
925
|
-
return
|
|
1100
|
+
return bestMatch?.target && bestMatch.target !== PACKAGE_IMPORT_EXCLUDED
|
|
1101
|
+
? resolvePackageExportTarget(packageRoot, bestMatch.target, bestMatch.wildcard)
|
|
1102
|
+
: null;
|
|
926
1103
|
}
|
|
927
1104
|
|
|
928
1105
|
async function resolveNodePackageFallback(
|
|
@@ -931,16 +1108,16 @@ async function resolveNodePackageFallback(
|
|
|
931
1108
|
manifest: Record<string, unknown>,
|
|
932
1109
|
): Promise<string | null> {
|
|
933
1110
|
if (subpath !== null) {
|
|
934
|
-
return
|
|
1111
|
+
return resolvePackageSourceTarget(packageRoot, path.join(packageRoot, subpath));
|
|
935
1112
|
}
|
|
936
1113
|
for (const field of ["module", "main"]) {
|
|
937
1114
|
const target = manifest[field];
|
|
938
1115
|
if (typeof target === "string") {
|
|
939
|
-
const resolved = await
|
|
1116
|
+
const resolved = await resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, target));
|
|
940
1117
|
if (resolved) return resolved;
|
|
941
1118
|
}
|
|
942
1119
|
}
|
|
943
|
-
return
|
|
1120
|
+
return resolvePackageSourceTarget(packageRoot, path.join(packageRoot, "index"));
|
|
944
1121
|
}
|
|
945
1122
|
|
|
946
1123
|
async function resolveNodePackageDependency(specifier: string, importerPath: string): Promise<string | null> {
|
|
@@ -950,9 +1127,72 @@ async function resolveNodePackageDependency(specifier: string, importerPath: str
|
|
|
950
1127
|
if (!packageRoot) return null;
|
|
951
1128
|
const manifest = await readPackageManifest(packageRoot);
|
|
952
1129
|
if (!manifest) return null;
|
|
953
|
-
return (
|
|
954
|
-
|
|
955
|
-
|
|
1130
|
+
return Object.hasOwn(manifest, "exports")
|
|
1131
|
+
? resolveNodePackageExport(packageRoot, parsed.subpath, manifest)
|
|
1132
|
+
: resolveNodePackageFallback(packageRoot, parsed.subpath, manifest);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
async function resolveNodePackageRequire(specifier: string, importerPath: string): Promise<string | null> {
|
|
1136
|
+
const parsed = splitBarePackageSpecifier(specifier);
|
|
1137
|
+
if (!parsed) return null;
|
|
1138
|
+
const packageRoot = await findNodePackageRoot(parsed.name, importerPath);
|
|
1139
|
+
if (!packageRoot) return null;
|
|
1140
|
+
const manifest = await readPackageManifest(packageRoot);
|
|
1141
|
+
if (!manifest) return null;
|
|
1142
|
+
|
|
1143
|
+
if (Object.hasOwn(manifest, "exports")) {
|
|
1144
|
+
return resolveNodePackageExport(packageRoot, parsed.subpath, manifest, SUPPORTED_PACKAGE_REQUIRE_CONDITIONS);
|
|
1145
|
+
}
|
|
1146
|
+
if (parsed.subpath !== null) {
|
|
1147
|
+
return resolvePackageSourceTarget(packageRoot, path.join(packageRoot, parsed.subpath));
|
|
1148
|
+
}
|
|
1149
|
+
const main = manifest.main;
|
|
1150
|
+
return typeof main === "string"
|
|
1151
|
+
? await resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, main))
|
|
1152
|
+
: await resolvePackageSourceTarget(packageRoot, path.join(packageRoot, "index"));
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
async function validateResolvedBarePackagePath(
|
|
1156
|
+
specifier: string,
|
|
1157
|
+
importerPath: string,
|
|
1158
|
+
resolvedPath: string,
|
|
1159
|
+
): Promise<string | null> {
|
|
1160
|
+
const parsed = splitBarePackageSpecifier(specifier);
|
|
1161
|
+
const packageRoot = parsed ? await findNodePackageRoot(parsed.name, importerPath) : null;
|
|
1162
|
+
return packageRoot ? resolvePackageFileTarget(packageRoot, resolvedPath) : null;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
async function isSelectedNoTypeEsmPackageBranch(
|
|
1166
|
+
specifier: string,
|
|
1167
|
+
importerPath: string,
|
|
1168
|
+
resolvedPath: string,
|
|
1169
|
+
): Promise<boolean> {
|
|
1170
|
+
const parsed = splitBarePackageSpecifier(specifier);
|
|
1171
|
+
const packageRoot = parsed ? await findNodePackageRoot(parsed.name, importerPath) : null;
|
|
1172
|
+
const manifest = packageRoot ? await readPackageManifest(packageRoot) : null;
|
|
1173
|
+
if (!packageRoot || !manifest || manifest.type !== undefined) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
if (parsed?.subpath === null && typeof manifest.module === "string") {
|
|
1177
|
+
const moduleEntry = await resolvePackageSourceTarget(packageRoot, path.resolve(packageRoot, manifest.module));
|
|
1178
|
+
if (moduleEntry && path.resolve(moduleEntry) === path.resolve(resolvedPath)) {
|
|
1179
|
+
return true;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
if (!Object.hasOwn(manifest, "exports")) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
const importTarget = await resolveNodePackageExport(packageRoot, parsed?.subpath ?? null, manifest);
|
|
1186
|
+
const requireTarget = await resolveNodePackageExport(
|
|
1187
|
+
packageRoot,
|
|
1188
|
+
parsed?.subpath ?? null,
|
|
1189
|
+
manifest,
|
|
1190
|
+
SUPPORTED_PACKAGE_REQUIRE_CONDITIONS,
|
|
1191
|
+
);
|
|
1192
|
+
return Boolean(
|
|
1193
|
+
importTarget &&
|
|
1194
|
+
path.resolve(importTarget) === path.resolve(resolvedPath) &&
|
|
1195
|
+
(!requireTarget || path.resolve(requireTarget) !== path.resolve(importTarget)),
|
|
956
1196
|
);
|
|
957
1197
|
}
|
|
958
1198
|
|
|
@@ -971,24 +1211,26 @@ async function resolveExtensionBareDependency(specifier: string, importerPath: s
|
|
|
971
1211
|
}
|
|
972
1212
|
|
|
973
1213
|
async function resolveExtensionBareDependencyUncached(specifier: string, importerPath: string): Promise<string | null> {
|
|
1214
|
+
// Resolve against the runtime package manifest first. Besides working in a
|
|
1215
|
+
// compiled binary, this preserves the package's ESM `import` condition when
|
|
1216
|
+
// the absolute target is later loaded outside normal package resolution.
|
|
1217
|
+
const packageResolved = await resolveNodePackageDependency(specifier, importerPath);
|
|
1218
|
+
if (packageResolved) {
|
|
1219
|
+
return packageResolved;
|
|
1220
|
+
}
|
|
974
1221
|
try {
|
|
975
1222
|
const resolved = Bun.resolveSync(specifier, path.dirname(importerPath));
|
|
976
1223
|
if (resolved && resolved !== specifier && !resolved.startsWith("node:") && !resolved.startsWith("bun:")) {
|
|
977
|
-
return resolved;
|
|
1224
|
+
return validateResolvedBarePackagePath(specifier, importerPath, resolved);
|
|
978
1225
|
}
|
|
979
1226
|
} catch {
|
|
980
1227
|
// Compiled binaries do not reliably resolve runtime extension node_modules.
|
|
981
1228
|
}
|
|
982
|
-
return
|
|
1229
|
+
return null;
|
|
983
1230
|
}
|
|
984
1231
|
|
|
985
1232
|
const NATIVE_ADDON_EXTENSION = ".node";
|
|
986
1233
|
|
|
987
|
-
// Match CommonJS require calls so bare native-addon specifiers can be pinned
|
|
988
|
-
// to absolute paths. Only requires whose resolution lands on a `.node` addon
|
|
989
|
-
// are rewritten; everything else stays on Bun's native resolver.
|
|
990
|
-
const NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX = /(\brequire\s*\(\s*["'])([^"'()\s]+)(["']\s*\))/g;
|
|
991
|
-
|
|
992
1234
|
/**
|
|
993
1235
|
* Resolve a bare specifier whose target is a native `.node` addon — either a
|
|
994
1236
|
* package subpath ending in `.node`, or a package whose `main` points at an
|
|
@@ -1025,48 +1267,119 @@ async function resolveExtensionNativeAddonUncached(specifier: string, importerPa
|
|
|
1025
1267
|
target =
|
|
1026
1268
|
typeof main === "string" && main.endsWith(NATIVE_ADDON_EXTENSION) ? path.resolve(packageRoot, main) : null;
|
|
1027
1269
|
}
|
|
1028
|
-
if (!target
|
|
1270
|
+
if (!target) {
|
|
1029
1271
|
return null;
|
|
1030
1272
|
}
|
|
1031
|
-
return
|
|
1273
|
+
return resolvePackageFileTarget(packageRoot, target);
|
|
1032
1274
|
}
|
|
1033
1275
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
* module '@scope/pkg-<platform>'") even when the package sits in the
|
|
1039
|
-
* extension's own node_modules; requiring the addon by absolute path works.
|
|
1040
|
-
*/
|
|
1041
|
-
async function rewriteExtensionNativeAddonRequires(source: string, importerPath: string): Promise<string> {
|
|
1042
|
-
let rewritten = "";
|
|
1043
|
-
let lastIndex = 0;
|
|
1044
|
-
for (const match of source.matchAll(NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX)) {
|
|
1045
|
-
const matchIndex = match.index;
|
|
1046
|
-
if (matchIndex === undefined) continue;
|
|
1276
|
+
async function resolveExtensionBareRequire(specifier: string, importerPath: string): Promise<string | null> {
|
|
1277
|
+
if (!isBareExtensionDependencySpecifier(specifier)) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1047
1280
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1281
|
+
const cacheKey = `${specifier}\0${path.resolve(path.dirname(importerPath))}`;
|
|
1282
|
+
const cached = bareRequireResolutionCache.get(cacheKey);
|
|
1283
|
+
if (cached) return cached;
|
|
1050
1284
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1285
|
+
const resolution = (async () => {
|
|
1286
|
+
const nativeAddon = await resolveExtensionNativeAddon(specifier, importerPath);
|
|
1287
|
+
if (nativeAddon) {
|
|
1288
|
+
return nativeAddon;
|
|
1289
|
+
}
|
|
1290
|
+
const packageResolved = await resolveNodePackageRequire(specifier, importerPath);
|
|
1291
|
+
if (packageResolved) {
|
|
1292
|
+
return realpathOrSelf(packageResolved);
|
|
1293
|
+
}
|
|
1294
|
+
try {
|
|
1295
|
+
const resolved = createRequire(importerPath).resolve(specifier);
|
|
1296
|
+
return resolved === specifier || resolved.startsWith("node:") || resolved.startsWith("bun:")
|
|
1297
|
+
? null
|
|
1298
|
+
: await validateResolvedBarePackagePath(specifier, importerPath, resolved);
|
|
1299
|
+
} catch {
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
})();
|
|
1303
|
+
bareRequireResolutionCache.set(cacheKey, resolution);
|
|
1304
|
+
return resolution;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
async function resolveExtensionCommonJsRequire(specifier: string, importerPath: string): Promise<string | null> {
|
|
1308
|
+
const remappedSpecifier = remapLegacyPiSpecifier(specifier);
|
|
1309
|
+
if (remappedSpecifier) {
|
|
1310
|
+
try {
|
|
1311
|
+
const resolved = resolveCanonicalPiSpecifier(remappedSpecifier);
|
|
1312
|
+
if (isBundledVirtualSpecifier(resolved)) {
|
|
1313
|
+
const moduleKey = resolved.slice(BUNDLED_VIRTUAL_SCHEME.length);
|
|
1314
|
+
if (!(moduleKey in loadedBundledModules)) {
|
|
1315
|
+
await loadBundledModule(moduleKey);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return resolved;
|
|
1319
|
+
} catch {
|
|
1320
|
+
// A malformed compiled registry can still fall through to an
|
|
1321
|
+
// extension-installed legacy peer dependency.
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return resolveExtensionBareRequire(specifier, importerPath);
|
|
1325
|
+
}
|
|
1053
1326
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1327
|
+
/**
|
|
1328
|
+
* Rewrite CommonJS graph specifiers that cannot resolve from the bridge's
|
|
1329
|
+
* generated function: bare `require()` calls and, for graph-owned CommonJS
|
|
1330
|
+
* sources, import specifiers. Resolved targets are retained for synchronous
|
|
1331
|
+
* lazy hydration after load-time source caches clear.
|
|
1332
|
+
*/
|
|
1333
|
+
async function rewriteExtensionSpecifiers(
|
|
1334
|
+
source: string,
|
|
1335
|
+
importerPath: string,
|
|
1336
|
+
rewriteImports = false,
|
|
1337
|
+
): Promise<string> {
|
|
1338
|
+
const references = collectExtensionSpecifierReferences(source, importerPath);
|
|
1339
|
+
const resolvedSpecifierTargets = new Map<string, string>();
|
|
1340
|
+
const replacements: Array<ExtensionSpecifierReference & { replacement: string }> = [];
|
|
1341
|
+
for (const reference of references) {
|
|
1342
|
+
let resolved: string | null = null;
|
|
1343
|
+
if (reference.kind === "require") {
|
|
1344
|
+
resolved = await resolveExtensionCommonJsRequire(reference.specifier, importerPath);
|
|
1345
|
+
} else if (rewriteImports) {
|
|
1346
|
+
if (reference.specifier.startsWith(".")) {
|
|
1347
|
+
const candidate = Bun.resolveSync(reference.specifier, path.dirname(importerPath));
|
|
1348
|
+
resolved = hasSourceModuleExtension(candidate) ? await realpathOrSelf(candidate) : null;
|
|
1349
|
+
} else if (reference.specifier.startsWith("#")) {
|
|
1350
|
+
resolved = await resolvePackageImportSpecifier(reference.specifier, importerPath);
|
|
1351
|
+
} else {
|
|
1352
|
+
resolved = await resolveExtensionBareDependency(reference.specifier, importerPath);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (!resolved) continue;
|
|
1356
|
+
const replacement = stripWindowsExtendedLengthPathPrefix(resolved).replaceAll("\\", "/");
|
|
1357
|
+
resolvedSpecifierTargets.set(`${reference.kind}\0${reference.specifier}`, replacement);
|
|
1358
|
+
replacements.push({ ...reference, replacement });
|
|
1058
1359
|
}
|
|
1360
|
+
extensionSynchronousSpecifierTargets.set(importerPath, resolvedSpecifierTargets);
|
|
1361
|
+
return applySpecifierReplacements(source, replacements);
|
|
1362
|
+
}
|
|
1059
1363
|
|
|
1060
|
-
|
|
1364
|
+
function rewriteExtensionSpecifiersFromCache(source: string, importerPath: string): string {
|
|
1365
|
+
const resolvedSpecifierTargets = extensionSynchronousSpecifierTargets.get(importerPath);
|
|
1366
|
+
if (!resolvedSpecifierTargets || resolvedSpecifierTargets.size === 0) {
|
|
1061
1367
|
return source;
|
|
1062
1368
|
}
|
|
1063
|
-
|
|
1369
|
+
const replacements: Array<ExtensionSpecifierReference & { replacement: string }> = [];
|
|
1370
|
+
for (const reference of collectExtensionSpecifierReferences(source, importerPath)) {
|
|
1371
|
+
const replacement = resolvedSpecifierTargets.get(`${reference.kind}\0${reference.specifier}`);
|
|
1372
|
+
if (replacement) {
|
|
1373
|
+
replacements.push({ ...reference, replacement });
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
return applySpecifierReplacements(source, replacements);
|
|
1064
1377
|
}
|
|
1065
1378
|
|
|
1066
1379
|
/**
|
|
1067
1380
|
* Whether a module's source contains a bare require that resolves to a native
|
|
1068
1381
|
* `.node` addon — i.e. a napi-rs style loader that must be hooked into the
|
|
1069
|
-
* extension graph so {@link
|
|
1382
|
+
* extension graph so {@link rewriteExtensionSpecifiers} can pin its
|
|
1070
1383
|
* platform-package requires to absolute paths.
|
|
1071
1384
|
*/
|
|
1072
1385
|
async function moduleRequiresNativeAddon(modulePath: string): Promise<boolean> {
|
|
@@ -1085,63 +1398,33 @@ async function moduleRequiresNativeAddonUncached(modulePath: string): Promise<bo
|
|
|
1085
1398
|
} catch {
|
|
1086
1399
|
return false;
|
|
1087
1400
|
}
|
|
1088
|
-
for (const
|
|
1089
|
-
|
|
1090
|
-
if (specifier && (await resolveExtensionNativeAddon(specifier, modulePath))) {
|
|
1401
|
+
for (const reference of collectExtensionSpecifierReferences(source, modulePath)) {
|
|
1402
|
+
if (reference.kind === "require" && (await resolveExtensionNativeAddon(reference.specifier, modulePath))) {
|
|
1091
1403
|
return true;
|
|
1092
1404
|
}
|
|
1093
1405
|
}
|
|
1094
1406
|
return false;
|
|
1095
1407
|
}
|
|
1096
1408
|
|
|
1097
|
-
async function rewriteExtensionBareImports(
|
|
1098
|
-
source: string,
|
|
1099
|
-
importerPath: string,
|
|
1100
|
-
mtimeTag: string | null = null,
|
|
1101
|
-
): Promise<string> {
|
|
1102
|
-
let rewritten = "";
|
|
1103
|
-
let lastIndex = 0;
|
|
1104
|
-
for (const match of source.matchAll(BARE_EXTENSION_IMPORT_SPECIFIER_REGEX)) {
|
|
1105
|
-
const matchIndex = match.index;
|
|
1106
|
-
if (matchIndex === undefined) continue;
|
|
1107
|
-
|
|
1108
|
-
const [fullMatch, prefix, specifier, suffix] = match;
|
|
1109
|
-
if (!prefix || !specifier || !suffix) continue;
|
|
1110
|
-
|
|
1111
|
-
const resolved = await resolveExtensionBareDependency(specifier, importerPath);
|
|
1112
|
-
if (!resolved) continue;
|
|
1113
|
-
|
|
1114
|
-
rewritten += source.slice(lastIndex, matchIndex);
|
|
1115
|
-
rewritten += `${prefix}${toGraphImportSpecifier(resolved, mtimeTag)}${suffix}`;
|
|
1116
|
-
lastIndex = matchIndex + fullMatch.length;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
if (lastIndex === 0) {
|
|
1120
|
-
return source;
|
|
1121
|
-
}
|
|
1122
|
-
return `${rewritten}${source.slice(lastIndex)}`;
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
1409
|
function escapeRegExp(value: string): string {
|
|
1126
1410
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1127
1411
|
}
|
|
1128
1412
|
|
|
1129
|
-
//
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
// still collected and rewritten with the reload mtime tag. `require()` calls
|
|
1134
|
-
// are scanned too so CJS entries and napi-rs loaders reached without an
|
|
1135
|
-
// import statement still join the graph.
|
|
1136
|
-
const EXTENSION_GRAPH_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])([^"'()\s]+)(["'])/g;
|
|
1413
|
+
// Source modules in an extension graph are discovered from parsed static,
|
|
1414
|
+
// dynamic, re-export, and direct CommonJS require specifiers. Parsing keeps
|
|
1415
|
+
// import-looking text in strings, templates, regex literals, and comments out
|
|
1416
|
+
// of dependency resolution.
|
|
1137
1417
|
|
|
1138
1418
|
// Extension source realpaths already covered by an installed load-time hook for
|
|
1139
1419
|
// each entry. `Bun.plugin()` registrations are process-global and permanent, so
|
|
1140
1420
|
// reloads install supplemental hooks only for modules added to the graph since
|
|
1141
1421
|
// the previous load.
|
|
1142
1422
|
const extensionGraphHookModules = new Map<string, Set<string>>();
|
|
1423
|
+
const extensionGraphCacheBustResolvedImportModules = new Map<string, Set<string>>();
|
|
1143
1424
|
const commonJsModuleSources = new Map<string, string>();
|
|
1144
1425
|
const commonJsFallbackModulePaths = new Map<string, string>();
|
|
1426
|
+
const extensionSynchronousSpecifierTargets = new Map<string, Map<string, string>>();
|
|
1427
|
+
const commonJsGraphModulePaths = new Set<string>();
|
|
1145
1428
|
const COMMONJS_REQUIRE_GLOBAL = "__ompLegacyPiRequireGraphModule";
|
|
1146
1429
|
const commonJsModuleDefinitions = new Map<string, { source: string; filename: string; dirname: string }>();
|
|
1147
1430
|
const commonJsModuleCache = new Map<
|
|
@@ -1162,7 +1445,13 @@ function evaluateGraphCommonJs(modulePath: string): unknown {
|
|
|
1162
1445
|
if (cached) {
|
|
1163
1446
|
return cached.exports;
|
|
1164
1447
|
}
|
|
1165
|
-
|
|
1448
|
+
let definition = commonJsModuleDefinitions.get(modulePath);
|
|
1449
|
+
if (!definition && commonJsGraphModulePaths.has(modulePath)) {
|
|
1450
|
+
const targetPath = commonJsFallbackModulePaths.get(modulePath) ?? modulePath;
|
|
1451
|
+
const source = rewriteExtensionSpecifiersFromCache(fs.readFileSync(targetPath, "utf8"), modulePath);
|
|
1452
|
+
synthesizeCommonJsDefaultModule(modulePath, source, targetPath);
|
|
1453
|
+
definition = commonJsModuleDefinitions.get(modulePath);
|
|
1454
|
+
}
|
|
1166
1455
|
if (!definition) {
|
|
1167
1456
|
throw new Error(`Missing graph-owned CommonJS definition: ${modulePath}`);
|
|
1168
1457
|
}
|
|
@@ -1179,6 +1468,14 @@ function evaluateGraphCommonJs(modulePath: string): unknown {
|
|
|
1179
1468
|
commonJsModuleCache.set(modulePath, module);
|
|
1180
1469
|
const graphRequire: NodeJS.Require = Object.assign(
|
|
1181
1470
|
(specifier: string) => {
|
|
1471
|
+
if (isBundledVirtualSpecifier(specifier)) {
|
|
1472
|
+
const moduleKey = specifier.slice(BUNDLED_VIRTUAL_SCHEME.length);
|
|
1473
|
+
const bundledModule = loadedBundledModules[moduleKey];
|
|
1474
|
+
if (!bundledModule) {
|
|
1475
|
+
throw new Error(`Missing bundled CommonJS host module: ${moduleKey}`);
|
|
1476
|
+
}
|
|
1477
|
+
return bundledModule;
|
|
1478
|
+
}
|
|
1182
1479
|
const resolved = nativeRequire.resolve(specifier);
|
|
1183
1480
|
let graphPath = resolved;
|
|
1184
1481
|
try {
|
|
@@ -1186,7 +1483,7 @@ function evaluateGraphCommonJs(modulePath: string): unknown {
|
|
|
1186
1483
|
} catch {
|
|
1187
1484
|
// Builtins and virtual modules have no filesystem realpath.
|
|
1188
1485
|
}
|
|
1189
|
-
return
|
|
1486
|
+
return commonJsGraphModulePaths.has(graphPath) ? evaluateGraphCommonJs(graphPath) : nativeRequire(specifier);
|
|
1190
1487
|
},
|
|
1191
1488
|
{
|
|
1192
1489
|
resolve: nativeRequire.resolve,
|
|
@@ -1234,30 +1531,43 @@ async function realpathOrSelfUncached(p: string): Promise<string> {
|
|
|
1234
1531
|
}
|
|
1235
1532
|
}
|
|
1236
1533
|
|
|
1534
|
+
interface ExtensionModuleGraph {
|
|
1535
|
+
readonly modules: Map<string, string>;
|
|
1536
|
+
readonly cacheBustResolvedImportModules: Set<string>;
|
|
1537
|
+
readonly commonJsPaths: Set<string>;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1237
1540
|
/**
|
|
1238
1541
|
* Walk the extension's import graph starting at `entryRealPath`, returning the
|
|
1239
1542
|
* realpath of every reachable source module OMP must rewrite at load time.
|
|
1240
|
-
* Relative imports
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
* graph-owned so the load hook can expose its exports through an ESM default.
|
|
1543
|
+
* Relative imports, package `imports` aliases, and ESM bare dependencies are
|
|
1544
|
+
* graph-owned recursively because compiled Bun cannot resolve runtime
|
|
1545
|
+
* `node_modules` from those modules. Graph-owned CommonJS modules also own
|
|
1546
|
+
* their relative and bare CommonJS descendants, which are evaluated by the
|
|
1547
|
+
* synchronous bridge. Resolved ESM imports inside third-party dependencies
|
|
1548
|
+
* omit the reload tag so their importer paths stay query-free.
|
|
1247
1549
|
*/
|
|
1248
|
-
async function collectExtensionModules(entryRealPath: string): Promise<
|
|
1550
|
+
async function collectExtensionModules(entryRealPath: string): Promise<ExtensionModuleGraph> {
|
|
1249
1551
|
const modules = new Map<string, string>();
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1552
|
+
const commonJsPaths = new Set<string>();
|
|
1553
|
+
const queuedCacheBustResolvedImports = new Map<string, boolean>([[entryRealPath, true]]);
|
|
1554
|
+
const queuedModuleKinds = new Map<string, ExtensionModuleKind>([[entryRealPath, "esm"]]);
|
|
1555
|
+
const queuedEsmBranchPaths = new Set<string>();
|
|
1556
|
+
const queue: Array<{
|
|
1557
|
+
file: string;
|
|
1558
|
+
cacheBustResolvedImports: boolean;
|
|
1559
|
+
moduleKind?: ExtensionModuleKind;
|
|
1560
|
+
esmBranch?: boolean;
|
|
1561
|
+
}> = [{ file: entryRealPath, cacheBustResolvedImports: true, moduleKind: "esm" }];
|
|
1254
1562
|
while (queue.length > 0) {
|
|
1255
1563
|
const item = queue.pop();
|
|
1256
1564
|
if (!item) {
|
|
1257
1565
|
continue;
|
|
1258
1566
|
}
|
|
1259
1567
|
const file = item.file;
|
|
1260
|
-
const
|
|
1568
|
+
const cacheBustResolvedImports = queuedCacheBustResolvedImports.get(file) ?? item.cacheBustResolvedImports;
|
|
1569
|
+
const inheritedModuleKind = queuedModuleKinds.get(file) ?? item.moduleKind;
|
|
1570
|
+
const esmBranch = queuedEsmBranchPaths.has(file) || item.esmBranch === true;
|
|
1261
1571
|
if (modules.has(file)) {
|
|
1262
1572
|
continue;
|
|
1263
1573
|
}
|
|
@@ -1268,109 +1578,330 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1268
1578
|
continue;
|
|
1269
1579
|
}
|
|
1270
1580
|
modules.set(file, source);
|
|
1271
|
-
const
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
requiredSpecifiers.add(match[2]);
|
|
1281
|
-
}
|
|
1581
|
+
const ast = parseExtensionSource(source, file);
|
|
1582
|
+
const sourceIsCommonJs = await isGraphOwnedCommonJsModule(
|
|
1583
|
+
file,
|
|
1584
|
+
entryRealPath,
|
|
1585
|
+
ast.program.sourceType,
|
|
1586
|
+
inheritedModuleKind,
|
|
1587
|
+
);
|
|
1588
|
+
if (sourceIsCommonJs) {
|
|
1589
|
+
commonJsPaths.add(file);
|
|
1282
1590
|
}
|
|
1283
|
-
|
|
1591
|
+
const dir = path.dirname(file);
|
|
1592
|
+
const references = collectExtensionSpecifierReferences(source, file, ast);
|
|
1593
|
+
for (const reference of references) {
|
|
1594
|
+
const specifier = reference.specifier;
|
|
1284
1595
|
try {
|
|
1285
1596
|
let resolved: string | null = null;
|
|
1286
|
-
let
|
|
1287
|
-
|
|
1597
|
+
let nextCacheBustResolvedImports = cacheBustResolvedImports;
|
|
1598
|
+
let resolvedModuleKind: ExtensionModuleKind | undefined;
|
|
1599
|
+
let resolvedEsmBranch = false;
|
|
1600
|
+
let requiresNativeAddonRewrite = false;
|
|
1601
|
+
const isRequired = reference.kind === "require";
|
|
1288
1602
|
if (specifier.startsWith(".")) {
|
|
1289
1603
|
const candidate = Bun.resolveSync(specifier, dir);
|
|
1290
|
-
if (
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1604
|
+
if (hasSourceModuleExtension(candidate)) {
|
|
1605
|
+
const inheritedTargetKind = isRequired
|
|
1606
|
+
? sourceIsCommonJs
|
|
1607
|
+
? "commonjs"
|
|
1608
|
+
: undefined
|
|
1609
|
+
: sourceIsCommonJs
|
|
1610
|
+
? "commonjs"
|
|
1611
|
+
: esmBranch
|
|
1612
|
+
? "esm"
|
|
1613
|
+
: undefined;
|
|
1614
|
+
const targetIsCommonJs = await isCommonJsModulePath(candidate, undefined, inheritedTargetKind);
|
|
1615
|
+
const isCommonJsDescendant = isRequired && sourceIsCommonJs && targetIsCommonJs;
|
|
1616
|
+
requiresNativeAddonRewrite =
|
|
1617
|
+
isRequired && !isCommonJsDescendant && (await moduleRequiresNativeAddon(candidate));
|
|
1618
|
+
if (!isRequired || isCommonJsDescendant || requiresNativeAddonRewrite) {
|
|
1619
|
+
resolved = await realpathOrSelf(candidate);
|
|
1620
|
+
resolvedModuleKind = targetIsCommonJs ? "commonjs" : "esm";
|
|
1621
|
+
resolvedEsmBranch = !targetIsCommonJs && esmBranch;
|
|
1622
|
+
}
|
|
1295
1623
|
}
|
|
1296
1624
|
} else if (specifier.startsWith("#")) {
|
|
1297
1625
|
const candidate = await resolvePackageImportSpecifier(specifier, file);
|
|
1298
|
-
if (candidate
|
|
1299
|
-
|
|
1626
|
+
if (candidate) {
|
|
1627
|
+
const inheritedTargetKind = isRequired
|
|
1628
|
+
? sourceIsCommonJs
|
|
1629
|
+
? "commonjs"
|
|
1630
|
+
: undefined
|
|
1631
|
+
: sourceIsCommonJs
|
|
1632
|
+
? "commonjs"
|
|
1633
|
+
: esmBranch
|
|
1634
|
+
? "esm"
|
|
1635
|
+
: undefined;
|
|
1636
|
+
const targetIsCommonJs = await isCommonJsModulePath(candidate, undefined, inheritedTargetKind);
|
|
1637
|
+
const isCommonJsDescendant = isRequired && sourceIsCommonJs && targetIsCommonJs;
|
|
1638
|
+
requiresNativeAddonRewrite =
|
|
1639
|
+
isRequired && !isCommonJsDescendant && (await moduleRequiresNativeAddon(candidate));
|
|
1640
|
+
if (!isRequired || isCommonJsDescendant || requiresNativeAddonRewrite) {
|
|
1641
|
+
resolved = candidate;
|
|
1642
|
+
resolvedModuleKind = targetIsCommonJs ? "commonjs" : "esm";
|
|
1643
|
+
resolvedEsmBranch = !targetIsCommonJs && esmBranch;
|
|
1644
|
+
}
|
|
1300
1645
|
}
|
|
1301
1646
|
} else if (
|
|
1302
|
-
followBareDependencies &&
|
|
1303
1647
|
isBareExtensionDependencySpecifier(specifier) &&
|
|
1304
1648
|
!remapLegacyPiSpecifier(specifier) &&
|
|
1305
1649
|
specifier !== "typebox" &&
|
|
1306
1650
|
specifier !== "@sinclair/typebox"
|
|
1307
1651
|
) {
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
const dependencyEntry = manifest ? await resolveExtensionBareDependency(specifier, file) : null;
|
|
1312
|
-
const dependencyExtension = dependencyEntry ? path.extname(dependencyEntry) : null;
|
|
1313
|
-
const isCommonJsEntry =
|
|
1314
|
-
dependencyExtension === ".cjs" ||
|
|
1315
|
-
dependencyExtension === ".cts" ||
|
|
1316
|
-
((dependencyExtension === ".js" || dependencyExtension === ".jsx") && manifest?.type !== "module");
|
|
1652
|
+
const dependencyEntry = isRequired
|
|
1653
|
+
? await resolveExtensionBareRequire(specifier, file)
|
|
1654
|
+
: await resolveExtensionBareDependency(specifier, file);
|
|
1317
1655
|
const isHookableEntry = Boolean(dependencyEntry && hasSourceModuleExtension(dependencyEntry));
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1656
|
+
const selectedEsmBranch =
|
|
1657
|
+
!isRequired &&
|
|
1658
|
+
isHookableEntry &&
|
|
1659
|
+
dependencyEntry !== null &&
|
|
1660
|
+
(await isSelectedNoTypeEsmPackageBranch(specifier, file, dependencyEntry));
|
|
1661
|
+
const inheritedTargetKind = isRequired
|
|
1662
|
+
? sourceIsCommonJs
|
|
1663
|
+
? "commonjs"
|
|
1664
|
+
: undefined
|
|
1665
|
+
: selectedEsmBranch
|
|
1666
|
+
? "esm"
|
|
1667
|
+
: undefined;
|
|
1668
|
+
const isCommonJsEntry =
|
|
1669
|
+
isHookableEntry && dependencyEntry
|
|
1670
|
+
? await isCommonJsModulePath(dependencyEntry, undefined, inheritedTargetKind)
|
|
1321
1671
|
: false;
|
|
1322
|
-
if (isHookableEntry && dependencyEntry && (
|
|
1672
|
+
if (isHookableEntry && dependencyEntry && (!isRequired || (sourceIsCommonJs && isCommonJsEntry))) {
|
|
1323
1673
|
resolved = await realpathOrSelf(dependencyEntry);
|
|
1674
|
+
} else if (isHookableEntry && dependencyEntry && isRequired) {
|
|
1675
|
+
requiresNativeAddonRewrite = await moduleRequiresNativeAddon(dependencyEntry);
|
|
1676
|
+
if (requiresNativeAddonRewrite) {
|
|
1677
|
+
resolved = await realpathOrSelf(dependencyEntry);
|
|
1678
|
+
}
|
|
1324
1679
|
}
|
|
1325
|
-
if (resolved
|
|
1326
|
-
|
|
1680
|
+
if (resolved) {
|
|
1681
|
+
resolvedModuleKind = isCommonJsEntry ? "commonjs" : "esm";
|
|
1682
|
+
resolvedEsmBranch = selectedEsmBranch && !isCommonJsEntry;
|
|
1327
1683
|
}
|
|
1328
|
-
|
|
1684
|
+
nextCacheBustResolvedImports = false;
|
|
1329
1685
|
}
|
|
1330
|
-
if (resolved &&
|
|
1686
|
+
if (resolved && requiresNativeAddonRewrite) {
|
|
1331
1687
|
nativeAddonLoaderModulePaths.add(resolved);
|
|
1332
1688
|
}
|
|
1333
|
-
if (resolved
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1689
|
+
if (resolved) {
|
|
1690
|
+
const queuedCacheBust = queuedCacheBustResolvedImports.get(resolved) ?? false;
|
|
1691
|
+
const mergedCacheBust = queuedCacheBust || nextCacheBustResolvedImports;
|
|
1692
|
+
queuedCacheBustResolvedImports.set(resolved, mergedCacheBust);
|
|
1693
|
+
const queuedModuleKind = queuedModuleKinds.get(resolved);
|
|
1694
|
+
if (queuedModuleKind && resolvedModuleKind && queuedModuleKind !== resolvedModuleKind) {
|
|
1695
|
+
throw new ExtensionModuleKindConflictError(
|
|
1696
|
+
`Conflicting extension module kinds for ${resolved}: ${queuedModuleKind} and ${resolvedModuleKind}`,
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
const mergedModuleKind = queuedModuleKind ?? resolvedModuleKind;
|
|
1700
|
+
if (mergedModuleKind) {
|
|
1701
|
+
queuedModuleKinds.set(resolved, mergedModuleKind);
|
|
1702
|
+
}
|
|
1703
|
+
if (resolvedEsmBranch) {
|
|
1704
|
+
queuedEsmBranchPaths.add(resolved);
|
|
1705
|
+
}
|
|
1706
|
+
if (!modules.has(resolved)) {
|
|
1707
|
+
queue.push({
|
|
1708
|
+
file: resolved,
|
|
1709
|
+
cacheBustResolvedImports: mergedCacheBust,
|
|
1710
|
+
moduleKind: mergedModuleKind,
|
|
1711
|
+
esmBranch: resolvedEsmBranch,
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
} catch (error) {
|
|
1716
|
+
if (error instanceof ExtensionModuleKindConflictError) {
|
|
1717
|
+
throw error;
|
|
1338
1718
|
}
|
|
1339
|
-
} catch {
|
|
1340
1719
|
// Unresolvable import (e.g. a type-only path); skip it.
|
|
1341
1720
|
}
|
|
1342
1721
|
}
|
|
1343
1722
|
}
|
|
1344
|
-
for (const modulePath of
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
modules.set(modulePath, await rewriteExtensionNativeAddonRequires(source, modulePath));
|
|
1723
|
+
for (const [modulePath, source] of modules) {
|
|
1724
|
+
if (commonJsPaths.has(modulePath) || nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1725
|
+
modules.set(modulePath, await rewriteExtensionSpecifiers(source, modulePath, commonJsPaths.has(modulePath)));
|
|
1348
1726
|
}
|
|
1349
1727
|
}
|
|
1350
|
-
return
|
|
1728
|
+
return {
|
|
1729
|
+
modules,
|
|
1730
|
+
commonJsPaths,
|
|
1731
|
+
cacheBustResolvedImportModules: new Set(
|
|
1732
|
+
[...queuedCacheBustResolvedImports]
|
|
1733
|
+
.filter(([modulePath, enabled]) => enabled && modules.has(modulePath))
|
|
1734
|
+
.map(([modulePath]) => modulePath),
|
|
1735
|
+
),
|
|
1736
|
+
};
|
|
1351
1737
|
}
|
|
1352
1738
|
|
|
1353
1739
|
/**
|
|
1354
1740
|
* Discovers CommonJS export names Bun normally exposes to ESM importers. The
|
|
1355
1741
|
* bridge must declare them statically because its default export is synthetic.
|
|
1356
1742
|
*/
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1743
|
+
const COMMONJS_NAMED_EXPORT_IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
1744
|
+
|
|
1745
|
+
function collectCommonJsNamedExports(source: string, modulePath: string, visited = new Set<string>()): string[] {
|
|
1746
|
+
let realModulePath = modulePath;
|
|
1747
|
+
try {
|
|
1748
|
+
realModulePath = fs.realpathSync(modulePath);
|
|
1749
|
+
} catch {
|
|
1750
|
+
// The caller's path remains the stable cycle key when realpath fails.
|
|
1751
|
+
}
|
|
1752
|
+
if (visited.has(realModulePath)) {
|
|
1753
|
+
return [];
|
|
1365
1754
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1755
|
+
visited.add(realModulePath);
|
|
1756
|
+
|
|
1757
|
+
const names = new Set<string>();
|
|
1758
|
+
|
|
1759
|
+
const reexportSpecifiers = new Set<string>();
|
|
1760
|
+
const ast = parseExtensionSource(source, modulePath);
|
|
1761
|
+
traverseAst(ast, {
|
|
1762
|
+
enter(nodePath) {
|
|
1763
|
+
const node = nodePath.node;
|
|
1764
|
+
if (node.type === "CallExpression") {
|
|
1765
|
+
const definePropertyCall =
|
|
1766
|
+
node.callee.type === "MemberExpression" &&
|
|
1767
|
+
!node.callee.computed &&
|
|
1768
|
+
node.callee.object.type === "Identifier" &&
|
|
1769
|
+
node.callee.object.name === "Object" &&
|
|
1770
|
+
node.callee.property.type === "Identifier" &&
|
|
1771
|
+
node.callee.property.name === "defineProperty" &&
|
|
1772
|
+
!nodePath.scope.hasBinding("Object", true);
|
|
1773
|
+
if (definePropertyCall) {
|
|
1774
|
+
const target = node.arguments[0];
|
|
1775
|
+
const property = node.arguments[1];
|
|
1776
|
+
const targetsExports =
|
|
1777
|
+
(target?.type === "Identifier" &&
|
|
1778
|
+
target.name === "exports" &&
|
|
1779
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1780
|
+
(target?.type === "MemberExpression" &&
|
|
1781
|
+
!target.computed &&
|
|
1782
|
+
target.object.type === "Identifier" &&
|
|
1783
|
+
target.object.name === "module" &&
|
|
1784
|
+
target.property.type === "Identifier" &&
|
|
1785
|
+
target.property.name === "exports" &&
|
|
1786
|
+
!nodePath.scope.hasBinding("module", true));
|
|
1787
|
+
if (
|
|
1788
|
+
targetsExports &&
|
|
1789
|
+
property?.type === "StringLiteral" &&
|
|
1790
|
+
property.value !== "default" &&
|
|
1791
|
+
COMMONJS_NAMED_EXPORT_IDENTIFIER.test(property.value)
|
|
1792
|
+
) {
|
|
1793
|
+
names.add(property.value);
|
|
1794
|
+
}
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
if (node.callee.type === "Identifier" && node.callee.name === "__exportStar") {
|
|
1798
|
+
const source = node.arguments[0];
|
|
1799
|
+
const target = node.arguments[1];
|
|
1800
|
+
const targetsExports =
|
|
1801
|
+
(target?.type === "Identifier" &&
|
|
1802
|
+
target.name === "exports" &&
|
|
1803
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1804
|
+
(target?.type === "MemberExpression" &&
|
|
1805
|
+
!target.computed &&
|
|
1806
|
+
target.object.type === "Identifier" &&
|
|
1807
|
+
target.object.name === "module" &&
|
|
1808
|
+
target.property.type === "Identifier" &&
|
|
1809
|
+
target.property.name === "exports" &&
|
|
1810
|
+
!nodePath.scope.hasBinding("module", true));
|
|
1811
|
+
if (
|
|
1812
|
+
targetsExports &&
|
|
1813
|
+
source?.type === "CallExpression" &&
|
|
1814
|
+
source.callee.type === "Identifier" &&
|
|
1815
|
+
source.callee.name === "require" &&
|
|
1816
|
+
!nodePath.scope.hasBinding("require", true)
|
|
1817
|
+
) {
|
|
1818
|
+
const argument = source.arguments[0];
|
|
1819
|
+
if (argument?.type === "StringLiteral") {
|
|
1820
|
+
reexportSpecifiers.add(argument.value);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
if (node.type !== "AssignmentExpression" || node.operator !== "=" || node.left.type !== "MemberExpression") {
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
const left = node.left;
|
|
1830
|
+
const propertyName =
|
|
1831
|
+
!left.computed && left.property.type === "Identifier"
|
|
1832
|
+
? left.property.name
|
|
1833
|
+
: left.computed && left.property.type === "StringLiteral"
|
|
1834
|
+
? left.property.value
|
|
1835
|
+
: null;
|
|
1836
|
+
const object = left.object;
|
|
1837
|
+
const assignsExportsProperty =
|
|
1838
|
+
propertyName !== null &&
|
|
1839
|
+
((object.type === "Identifier" &&
|
|
1840
|
+
object.name === "exports" &&
|
|
1841
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1842
|
+
(object.type === "MemberExpression" &&
|
|
1843
|
+
!object.computed &&
|
|
1844
|
+
object.object.type === "Identifier" &&
|
|
1845
|
+
object.object.name === "module" &&
|
|
1846
|
+
object.property.type === "Identifier" &&
|
|
1847
|
+
object.property.name === "exports" &&
|
|
1848
|
+
!nodePath.scope.hasBinding("module", true)));
|
|
1849
|
+
if (assignsExportsProperty) {
|
|
1850
|
+
if (propertyName !== "default" && COMMONJS_NAMED_EXPORT_IDENTIFIER.test(propertyName)) {
|
|
1851
|
+
names.add(propertyName);
|
|
1852
|
+
}
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
const assignsModuleExports =
|
|
1856
|
+
!left.computed &&
|
|
1857
|
+
left.object.type === "Identifier" &&
|
|
1858
|
+
left.object.name === "module" &&
|
|
1859
|
+
left.property.type === "Identifier" &&
|
|
1860
|
+
left.property.name === "exports" &&
|
|
1861
|
+
!nodePath.scope.hasBinding("module", true);
|
|
1862
|
+
if (!assignsModuleExports) return;
|
|
1863
|
+
|
|
1864
|
+
const right = node.right;
|
|
1865
|
+
if (right.type === "ObjectExpression") {
|
|
1866
|
+
for (const property of right.properties) {
|
|
1867
|
+
if ((property.type !== "ObjectProperty" && property.type !== "ObjectMethod") || property.computed) {
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
const name =
|
|
1871
|
+
property.key.type === "Identifier"
|
|
1872
|
+
? property.key.name
|
|
1873
|
+
: property.key.type === "StringLiteral"
|
|
1874
|
+
? property.key.value
|
|
1875
|
+
: null;
|
|
1876
|
+
if (name && name !== "default" && COMMONJS_NAMED_EXPORT_IDENTIFIER.test(name)) {
|
|
1877
|
+
names.add(name);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
if (
|
|
1883
|
+
right.type === "CallExpression" &&
|
|
1884
|
+
right.callee.type === "Identifier" &&
|
|
1885
|
+
right.callee.name === "require" &&
|
|
1886
|
+
!nodePath.scope.hasBinding("require", true)
|
|
1887
|
+
) {
|
|
1888
|
+
const argument = right.arguments[0];
|
|
1889
|
+
if (argument?.type === "StringLiteral") {
|
|
1890
|
+
reexportSpecifiers.add(argument.value);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
},
|
|
1894
|
+
});
|
|
1895
|
+
const nativeRequire = createRequire(modulePath);
|
|
1896
|
+
for (const specifier of reexportSpecifiers) {
|
|
1897
|
+
try {
|
|
1898
|
+
const resolved = fs.realpathSync(nativeRequire.resolve(specifier));
|
|
1899
|
+
const reexportedSource = rewriteExtensionSpecifiersFromCache(fs.readFileSync(resolved, "utf8"), resolved);
|
|
1900
|
+
for (const name of collectCommonJsNamedExports(reexportedSource, resolved, visited)) {
|
|
1372
1901
|
names.add(name);
|
|
1373
1902
|
}
|
|
1903
|
+
} catch {
|
|
1904
|
+
// Native modules and non-source re-exports do not expose analyzable names.
|
|
1374
1905
|
}
|
|
1375
1906
|
}
|
|
1376
1907
|
return [...names];
|
|
@@ -1397,7 +1928,7 @@ function synthesizeCommonJsDefaultModule(modulePath: string, source: string, tar
|
|
|
1397
1928
|
});
|
|
1398
1929
|
commonJsModuleCache.delete(modulePath);
|
|
1399
1930
|
const exportsBinding = "__ompLegacyPiCommonJsExports";
|
|
1400
|
-
const namedExports = collectCommonJsNamedExports(executableSource)
|
|
1931
|
+
const namedExports = collectCommonJsNamedExports(executableSource, targetPath)
|
|
1401
1932
|
.map(
|
|
1402
1933
|
(name, index) =>
|
|
1403
1934
|
`const __ompLegacyPiCommonJsExport${index} = ${exportsBinding}[${JSON.stringify(name)}]; export { __ompLegacyPiCommonJsExport${index} as ${name} };`,
|
|
@@ -1428,24 +1959,23 @@ async function prepareCommonJsDefaultModule(modulePath: string, source: string):
|
|
|
1428
1959
|
|
|
1429
1960
|
/**
|
|
1430
1961
|
* Install exact-path load hooks for the current extension graph. ESM/TS source
|
|
1431
|
-
* retains the async rewrite path. CommonJS
|
|
1432
|
-
* stay synchronous because Bun rejects `require()` targets backed by
|
|
1433
|
-
* `onLoad` callbacks.
|
|
1962
|
+
* retains the async rewrite path. Graph-owned CommonJS modules and native-addon
|
|
1963
|
+
* loaders stay synchronous because Bun rejects `require()` targets backed by
|
|
1964
|
+
* async `onLoad` callbacks.
|
|
1434
1965
|
*/
|
|
1435
1966
|
async function installExtensionGraphHook(
|
|
1436
1967
|
entryRealPath: string,
|
|
1437
1968
|
modules: Map<string, string>,
|
|
1438
1969
|
commonJsPaths: Set<string>,
|
|
1970
|
+
cacheBustResolvedImportModules: ReadonlySet<string>,
|
|
1439
1971
|
): Promise<{ asyncModules: Map<string, string>; syncSourceModules: Map<string, string> }> {
|
|
1440
1972
|
const asyncModules = new Map<string, string>();
|
|
1441
1973
|
const syncSourceModules = new Map<string, string>();
|
|
1442
1974
|
for (const [modulePath, source] of modules) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}
|
|
1448
|
-
} else if (nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1975
|
+
if (commonJsPaths.has(modulePath)) {
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1449
1979
|
syncSourceModules.set(modulePath, source);
|
|
1450
1980
|
} else {
|
|
1451
1981
|
asyncModules.set(modulePath, source);
|
|
@@ -1472,8 +2002,9 @@ async function installExtensionGraphHook(
|
|
|
1472
2002
|
} else {
|
|
1473
2003
|
raw = await Bun.file(sourcePath).text();
|
|
1474
2004
|
}
|
|
2005
|
+
const resolvedImportMtimeTag = cacheBustResolvedImportModules.has(sourcePath) ? mtimeTag : null;
|
|
1475
2006
|
return {
|
|
1476
|
-
contents: await rewriteLegacyExtensionSource(raw, sourcePath, mtimeTag),
|
|
2007
|
+
contents: await rewriteLegacyExtensionSource(raw, sourcePath, mtimeTag, resolvedImportMtimeTag),
|
|
1477
2008
|
loader: getLoader(sourcePath),
|
|
1478
2009
|
};
|
|
1479
2010
|
});
|
|
@@ -1491,13 +2022,12 @@ async function installExtensionGraphHook(
|
|
|
1491
2022
|
build.onLoad({ filter, namespace: "file" }, args => {
|
|
1492
2023
|
const queryIndex = args.path.indexOf("?mtime=");
|
|
1493
2024
|
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
);
|
|
2025
|
+
let source = commonJsModuleSources.get(sourcePath);
|
|
2026
|
+
if (source === undefined) {
|
|
2027
|
+
const targetPath = commonJsFallbackModulePaths.get(sourcePath) ?? sourcePath;
|
|
2028
|
+
const raw = rewriteExtensionSpecifiersFromCache(fs.readFileSync(targetPath, "utf8"), sourcePath);
|
|
2029
|
+
source = synthesizeCommonJsDefaultModule(sourcePath, raw, targetPath);
|
|
2030
|
+
}
|
|
1501
2031
|
return { contents: source, loader: getLoader(sourcePath) };
|
|
1502
2032
|
});
|
|
1503
2033
|
},
|
|
@@ -1537,13 +2067,23 @@ async function installExtensionGraphHook(
|
|
|
1537
2067
|
* during the initial load; `undefined` when no new modules were discovered.
|
|
1538
2068
|
*/
|
|
1539
2069
|
async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(): void } | undefined> {
|
|
1540
|
-
const
|
|
1541
|
-
|
|
2070
|
+
const {
|
|
2071
|
+
modules: currentModules,
|
|
2072
|
+
commonJsPaths,
|
|
2073
|
+
cacheBustResolvedImportModules: discoveredCacheBustModules,
|
|
2074
|
+
} = await collectExtensionModules(entryRealPath);
|
|
2075
|
+
let cacheBustResolvedImportModules = extensionGraphCacheBustResolvedImportModules.get(entryRealPath);
|
|
2076
|
+
if (!cacheBustResolvedImportModules) {
|
|
2077
|
+
cacheBustResolvedImportModules = new Set<string>();
|
|
2078
|
+
extensionGraphCacheBustResolvedImportModules.set(entryRealPath, cacheBustResolvedImportModules);
|
|
2079
|
+
}
|
|
2080
|
+
for (const modulePath of discoveredCacheBustModules) {
|
|
2081
|
+
cacheBustResolvedImportModules.add(modulePath);
|
|
2082
|
+
}
|
|
1542
2083
|
for (const [modulePath, source] of currentModules) {
|
|
1543
|
-
|
|
1544
|
-
if (extension === ".cjs" || extension === ".cts") {
|
|
2084
|
+
if (commonJsPaths.has(modulePath)) {
|
|
1545
2085
|
commonJsModuleSources.set(modulePath, await prepareCommonJsDefaultModule(modulePath, source));
|
|
1546
|
-
|
|
2086
|
+
commonJsGraphModulePaths.add(modulePath);
|
|
1547
2087
|
}
|
|
1548
2088
|
}
|
|
1549
2089
|
let hookedModules = extensionGraphHookModules.get(entryRealPath);
|
|
@@ -1573,6 +2113,7 @@ async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(
|
|
|
1573
2113
|
entryRealPath,
|
|
1574
2114
|
pendingModules,
|
|
1575
2115
|
pendingCommonJsPaths,
|
|
2116
|
+
cacheBustResolvedImportModules,
|
|
1576
2117
|
));
|
|
1577
2118
|
for (const modulePath of pendingModules.keys()) {
|
|
1578
2119
|
hookedModules.add(modulePath);
|