Skip to content

Commit

Permalink
module: add submodules builtins in addBuiltinLibsToObject
Browse files Browse the repository at this point in the history
  • Loading branch information
Mesteery committed Sep 25, 2021
1 parent fd86dad commit 080ecdd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 42 deletions.
7 changes: 7 additions & 0 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ global or scoped variable, the input `fs` will be evaluated on-demand as
> fs.createReadStream('./some/file');
```

Node.js core submodules are exposed in parent module object,
for example, `stream/consumers` would be accessible in `stream.consumers`.

```console
> stream.consumers.text(process.stdin);
```

#### Global uncaught exceptions
<!-- YAML
changes:
Expand Down
90 changes: 49 additions & 41 deletions lib/internal/modules/cjs/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {
SafeMap,
SafeSet,
StringPrototypeCharCodeAt,
StringPrototypeIncludes,
StringPrototypeSplit,
StringPrototypeSlice,
StringPrototypeStartsWith,
} = primordials;
Expand Down Expand Up @@ -149,48 +149,56 @@ function addBuiltinLibsToObject(object, dummyModuleName) {
// use `require()` directly.
const dummyModule = new Module(dummyModuleName);

ArrayPrototypeForEach(builtinModules, (name) => {
// Neither add underscored modules, nor ones that contain slashes (e.g.,
// 'fs/promises') or ones that are already defined.
if (StringPrototypeStartsWith(name, '_') ||
StringPrototypeIncludes(name, '/') ||
ObjectPrototypeHasOwnProperty(object, name)) {
return;
}
// Goals of this mechanism are:
// - Lazy loading of built-in modules
// - Having all built-in modules available as non-enumerable properties
// - Allowing the user to re-assign these variables as if there were no
// pre-existing globals with the same name.

const setReal = (val) => {
// Deleting the property before re-assigning it disables the
// getter/setter mechanism.
delete object[name];
object[name] = val;
};
// Goals of this mechanism are:
// - Lazy loading of built-in modules
// - Having all built-in modules available as non-enumerable properties
// - Allowing the user to re-assign these variables as if there were no
// pre-existing globals with the same name.

function attachModulesWrapper(object, parent, modules) {
ArrayPrototypeForEach(modules, (mod) => {
const name = parent ? StringPrototypeSplit(name, '/')[1] : mod;

// Neither add underscored modules or ones that are already defined.
if (StringPrototypeStartsWith(name, '_') ||
ObjectPrototypeHasOwnProperty(object, name)) {
return;
}

ObjectDefineProperty(object, name, {
get: () => {
const lib = dummyModule.require(name);

// Disable the current getter/setter and set up a new
// non-enumerable property.
delete object[name];
ObjectDefineProperty(object, name, {
get: () => lib,
set: setReal,
configurable: true,
enumerable: false
});

return lib;
},
set: setReal,
configurable: true,
enumerable: false
ObjectDefineProperty(object, name, {
get() {
const lib = dummyModule.require(mod);

const submodules = builtinModules
.filter((module) => module.startsWith(`${mod}/`));
if (submodules.length > 0)
attachModulesWrapper(lib, mod, submodules);

// Disable the current getter/setter and set up a new
// non-enumerable property.
delete object[name];
ObjectDefineProperty(object, name, {
value: lib,
writable: true,
configurable: true,
enumerable: false,
});
return lib;
},

set(val) {
// Deleting the property before re-assigning it disables the
// getter/setter mechanism.
delete object[name];
object[name] = val;
},
configurable: true,
enumerable: false,
});
});
});
}

attachModulesWrapper(object, '', builtinModules);
}

function normalizeReferrerURL(referrer) {
Expand Down
19 changes: 18 additions & 1 deletion test/parallel/test-repl-autolibs.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ function test1() {
putIn.write = function(data) {
gotWrite = true;
if (data.length) {

// Inspect output matches repl output
assert.strictEqual(data,
`${util.inspect(require('fs'), null, 2, false)}\n`);
Expand All @@ -59,6 +58,7 @@ function test2() {
assert.strictEqual(data, '{}\n');
// Original value wasn't overwritten
assert.strictEqual(val, global.url);
test3();
}
};
const val = {};
Expand All @@ -68,3 +68,20 @@ function test2() {
putIn.run(['url']);
assert(gotWrite);
}

function test3() {
let gotWrite = false;
putIn.write = function(data) {
gotWrite = true;
if (data.length) {
// Inspect output matches repl output
assert.strictEqual(data,
`${util.inspect(require('stream/consumers'), null, 2, false)}\n`);
// Globally added lib matches required lib
assert.strictEqual(global.stream.consumers, require('stream/consumers'));
}
};
assert(!gotWrite);
putIn.run(['stream.consumers']);
assert(gotWrite);
}

0 comments on commit 080ecdd

Please sign in to comment.