@oh-my-pi/pi-coding-agent 17.0.8 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -1
- package/README.md +1 -1
- package/dist/cli.js +5495 -5021
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +350 -50
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- 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 +14 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +18 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +8 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -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/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +120 -750
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +95 -1
- package/package.json +13 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +11 -2
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +50 -2
- package/src/config/model-resolver.ts +0 -1
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +250 -54
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- 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 +870 -311
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +52 -9
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +44 -11
- package/src/main.ts +9 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/render.ts +94 -35
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +27 -10
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +28 -3
- package/src/modes/components/settings-selector.ts +178 -6
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +70 -22
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +80 -41
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +160 -38
- 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/runtime-init.ts +2 -0
- 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 +23 -10
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/task.md +12 -2
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +255 -139
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1910 -12213
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +47 -5
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +10 -51
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +25 -4
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +288 -70
- package/src/task/index.ts +148 -111
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +4 -1
- package/src/task/types.ts +16 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +12 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +8 -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,
|
|
@@ -1207,7 +1504,25 @@ function evaluateGraphCommonJs(modulePath: string): unknown {
|
|
|
1207
1504
|
}
|
|
1208
1505
|
}
|
|
1209
1506
|
|
|
1210
|
-
|
|
1507
|
+
/**
|
|
1508
|
+
* Register {@link evaluateGraphCommonJs} as the graph-owned CommonJS require
|
|
1509
|
+
* bridge on `globalThis`, first-wins.
|
|
1510
|
+
*
|
|
1511
|
+
* On source-link installs the `@(scope)/pi-coding-agent` root shim is served
|
|
1512
|
+
* from `src/`, so an extension import can evaluate a second instance of this
|
|
1513
|
+
* module with empty graph state. An unconditional set would let that empty
|
|
1514
|
+
* instance clobber the host bundle's populated bridge and break transitive
|
|
1515
|
+
* CommonJS resolution (#6449); guarding preserves the first (host-owned)
|
|
1516
|
+
* registration. Idempotent: a subsequent call with a value already present is a
|
|
1517
|
+
* no-op.
|
|
1518
|
+
*/
|
|
1519
|
+
export function ensureGraphCommonJsRequireRegistered(): void {
|
|
1520
|
+
if (!Reflect.get(globalThis, COMMONJS_REQUIRE_GLOBAL)) {
|
|
1521
|
+
Reflect.set(globalThis, COMMONJS_REQUIRE_GLOBAL, evaluateGraphCommonJs);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
ensureGraphCommonJsRequireRegistered();
|
|
1211
1526
|
|
|
1212
1527
|
let legacyPiLoadTag = 0;
|
|
1213
1528
|
|
|
@@ -1234,30 +1549,43 @@ async function realpathOrSelfUncached(p: string): Promise<string> {
|
|
|
1234
1549
|
}
|
|
1235
1550
|
}
|
|
1236
1551
|
|
|
1552
|
+
interface ExtensionModuleGraph {
|
|
1553
|
+
readonly modules: Map<string, string>;
|
|
1554
|
+
readonly cacheBustResolvedImportModules: Set<string>;
|
|
1555
|
+
readonly commonJsPaths: Set<string>;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1237
1558
|
/**
|
|
1238
1559
|
* Walk the extension's import graph starting at `entryRealPath`, returning the
|
|
1239
1560
|
* 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.
|
|
1561
|
+
* Relative imports, package `imports` aliases, and ESM bare dependencies are
|
|
1562
|
+
* graph-owned recursively because compiled Bun cannot resolve runtime
|
|
1563
|
+
* `node_modules` from those modules. Graph-owned CommonJS modules also own
|
|
1564
|
+
* their relative and bare CommonJS descendants, which are evaluated by the
|
|
1565
|
+
* synchronous bridge. Resolved ESM imports inside third-party dependencies
|
|
1566
|
+
* omit the reload tag so their importer paths stay query-free.
|
|
1247
1567
|
*/
|
|
1248
|
-
async function collectExtensionModules(entryRealPath: string): Promise<
|
|
1568
|
+
async function collectExtensionModules(entryRealPath: string): Promise<ExtensionModuleGraph> {
|
|
1249
1569
|
const modules = new Map<string, string>();
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1570
|
+
const commonJsPaths = new Set<string>();
|
|
1571
|
+
const queuedCacheBustResolvedImports = new Map<string, boolean>([[entryRealPath, true]]);
|
|
1572
|
+
const queuedModuleKinds = new Map<string, ExtensionModuleKind>([[entryRealPath, "esm"]]);
|
|
1573
|
+
const queuedEsmBranchPaths = new Set<string>();
|
|
1574
|
+
const queue: Array<{
|
|
1575
|
+
file: string;
|
|
1576
|
+
cacheBustResolvedImports: boolean;
|
|
1577
|
+
moduleKind?: ExtensionModuleKind;
|
|
1578
|
+
esmBranch?: boolean;
|
|
1579
|
+
}> = [{ file: entryRealPath, cacheBustResolvedImports: true, moduleKind: "esm" }];
|
|
1254
1580
|
while (queue.length > 0) {
|
|
1255
1581
|
const item = queue.pop();
|
|
1256
1582
|
if (!item) {
|
|
1257
1583
|
continue;
|
|
1258
1584
|
}
|
|
1259
1585
|
const file = item.file;
|
|
1260
|
-
const
|
|
1586
|
+
const cacheBustResolvedImports = queuedCacheBustResolvedImports.get(file) ?? item.cacheBustResolvedImports;
|
|
1587
|
+
const inheritedModuleKind = queuedModuleKinds.get(file) ?? item.moduleKind;
|
|
1588
|
+
const esmBranch = queuedEsmBranchPaths.has(file) || item.esmBranch === true;
|
|
1261
1589
|
if (modules.has(file)) {
|
|
1262
1590
|
continue;
|
|
1263
1591
|
}
|
|
@@ -1268,109 +1596,330 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1268
1596
|
continue;
|
|
1269
1597
|
}
|
|
1270
1598
|
modules.set(file, source);
|
|
1271
|
-
const
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
requiredSpecifiers.add(match[2]);
|
|
1281
|
-
}
|
|
1599
|
+
const ast = parseExtensionSource(source, file);
|
|
1600
|
+
const sourceIsCommonJs = await isGraphOwnedCommonJsModule(
|
|
1601
|
+
file,
|
|
1602
|
+
entryRealPath,
|
|
1603
|
+
ast.program.sourceType,
|
|
1604
|
+
inheritedModuleKind,
|
|
1605
|
+
);
|
|
1606
|
+
if (sourceIsCommonJs) {
|
|
1607
|
+
commonJsPaths.add(file);
|
|
1282
1608
|
}
|
|
1283
|
-
|
|
1609
|
+
const dir = path.dirname(file);
|
|
1610
|
+
const references = collectExtensionSpecifierReferences(source, file, ast);
|
|
1611
|
+
for (const reference of references) {
|
|
1612
|
+
const specifier = reference.specifier;
|
|
1284
1613
|
try {
|
|
1285
1614
|
let resolved: string | null = null;
|
|
1286
|
-
let
|
|
1287
|
-
|
|
1615
|
+
let nextCacheBustResolvedImports = cacheBustResolvedImports;
|
|
1616
|
+
let resolvedModuleKind: ExtensionModuleKind | undefined;
|
|
1617
|
+
let resolvedEsmBranch = false;
|
|
1618
|
+
let requiresNativeAddonRewrite = false;
|
|
1619
|
+
const isRequired = reference.kind === "require";
|
|
1288
1620
|
if (specifier.startsWith(".")) {
|
|
1289
1621
|
const candidate = Bun.resolveSync(specifier, dir);
|
|
1290
|
-
if (
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1622
|
+
if (hasSourceModuleExtension(candidate)) {
|
|
1623
|
+
const inheritedTargetKind = isRequired
|
|
1624
|
+
? sourceIsCommonJs
|
|
1625
|
+
? "commonjs"
|
|
1626
|
+
: undefined
|
|
1627
|
+
: sourceIsCommonJs
|
|
1628
|
+
? "commonjs"
|
|
1629
|
+
: esmBranch
|
|
1630
|
+
? "esm"
|
|
1631
|
+
: undefined;
|
|
1632
|
+
const targetIsCommonJs = await isCommonJsModulePath(candidate, undefined, inheritedTargetKind);
|
|
1633
|
+
const isCommonJsDescendant = isRequired && sourceIsCommonJs && targetIsCommonJs;
|
|
1634
|
+
requiresNativeAddonRewrite =
|
|
1635
|
+
isRequired && !isCommonJsDescendant && (await moduleRequiresNativeAddon(candidate));
|
|
1636
|
+
if (!isRequired || isCommonJsDescendant || requiresNativeAddonRewrite) {
|
|
1637
|
+
resolved = await realpathOrSelf(candidate);
|
|
1638
|
+
resolvedModuleKind = targetIsCommonJs ? "commonjs" : "esm";
|
|
1639
|
+
resolvedEsmBranch = !targetIsCommonJs && esmBranch;
|
|
1640
|
+
}
|
|
1295
1641
|
}
|
|
1296
1642
|
} else if (specifier.startsWith("#")) {
|
|
1297
1643
|
const candidate = await resolvePackageImportSpecifier(specifier, file);
|
|
1298
|
-
if (candidate
|
|
1299
|
-
|
|
1644
|
+
if (candidate) {
|
|
1645
|
+
const inheritedTargetKind = isRequired
|
|
1646
|
+
? sourceIsCommonJs
|
|
1647
|
+
? "commonjs"
|
|
1648
|
+
: undefined
|
|
1649
|
+
: sourceIsCommonJs
|
|
1650
|
+
? "commonjs"
|
|
1651
|
+
: esmBranch
|
|
1652
|
+
? "esm"
|
|
1653
|
+
: undefined;
|
|
1654
|
+
const targetIsCommonJs = await isCommonJsModulePath(candidate, undefined, inheritedTargetKind);
|
|
1655
|
+
const isCommonJsDescendant = isRequired && sourceIsCommonJs && targetIsCommonJs;
|
|
1656
|
+
requiresNativeAddonRewrite =
|
|
1657
|
+
isRequired && !isCommonJsDescendant && (await moduleRequiresNativeAddon(candidate));
|
|
1658
|
+
if (!isRequired || isCommonJsDescendant || requiresNativeAddonRewrite) {
|
|
1659
|
+
resolved = candidate;
|
|
1660
|
+
resolvedModuleKind = targetIsCommonJs ? "commonjs" : "esm";
|
|
1661
|
+
resolvedEsmBranch = !targetIsCommonJs && esmBranch;
|
|
1662
|
+
}
|
|
1300
1663
|
}
|
|
1301
1664
|
} else if (
|
|
1302
|
-
followBareDependencies &&
|
|
1303
1665
|
isBareExtensionDependencySpecifier(specifier) &&
|
|
1304
1666
|
!remapLegacyPiSpecifier(specifier) &&
|
|
1305
1667
|
specifier !== "typebox" &&
|
|
1306
1668
|
specifier !== "@sinclair/typebox"
|
|
1307
1669
|
) {
|
|
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");
|
|
1670
|
+
const dependencyEntry = isRequired
|
|
1671
|
+
? await resolveExtensionBareRequire(specifier, file)
|
|
1672
|
+
: await resolveExtensionBareDependency(specifier, file);
|
|
1317
1673
|
const isHookableEntry = Boolean(dependencyEntry && hasSourceModuleExtension(dependencyEntry));
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1674
|
+
const selectedEsmBranch =
|
|
1675
|
+
!isRequired &&
|
|
1676
|
+
isHookableEntry &&
|
|
1677
|
+
dependencyEntry !== null &&
|
|
1678
|
+
(await isSelectedNoTypeEsmPackageBranch(specifier, file, dependencyEntry));
|
|
1679
|
+
const inheritedTargetKind = isRequired
|
|
1680
|
+
? sourceIsCommonJs
|
|
1681
|
+
? "commonjs"
|
|
1682
|
+
: undefined
|
|
1683
|
+
: selectedEsmBranch
|
|
1684
|
+
? "esm"
|
|
1685
|
+
: undefined;
|
|
1686
|
+
const isCommonJsEntry =
|
|
1687
|
+
isHookableEntry && dependencyEntry
|
|
1688
|
+
? await isCommonJsModulePath(dependencyEntry, undefined, inheritedTargetKind)
|
|
1321
1689
|
: false;
|
|
1322
|
-
if (isHookableEntry && dependencyEntry && (
|
|
1690
|
+
if (isHookableEntry && dependencyEntry && (!isRequired || (sourceIsCommonJs && isCommonJsEntry))) {
|
|
1323
1691
|
resolved = await realpathOrSelf(dependencyEntry);
|
|
1692
|
+
} else if (isHookableEntry && dependencyEntry && isRequired) {
|
|
1693
|
+
requiresNativeAddonRewrite = await moduleRequiresNativeAddon(dependencyEntry);
|
|
1694
|
+
if (requiresNativeAddonRewrite) {
|
|
1695
|
+
resolved = await realpathOrSelf(dependencyEntry);
|
|
1696
|
+
}
|
|
1324
1697
|
}
|
|
1325
|
-
if (resolved
|
|
1326
|
-
|
|
1698
|
+
if (resolved) {
|
|
1699
|
+
resolvedModuleKind = isCommonJsEntry ? "commonjs" : "esm";
|
|
1700
|
+
resolvedEsmBranch = selectedEsmBranch && !isCommonJsEntry;
|
|
1327
1701
|
}
|
|
1328
|
-
|
|
1702
|
+
nextCacheBustResolvedImports = false;
|
|
1329
1703
|
}
|
|
1330
|
-
if (resolved &&
|
|
1704
|
+
if (resolved && requiresNativeAddonRewrite) {
|
|
1331
1705
|
nativeAddonLoaderModulePaths.add(resolved);
|
|
1332
1706
|
}
|
|
1333
|
-
if (resolved
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1707
|
+
if (resolved) {
|
|
1708
|
+
const queuedCacheBust = queuedCacheBustResolvedImports.get(resolved) ?? false;
|
|
1709
|
+
const mergedCacheBust = queuedCacheBust || nextCacheBustResolvedImports;
|
|
1710
|
+
queuedCacheBustResolvedImports.set(resolved, mergedCacheBust);
|
|
1711
|
+
const queuedModuleKind = queuedModuleKinds.get(resolved);
|
|
1712
|
+
if (queuedModuleKind && resolvedModuleKind && queuedModuleKind !== resolvedModuleKind) {
|
|
1713
|
+
throw new ExtensionModuleKindConflictError(
|
|
1714
|
+
`Conflicting extension module kinds for ${resolved}: ${queuedModuleKind} and ${resolvedModuleKind}`,
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
const mergedModuleKind = queuedModuleKind ?? resolvedModuleKind;
|
|
1718
|
+
if (mergedModuleKind) {
|
|
1719
|
+
queuedModuleKinds.set(resolved, mergedModuleKind);
|
|
1720
|
+
}
|
|
1721
|
+
if (resolvedEsmBranch) {
|
|
1722
|
+
queuedEsmBranchPaths.add(resolved);
|
|
1723
|
+
}
|
|
1724
|
+
if (!modules.has(resolved)) {
|
|
1725
|
+
queue.push({
|
|
1726
|
+
file: resolved,
|
|
1727
|
+
cacheBustResolvedImports: mergedCacheBust,
|
|
1728
|
+
moduleKind: mergedModuleKind,
|
|
1729
|
+
esmBranch: resolvedEsmBranch,
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
if (error instanceof ExtensionModuleKindConflictError) {
|
|
1735
|
+
throw error;
|
|
1338
1736
|
}
|
|
1339
|
-
} catch {
|
|
1340
1737
|
// Unresolvable import (e.g. a type-only path); skip it.
|
|
1341
1738
|
}
|
|
1342
1739
|
}
|
|
1343
1740
|
}
|
|
1344
|
-
for (const modulePath of
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
modules.set(modulePath, await rewriteExtensionNativeAddonRequires(source, modulePath));
|
|
1741
|
+
for (const [modulePath, source] of modules) {
|
|
1742
|
+
if (commonJsPaths.has(modulePath) || nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1743
|
+
modules.set(modulePath, await rewriteExtensionSpecifiers(source, modulePath, commonJsPaths.has(modulePath)));
|
|
1348
1744
|
}
|
|
1349
1745
|
}
|
|
1350
|
-
return
|
|
1746
|
+
return {
|
|
1747
|
+
modules,
|
|
1748
|
+
commonJsPaths,
|
|
1749
|
+
cacheBustResolvedImportModules: new Set(
|
|
1750
|
+
[...queuedCacheBustResolvedImports]
|
|
1751
|
+
.filter(([modulePath, enabled]) => enabled && modules.has(modulePath))
|
|
1752
|
+
.map(([modulePath]) => modulePath),
|
|
1753
|
+
),
|
|
1754
|
+
};
|
|
1351
1755
|
}
|
|
1352
1756
|
|
|
1353
1757
|
/**
|
|
1354
1758
|
* Discovers CommonJS export names Bun normally exposes to ESM importers. The
|
|
1355
1759
|
* bridge must declare them statically because its default export is synthetic.
|
|
1356
1760
|
*/
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1761
|
+
const COMMONJS_NAMED_EXPORT_IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
1762
|
+
|
|
1763
|
+
function collectCommonJsNamedExports(source: string, modulePath: string, visited = new Set<string>()): string[] {
|
|
1764
|
+
let realModulePath = modulePath;
|
|
1765
|
+
try {
|
|
1766
|
+
realModulePath = fs.realpathSync(modulePath);
|
|
1767
|
+
} catch {
|
|
1768
|
+
// The caller's path remains the stable cycle key when realpath fails.
|
|
1769
|
+
}
|
|
1770
|
+
if (visited.has(realModulePath)) {
|
|
1771
|
+
return [];
|
|
1365
1772
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1773
|
+
visited.add(realModulePath);
|
|
1774
|
+
|
|
1775
|
+
const names = new Set<string>();
|
|
1776
|
+
|
|
1777
|
+
const reexportSpecifiers = new Set<string>();
|
|
1778
|
+
const ast = parseExtensionSource(source, modulePath);
|
|
1779
|
+
traverseAst(ast, {
|
|
1780
|
+
enter(nodePath) {
|
|
1781
|
+
const node = nodePath.node;
|
|
1782
|
+
if (node.type === "CallExpression") {
|
|
1783
|
+
const definePropertyCall =
|
|
1784
|
+
node.callee.type === "MemberExpression" &&
|
|
1785
|
+
!node.callee.computed &&
|
|
1786
|
+
node.callee.object.type === "Identifier" &&
|
|
1787
|
+
node.callee.object.name === "Object" &&
|
|
1788
|
+
node.callee.property.type === "Identifier" &&
|
|
1789
|
+
node.callee.property.name === "defineProperty" &&
|
|
1790
|
+
!nodePath.scope.hasBinding("Object", true);
|
|
1791
|
+
if (definePropertyCall) {
|
|
1792
|
+
const target = node.arguments[0];
|
|
1793
|
+
const property = node.arguments[1];
|
|
1794
|
+
const targetsExports =
|
|
1795
|
+
(target?.type === "Identifier" &&
|
|
1796
|
+
target.name === "exports" &&
|
|
1797
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1798
|
+
(target?.type === "MemberExpression" &&
|
|
1799
|
+
!target.computed &&
|
|
1800
|
+
target.object.type === "Identifier" &&
|
|
1801
|
+
target.object.name === "module" &&
|
|
1802
|
+
target.property.type === "Identifier" &&
|
|
1803
|
+
target.property.name === "exports" &&
|
|
1804
|
+
!nodePath.scope.hasBinding("module", true));
|
|
1805
|
+
if (
|
|
1806
|
+
targetsExports &&
|
|
1807
|
+
property?.type === "StringLiteral" &&
|
|
1808
|
+
property.value !== "default" &&
|
|
1809
|
+
COMMONJS_NAMED_EXPORT_IDENTIFIER.test(property.value)
|
|
1810
|
+
) {
|
|
1811
|
+
names.add(property.value);
|
|
1812
|
+
}
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (node.callee.type === "Identifier" && node.callee.name === "__exportStar") {
|
|
1816
|
+
const source = node.arguments[0];
|
|
1817
|
+
const target = node.arguments[1];
|
|
1818
|
+
const targetsExports =
|
|
1819
|
+
(target?.type === "Identifier" &&
|
|
1820
|
+
target.name === "exports" &&
|
|
1821
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1822
|
+
(target?.type === "MemberExpression" &&
|
|
1823
|
+
!target.computed &&
|
|
1824
|
+
target.object.type === "Identifier" &&
|
|
1825
|
+
target.object.name === "module" &&
|
|
1826
|
+
target.property.type === "Identifier" &&
|
|
1827
|
+
target.property.name === "exports" &&
|
|
1828
|
+
!nodePath.scope.hasBinding("module", true));
|
|
1829
|
+
if (
|
|
1830
|
+
targetsExports &&
|
|
1831
|
+
source?.type === "CallExpression" &&
|
|
1832
|
+
source.callee.type === "Identifier" &&
|
|
1833
|
+
source.callee.name === "require" &&
|
|
1834
|
+
!nodePath.scope.hasBinding("require", true)
|
|
1835
|
+
) {
|
|
1836
|
+
const argument = source.arguments[0];
|
|
1837
|
+
if (argument?.type === "StringLiteral") {
|
|
1838
|
+
reexportSpecifiers.add(argument.value);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
if (node.type !== "AssignmentExpression" || node.operator !== "=" || node.left.type !== "MemberExpression") {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
const left = node.left;
|
|
1848
|
+
const propertyName =
|
|
1849
|
+
!left.computed && left.property.type === "Identifier"
|
|
1850
|
+
? left.property.name
|
|
1851
|
+
: left.computed && left.property.type === "StringLiteral"
|
|
1852
|
+
? left.property.value
|
|
1853
|
+
: null;
|
|
1854
|
+
const object = left.object;
|
|
1855
|
+
const assignsExportsProperty =
|
|
1856
|
+
propertyName !== null &&
|
|
1857
|
+
((object.type === "Identifier" &&
|
|
1858
|
+
object.name === "exports" &&
|
|
1859
|
+
!nodePath.scope.hasBinding("exports", true)) ||
|
|
1860
|
+
(object.type === "MemberExpression" &&
|
|
1861
|
+
!object.computed &&
|
|
1862
|
+
object.object.type === "Identifier" &&
|
|
1863
|
+
object.object.name === "module" &&
|
|
1864
|
+
object.property.type === "Identifier" &&
|
|
1865
|
+
object.property.name === "exports" &&
|
|
1866
|
+
!nodePath.scope.hasBinding("module", true)));
|
|
1867
|
+
if (assignsExportsProperty) {
|
|
1868
|
+
if (propertyName !== "default" && COMMONJS_NAMED_EXPORT_IDENTIFIER.test(propertyName)) {
|
|
1869
|
+
names.add(propertyName);
|
|
1870
|
+
}
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
const assignsModuleExports =
|
|
1874
|
+
!left.computed &&
|
|
1875
|
+
left.object.type === "Identifier" &&
|
|
1876
|
+
left.object.name === "module" &&
|
|
1877
|
+
left.property.type === "Identifier" &&
|
|
1878
|
+
left.property.name === "exports" &&
|
|
1879
|
+
!nodePath.scope.hasBinding("module", true);
|
|
1880
|
+
if (!assignsModuleExports) return;
|
|
1881
|
+
|
|
1882
|
+
const right = node.right;
|
|
1883
|
+
if (right.type === "ObjectExpression") {
|
|
1884
|
+
for (const property of right.properties) {
|
|
1885
|
+
if ((property.type !== "ObjectProperty" && property.type !== "ObjectMethod") || property.computed) {
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
const name =
|
|
1889
|
+
property.key.type === "Identifier"
|
|
1890
|
+
? property.key.name
|
|
1891
|
+
: property.key.type === "StringLiteral"
|
|
1892
|
+
? property.key.value
|
|
1893
|
+
: null;
|
|
1894
|
+
if (name && name !== "default" && COMMONJS_NAMED_EXPORT_IDENTIFIER.test(name)) {
|
|
1895
|
+
names.add(name);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
if (
|
|
1901
|
+
right.type === "CallExpression" &&
|
|
1902
|
+
right.callee.type === "Identifier" &&
|
|
1903
|
+
right.callee.name === "require" &&
|
|
1904
|
+
!nodePath.scope.hasBinding("require", true)
|
|
1905
|
+
) {
|
|
1906
|
+
const argument = right.arguments[0];
|
|
1907
|
+
if (argument?.type === "StringLiteral") {
|
|
1908
|
+
reexportSpecifiers.add(argument.value);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
},
|
|
1912
|
+
});
|
|
1913
|
+
const nativeRequire = createRequire(modulePath);
|
|
1914
|
+
for (const specifier of reexportSpecifiers) {
|
|
1915
|
+
try {
|
|
1916
|
+
const resolved = fs.realpathSync(nativeRequire.resolve(specifier));
|
|
1917
|
+
const reexportedSource = rewriteExtensionSpecifiersFromCache(fs.readFileSync(resolved, "utf8"), resolved);
|
|
1918
|
+
for (const name of collectCommonJsNamedExports(reexportedSource, resolved, visited)) {
|
|
1372
1919
|
names.add(name);
|
|
1373
1920
|
}
|
|
1921
|
+
} catch {
|
|
1922
|
+
// Native modules and non-source re-exports do not expose analyzable names.
|
|
1374
1923
|
}
|
|
1375
1924
|
}
|
|
1376
1925
|
return [...names];
|
|
@@ -1397,7 +1946,7 @@ function synthesizeCommonJsDefaultModule(modulePath: string, source: string, tar
|
|
|
1397
1946
|
});
|
|
1398
1947
|
commonJsModuleCache.delete(modulePath);
|
|
1399
1948
|
const exportsBinding = "__ompLegacyPiCommonJsExports";
|
|
1400
|
-
const namedExports = collectCommonJsNamedExports(executableSource)
|
|
1949
|
+
const namedExports = collectCommonJsNamedExports(executableSource, targetPath)
|
|
1401
1950
|
.map(
|
|
1402
1951
|
(name, index) =>
|
|
1403
1952
|
`const __ompLegacyPiCommonJsExport${index} = ${exportsBinding}[${JSON.stringify(name)}]; export { __ompLegacyPiCommonJsExport${index} as ${name} };`,
|
|
@@ -1428,24 +1977,23 @@ async function prepareCommonJsDefaultModule(modulePath: string, source: string):
|
|
|
1428
1977
|
|
|
1429
1978
|
/**
|
|
1430
1979
|
* 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.
|
|
1980
|
+
* retains the async rewrite path. Graph-owned CommonJS modules and native-addon
|
|
1981
|
+
* loaders stay synchronous because Bun rejects `require()` targets backed by
|
|
1982
|
+
* async `onLoad` callbacks.
|
|
1434
1983
|
*/
|
|
1435
1984
|
async function installExtensionGraphHook(
|
|
1436
1985
|
entryRealPath: string,
|
|
1437
1986
|
modules: Map<string, string>,
|
|
1438
1987
|
commonJsPaths: Set<string>,
|
|
1988
|
+
cacheBustResolvedImportModules: ReadonlySet<string>,
|
|
1439
1989
|
): Promise<{ asyncModules: Map<string, string>; syncSourceModules: Map<string, string> }> {
|
|
1440
1990
|
const asyncModules = new Map<string, string>();
|
|
1441
1991
|
const syncSourceModules = new Map<string, string>();
|
|
1442
1992
|
for (const [modulePath, source] of modules) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}
|
|
1448
|
-
} else if (nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1993
|
+
if (commonJsPaths.has(modulePath)) {
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
if (nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1449
1997
|
syncSourceModules.set(modulePath, source);
|
|
1450
1998
|
} else {
|
|
1451
1999
|
asyncModules.set(modulePath, source);
|
|
@@ -1472,8 +2020,9 @@ async function installExtensionGraphHook(
|
|
|
1472
2020
|
} else {
|
|
1473
2021
|
raw = await Bun.file(sourcePath).text();
|
|
1474
2022
|
}
|
|
2023
|
+
const resolvedImportMtimeTag = cacheBustResolvedImportModules.has(sourcePath) ? mtimeTag : null;
|
|
1475
2024
|
return {
|
|
1476
|
-
contents: await rewriteLegacyExtensionSource(raw, sourcePath, mtimeTag),
|
|
2025
|
+
contents: await rewriteLegacyExtensionSource(raw, sourcePath, mtimeTag, resolvedImportMtimeTag),
|
|
1477
2026
|
loader: getLoader(sourcePath),
|
|
1478
2027
|
};
|
|
1479
2028
|
});
|
|
@@ -1491,13 +2040,12 @@ async function installExtensionGraphHook(
|
|
|
1491
2040
|
build.onLoad({ filter, namespace: "file" }, args => {
|
|
1492
2041
|
const queryIndex = args.path.indexOf("?mtime=");
|
|
1493
2042
|
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
);
|
|
2043
|
+
let source = commonJsModuleSources.get(sourcePath);
|
|
2044
|
+
if (source === undefined) {
|
|
2045
|
+
const targetPath = commonJsFallbackModulePaths.get(sourcePath) ?? sourcePath;
|
|
2046
|
+
const raw = rewriteExtensionSpecifiersFromCache(fs.readFileSync(targetPath, "utf8"), sourcePath);
|
|
2047
|
+
source = synthesizeCommonJsDefaultModule(sourcePath, raw, targetPath);
|
|
2048
|
+
}
|
|
1501
2049
|
return { contents: source, loader: getLoader(sourcePath) };
|
|
1502
2050
|
});
|
|
1503
2051
|
},
|
|
@@ -1537,13 +2085,23 @@ async function installExtensionGraphHook(
|
|
|
1537
2085
|
* during the initial load; `undefined` when no new modules were discovered.
|
|
1538
2086
|
*/
|
|
1539
2087
|
async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(): void } | undefined> {
|
|
1540
|
-
const
|
|
1541
|
-
|
|
2088
|
+
const {
|
|
2089
|
+
modules: currentModules,
|
|
2090
|
+
commonJsPaths,
|
|
2091
|
+
cacheBustResolvedImportModules: discoveredCacheBustModules,
|
|
2092
|
+
} = await collectExtensionModules(entryRealPath);
|
|
2093
|
+
let cacheBustResolvedImportModules = extensionGraphCacheBustResolvedImportModules.get(entryRealPath);
|
|
2094
|
+
if (!cacheBustResolvedImportModules) {
|
|
2095
|
+
cacheBustResolvedImportModules = new Set<string>();
|
|
2096
|
+
extensionGraphCacheBustResolvedImportModules.set(entryRealPath, cacheBustResolvedImportModules);
|
|
2097
|
+
}
|
|
2098
|
+
for (const modulePath of discoveredCacheBustModules) {
|
|
2099
|
+
cacheBustResolvedImportModules.add(modulePath);
|
|
2100
|
+
}
|
|
1542
2101
|
for (const [modulePath, source] of currentModules) {
|
|
1543
|
-
|
|
1544
|
-
if (extension === ".cjs" || extension === ".cts") {
|
|
2102
|
+
if (commonJsPaths.has(modulePath)) {
|
|
1545
2103
|
commonJsModuleSources.set(modulePath, await prepareCommonJsDefaultModule(modulePath, source));
|
|
1546
|
-
|
|
2104
|
+
commonJsGraphModulePaths.add(modulePath);
|
|
1547
2105
|
}
|
|
1548
2106
|
}
|
|
1549
2107
|
let hookedModules = extensionGraphHookModules.get(entryRealPath);
|
|
@@ -1573,6 +2131,7 @@ async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(
|
|
|
1573
2131
|
entryRealPath,
|
|
1574
2132
|
pendingModules,
|
|
1575
2133
|
pendingCommonJsPaths,
|
|
2134
|
+
cacheBustResolvedImportModules,
|
|
1576
2135
|
));
|
|
1577
2136
|
for (const modulePath of pendingModules.keys()) {
|
|
1578
2137
|
hookedModules.add(modulePath);
|