Repository navigation
feat(webpack,vite): keep ns: builtin module imports for the runtime - #11626
Merged
Merged
Conversation
The iOS and Android runtimes resolve `ns:<name>` builtin modules (and a few `node:<name>` compatibility shims) themselves. Webpack failed on these imports with an unhandled-scheme error, and Vite failed to resolve `ns:` specifiers and replaced `node:` ones with an empty browser stub. Every `ns:` specifier is now left verbatim in the output, matched by prefix so modules added in future runtime versions work without changes. Both packages expose the same helpers: - addBuiltinModule(matcher, target?) opts more specifiers in, e.g. the `node:` shims a runtime version ships, optionally forwarding a bare specifier such as `worker_threads` to `node:worker_threads`. - polyfillBuiltinModule(specifier, path) bundles a replacement instead, for runtimes that don't provide a builtin. In Vite this covers `vite build`, workers, dep optimization and the HMR dev server (esbuild dependency bundles, `/@id/` unwrapping, worker realm routing, core rewriting, the dev import map and the CommonJS require shim).
|
View your CI Pipeline Execution ↗ for commit 8635dd3
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
commit: |
…ive hooks
Replace the addBuiltinModule/polyfillBuiltinModule helpers with a single
plugin per bundler, customized the way each bundler is normally
customized:
- webpack: NativeScriptBuiltinModulesPlugin, registered by name so
webpack-chain can `.tap()` its `include` list. It externalizes on the
request after `beforeResolve`, so a NormalModuleReplacementPlugin can
redirect a builtin to a polyfill or forward a bare specifier to one.
- vite: nativescriptBuiltinModules({ include }). Extra instances add
specifiers; resolve.alias handles polyfills and forwarding, since
aliases resolve before the plugin.
NathanWalker
approved these changes
Nov 9, 2026
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
What is the current behavior?
The iOS and Android runtimes resolve
ns:<name>builtin modules (ns:util,ns:module,ns:runtime,ns:url,ns:worker_threads, …) and a fewnode:<name>compatibility shims themselves, before any file or npm resolution. Neither bundler lets these imports through:ns:utilas a URL scheme, which skipsresolve.alias, and fails withUnhandledSchemeError.node:*fails the same way, becauseexternalsPresets.nodeis off.Rolldown failed to resolve import "ns:util", and silently replacesnode:url/node:util/node:worker_threadswith an empty browser stub. Under HMR, several dev-server passes also rewrite or stub these specifiers:/@id/wrapping, routing to/ns/m/node_modules/ns:util, the esbuild dependency bundles, the dev import map, and the CommonJSrequireshim.What is the new behavior?
Every
ns:import is left verbatim in the output. Specifiers are matched by thens:prefix, so modules added in future runtime versions work without changes here. ESM output keepsimport … from "ns:util"; CommonJS output keepsrequire("ns:util").Customization uses each bundler's own mechanisms:
webpack: the base config registers
NativeScriptBuiltinModulesPlugin(defaultinclude: [/^ns:/]).includelist with webpack-chain:config.plugin('NativeScriptBuiltinModulesPlugin').tap(...).beforeResolve, so a stockNormalModuleReplacementPlugincan:/^ns:url$/→ a file);/^worker_threads$/→node:worker_threads).Vite: the base config adds
nativescriptBuiltinModules().{ include: ['node:worker_threads'] }to opt in more specifiers.resolve.aliashandles polyfills and forwarding, since aliases resolve before the plugin.node:stays opt-in rather than hardcoding the current shim list, because the set grows with each runtime version. Both READMEs show how to forward to the runtime's shims, usingworker_threadsas the example.Vite coverage:
vite build: the plugin runs first in the main build, worker builds and the dependency optimizer./@id/<builtin>is unwrapped before the device rewriting passes;rewriteSpec, worker-realm module bindings and the plugin-specifier check keep these imports untouched;node:names are left out of the dev import map;requireshim tries the runtime'srequirefirst forns:/node:ids.Tests:
packages/webpack5/__tests__/plugins/NativeScriptBuiltinModulesPlugin.spec.ts: real webpack compiles checking ESM and CommonJS output, polyfill and forwarding throughNormalModuleReplacementPlugin, and the build failure without the plugin.packages/vite/helpers/builtin-modules.spec.ts: in-processvite build(ESM,import(),require()in a CommonJS dependency, alias forwarding and polyfill, and the failure without the plugin), plus the esbuild plugin.packages/vite/hmr/server/builtin-modules-hmr.spec.ts: each HMR touch point. All four tests fail without the HMR changes.Notes for reviewers:
main: they contain absolute paths from another machine and aFixSourceMapUrlPluginthat no longer exists. I added only the new plugin entry to them rather than regenerating. The set of failing tests is identical tomain. The mdn-data alias re-wrap inbase.tscomes from the prettier pre-commit hook.packages/vite/typescript-optional.spec.tsalso fails onmain(5 tests) and is unchanged by this PR.resolve.alias. Ans:polyfill or bare-name forward set up with an alias applies tovite buildand the Vite module graph, but not to dependencies bundled by esbuild during a dev session.ns:utilfrom app code, a CommonJS dependency and a worker. Opt innode:worker_threadsand check that it reaches the runtime, under bothns run(HMR) and a release build.