diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 6a841a2f72f485..a320736d1b6fd7 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -1931,6 +1931,15 @@ Module._extensions['.js'] = function(module, filename) { } else { format = 'typescript'; } + } else if (path.extname(filename) === '') { + // Extensionless files skip the .js suffix check above. When type is explicit, + // follow it so ESM syntax surfaces as SyntaxError for commonjs instead of + // silently delegating to ESM. + pkg = packageJsonReader.getNearestParentPackageJSON(filename); + const typeFromPjson = pkg?.data?.type; + if (typeFromPjson === 'commonjs' || typeFromPjson === 'module') { + format = typeFromPjson; + } } const { source, format: loadedFormat } = loadSource(module, filename, format); // Function require shouldn't be used in ES modules when require(esm) is disabled. diff --git a/test/es-module/test-extensionless-esm-type-commonjs.js b/test/es-module/test-extensionless-esm-type-commonjs.js new file mode 100644 index 00000000000000..cdfdf9361393e9 --- /dev/null +++ b/test/es-module/test-extensionless-esm-type-commonjs.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +spawnSyncAndAssert(process.execPath, [ + fixtures.path('es-modules', 'extensionless-esm-commonjs', 'script'), +], { + status: 1, + stderr: /SyntaxError: Cannot use import statement outside a module/, +}); + +spawnSyncAndAssert(process.execPath, [ + fixtures.path('es-modules', 'extensionless-esm-module', 'script'), +], { + stdout: /script STARTED[\s\S]*v\d+\./, + trim: true, +}); diff --git a/test/fixtures/es-modules/extensionless-esm-commonjs/package.json b/test/fixtures/es-modules/extensionless-esm-commonjs/package.json new file mode 100644 index 00000000000000..5bbefffbabee39 --- /dev/null +++ b/test/fixtures/es-modules/extensionless-esm-commonjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test/fixtures/es-modules/extensionless-esm-commonjs/script b/test/fixtures/es-modules/extensionless-esm-commonjs/script new file mode 100755 index 00000000000000..75798ebfc99202 --- /dev/null +++ b/test/fixtures/es-modules/extensionless-esm-commonjs/script @@ -0,0 +1,4 @@ +#!/usr/bin/env node +console.log('script STARTED') +import { version } from 'node:process' +console.log(version) diff --git a/test/fixtures/es-modules/extensionless-esm-module/package.json b/test/fixtures/es-modules/extensionless-esm-module/package.json new file mode 100644 index 00000000000000..3dbc1ca591c055 --- /dev/null +++ b/test/fixtures/es-modules/extensionless-esm-module/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/test/fixtures/es-modules/extensionless-esm-module/script b/test/fixtures/es-modules/extensionless-esm-module/script new file mode 100755 index 00000000000000..75798ebfc99202 --- /dev/null +++ b/test/fixtures/es-modules/extensionless-esm-module/script @@ -0,0 +1,4 @@ +#!/usr/bin/env node +console.log('script STARTED') +import { version } from 'node:process' +console.log(version) diff --git a/tools/generate_config_gypi.py b/tools/generate_config_gypi.py index abd11e1dbda8d4..436767e633e1f4 100755 --- a/tools/generate_config_gypi.py +++ b/tools/generate_config_gypi.py @@ -58,7 +58,7 @@ def translate_config(out_dir, config, v8_config): 'llvm_version': 13, 'napi_build_version': config['napi_build_version'], 'node_builtin_shareable_builtins': - eval(config['node_builtin_shareable_builtins']), + json.loads(config['node_builtin_shareable_builtins']), 'node_module_version': int(config['node_module_version']), 'node_use_openssl': config['node_use_openssl'], 'node_use_amaro': config['node_use_amaro'], @@ -102,7 +102,8 @@ def main(): # Write output. with open(args.target, 'w') as f: - f.write(repr(translate_config(args.out_dir, config, v8_config))) + f.write(json.dumps(translate_config(args.out_dir, config, v8_config), + sort_keys=True)) # Write depfile. Force regenerating config.gypi when GN configs change. if args.dep_file: