Skip to content

Commit

Permalink
Support bundling node native modules (#10066)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Jan 2, 2025
1 parent 1dd802c commit 4f3bcaa
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 29 deletions.
1 change: 1 addition & 0 deletions packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
],
"*.svg": ["@parcel/transformer-svg"],
"*.{xml,rss,atom}": ["@parcel/transformer-xml"],
"*.node": ["@parcel/transformer-node"],
"url:*": ["...", "@parcel/transformer-raw"]
},
"namers": ["@parcel/namer-default"],
Expand Down
1 change: 1 addition & 0 deletions packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@parcel/transformer-image": "2.13.3",
"@parcel/transformer-js": "2.13.3",
"@parcel/transformer-json": "2.13.3",
"@parcel/transformer-node": "2.13.3",
"@parcel/transformer-postcss": "2.13.3",
"@parcel/transformer-posthtml": "2.13.3",
"@parcel/transformer-raw": "2.13.3",
Expand Down
48 changes: 48 additions & 0 deletions packages/core/integration-tests/test/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -6231,4 +6231,52 @@ describe('javascript', function () {
assert.equal(res.result, 2);
});
}

for (let defaultTargetOptions of [
{shouldScopeHoist: false},
{shouldScopeHoist: true, outputFormat: 'commonjs'},
{shouldScopeHoist: true, outputFormat: 'esmodule'},
]) {
it(
'supports native .node modules with options: ' +
JSON.stringify(defaultTargetOptions),
async function () {
await fsFixture(overlayFS, __dirname)`
native-node
index.js:
output = require('@parcel/rust');
package.json:
{
"targets": {
"default": {
"context": "node",
"includeNodeModules": true
}
}
}
yarn.lock:`;

let b = await bundle(path.join(__dirname, 'native-node/index.js'), {
defaultTargetOptions,
inputFS: overlayFS,
outputFS: inputFS,
});

let res = await run(
b,
{output: null},
{require: false},
{
fs: () => require('fs'),
path: () => require('path'),
module: () => require('module'),
url: () => require('url'),
},
);
assert.equal(typeof res.output.hashString, 'function');
},
);
}
});
2 changes: 1 addition & 1 deletion packages/core/integration-tests/test/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ describe('svg', function () {

assertBundles(b, [
{
assets: ['index.js', 'bundle-url.js'],
assets: ['index.js'],
},
{
assets: ['circle.svg'],
Expand Down
18 changes: 17 additions & 1 deletion packages/core/test-utils/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ export async function runBundles(
overlayFS,
externalModules,
true,
target === 'node' ||
target === 'electron-main' ||
target === 'react-server',
);

esmOutput = bundles.length === 1 ? res[0] : res;
Expand Down Expand Up @@ -927,6 +930,9 @@ function prepareNodeContext(
readFileSync: (file, encoding) => {
return overlayFS.readFileSync(file, encoding);
},
existsSync: file => {
return overlayFS.existsSync(file);
},
};
}

Expand All @@ -939,6 +945,11 @@ function prepareNodeContext(
return {};
}

if (path.extname(res) === '.node') {
// $FlowFixMe[unsupported-syntax]
return require(res);
}

let cached = nodeCache.get(res);
if (cached) {
return cached.module.exports;
Expand Down Expand Up @@ -1002,6 +1013,7 @@ export async function runESM(
fs: FileSystem,
externalModules: ExternalModules = {},
requireExtensions: boolean = false,
isNode: boolean = false,
): Promise<Array<{|[string]: mixed|}>> {
let id = instanceId++;
let cache = new Map();
Expand Down Expand Up @@ -1048,7 +1060,11 @@ export async function runESM(
entry(specifier, referrer),
context,
initializeImportMeta(meta) {
meta.url = `http://localhost/${path.basename(filename)}`;
if (isNode) {
meta.url = url.pathToFileURL(filename).toString();
} else {
meta.url = `http://localhost/${path.basename(filename)}`;
}
},
});
cache.set(filename, m);
Expand Down
18 changes: 16 additions & 2 deletions packages/runtimes/js/src/JSRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,23 @@ export default (new Runtime({
.find(e => e.id === bundleGroup.entryAssetId);
if (
dependency.specifierType === 'url' ||
mainBundle.type !== 'js' ||
mainAsset?.meta.jsRuntime === 'url'
) {
assets.push(getURLRuntime(dependency, bundle, mainBundle, options));
continue;
}

if (mainBundle.type === 'node' && mainBundle.env.isNode()) {
let relativePathExpr = getAbsoluteUrlExpr(
getRelativePathExpr(bundle, mainBundle, options),
mainBundle,
);
assets.push({
filePath: __filename,
code: `module.exports = require('./helpers/node/node-loader.js')(${relativePathExpr});`,
dependency,
env: {sourceType: 'module'},
});
}
}

Expand Down Expand Up @@ -655,7 +668,8 @@ function getAbsoluteUrlExpr(relativePathExpr: string, bundle: NamedBundle) {
if (
(bundle.env.outputFormat === 'esmodule' &&
bundle.env.supports('import-meta-url')) ||
bundle.env.outputFormat === 'commonjs'
bundle.env.outputFormat === 'commonjs' ||
bundle.env.isNode()
) {
// This will be compiled to new URL(url, import.meta.url) or new URL(url, 'file:' + __filename).
return `new __parcel__URL__(${relativePathExpr}).toString()`;
Expand Down
8 changes: 8 additions & 0 deletions packages/runtimes/js/src/helpers/node/node-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const url = require('url');
const {createRequire} = require('module');

module.exports = function loadNodeModule(bundle) {
let path = url.fileURLToPath(bundle);
let require = createRequire(path);
return require(path);
};
3 changes: 2 additions & 1 deletion packages/transformers/js/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,11 @@ pub fn transform(
source_map: source_map.clone(),
items: &mut global_deps,
global_mark,
globals: HashMap::new(),
globals: IndexMap::new(),
filename: Path::new(&config.filename),
unresolved_mark,
has_node_replacements: &mut result.has_node_replacements,
is_esm: config.is_esm_output,
},
config.node_replacer(),
),
Expand Down
Loading

0 comments on commit 4f3bcaa

Please sign in to comment.