GeezerVison is showing github.com — module: implement the "module-sync" exports condition · nodejs/node@df8a045
File treeExpand file treeCollapse file tree Open diff view settings fixtures/es-modules/module-condition
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/modules.md +12-6Lines changed: 12 additions & 6 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -342,9 +342,12 @@ LOAD_PACKAGE_IMPORTS(X, DIR) | 342 | 342 | 1. Find the closest package scope SCOPE to DIR. | 343 | 343 | 2. If no scope was found, return. | 344 | 344 | 3. If the SCOPE/package.json "imports" is null or undefined, return. | 345 | | -4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), | 346 | | - ["node", "require"]) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>. | 347 | | -5. RESOLVE_ESM_MATCH(MATCH). | | 345 | +4. If `--experimental-require-module` is enabled | | 346 | + a. let CONDITIONS = ["node", "require", "module-sync"] | | 347 | + b. Else, let CONDITIONS = ["node", "require"] | | 348 | +5. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), | | 349 | + CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>. | | 350 | +6. RESOLVE_ESM_MATCH(MATCH). | 348 | 351 |
| 349 | 352 | LOAD_PACKAGE_EXPORTS(X, DIR) | 350 | 353 | 1. Try to interpret X as a combination of NAME and SUBPATH where the name | @@ -353,9 +356,12 @@ LOAD_PACKAGE_EXPORTS(X, DIR) | 353 | 356 | return. | 354 | 357 | 3. Parse DIR/NAME/package.json, and look for "exports" field. | 355 | 358 | 4. If "exports" is null or undefined, return. | 356 | | -5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, | 357 | | - `package.json` "exports", ["node", "require"]) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>. | 358 | | -6. RESOLVE_ESM_MATCH(MATCH) | | 359 | +5. If `--experimental-require-module` is enabled | | 360 | + a. let CONDITIONS = ["node", "require", "module-sync"] | | 361 | + b. Else, let CONDITIONS = ["node", "require"] | | 362 | +6. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, | | 363 | + `package.json` "exports", CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>. | | 364 | +7. RESOLVE_ESM_MATCH(MATCH) | 359 | 365 |
| 360 | 366 | LOAD_PACKAGE_SELF(X, DIR) | 361 | 367 | 1. Find the closest package scope SCOPE to DIR. | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/packages.md +17-2Lines changed: 17 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -665,6 +665,10 @@ specific to least specific as conditions should be defined: | 665 | 665 | formats include CommonJS, JSON, native addons, and ES modules | 666 | 666 | if `--experimental-require-module` is enabled. _Always mutually | 667 | 667 | exclusive with `"import"`._ | | 668 | +* `"module-sync"` - matches no matter the package is loaded via `import`, | | 669 | + `import()` or `require()`. The format is expected to be ES modules that does | | 670 | + not contain top-level await in its module graph - if it does, | | 671 | + `ERR_REQUIRE_ASYNC_MODULE` will be thrown when the module is `require()`-ed. | 668 | 672 | * `"default"` - the generic fallback that always matches. Can be a CommonJS | 669 | 673 | or ES module file. _This condition should always come last._ | 670 | 674 |
| @@ -769,7 +773,7 @@ In node, conditions have very few restrictions, but specifically these include: | 769 | 773 |
| 770 | 774 | ### Community Conditions Definitions | 771 | 775 |
| 772 | | -Condition strings other than the `"import"`, `"require"`, `"node"`, | | 776 | +Condition strings other than the `"import"`, `"require"`, `"node"`, `"module-sync"`, | 773 | 777 | `"node-addons"` and `"default"` conditions | 774 | 778 | [implemented in Node.js core](#conditional-exports) are ignored by default. | 775 | 779 |
| @@ -900,6 +904,17 @@ $ node other.js | 900 | 904 |
| 901 | 905 | ## Dual CommonJS/ES module packages | 902 | 906 |
| | 907 | +<!-- This section should not be in the API documentation: | | 908 | +
| | 909 | +1. It teaches opinionated practices that some consider dangerous, see | | 910 | + https://github.com/nodejs/node/issues/52174 | | 911 | +2. It will soon be obsolete when we unflag --experimental-require-module. | | 912 | +3. It's difficult to understand a multi-file structure via long texts and snippets in | | 913 | + a markdown document. | | 914 | +
| | 915 | +TODO(?): Move this section to its own repository with example folders. | | 916 | +--> | | 917 | + | 903 | 918 | Prior to the introduction of support for ES modules in Node.js, it was a common | 904 | 919 | pattern for package authors to include both CommonJS and ES module JavaScript | 905 | 920 | sources in their package, with `package.json` [`"main"`][] specifying the | @@ -912,7 +927,7 @@ ignores) the top-level `"module"` field. | 912 | 927 | Node.js can now run ES module entry points, and a package can contain both | 913 | 928 | CommonJS and ES module entry points (either via separate specifiers such as | 914 | 929 | `'pkg'` and `'pkg/es-module'`, or both at the same specifier via [Conditional | 915 | | -exports][]). Unlike in the scenario where `"module"` is only used by bundlers, | | 930 | +exports][]). Unlike in the scenario where top-level `"module"` field is only used by bundlers, | 916 | 931 | or ES module files are transpiled into CommonJS on the fly before evaluation by | 917 | 932 | Node.js, the files referenced by the ES module entry point are evaluated as ES | 918 | 933 | modules. | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/get_format.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -97,7 +97,7 @@ function underNodeModules(url) { | 97 | 97 | let typelessPackageJsonFilesWarnedAbout; | 98 | 98 | function warnTypelessPackageJsonFile(pjsonPath, url) { | 99 | 99 | typelessPackageJsonFilesWarnedAbout ??= new SafeSet(); | 100 | | - if (!typelessPackageJsonFilesWarnedAbout.has(pjsonPath)) { | | 100 | + if (!underNodeModules(url) && !typelessPackageJsonFilesWarnedAbout.has(pjsonPath)) { | 101 | 101 | const warning = `Module type of ${url} is not specified and it doesn't parse as CommonJS.\n` + | 102 | 102 | 'Reparsing as ES module because module syntax was detected. This incurs a performance overhead.\n' + | 103 | 103 | `To eliminate this warning, add "type": "module" to ${pjsonPath}.`; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/utils.js +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -83,6 +83,9 @@ function initializeDefaultConditions() { | 83 | 83 | ...userConditions, | 84 | 84 | ]); | 85 | 85 | defaultConditionsSet = new SafeSet(defaultConditions); | | 86 | + if (getOptionValue('--experimental-require-module')) { | | 87 | + defaultConditionsSet.add('module-sync'); | | 88 | + } | 86 | 89 | } | 87 | 90 |
| 88 | 91 | /** | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/helpers.js +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -76,6 +76,9 @@ function initializeCjsConditions() { | 76 | 76 | ...addonConditions, | 77 | 77 | ...userConditions, | 78 | 78 | ]); | | 79 | + if (getOptionValue('--experimental-require-module')) { | | 80 | + cjsConditions.add('module-sync'); | | 81 | + } | 79 | 82 | } | 80 | 83 |
| 81 | 84 | /** | |
Collapse file Copy file name to clipboard +29Lines changed: 29 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +// Flags: --experimental-require-module | | 2 | + | | 3 | +import '../common/index.mjs'; | | 4 | +import assert from 'node:assert'; | | 5 | +import * as staticImport from '../fixtures/es-modules/module-condition/import.mjs'; | | 6 | +import { import as _import } from '../fixtures/es-modules/module-condition/dynamic_import.js'; | | 7 | + | | 8 | +async function dynamicImport(id) { | | 9 | + const result = await _import(id); | | 10 | + return result.resolved; | | 11 | +} | | 12 | + | | 13 | +assert.deepStrictEqual({ ...staticImport }, { | | 14 | + import_module_require: 'import', | | 15 | + module_and_import: 'module', | | 16 | + module_and_require: 'module', | | 17 | + module_import_require: 'module', | | 18 | + module_only: 'module', | | 19 | + module_require_import: 'module', | | 20 | + require_module_import: 'module', | | 21 | +}); | | 22 | + | | 23 | +assert.strictEqual(await dynamicImport('import-module-require'), 'import'); | | 24 | +assert.strictEqual(await dynamicImport('module-and-import'), 'module'); | | 25 | +assert.strictEqual(await dynamicImport('module-and-require'), 'module'); | | 26 | +assert.strictEqual(await dynamicImport('module-import-require'), 'module'); | | 27 | +assert.strictEqual(await dynamicImport('module-only'), 'module'); | | 28 | +assert.strictEqual(await dynamicImport('module-require-import'), 'module'); | | 29 | +assert.strictEqual(await dynamicImport('require-module-import'), 'module'); |
Collapse file Copy file name to clipboard +15Lines changed: 15 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +// Flags: --experimental-require-module | | 2 | +'use strict'; | | 3 | + | | 4 | +require('../common'); | | 5 | +const assert = require('assert'); | | 6 | + | | 7 | +const loader = require('../fixtures/es-modules/module-condition/require.cjs'); | | 8 | + | | 9 | +assert.strictEqual(loader.require('import-module-require').resolved, 'module'); | | 10 | +assert.strictEqual(loader.require('module-and-import').resolved, 'module'); | | 11 | +assert.strictEqual(loader.require('module-and-require').resolved, 'module'); | | 12 | +assert.strictEqual(loader.require('module-import-require').resolved, 'module'); | | 13 | +assert.strictEqual(loader.require('module-only').resolved, 'module'); | | 14 | +assert.strictEqual(loader.require('module-require-import').resolved, 'module'); | | 15 | +assert.strictEqual(loader.require('require-module-import').resolved, 'require'); |
Collapse file Copy file name to clipboard +5Lines changed: 5 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +function load(id) { | | 2 | + return import(id); | | 3 | +} | | 4 | + | | 5 | +export { load as import }; |
Collapse file Copy file name to clipboard +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +export { resolved as import_module_require } from 'import-module-require'; | | 2 | +export { resolved as module_and_import } from 'module-and-import'; | | 3 | +export { resolved as module_and_require } from 'module-and-require'; | | 4 | +export { resolved as module_import_require } from 'module-import-require'; | | 5 | +export { resolved as module_only } from 'module-only'; | | 6 | +export { resolved as module_require_import } from 'module-require-import'; | | 7 | +export { resolved as require_module_import } from 'require-module-import'; |
Collapse file Copy file name to clipboardExpand all lines: test/fixtures/es-modules/module-condition/node_modules/import-module-require/import.js +1Lines changed: 1 addition & 0 deletions
|