Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support node: import like require('node:fs/promises') #37

Merged
merged 1 commit into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,9 @@ module.exports = class Bundler {
const bareId = parsedId.bareId;
const packageName = parsedId.parts[0];
const resource = bareId.slice(packageName.length + 1);
const isNodeCore = packageName.startsWith('node:');

const stub = stubModule(bareId, this._resolve);
const stub = stubModule(isNodeCore ? packageName.slice(5) : packageName, this._resolve);

if (typeof stub === 'string') {
return this.capture({
Expand All @@ -339,6 +340,10 @@ module.exports = class Bundler {
});
}

if (isNodeCore && !stub) {
throw new Error(`Core Node.js module "${packageName}" is not stubbed`);
}

return this.packageReaderFor(stub || {name: packageName})
.then(reader => resource ? reader.readResource(resource, isRelative) : reader.readMain())
.then(unit => this.capture(unit))
Expand Down
10 changes: 5 additions & 5 deletions lib/modules-todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ module.exports = class ModulesTodo {
let pluginSpace = space;
if (space !== PACKAGE) pluginSpace = USER_OR_PACKAGE;
const isRelativePlugin = pluginName.startsWith('.') ? 1 : 0;
const key = pluginSpace + ':' + pluginName + ':' + isRelativePlugin;
const key = pluginSpace + ' ' + pluginName + ' ' + isRelativePlugin;
if (!this.todos[key]) this.todos[key] = [];
this.todos[key].push(moduleId);
}

const key = space + ':' + parsedId.cleanId + ':' + isRelative;
const key = space + ' ' + parsedId.cleanId + ' ' + isRelative;
if (!this.todos[key]) this.todos[key] = [];
this.todos[key].push(moduleId);
});
Expand All @@ -87,15 +87,15 @@ module.exports = class ModulesTodo {
groups[SERIAL_GROUP] = [];

keys.forEach(key => {
const [space, id] = key.split(':');
const [space, id] = key.split(' ');

// Don't need the parallel optimization when running
// in nodejs.
if (!process.browser || space === USER) {
groups[SERIAL_GROUP].push(key);
} else {
const packageName = parse(id).parts[0];
const group = space + ':' + packageName;
const group = space + ' ' + packageName;
if (!groups[group]) groups[group] = [];
groups[group].push(key);
}
Expand All @@ -108,7 +108,7 @@ module.exports = class ModulesTodo {
let p = Promise.resolve();

keys.forEach(key => {
const [space, id, isRelative] = key.split(':');
const [space, id, isRelative] = key.split(' ');
const requiredBy = todos[key];

p = p.then(() => cb(id, {
Expand Down
5 changes: 5 additions & 0 deletions lib/transformers/replace.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ module.exports = function replace(unit) {
dep = dep.slice(0, -1);
}

if (dep.startsWith('node:')) {
// remove node: prefix for node built-in modules
dep = dep.slice(5);
}

// browser replacement;
if (replacement && replacement[dep]) {
dep = replacement[dep];
Expand Down
69 changes: 69 additions & 0 deletions test/bundler.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1958,3 +1958,72 @@ test("Bundler ignores runtime modules", async (t) => {
(err) => t.fail(err.stack)
);
});

test("Bundler traces node:fs/promises", async (t) => {
const fakeFs = {
"node_modules/dumber-module-loader/dist/index.debug.js":
"dumber-module-loader",

"node_modules/fs-browser-stub/package.json": JSON.stringify({
name: "fs-browser-stub",
main: "index.js",
exports: {
".": "./index.js",
"./promises": "./promises.js"
}
}),
"node_modules/fs-browser-stub/index.js": "module.exports = { promises: {}}",
"node_modules/fs-browser-stub/promises.js": "module.exports = {}",
};
const bundler = mockBundler(fakeFs);

return Promise.resolve()
.then(() =>
bundler.capture({
path: "src/app.js",
contents: "require('node:fs/promises');",
moduleId: "app.js",
})
)
.then(() => bundler.resolve())
.then(() => bundler.bundle())
.then(
(bundleMap) => {
t.deepEqual(bundleMap, {
"entry-bundle": {
files: [
{
path: "node_modules/dumber-module-loader/dist/index.debug.js",
contents: "dumber-module-loader;",
},
{
contents: "define.switchToUserSpace();",
},
{
path: "src/app.js",
contents:
"define('app.js',['require','exports','module','fs/promises'],function (require, exports, module) {\nrequire('fs/promises');\n});\n",
},
{
contents: "define.switchToPackageSpace();",
},
{
path: "node_modules/fs-browser-stub/promises.js",
contents:
"define('fs/promises.js',['require','exports','module'],function (require, exports, module) {\nmodule.exports = {}\n});\n",
},
{
contents: "define.switchToUserSpace();",
},
],
config: {
baseUrl: "/dist",
paths: {},
bundles: {},
},
},
});
},
(err) => t.fail(err.stack)
);
});
24 changes: 12 additions & 12 deletions test/modules-todo.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ test('ModulesTodo process traced unit', t => {
});

t.deepEqual(Object.assign({}, md.todos), {
'0:text!foo.html:1': ['foo'],
'2:some-plugin:0': ['foo'],
'0:some-plugin!readme.md:1': ['foo'],
'2:bar:0': ['foo'],
'1:bar/lo:1': ['bar/index']
'0 text!foo.html 1': ['foo'],
'2 some-plugin 0': ['foo'],
'0 some-plugin!readme.md 1': ['foo'],
'2 bar 0': ['foo'],
'1 bar/lo 1': ['bar/index']
});
t.notOk(md.needCssInjection);
t.ok(md.hasTodo());
Expand Down Expand Up @@ -109,7 +109,7 @@ test('ModulesTodo handles additional todos, set needCssInjection', async t => {
]);
t.notOk(md.needCssInjection);
t.deepEqual(Object.assign({}, md.todos), {
'1:bar/lor:1': ['bar/lo'],
'1 bar/lor 1': ['bar/lo'],
});
t.ok(md.hasTodo());

Expand All @@ -123,8 +123,8 @@ test('ModulesTodo handles additional todos, set needCssInjection', async t => {
]);
t.ok(md.needCssInjection);
t.deepEqual(Object.assign({}, md.todos), {
'1:bar/lor/tool.css:1': ['bar/lor/index'],
'1:bar/lor/tool2:1': ['bar/lor/index']
'1 bar/lor/tool.css 1': ['bar/lor/index'],
'1 bar/lor/tool2 1': ['bar/lor/index']
});
t.ok(md.hasTodo());

Expand Down Expand Up @@ -154,7 +154,7 @@ test('ModulesTodo sets needCssInjection for less module', t => {
});

t.deepEqual(Object.assign({}, md.todos), {
'0:foo.less:1': ['foo']
'0 foo.less 1': ['foo']
});
t.ok(md.needCssInjection);
t.ok(md.hasTodo());
Expand All @@ -168,7 +168,7 @@ test('ModulesTodo sets needCssInjection for scss module', t => {
});

t.deepEqual(Object.assign({}, md.todos), {
'0:foo.scss:1': ['foo']
'0 foo.scss 1': ['foo']
});
t.ok(md.needCssInjection);
t.ok(md.hasTodo());
Expand All @@ -182,7 +182,7 @@ test('ModulesTodo sets needCssInjection for sass module', t => {
});

t.deepEqual(Object.assign({}, md.todos), {
'0:foo.sass:1': ['foo']
'0 foo.sass 1': ['foo']
});
t.ok(md.needCssInjection);
t.ok(md.hasTodo());
Expand All @@ -196,7 +196,7 @@ test('ModulesTodo sets needCssInjection for styl module', t => {
});

t.deepEqual(Object.assign({}, md.todos), {
'0:foo.styl:1': ['foo']
'0 foo.styl 1': ['foo']
});
t.ok(md.needCssInjection);
t.ok(md.hasTodo());
Expand Down
4 changes: 4 additions & 0 deletions test/transformers/replace.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ test('replace transform ignores empty replacement', t => {
test('replace transform cleanup dep, even with empty replacement', t => {
const source = `define('foo', ['require', 'module-a.js', './bar/', 'o/a.js'], function (require) {
require('module-a.js');
const {rm} = require('node:fs');
const {rmdir} = require('node:fs/promises');
require('./bar/');
require('o/a.js');
})`;

const result = `define('foo', ['require', 'module-a.js', './bar', 'o/a.js'], function (require) {
require('module-a.js');
const {rm} = require('fs');
const {rmdir} = require('fs/promises');
require('./bar');
require('o/a.js');
})`;
Expand Down
Loading