I am reading the documentation on builtin and why I can import a CJS builtin like dgram using import { createSocket } instead of import dgram from 'dgram' like CJS modules in node_modules.
In the builtin modules documentation I found a reference to syncBuiltinESMExports() : https://nodejs.org/api/esm.html#esm_builtin_modules
I am really confused why the code snippet works.
I've created an extended code snippet
import fs, { readFileSync } from 'fs';
import { syncBuiltinESMExports, createRequire } from 'module';
const require = createRequire(import.meta.url)
const { readFileSync: readFileSync2 } = require('fs')
var ref = readFileSync
console.log('import', readFileSync)
console.log('var ok ?', ref)
console.log('const destruct ?', readFileSync2)
fs.readFileSync = () => { console.log('lol') }
syncBuiltinESMExports()
console.log(fs.readFileSync === readFileSync)
console.log('import mutated ?', readFileSync)
console.log('var mutated ?', ref)
console.log('const destruct mutated ?', readFileSync2)
I was verify suprised why the local identifier readFileSync can be mutated by the function syncBuiltinESMExports as if it's run eval.
The local variable ref and the local variable readFileSync2 are not mutated.
Would it be possible to document how import { readFileSync } from 'fs' is different and why readFileSync is not a variable.
It is a variable, esm uses a concept called "live bindings" where the names you import are the actual exported variable, not the value.
// a.mjs
export let a = 1;
export function incrementA() {
a += 1;
}
// b.mjs
import { a, incrementA } from './a.mjs';
console.log(a); // 1
incrementA();
console.log(a); // 2
I modified b.mjs
// b.mjs
import { a, incrementA, assignFive } from './a.mjs';
console.log(a); // 1
incrementA();
console.log(a); // 2
a = 6 // TypeError: Assignment to constant variable.
a is clearly not a constant because it's value has changed but the exception complains about assignment to a constant.
Is that a V8 issue ?
@Raynos that is by design, only the a.mjs module can modify its exports. Changing the error message might be helpful but I don't think it's that bad atm.
It would be super useful to document the existance of live bindings and exporting let declarations on ( https://nodejs.org/api/esm.html ).
Does the feature of a live binding exist anywhere else except in an import { a } from 'b' statement ?
This actually introduces pass by reference into the language, which is a new piece of functionality.
Alternatively, linking out to another Language definition document ( preferably not the actual spec ( https://tc39.es/ecma262/#sec-modules ) ) so that I can learn what ECMAScript modules are before reading how to use ECMAScript modules in nodejs.
We've tried not to document ESM itself, only the specifics of node's behaviour, and in the first sentence on that page we link to the specification and mdn.
Also, I think the only other place that JS exposes references is parenthesized expressions (let a = 1; (a) = 2; console.log(a);)
Unfortunatetly neither the import or export documentation on MDN mentions live bindings.
https://nodejs.org/api/modules.html#modules_module_syncbuiltinesmexports
The module.syncBuiltinESMExports() method updates all the live bindings for builtin ES Modules to match the properties of the CommonJS exports. It does not add or remove exported names from the ES Modules.
Ok syncBuiltinESMExports() clearly documents it updates all the live bindings. I was just not aware that "live bindings" exist in the language or what that meant.
I think it would be beneficial to add a paragraph explaining what a live binding is to the documentation of syncBuiltinESMExports, alternatively to adding a paragraph we can add a link to the phrase "live bindings" and link out to this blog post documenting it ( https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/ )
Note that I found zero references to live binding[s?] on MDN
I don't think we should document how esm works, but you can update the mdn pages as it is a openly editable wiki :)
I'll update MDN
@Raynos I've modified:
the export statement
the import statement
the modules guide
Does that seem sufficient?
Thanls, that helps.
Most helpful comment
@Raynos I've modified:
the export statement
the import statement
the modules guide
Does that seem sufficient?