Skip to content

fix(build): scope external() to own src/external tree - #224

Merged
John-David Dalton (jdalton) merged 1 commit into
mainfrom
jdalton/scope-external-predicate
Aug 4, 2026
Merged

John-David Dalton (jdalton) merged 1 commit into
mainfrom
jdalton/scope-external-predicate

Conversation

@jdalton

@jdalton John-David Dalton (jdalton) commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

The main build's external() treats any path with an external/ segment as external. That regex also matches a dependency's nested dist/external/* — an inlined package vendoring its own externals — externalizing modules that were meant to be bundled and emitting relative requires into files that don't exist next to the output.

This isn't hypothetical: socket-sdk-js copied this predicate while adopting the src/external/ vendoring convention (socket-sdk-js#681) and its build broke exactly this way — the SDK inlines @socketsecurity/lib, and the blanket match externalized lib's own dist/external/pony-cause, producing Could not resolve "../node_modules/.../dist/external/pony-cause". The same class of break (a specifier shipped with nothing behind it) is what took down socket@1.1.151's uploads for a customer.

Fix: match by resolved path under this repo's src/external/ instead. Bare specifiers stay external exactly as before; own shims are recognized whether imported relatively (../external/fast-glob.js from src/globs/_internal.ts) or absolutely; anything under a dependency's node_modules no longer qualifies.

Behavior-preserving, proven rather than argued: the emitted dist is byte-identical across every .js file before and after the change (sha256 manifest of the full tree: 3996e6c9deea39ca both ways), and the build's own validators pass unchanged (610 public exports, 48/51 external-module interop checks). Six unit tests pin the predicate semantics, including the nested-node_modules regression case.

Actions needed

  • Review + merge. Nothing downstream depends on ordering — the emitted output is identical, so this can land whenever.

The main build's external() treated ANY path with an external/
segment as external. That also matches a dependency's nested
dist/external/* — an inlined package vendoring its own externals —
externalizing modules that were meant to be bundled and emitting
relative requires into files that don't exist next to the output.
socket-sdk-js copied this predicate while adopting the src/external
vendoring convention and its build broke exactly this way on
@socketsecurity/lib's own dist/external/pony-cause; the same class
of break shipped socket-cli 1.1.151's "Cannot find module
'form-data'" outage.

Match by resolved path under THIS repo's src/external instead:
bare specifiers stay external as before, own shims are recognized
whether imported relatively or absolutely, and anything under a
dependency's node_modules no longer qualifies.

Behavior-preserving for this repo, proven empirically: the emitted
dist is byte-identical across every .js file before and after the
change (sha256 manifest compare), and the build's own export/interop
validators pass unchanged. Unit tests pin the predicate semantics,
including the nested-node_modules regression case.
@jdalton
John-David Dalton (jdalton) merged commit b4e24f9 into main Aug 4, 2026
4 checks passed
@jdalton
John-David Dalton (jdalton) deleted the jdalton/scope-external-predicate branch August 4, 2026 19:33
John-David Dalton (jdalton) added a commit that referenced this pull request Aug 4, 2026
The main build's external() treated ANY path with an external/
segment as external. That also matches a dependency's nested
dist/external/* — an inlined package vendoring its own externals —
externalizing modules that were meant to be bundled and emitting
relative requires into files that don't exist next to the output.
socket-sdk-js copied this predicate while adopting the src/external
vendoring convention and its build broke exactly this way on
@socketsecurity/lib's own dist/external/pony-cause; the same class
of break shipped socket-cli 1.1.151's "Cannot find module
'form-data'" outage.

Match by resolved path under THIS repo's src/external instead:
bare specifiers stay external as before, own shims are recognized
whether imported relatively or absolutely, and anything under a
dependency's node_modules no longer qualifies.

Behavior-preserving for this repo, proven empirically: the emitted
dist is byte-identical across every .js file before and after the
change (sha256 manifest compare), and the build's own export/interop
validators pass unchanged. Unit tests pin the predicate semantics,
including the nested-node_modules regression case.
John-David Dalton (jdalton) added a commit that referenced this pull request Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant