Skip to content

feat(runner): add .ts↔.js ESM fallback for flow module resolution#1385

Merged
Michael Price (michael-pr) merged 8 commits into
mainfrom
investigate-module-resolution-failures
Jul 1, 2026
Merged

feat(runner): add .ts↔.js ESM fallback for flow module resolution#1385
Michael Price (michael-pr) merged 8 commits into
mainfrom
investigate-module-resolution-failures

Conversation

@michael-pr

@michael-pr Michael Price (michael-pr) commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description drafted with AI, edited and reviewed by me.

Overview of Changes

Platform-generated flow bundles frequently import sibling utilities with a .ts extension (e.g. import codeSnippets from "../../../utilities/code-snippets.ts"), but those modules ship compiled as .js. Native Node ESM (Node 24 type-stripping) resolves extensions literally and throws ERR_MODULE_NOT_FOUND — 301 of 465 flows in a representative bundle hit this. QA Wolf's cloud runtime tolerates the mismatch; native Node does not.

A synchronous module.registerHooks({ resolve }) hook is now registered in loadFlowDefault immediately before the non-compiled native import. Only on ERR_MODULE_NOT_FOUND does it retry the sibling source extension (.ts.js, .mts.mjs, .cts.cjs) and return the first that resolves. Literal matches always win, nothing is converted on disk (users keep authoring .ts), there is zero overhead on the normal path, and it emits nothing. The hook no-ops where registerHooks is unavailable (Bun, Node < 22.15).

Follow-up (out of scope): the compiled-binary path (Bun.build in bundleFlow/executorPlugin) needs the same swap as an onResolve fallback to cover binary distributions.

Testing

No meaningful standalone manual check — the fallback activates transparently when a flow bundle imports .ts siblings that only exist as .js on disk. Unit tests cover the extension-swap helper and all hook paths (passthrough, retry-on-not-found, non-ENOENT rethrow, original-error rethrow when swap also fails, bare-specifier no-swap).

Checklist

  • Changes follow the code style of this project
  • Self-review completed
  • Tests added/updated (or not applicable)
  • No breaking changes (or described below)

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

Walkthrough

A new swapSourceExtension helper maps .ts/.js, .mts/.mjs, and .cts/.cjs pairs for local file-like specifiers. A new flowResolveHook retries nextResolve on ERR_MODULE_NOT_FOUND by swapping the specifier extension and rethrowing otherwise. registerFlowModuleResolver registers the hook once with module.registerHooks when available. loadFlowDefault now invokes that registration before the unbundled import path. errorCode was added for error code extraction, and flushAndExit was added to flush stdout and stderr before process exit.

Sequence Diagram(s)

sequenceDiagram
  participant main
  participant parseAsync
  participant flushAndExit
  participant stdout
  participant stderr
  participant process

  main->>parseAsync: run command
  parseAsync-->>main: resolved or rejected
  main->>flushAndExit: exit code
  flushAndExit->>stdout: write empty buffer
  flushAndExit->>stderr: write empty buffer
  stdout-->>flushAndExit: flush callback
  stderr-->>flushAndExit: flush callback
  flushAndExit->>process: exit(code)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • smonn
  • chajac
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the flow-module ESM fallback change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description follows the template with overview, testing, and checklist sections; only the optional issue link and command examples are omitted.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch investigate-module-resolution-failures

Comment @coderabbitai help to get the list of available commands.

@michael-pr

Copy link
Copy Markdown
Contributor Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/shell/resolver/swapSourceExtension.ts`:
- Around line 16-22: Restrict swapSourceExtension to local file specifiers only,
since the current extension lookup in swapSourceExtension can rewrite
package-like imports as well as sibling files. Update the guard logic in
swapSourceExtension to allow only relative paths, absolute paths, and file: URLs
before checking extensionSwaps, and leave all other specifiers unchanged. Add a
regression test covering a package subpath such as pkg/file.js to verify it is
not swapped.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 19eaf8bb-e1a4-4657-803c-b5e5561affb3

📥 Commits

Reviewing files that changed from the base of the PR and between 9850bc3 and 259dbf8.

📒 Files selected for processing (5)
  • src/domains/runner/loadFlowDefault.ts
  • src/shell/resolver/registerFlowModuleResolver.test.ts
  • src/shell/resolver/registerFlowModuleResolver.ts
  • src/shell/resolver/swapSourceExtension.test.ts
  • src/shell/resolver/swapSourceExtension.ts

Comment thread src/shell/resolver/swapSourceExtension.ts
@michael-pr

Copy link
Copy Markdown
Contributor Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/shell/resolver/registerFlowModuleResolver.ts (1)

68-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a TODO $TICKET_ID tracking comment for this resolution fallback.

This registerHooks-based ESM extension-swap fallback is exactly the kind of workaround the repo conventions require tracking. There is no TODO $TICKET_ID comment referencing a Linear follow-up. Please file a ticket and add the reference (e.g. above registerFlowModuleResolver) so the fallback can be revisited/removed once bundles emit matching extensions.

As per path instructions: "For hacky workarounds, file a follow-up ticket in Linear and add a TODO $TICKET_ID comment to track it (relevant because this PR introduces an ESM resolution fallback via module.registerHooks)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shell/resolver/registerFlowModuleResolver.ts` around lines 68 - 89, Add a
tracking comment for the ESM extension-swap fallback in
registerFlowModuleResolver by filing a Linear ticket and placing a TODO
$TICKET_ID near the resolver setup (for example above registerFlowModuleResolver
or the registerHooks call). Keep the comment specific to the
flowResolveHook/registerHooks workaround so it is easy to find and remove once
bundled extensions match native Node resolution.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/shell/resolver/registerFlowModuleResolver.ts`:
- Around line 68-89: Add a tracking comment for the ESM extension-swap fallback
in registerFlowModuleResolver by filing a Linear ticket and placing a TODO
$TICKET_ID near the resolver setup (for example above registerFlowModuleResolver
or the registerHooks call). Keep the comment specific to the
flowResolveHook/registerHooks workaround so it is easy to find and remove once
bundled extensions match native Node resolution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b3727f34-eed3-4ce5-ad6f-17652a19ca32

📥 Commits

Reviewing files that changed from the base of the PR and between 259dbf8 and 621f5d4.

📒 Files selected for processing (5)
  • .changeset/ts-js-module-resolution-fallback.md
  • package.json
  • src/shell/resolver/registerFlowModuleResolver.ts
  • src/shell/resolver/swapSourceExtension.test.ts
  • src/shell/resolver/swapSourceExtension.ts

@michael-pr Michael Price (michael-pr) marked this pull request as ready for review June 30, 2026 19:08
@michael-pr Michael Price (michael-pr) merged commit f2ceb83 into main Jul 1, 2026
2 checks passed
@michael-pr Michael Price (michael-pr) deleted the investigate-module-resolution-failures branch July 1, 2026 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants