Javascript, somewhat infamously, allows your entire program to modify any object. In Node.js this means you can modify a CommonJS module at runtime. If you have a program like this
// File: change-it.js
const express = require('express')
express.myProp = 'hello world'
// File: index.js
require('./change-it')
const express = require('express')
console.log(express.myProp)
and run it, you'll get the following output.
% node index.js
hello world
That is, we've successfully added a property to the express module.
However, the new native ECMAScript modules (also know as ESM modules -- the ones that use import
) do not apear to support this behavior. In fact, Node.js outright rejects it.
That is, a program like this
// File: index.mjs
import * as a from './a.mjs'
import * as b from './b.mjs'
console.log(a)
console.log(b)
// File: a.mjs
import * as b from './a.mjs'
b.myProp = 'hijacked!'
export const a = 'hello world'
// File: b.mjs
export const b = 'goodbye world'
Will error out with the following error
% node index.mjs
file:///path/to/a.mjs:2
b.myProp = 'hijacked!'
^
TypeError: Cannot add property myProp, object is not extensible
at file:///path/to/a.mjs:2:10
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:61:12)
My Question: Is this speced behavior of ECMAScript modules, or is this a choice the Node.js developers made? If speced is there a section (or sections?) of the ECMAScript that covers expectations here?
(I've tried looking at the ECMAScript spec and I'm afraid I don't speak whatever dialect of engineering english the smart folks who put that together do)
CodePudding user response:
Yes, module namespaces - for example, the a
after the line:
import * as a from './a.mjs'
cannot be mutated. Its IsExtensible property is:
The [[IsExtensible]] internal method of a module namespace exotic object takes no arguments and returns a normal completion containing false. It performs the following steps when called:
- Return false.
Only extensible objects can have properties added to them.