Skip to content

Commit d24f700

Browse files
committed
Merge branch 'main' of github.com:puskin94/node
2 parents 6764a41 + c969649 commit d24f700

16 files changed

+218
-17
lines changed

doc/api/esm.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,35 @@ import { readFileSync } from 'node:fs';
400400
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
401401
```
402402
403+
### `import.meta.main`
404+
405+
<!-- YAML
406+
added:
407+
- REPLACEME
408+
-->
409+
410+
> Stability: 1.0 - Early development
411+
412+
* {boolean} `true` when the current module is the entry point of the current process; `false` otherwise.
413+
414+
Equivalent to `require.main === module` in CommonJS.
415+
416+
Analogous to Python's `__name__ == "__main__"`.
417+
418+
```js
419+
export function foo() {
420+
return 'Hello, world';
421+
}
422+
423+
function main() {
424+
const message = foo();
425+
console.log(message);
426+
}
427+
428+
if (import.meta.main) main();
429+
// `foo` can be imported from another module without possible side-effects from `main`
430+
```
431+
403432
### `import.meta.resolve(specifier)`
404433
405434
<!-- YAML
@@ -616,6 +645,10 @@ These CommonJS variables are not available in ES modules.
616645
They can instead be loaded with [`module.createRequire()`][] or
617646
[`process.dlopen`][].
618647
648+
#### No `require.main`
649+
650+
To replace `require.main === module`, there is the [`import.meta.main`][] API.
651+
619652
#### No `require.resolve`
620653
621654
Relative resolution can be handled via `new URL('./local', import.meta.url)`.
@@ -1181,6 +1214,7 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
11811214
[`import()`]: #import-expressions
11821215
[`import.meta.dirname`]: #importmetadirname
11831216
[`import.meta.filename`]: #importmetafilename
1217+
[`import.meta.main`]: #importmetamain
11841218
[`import.meta.resolve`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve
11851219
[`import.meta.url`]: #importmetaurl
11861220
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

doc/api/zlib.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,6 @@ Allowed flush values.
615615
* `zlib.constants.Z_FULL_FLUSH`
616616
* `zlib.constants.Z_FINISH`
617617
* `zlib.constants.Z_BLOCK`
618-
* `zlib.constants.Z_TREES`
619618

620619
Return codes for the compression/decompression functions. Negative
621620
values are errors, positive values are used for special but normal

lib/internal/errors.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,12 @@ E('ERR_IMPORT_ATTRIBUTE_MISSING',
13381338
E('ERR_IMPORT_ATTRIBUTE_TYPE_INCOMPATIBLE',
13391339
'Module "%s" is not of type "%s"', TypeError);
13401340
E('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
1341-
'Import attribute "%s" with value "%s" is not supported', TypeError);
1341+
function error(attribute, value, url = undefined) {
1342+
if (url === undefined) {
1343+
return `Import attribute "${attribute}" with value "${value}" is not supported`;
1344+
}
1345+
return `Import attribute "${attribute}" with value "${value}" is not supported in ${url}`;
1346+
}, TypeError);
13421347
E('ERR_INCOMPATIBLE_OPTION_PAIR',
13431348
'Option "%s" cannot be used in combination with option "%s"', TypeError, HideStackFramesError);
13441349
E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' +

lib/internal/main/worker_thread.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,20 @@ port.on('message', (message) => {
203203
break;
204204
}
205205

206+
case 'data-url': {
207+
const { runEntryPointWithESMLoader } = require('internal/modules/run_main');
208+
209+
RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs.
210+
const promise = runEntryPointWithESMLoader((cascadedLoader) => {
211+
return cascadedLoader.import(filename, undefined, { __proto__: null }, undefined, true);
212+
});
213+
214+
PromisePrototypeThen(promise, undefined, (e) => {
215+
workerOnGlobalUncaughtException(e, true);
216+
});
217+
break;
218+
}
219+
206220
default: {
207221
// script filename
208222
// runMain here might be monkey-patched by users in --require.

lib/internal/modules/esm/assert.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function validateAttributes(url, format,
5757
const keys = ObjectKeys(importAttributes);
5858
for (let i = 0; i < keys.length; i++) {
5959
if (keys[i] !== 'type') {
60-
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]]);
60+
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]], url);
6161
}
6262
}
6363
const validType = formatTypeMap[format];
@@ -102,7 +102,7 @@ function handleInvalidType(url, type) {
102102

103103
// `type` might not have been one of the types we understand.
104104
if (!ArrayPrototypeIncludes(supportedTypeAttributes, type)) {
105-
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED('type', type);
105+
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED('type', type, url);
106106
}
107107

108108
// `type` was the wrong value for this format.

lib/internal/modules/esm/initialize_import_meta.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ function createImportMetaResolve(defaultParentURL, loader, allowParentURL) {
5151
/**
5252
* Create the `import.meta` object for a module.
5353
* @param {object} meta
54-
* @param {{url: string}} context
54+
* @param {{url: string, isMain?: boolean}} context
5555
* @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader
5656
* @returns {{dirname?: string, filename?: string, url: string, resolve?: Function}}
5757
*/
5858
function initializeImportMeta(meta, context, loader) {
59-
const { url } = context;
59+
const { url, isMain } = context;
6060

6161
// Alphabetical
6262
if (StringPrototypeStartsWith(url, 'file:') === true) {
@@ -65,6 +65,8 @@ function initializeImportMeta(meta, context, loader) {
6565
setLazyPathHelpers(meta, url);
6666
}
6767

68+
meta.main = !!isMain;
69+
6870
if (!loader || loader.allowImportMetaResolve) {
6971
meta.resolve = createImportMetaResolve(url, loader, experimentalImportMetaResolve);
7072
}

lib/internal/modules/esm/loader.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,11 @@ class ModuleLoader {
248248
*
249249
* @param {string} source Source code of the module.
250250
* @param {string} url URL of the module.
251+
* @param {{ isMain?: boolean }|undefined} context - context object containing module metadata.
251252
* @returns {object} The module wrap object.
252253
*/
253-
createModuleWrap(source, url) {
254-
return compileSourceTextModule(url, source, this);
254+
createModuleWrap(source, url, context = kEmptyObject) {
255+
return compileSourceTextModule(url, source, this, context);
255256
}
256257

257258
/**
@@ -289,7 +290,8 @@ class ModuleLoader {
289290
* @returns {Promise<object>} The module object.
290291
*/
291292
eval(source, url, isEntryPoint = false) {
292-
const wrap = this.createModuleWrap(source, url);
293+
const context = isEntryPoint ? { isMain: true } : undefined;
294+
const wrap = this.createModuleWrap(source, url, context);
293295
return this.executeModuleJob(url, wrap, isEntryPoint);
294296
}
295297

lib/internal/modules/esm/translators.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ translators.set('module', function moduleStrategy(url, source, isMain) {
103103
source = stringify(source);
104104
debug(`Translating StandardModule ${url}`);
105105
const { compileSourceTextModule } = require('internal/modules/esm/utils');
106-
const module = compileSourceTextModule(url, source, this);
106+
const context = isMain ? { isMain } : undefined;
107+
const module = compileSourceTextModule(url, source, this, context);
107108
return module;
108109
});
109110

lib/internal/modules/esm/utils.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const {
4242
const {
4343
emitExperimentalWarning,
4444
getCWDURL,
45+
kEmptyObject,
4546
} = require('internal/util');
4647
const assert = require('internal/assert');
4748
const {
@@ -188,7 +189,7 @@ function registerModule(referrer, registry) {
188189
*/
189190
function defaultInitializeImportMetaForModule(meta, wrap) {
190191
const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
191-
return cascadedLoader.importMetaInitialize(meta, { url: wrap.url });
192+
return cascadedLoader.importMetaInitialize(meta, { url: wrap.url, isMain: wrap.isMain });
192193
}
193194

194195
/**
@@ -342,15 +343,22 @@ async function initializeHooks() {
342343
* @param {string} source Source code of the module.
343344
* @param {typeof import('./loader.js').ModuleLoader|undefined} cascadedLoader If provided,
344345
* register the module for default handling.
346+
* @param {{ isMain?: boolean }|undefined} context - context object containing module metadata.
345347
* @returns {ModuleWrap}
346348
*/
347-
function compileSourceTextModule(url, source, cascadedLoader) {
349+
function compileSourceTextModule(url, source, cascadedLoader, context = kEmptyObject) {
348350
const hostDefinedOption = cascadedLoader ? source_text_module_default_hdo : undefined;
349351
const wrap = new ModuleWrap(url, undefined, source, 0, 0, hostDefinedOption);
350352

351353
if (!cascadedLoader) {
352354
return wrap;
353355
}
356+
357+
const { isMain } = context;
358+
if (isMain) {
359+
wrap.isMain = true;
360+
}
361+
354362
// Cache the source map for the module if present.
355363
if (wrap.sourceMapURL) {
356364
maybeCacheSourceMap(url, source, wrap, false, undefined, wrap.sourceMapURL);

lib/internal/worker.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const {
77
AtomicsAdd,
88
Float64Array,
99
FunctionPrototypeBind,
10-
JSONStringify,
1110
MathMax,
1211
ObjectEntries,
1312
Promise,
@@ -167,8 +166,8 @@ class Worker extends EventEmitter {
167166
doEval = 'classic';
168167
} else if (isURL(filename) && filename.protocol === 'data:') {
169168
url = null;
170-
doEval = 'module';
171-
filename = `import ${JSONStringify(`${filename}`)}`;
169+
doEval = 'data-url';
170+
filename = `${filename}`;
172171
} else {
173172
doEval = false;
174173
if (isURL(filename)) {

lib/zlib.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ ZlibBase.prototype._final = function(callback) {
307307
// This is currently only used to figure out which flush flag to use for the
308308
// last chunk.
309309
// Roughly, the following holds:
310-
// Z_NO_FLUSH (< Z_TREES) < Z_BLOCK < Z_PARTIAL_FLUSH <
310+
// Z_NO_FLUSH < Z_BLOCK < Z_PARTIAL_FLUSH <
311311
// Z_SYNC_FLUSH < Z_FULL_FLUSH < Z_FINISH
312312
const flushiness = [];
313313
const kFlushFlagList = [Z_NO_FLUSH, Z_BLOCK, Z_PARTIAL_FLUSH,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { spawnPromisified } from '../common/index.mjs';
2+
import * as fixtures from '../common/fixtures.js';
3+
import assert from 'node:assert/strict';
4+
import { describe, it } from 'node:test';
5+
6+
const importMetaMainScript = `
7+
import assert from 'node:assert/strict';
8+
9+
assert.strictEqual(import.meta.main, true, 'import.meta.main should evaluate true in main module');
10+
11+
const { isMain: importedModuleIsMain } = await import(
12+
${JSON.stringify(fixtures.fileURL('es-modules/import-meta-main.mjs'))}
13+
);
14+
assert.strictEqual(importedModuleIsMain, false, 'import.meta.main should evaluate false in imported module');
15+
`;
16+
17+
function wrapScriptInEvalWorker(script) {
18+
return `
19+
import { Worker } from 'node:worker_threads';
20+
new Worker(${JSON.stringify(script)}, { eval: true });
21+
`;
22+
}
23+
24+
function convertScriptSourceToDataUrl(script) {
25+
return new URL(`data:text/javascript,${encodeURIComponent(script)}`);
26+
}
27+
28+
function wrapScriptInUrlWorker(script) {
29+
return `
30+
import { Worker } from 'node:worker_threads';
31+
new Worker(new URL(${JSON.stringify(convertScriptSourceToDataUrl(script))}));
32+
`;
33+
}
34+
35+
describe('import.meta.main in evaluated scripts', () => {
36+
it('should evaluate true in evaluated script', async () => {
37+
const result = await spawnPromisified(
38+
process.execPath,
39+
['--input-type=module', '--eval', importMetaMainScript],
40+
);
41+
assert.deepStrictEqual(result, {
42+
stderr: '',
43+
stdout: '',
44+
code: 0,
45+
signal: null,
46+
});
47+
});
48+
49+
it('should evaluate true in worker instantiated with module source by evaluated script', async () => {
50+
const result = await spawnPromisified(
51+
process.execPath,
52+
['--input-type=module', '--eval', wrapScriptInEvalWorker(importMetaMainScript)],
53+
);
54+
assert.deepStrictEqual(result, {
55+
stderr: '',
56+
stdout: '',
57+
code: 0,
58+
signal: null,
59+
});
60+
});
61+
62+
it('should evaluate true in worker instantiated with `data:` URL by evaluated script', async () => {
63+
const result = await spawnPromisified(
64+
process.execPath,
65+
['--input-type=module', '--eval', wrapScriptInUrlWorker(importMetaMainScript)],
66+
);
67+
assert.deepStrictEqual(result, {
68+
stderr: '',
69+
stdout: '',
70+
code: 0,
71+
signal: null,
72+
});
73+
});
74+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import '../common/index.mjs';
2+
import assert from 'node:assert/strict';
3+
import { Worker } from 'node:worker_threads';
4+
5+
function get_environment() {
6+
if (process.env.HAS_STARTED_WORKER) return 'in worker thread started by ES Module';
7+
return 'in ES Module';
8+
}
9+
10+
assert.strictEqual(
11+
import.meta.main,
12+
true,
13+
`\`import.meta.main\` at top-level module ${get_environment()} should evaluate \`true\``
14+
);
15+
16+
const { isMain: importedModuleIsMain } = await import('../fixtures/es-modules/import-meta-main.mjs');
17+
assert.strictEqual(
18+
importedModuleIsMain,
19+
false,
20+
`\`import.meta.main\` at dynamically imported module ${get_environment()} should evaluate \`false\``
21+
);
22+
23+
if (!process.env.HAS_STARTED_WORKER) {
24+
process.env.HAS_STARTED_WORKER = 1;
25+
new Worker(import.meta.filename);
26+
}

test/es-module/test-esm-import-meta.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from 'assert';
33

44
assert.strictEqual(Object.getPrototypeOf(import.meta), null);
55

6-
const keys = ['dirname', 'filename', 'resolve', 'url'];
6+
const keys = ['dirname', 'filename', 'main', 'resolve', 'url'];
77
assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys);
88

99
const descriptors = Object.getOwnPropertyDescriptors(import.meta);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const isMain = import.meta.main;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import '../common/index.mjs';
2+
import tmpdir from '../common/tmpdir.js';
3+
import assert from 'node:assert/strict';
4+
import { once } from 'node:events';
5+
import fs from 'node:fs/promises';
6+
import { describe, test, before } from 'node:test';
7+
import { Worker } from 'node:worker_threads';
8+
9+
const accessInternalsSource = `
10+
import 'node:internal/freelist';
11+
`;
12+
13+
function convertScriptSourceToDataUrl(script) {
14+
return new URL(`data:text/javascript,${encodeURIComponent(script)}`);
15+
}
16+
17+
describe('Worker threads should not be able to access internal modules', () => {
18+
before(() => tmpdir.refresh());
19+
20+
test('worker instantiated with module file path', async () => {
21+
const moduleFilepath = tmpdir.resolve('test-worker-internal-modules.mjs');
22+
await fs.writeFile(moduleFilepath, accessInternalsSource);
23+
const w = new Worker(moduleFilepath);
24+
await assert.rejects(once(w, 'exit'), { code: 'ERR_UNKNOWN_BUILTIN_MODULE' });
25+
});
26+
27+
test('worker instantiated with module source', async () => {
28+
const w = new Worker(accessInternalsSource, { eval: true });
29+
await assert.rejects(once(w, 'exit'), { code: 'ERR_UNKNOWN_BUILTIN_MODULE' });
30+
});
31+
32+
test('worker instantiated with data: URL', async () => {
33+
const w = new Worker(convertScriptSourceToDataUrl(accessInternalsSource));
34+
await assert.rejects(once(w, 'exit'), { code: 'ERR_UNKNOWN_BUILTIN_MODULE' });
35+
});
36+
});

0 commit comments

Comments
 (0)