Skip to content

Commit e73bfed

Browse files
guybedfordtargos
authored andcommitted
esm: deprecate legacy main lookup for modules
PR-URL: #36918 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 8a2ce5d commit e73bfed

File tree

7 files changed

+89
-31
lines changed

7 files changed

+89
-31
lines changed

doc/api/deprecations.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2653,10 +2653,28 @@ In future versions of Node.js, `fs.rmdir(path, { recursive: true })` will throw
26532653
on nonexistent paths, or when given a file as a target.
26542654
Use `fs.rm(path, { recursive: true, force: true })` instead.
26552655

2656+
### DEP0XXX: Main index lookup and extension searching
2657+
<!-- YAML
2658+
changes:
2659+
- version: REPLACEME
2660+
pr-url: https://github.com/nodejs/node/pull/36918
2661+
description: Documentation-only deprecation
2662+
with `--pending-deprecation` support.
2663+
-->
2664+
2665+
Type: Documentation-only (supports [`--pending-deprecation`][])
2666+
2667+
Previously, `index.js` and extension searching lookups would apply to
2668+
`import 'pkg'` main entry point resolution, even when resolving ES modules.
2669+
2670+
With this deprecation, all ES module main entry point resolutions require
2671+
an explicit [`"exports"` or `"main"` entry][] with the exact file extension.
2672+
26562673
[Legacy URL API]: url.md#url_legacy_url_api
26572674
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
26582675
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
26592676
[WHATWG URL API]: url.md#url_the_whatwg_url_api
2677+
[`"exports"` or `"main"` entry]: packages.md#packages_main_entry_point_export
26602678
[`--pending-deprecation`]: cli.md#cli_pending_deprecation
26612679
[`--throw-deprecation`]: cli.md#cli_throw_deprecation
26622680
[`Buffer.allocUnsafeSlow(size)`]: buffer.md#buffer_static_method_buffer_allocunsafeslow_size

lib/internal/modules/esm/resolve.js

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,37 @@ const userConditions = getOptionValue('--conditions');
5959
const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]);
6060
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);
6161

62+
const pendingDeprecation = getOptionValue('--pending-deprecation');
63+
64+
function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
65+
if (!pendingDeprecation)
66+
return;
67+
const { format } = defaultGetFormat(url);
68+
if (format !== 'module')
69+
return;
70+
const path = fileURLToPath(url);
71+
const pkgPath = fileURLToPath(new URL('.', packageJSONUrl));
72+
const basePath = fileURLToPath(base);
73+
if (main)
74+
process.emitWarning(
75+
`Package ${pkgPath} has a "main" field set to ${JSONStringify(main)}, ` +
76+
`excluding the full filename and extension to the resolved file at "${
77+
StringPrototypeSlice(path, pkgPath.length)}", imported from ${
78+
basePath}.\n Automatic extension resolution of the "main" field is` +
79+
'deprecated for ES modules.',
80+
'DeprecationWarning',
81+
'DEP0150'
82+
);
83+
else
84+
process.emitWarning(
85+
`No "main" or "exports" field defined in the package.json for ${pkgPath
86+
} resolving the main entry point "${
87+
StringPrototypeSlice(path, pkgPath.length)}", imported from ${basePath
88+
}.\nDefault "index" lookups for the main are deprecated for ES modules.`,
89+
'DeprecationWarning',
90+
'DEP0150'
91+
);
92+
}
6293

6394
function getConditionsSet(conditions) {
6495
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
@@ -181,41 +212,33 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) {
181212
if (fileExists(guess = new URL(`./${packageConfig.main}`,
182213
packageJSONUrl))) {
183214
return guess;
184-
}
185-
if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
186-
packageJSONUrl))) {
187-
return guess;
188-
}
189-
if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
190-
packageJSONUrl))) {
191-
return guess;
192-
}
193-
if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
194-
packageJSONUrl))) {
195-
return guess;
196-
}
197-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
198-
packageJSONUrl))) {
199-
return guess;
200-
}
201-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
202-
packageJSONUrl))) {
203-
return guess;
204-
}
205-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
206-
packageJSONUrl))) {
215+
} else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
216+
packageJSONUrl)));
217+
else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
218+
packageJSONUrl)));
219+
else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
220+
packageJSONUrl)));
221+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
222+
packageJSONUrl)));
223+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
224+
packageJSONUrl)));
225+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
226+
packageJSONUrl)));
227+
else guess = undefined;
228+
if (guess) {
229+
emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
230+
packageConfig.main);
207231
return guess;
208232
}
209233
// Fallthrough.
210234
}
211-
if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
212-
return guess;
213-
}
235+
if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
214236
// So fs.
215-
if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
216-
return guess;
217-
}
218-
if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
237+
else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
238+
else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
239+
else guess = undefined;
240+
if (guess) {
241+
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
219242
return guess;
220243
}
221244
// Not found.
@@ -864,3 +887,6 @@ module.exports = {
864887
packageExportsResolve,
865888
packageImportsResolve
866889
};
890+
891+
// cycle
892+
const { defaultGetFormat } = require('internal/modules/esm/get_format');

test/es-module/test-esm-exports.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
3535
['pkgexports-sugar', { default: 'main' }],
3636
// Path patterns
3737
['pkgexports/subpath/sub-dir1', { default: 'main' }],
38-
['pkgexports/features/dir1', { default: 'main' }]
38+
['pkgexports/features/dir1', { default: 'main' }],
3939
]);
4040

4141
if (isRequire) {
@@ -44,6 +44,11 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
4444
validSpecifiers.set('pkgexports/subpath/dir1/', { default: 'main' });
4545
validSpecifiers.set('pkgexports/subpath/dir2', { default: 'index' });
4646
validSpecifiers.set('pkgexports/subpath/dir2/', { default: 'index' });
47+
} else {
48+
// No exports or main field
49+
validSpecifiers.set('no_exports', { default: 'index' });
50+
// Main field without extension
51+
validSpecifiers.set('default_index', { default: 'main' });
4752
}
4853

4954
for (const [validSpecifier, expected] of validSpecifiers) {

test/fixtures/node_modules/default_index/index.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/default_index/package.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/no_exports/index.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/no_exports/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)