| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent beba631 commit adef9af
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -396,6 +396,35 @@ import { readFileSync } from 'node:fs'; | |||
| 396 | 396 | const buffer = readFileSync(new URL('./data.proto', import.meta.url)); | |
| 397 | 397 | ``` | |
| 398 | 398 | ||
| 399 | + ### `import.meta.main` | ||
| 400 | + | ||
| 401 | + <!-- YAML | ||
| 402 | + added: | ||
| 403 | + - REPLACEME | ||
| 404 | + --> | ||
| 405 | + | ||
| 406 | + > Stability: 1.0 - Early development | ||
| 407 | + | ||
| 408 | + * {boolean} `true` when the current module is the entry point of the current process; `false` otherwise. | ||
| 409 | + | ||
| 410 | + Equivalent to `require.main === module` in CommonJS. | ||
| 411 | + | ||
| 412 | + Analogous to Python's `__name__ == "__main__"`. | ||
| 413 | + | ||
| 414 | + ```js | ||
| 415 | + export function foo() { | ||
| 416 | + return 'Hello, world'; | ||
| 417 | + } | ||
| 418 | + | ||
| 419 | + function main() { | ||
| 420 | + const message = foo(); | ||
| 421 | + console.log(message); | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + if (import.meta.main) main(); | ||
| 425 | + // `foo` can be imported from another module without possible side-effects from `main` | ||
| 426 | + ``` | ||
| 427 | + | ||
| 399 | 428 | ### `import.meta.resolve(specifier)` | |
| 400 | 429 | ||
| 401 | 430 | <!-- YAML | |
@@ -612,6 +641,10 @@ These CommonJS variables are not available in ES modules. | |||
| 612 | 641 | They can instead be loaded with [`module.createRequire()`][] or | |
| 613 | 642 | [`process.dlopen`][]. | |
| 614 | 643 | ||
| 644 | + #### No `require.main` | ||
| 645 | + | ||
| 646 | + To replace `require.main === module`, there is the [`import.meta.main`][] API. | ||
| 647 | + | ||
| 615 | 648 | #### No `require.resolve` | |
| 616 | 649 | ||
| 617 | 650 | Relative resolution can be handled via `new URL('./local', import.meta.url)`. | |
@@ -1177,6 +1210,7 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][]. | |||
| 1177 | 1210 | [`import()`]: #import-expressions | |
| 1178 | 1211 | [`import.meta.dirname`]: #importmetadirname | |
| 1179 | 1212 | [`import.meta.filename`]: #importmetafilename | |
| 1213 | + [`import.meta.main`]: #importmetamain | ||
| 1180 | 1214 | [`import.meta.resolve`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve | |
| 1181 | 1215 | [`import.meta.url`]: #importmetaurl | |
| 1182 | 1216 | [`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -203,6 +203,20 @@ port.on('message', (message) => { | |||
| 203 | 203 | break; | |
| 204 | 204 | } | |
| 205 | 205 | ||
| 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 | + | ||
| 206 | 220 | default: { | |
| 207 | 221 | // script filename | |
| 208 | 222 | // runMain here might be monkey-patched by users in --require. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,12 +51,12 @@ function createImportMetaResolve(defaultParentURL, loader, allowParentURL) { | |||
| 51 | 51 | /** | |
| 52 | 52 | * Create the `import.meta` object for a module. | |
| 53 | 53 | * @param {object} meta | |
| 54 | - * @param {{url: string}} context | ||
| 54 | + * @param {{url: string, isMain?: boolean}} context | ||
| 55 | 55 | * @param {typeof import('./loader.js').ModuleLoader} loader Reference to the current module loader | |
| 56 | 56 | * @returns {{dirname?: string, filename?: string, url: string, resolve?: Function}} | |
| 57 | 57 | */ | |
| 58 | 58 | function initializeImportMeta(meta, context, loader) { | |
| 59 | - const { url } = context; | ||
| 59 | + const { url, isMain } = context; | ||
| 60 | 60 | ||
| 61 | 61 | // Alphabetical | |
| 62 | 62 | if (StringPrototypeStartsWith(url, 'file:') === true) { | |
@@ -65,6 +65,8 @@ function initializeImportMeta(meta, context, loader) { | |||
| 65 | 65 | setLazyPathHelpers(meta, url); | |
| 66 | 66 | } | |
| 67 | 67 | ||
| 68 | + meta.main = !!isMain; | ||
| 69 | + | ||
| 68 | 70 | if (!loader || loader.allowImportMetaResolve) { | |
| 69 | 71 | meta.resolve = createImportMetaResolve(url, loader, experimentalImportMetaResolve); | |
| 70 | 72 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -248,10 +248,11 @@ class ModuleLoader { | |||
| 248 | 248 | * | |
| 249 | 249 | * @param {string} source Source code of the module. | |
| 250 | 250 | * @param {string} url URL of the module. | |
| 251 | + * @param {{ isMain?: boolean }|undefined} context - context object containing module metadata. | ||
| 251 | 252 | * @returns {object} The module wrap object. | |
| 252 | 253 | */ | |
| 253 | - createModuleWrap(source, url) { | ||
| 254 | - return compileSourceTextModule(url, source, this); | ||
| 254 | + createModuleWrap(source, url, context = kEmptyObject) { | ||
| 255 | + return compileSourceTextModule(url, source, this, context); | ||
| 255 | 256 | } | |
| 256 | 257 | ||
| 257 | 258 | /** | |
@@ -289,7 +290,8 @@ class ModuleLoader { | |||
| 289 | 290 | * @returns {Promise<object>} The module object. | |
| 290 | 291 | */ | |
| 291 | 292 | 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); | ||
| 293 | 295 | return this.executeModuleJob(url, wrap, isEntryPoint); | |
| 294 | 296 | } | |
| 295 | 297 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -103,7 +103,8 @@ translators.set('module', function moduleStrategy(url, source, isMain) { | |||
| 103 | 103 | source = stringify(source); | |
| 104 | 104 | debug(`Translating StandardModule ${url}`); | |
| 105 | 105 | 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); | ||
| 107 | 108 | return module; | |
| 108 | 109 | }); | |
| 109 | 110 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,6 +42,7 @@ const { | |||
| 42 | 42 | const { | |
| 43 | 43 | emitExperimentalWarning, | |
| 44 | 44 | getCWDURL, | |
| 45 | + kEmptyObject, | ||
| 45 | 46 | } = require('internal/util'); | |
| 46 | 47 | const assert = require('internal/assert'); | |
| 47 | 48 | const { | |
@@ -188,7 +189,7 @@ function registerModule(referrer, registry) { | |||
| 188 | 189 | */ | |
| 189 | 190 | function defaultInitializeImportMetaForModule(meta, wrap) { | |
| 190 | 191 | 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 }); | ||
| 192 | 193 | } | |
| 193 | 194 | ||
| 194 | 195 | /** | |
@@ -342,15 +343,22 @@ async function initializeHooks() { | |||
| 342 | 343 | * @param {string} source Source code of the module. | |
| 343 | 344 | * @param {typeof import('./loader.js').ModuleLoader|undefined} cascadedLoader If provided, | |
| 344 | 345 | * register the module for default handling. | |
| 346 | + * @param {{ isMain?: boolean }|undefined} context - context object containing module metadata. | ||
| 345 | 347 | * @returns {ModuleWrap} | |
| 346 | 348 | */ | |
| 347 | - function compileSourceTextModule(url, source, cascadedLoader) { | ||
| 349 | + function compileSourceTextModule(url, source, cascadedLoader, context = kEmptyObject) { | ||
| 348 | 350 | const hostDefinedOption = cascadedLoader ? source_text_module_default_hdo : undefined; | |
| 349 | 351 | const wrap = new ModuleWrap(url, undefined, source, 0, 0, hostDefinedOption); | |
| 350 | 352 | ||
| 351 | 353 | if (!cascadedLoader) { | |
| 352 | 354 | return wrap; | |
| 353 | 355 | } | |
| 356 | + | ||
| 357 | + const { isMain } = context; | ||
| 358 | + if (isMain) { | ||
| 359 | + wrap.isMain = true; | ||
| 360 | + } | ||
| 361 | + | ||
| 354 | 362 | // Cache the source map for the module if present. | |
| 355 | 363 | if (wrap.sourceMapURL) { | |
| 356 | 364 | maybeCacheSourceMap(url, source, wrap, false, undefined, wrap.sourceMapURL); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,6 @@ const { | |||
| 7 | 7 | AtomicsAdd, | |
| 8 | 8 | Float64Array, | |
| 9 | 9 | FunctionPrototypeBind, | |
| 10 | - JSONStringify, | ||
| 11 | 10 | MathMax, | |
| 12 | 11 | ObjectEntries, | |
| 13 | 12 | Promise, | |
@@ -167,8 +166,8 @@ class Worker extends EventEmitter { | |||
| 167 | 166 | doEval = 'classic'; | |
| 168 | 167 | } else if (isURL(filename) && filename.protocol === 'data:') { | |
| 169 | 168 | url = null; | |
| 170 | - doEval = 'module'; | ||
| 171 | - filename = `import ${JSONStringify(`${filename}`)}`; | ||
| 169 | + doEval = 'data-url'; | ||
| 170 | + filename = `${filename}`; | ||
| 172 | 171 | } else { | |
| 173 | 172 | doEval = false; | |
| 174 | 173 | if (isURL(filename)) { | |
| Original file line number | Diff line number | Diff 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 | + }); | ||
| Original file line number | Diff line number | Diff 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 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,7 @@ import assert from 'assert'; | |||
| 3 | 3 | ||
| 4 | 4 | assert.strictEqual(Object.getPrototypeOf(import.meta), null); | |
| 5 | 5 | ||
| 6 | - const keys = ['dirname', 'filename', 'resolve', 'url']; | ||
| 6 | + const keys = ['dirname', 'filename', 'main', 'resolve', 'url']; | ||
| 7 | 7 | assert.deepStrictEqual(Reflect.ownKeys(import.meta), keys); | |
| 8 | 8 | ||
| 9 | 9 | const descriptors = Object.getOwnPropertyDescriptors(import.meta); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments