-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
esm: provide named exports for builtin libs #20403
Conversation
@guybedford i'm not quite sure what the setter-based approach looks like (do we iterate through the exports and re-assign every property to have get/set instead of value?) how do we prevent common ways that userland monkeypatches while keeping property values updated (defineProperty) how do we make sure that deleted properties become representative in esm land |
@@ -1,10 +1,11 @@ | |||
import _url from 'url'; | |||
import { URL } from 'url'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note: This is global now, so we could just remove this line and/or split this change into a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i left it in just in case these changes get backported to other release lines
@devsnek yes exactly. There may well be issues with backwards compatibility for configuration/reflection or it may not provide such a performance benefit, but I think it's worth running the experiment. |
I just had a look through some common core-instrumenting packages (newrelic, old graceful-fs) and they both do a direct member expression assignment. I think we may well be ok with setter-only. |
@guybedford it is worth noting that the added time is from the iterating and creation and such at startup, not from actually calling those traps. i'm can't think of how you will build hundreds of getter/setters in less time while still having the majority of the same code for enumerable filtering and such |
@devsnek 10% startup time cost is a huge regression on its own. But I'm specifically referring to how turning core modules into proxies affects existing hot code like |
@guybedford The perf run mentioned was done on an incomplete implementation. None of the methods in your hot code example are wrapped ( Perf is important (duh), and at this stage it's good to keep an eye on for sure. There's plenty of perf work to be done on ES modules in Node in general (and that work will come). Since this is experimental things should be allowed to progress and take shape. Any performance concerns you have about specific JS constructions should be filed for V8 to tackle (there's plenty of runway to improve whatever needs improving). You too could experiment with your own implementation without obstructing. If a better implementation shakes out down the road then so be it. |
|
||
// bar.js | ||
import foo from './foo.js'; | ||
foo.one === 1; // true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignorable nit: as this is a file, not a REPL, maybe add assert()
or console.log()
here?
doc/api/esm.md
Outdated
|
||
```js | ||
import { readFile } from 'fs'; | ||
readFile('./foo.txt', (err, body) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: it seems a bit unexpected to name file content body
(no body
in fs
doc), it seems more natural for http(s)
docs. However, I can miss some conventions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does src
/source
work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it seems more neutral to me.
56fe149
to
d596353
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done an initial review here - looking through this more closely it is looking really great actually!
Agreed a setter approach would incur a lot of property definitions that might slow us down more anyway.
@jdalton points taken that we can improve perf here over time. I ran some initial subsets of the benchmarks against this code and haven't yet spotted any issues, but it's worth keeping an eye on the hot paths here.
@devsnek I would really appreciate it if you can help me understand the wrap magic to more properly review. I'll also aim to stare at it again this week if I get a moment. I must admit the layered proxy situation also doesn't seem ideal from a perf perspective, so would be nice to be able to follow the driving use cases to see where hot paths might be carved out from that further.
lib/internal/bootstrap/loaders.js
Outdated
const nativeRegExp = new RegExp(`^${re}$`); | ||
isNative = (fn) => { | ||
if (typeof fn === 'function' && | ||
nativeRegExp.test(toString.call(fn))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite tricky to follow, and if there are changes in v8 perhaps tricky to maintain.
Surely toString.call(fn).endsWith('{ [native code ] }')
would give just as strong a guarantee?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there are changes in v8 perhaps tricky to maintain.
just the opposite (ignoring that Function.prototype.toString is standardised now) different engines or different versions of the same engine may represent native functions differently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@guybedford For a reference implementation see this and this. The idea is to use a builtin native method as a template for detection of others. @devsnek has a todo to implement a v8 helper for this too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, perhaps a comment would help maintenance though - it's pretty tough to follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a fan of comments for sure 📖
lib/internal/bootstrap/loaders.js
Outdated
if (typeof fn === 'function' && | ||
nativeRegExp.test(toString.call(fn))) { | ||
const { name } = fn; | ||
if (typeof name !== 'string' || !/^bound /.test(name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name.startsWith('bound ')
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any particular reason? regex is still faster at this time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 It's micro-opt territory here so it's up to personal preference is all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I didn't realise that had changed in v8. Just paying attention to the getter hot path here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep in mind this a few steps/checks into the native check so many values will have already exited with false
by then. This check is also performed once per changed value (so not per repeated get request).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This very much does seem on the hot path to me? get -> wrap -> isNative is on every get in the proxy, so this path seems like it will hit every access to bound functions on exports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This startsWith
check is done after the first few check so will only apply to the first access of bound functions and native functions. I don't think there's a need to get hung up on micro-optimizations at this point. If you find the need later then sure we can tweak various ways to detect xyz.
lib/internal/bootstrap/loaders.js
Outdated
this.exportKeys = Object.keys(this.exports); | ||
|
||
const update = (property, value) => { | ||
if (this.reflect !== undefined && this.exportKeys.includes(property)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
property in this.reflect.exports
might be a nicer check here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is reflect.exports a null object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasOwnProperty could work too, just nice to avoid linear lookup if we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exportKeys could be made a Set as well
lib/internal/bootstrap/loaders.js
Outdated
if (typeof value === 'function') | ||
value = methodUnwrapMap.get(value) || value; | ||
if (ReflectSet(target, prop, value, receiver)) { | ||
update(prop, ReflectGet(target, prop, receiver)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the receiver
is not equal to the target, surely we don't want to be running the update
method?
For example, if someone does Object.create(module.exports).asdf = 'asdf'
, we don't want to be updating the ES module object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not quite sure i understand this one, but i do know that Object.create(module.exports).asdf = 'asdf'
doesn't change anything on module.exports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, so it shouldn't call the update function, whereas right now it does I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, so it shouldn't call the update function, whereas right now it does I believe.
I cannot reproduce your concern.
import { isArray } from 'util'
> Object.create(util).isArray = 'asdf'
'asdf'
> isArray
[Function: isArray]
> util.isArray
[Function: isArray]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Standard set will create a new "own" property on the receiver if it isn't the same as the target and won't update the target normally. Easiest example of this is with Object.prototype
not being updated in :
x={}; // has Object.prototype ; ~= Object.create(Object.prototype)
x.toString = null;
assert.notEqual(x.toString, Object.prototype.toString);
This seems to be a slight bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like userland can do some stuff since we allow defineProperty trapping that exhibits these behaviors,
In your example above you have removed the own property of fs
when you assigned it fs.readFile=null
because in the setter it performed delete fs.readFile
. So when you logged both fs.readFile
values they had a fs.hasOwnProperty("readFile")
of false
and are logging the Object.prototype.readFile
value. The deleteProperty
trap should have updated the ns values to be undefined
though, so I should check that out.
In my own implementation I get the expected results:
fs 123 from namespace undefined
fs 456 from namespace undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my local tests for the worstTargetEver
things are looking alright.
- we need to add a test to ensure we don't get value since that is an own property on the receiver so we can prevent regressions.
This looks good
import { foo, bar } from "worstTargetEver"
import w from "worstTargetEver"
w.foo = 3
foo // undefined
w.foo // undefined
bar // 3
w.bar // 3
- right now it could update to Object.prototype.foo since there isn't a descriptor anymore but we still do a non-guarded Get after the Set. I didn't see any existing things in core that would be problematic.
The worstTargetEver
example did not result in a value getting assigned to Object.prototype.foo
Reflect.has(Object.prototype, "foo") // false
This could be because this line of worstTargetEver
...
Object.defineProperty(this, 'foo', {value}) // not on target, need to ensure it isn't picked up
...the this
, while not the target
, is the proxy
, so it's ok.
- this somewhat shows a problem if setters are defining other properties that might need to be updated.
The example above works as expected, to me at least.
I'll work with @devsnek to add tests and ensure the PR results align with my local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bmeck I had the same concern at the beginning, but because of the limited scope of this PR (only applies to core modules) I didn't think we have to be that watertight, because we don't really have those weird setters/getters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton which example? we have multiple in this thread.
@TimothyGu since userland is able to create those situations by mutating core I think we still need to at least document how things work. I don't find the synchronization mismatch intuitive, but am not trying to stop this PR as long as it is well understood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bmeck @TimothyGu As mentioned above (here and here) there should be no synchronization mismatch or unexpected gotchas once I get a chance to update this PR (I'll add tests too).
lib/internal/bootstrap/loaders.js
Outdated
const methodWrapMap = new WeakMap(); | ||
const methodUnwrapMap = new WeakMap(); | ||
|
||
const wrap = (target, name, value) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide a few examples for what this is mitigating? Would help a lot to follow this, and a comment would likely help as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 There should be unit tests for it (or added if not) but the gist of the issue is that native methods (so not plain or bound ones) may be picky about their this
binding and need wrapping for general use when separated from their object.
process.cwd.call(new Proxy(process, {}))
// TypeError: Illegal invocation
We should totally file a v8 bug on this though since this doesn't affect NodeChakra.
Once fixed the workaround isn't needed.
Update: V8 bug here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing, that helps a lot to know! If it seems like it may well be removed by the time this is unflagged, then we really don't need to worry about the perf on this path for now.
Could be worth noting the justification and v8 issue in a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm 👍 on adding comments. I'll add moar to my implementation too.
Update:
Once the V8 bug is fixed in addition to dropping the proxy wrapper for native methods we can also conditionally exclude the get
trap on the module.exports
proxy for exports that are functions or those we expect to be [object Object]
(so likely all builtin modules). Since then, the only thing the get
trap will be used for is correcting toStringTag
values.
lib/internal/bootstrap/loaders.js
Outdated
typeof value !== 'string') { | ||
const toStringTag = ObjectToString.call(target).slice(8, -1); | ||
return toStringTag === 'Object' ? value : toStringTag; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
lib/internal/bootstrap/loaders.js
Outdated
const toStringTag = ObjectToString.call(target).slice(8, -1); | ||
return toStringTag === 'Object' ? value : toStringTag; | ||
} | ||
return wrap(target, prop, value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps do the function check here so we don't need to go into the wrap function unnecessarily as this is our hot path.
lib/internal/bootstrap/loaders.js
Outdated
return descriptor; | ||
}, | ||
get: (target, prop, receiver) => { | ||
const value = ReflectGet(target, prop, receiver); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder which is faster - target[prop]
or ReflectGet(target, prop)
? Worth thinking of this one from a perf perspective.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't matter, because i have to pass the receiver through
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'm not sure we want to support prototypal getters here? Could they not be a shortpath:
get (target, prop, receiver) {
if (target !== receiver)
return ReflectGet(target, prop, receiver);
// ... target receiver case
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The get trap here is just to detect a native method that needs wrapping and handle the toString proxy juggle. There is no other editorial bits going on here.
lib/internal/bootstrap/loaders.js
Outdated
} | ||
return wrap(target, prop, value); | ||
}, | ||
__proto__: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it's good to be safe, I'm not sure this is a necessary protection here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 It's a precaution since trap handlers, like descriptors, will read inherited traps/descriptors as well as own.
Does Node have a convention established for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
places needing to avoid Object.prototype
use Object.create
in the existing codebase but loader has some usage of __proto__
in literals. No linting or style guide rules exist for Node to my knowledge on this topic.
// slice 'node:' scheme | ||
const id = url.slice(5); | ||
NativeModule.require(id); | ||
const module = NativeModule.getCached(id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const module = NativeModule.require(id)
should work here and avoid a double lookup :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getCached returns the entire module object, require just gives the exports object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, I see, we can reconsider the interface if necessary then for future work.
interesting thought i should have had a while ago... i should be able to ignore this entire path for all the internal modules right? (conditionally on --expose-internals) |
Yep. I didn't even know |
that should really improve that degraded startup time 😅 |
lib/internal/bootstrap/loaders.js
Outdated
if (typeof value === 'function') | ||
value = methodUnwrapMap.get(value) || value; | ||
if (ReflectSet(target, prop, value, receiver)) { | ||
update(prop, ReflectGet(target, prop, receiver)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Standard set will create a new "own" property on the receiver if it isn't the same as the target and won't update the target normally. Easiest example of this is with Object.prototype
not being updated in :
x={}; // has Object.prototype ; ~= Object.create(Object.prototype)
x.toString = null;
assert.notEqual(x.toString, Object.prototype.toString);
This seems to be a slight bug.
lib/internal/bootstrap/loaders.js
Outdated
update(prop, ReflectGet(target, prop)); | ||
return true; | ||
} | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this line gets hit when you do:
import events from 'events';
Object.defineProperty(events, 'defaultMaxListeners', {value: 333})
/*
TypeError: 'defineProperty' on proxy: trap returned falsish for property 'defaultMaxListeners'
at Function.defineProperty (<anonymous>)
at file:///Users/bfarias/Documents/node/t.mjs:2:8
at ModuleJob.run (internal/modules/esm/module_job.js:106:14)
*/
But it should probably report this as an error for redefining like it does right now:
> Object.defineProperty(events, 'defaultMaxListeners', {value: 333})
TypeError: Cannot redefine property: defaultMaxListeners
at Function.defineProperty (<anonymous>)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@devsnek @bmeck
By changing the defineProperty
trap from
if (ReflectDefineProperty(target, prop, descriptor)) {
update(prop, ReflectGet(target, prop));
return true;
}
return false;
to
ObjectDefineProperty(target, prop, descriptor);
update(prop, ReflectGet(target, prop));
return true;
we get the expected error:
TypeError: Cannot redefine property: defaultMaxListeners
at Function.defineProperty (<anonymous>)
a084f53
to
d81d44d
Compare
d81d44d
to
6022738
Compare
lib/internal/bootstrap/loaders.js
Outdated
@@ -193,6 +206,12 @@ | |||
'\n});' | |||
]; | |||
|
|||
const getOwn = (target, property, receiver) => { | |||
return ObjectHasOwnProperty.call(target, property) ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Reflect.apply
instead.
|
||
if (threw) { | ||
throw error; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use finally
block instead. Just curious, why is it necessary to reset the property even in case of thrown exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure how the runner operates. If it loads tests and runs them in sequence in a shared process then even if there are failed tests augmented environments could effect other tests if not properly reset/cleaned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton Each test is in a separate process. The Python test runner spawns node
processes in parallel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has cleaned up really nicely! Will be great to see it moving forward. Note I see we're getting the fs promises warnings when loading fs now.
Thanks! Yap, I mentioned that to Gus this morning. That will be resolved by #20632. |
de87636
to
5df014e
Compare
Notable Changes: * **addons**: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) [#20668](#20668) * **assert**: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) [#20485](#20485) * **crypto**: - The `authTagLength` option has been made more flexible. (Tobias Nießen) [#20235](#20235), [#20039](#20039) * **http**: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) [#20075](#20075), [#20611](#20611) * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (helloshuangzi) [#20639](#20639) - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) [#19377](#19377) - Support for multi-threaded embedding has been improved. (Anna Henningsen) [#20542](#20542), [#20539](#20539), [#20541](#20541) * **esm**: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) [#20403](#20403) * **timers**: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) [#20298](#20298) PR-URL: #20724
Notable Changes: * **addons**: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) [#20668](#20668) * **assert**: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) [#20485](#20485) * **crypto**: - The `authTagLength` option has been made more flexible. (Tobias Nießen) [#20235](#20235), [#20039](#20039) * **http**: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) [#20075](#20075), [#20611](#20611) * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (Allen Yonghuang Wang) [#20639](#20639) - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) [#19377](#19377) - Support for multi-threaded embedding has been improved. (Anna Henningsen) [#20542](#20542), [#20539](#20539), [#20541](#20541) * **esm**: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) [#20403](#20403) * **timers**: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) [#20298](#20298) PR-URL: #20724
* addons: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) #20668 * assert: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) #20485 * crypto: - The `authTagLength` option has been made more flexible (Tobias Nießen) #20235) #20039 * esm: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) #20403 * http: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) #20075 #20611 * module: - add --preserve-symlinks-main (David Goldstein) #19911 * timers: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) #20298 * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (Allen Yonghuang Wang) #20639 - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) #19377 - Support for multi-threaded embedding has been improved. (Anna Henningsen) #20542 #20539 #20541 PR-URL: #20724
* addons: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) #20668 * assert: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) #20485 * crypto: - The `authTagLength` option has been made more flexible (Tobias Nießen) #20235) #20039 * esm: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) #20403 * http: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) #20075 #20611 * module: - add --preserve-symlinks-main (David Goldstein) #19911 * timers: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) #20298 * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (Allen Yonghuang Wang) #20639 - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) #19377 - Support for multi-threaded embedding has been improved. (Anna Henningsen) #20542 #20539 #20541 PR-URL: #20724
* addons: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) #20668 * assert: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) #20485 * crypto: - The `authTagLength` option has been made more flexible (Tobias Nießen) #20235) #20039 * esm: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) #20403 * http: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) #20075 #20611 * module: - add --preserve-symlinks-main (David Goldstein) #19911 * timers: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) #20298 * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (Allen Yonghuang Wang) #20639 - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) #19377 - Support for multi-threaded embedding has been improved. (Anna Henningsen) #20542 #20539 #20541 PR-URL: #20724
* addons: - Fixed a memory leak for users of `AsyncResource` and N-API. (Michael Dawson) #20668 * assert: - The `error` parameter of `assert.throws()` can be an object containing regular expressions now. (Ruben Bridgewater) #20485 * crypto: - The `authTagLength` option has been made more flexible (Tobias Nießen) #20235) #20039 * esm: - Builtin modules (e.g. `fs`) now provide named exports in ES6 modules. (Gus Caplan) #20403 * http: - Handling of `close` and `aborted` events has been made more consistent. (Robert Nagy) #20075 #20611 * module: - add --preserve-symlinks-main (David Goldstein) #19911 * timers: - `timeout.refresh()` has been added to the public API. (Jeremiah Senkpiel) #20298 * Embedder support: - Functions for creating V8 `Isolate` and `Context` objects with Node.js-specific behaviour have been added to the API. (Allen Yonghuang Wang) #20639 - Node.js `Environment`s clean up resources before exiting now. (Anna Henningsen) #19377 - Support for multi-threaded embedding has been improved. (Anna Henningsen) #20542 #20539 #20541 PR-URL: #20724
provide named exports for all builtin libraries so that the libraries may be
imported in a nicer way for esm users:
import { readFile } from 'fs'
instead of importing the entire namespace,
import fs from 'fs'
, andcalling
fs.readFile
. the default export is left as the entirenamespace (module.exports)
continuation of #18131 because it got too long and github was giving unicorns
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes