Skip to content

Commit 02f1d2f

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 b79b82d commit 02f1d2f

File tree

8 files changed

+95
-36
lines changed

8 files changed

+95
-36
lines changed

doc/api/deprecations.md

+22-4
Original file line numberDiff line numberDiff line change
@@ -2699,10 +2699,28 @@ resolutions not in `node_modules`. This means there will not be deprecation
26992699
warnings for `"exports"` in dependencies. With `--pending-deprecation`, a
27002700
runtime warning results no matter where the `"exports"` usage occurs.
27012701

2702+
### DEP0XXX: Main index lookup and extension searching
2703+
<!-- YAML
2704+
changes:
2705+
- version: REPLACEME
2706+
pr-url: https://github.com/nodejs/node/pull/36918
2707+
description: Documentation-only deprecation
2708+
with `--pending-deprecation` support.
2709+
-->
2710+
2711+
Type: Documentation-only (supports [`--pending-deprecation`][])
2712+
2713+
Previously, `index.js` and extension searching lookups would apply to
2714+
`import 'pkg'` main entry point resolution, even when resolving ES modules.
2715+
2716+
With this deprecation, all ES module main entry point resolutions require
2717+
an explicit [`"exports"` or `"main"` entry][] with the exact file extension.
2718+
27022719
[Legacy URL API]: url.md#url_legacy_url_api
27032720
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
27042721
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
27052722
[WHATWG URL API]: url.md#url_the_whatwg_url_api
2723+
[`"exports"` or `"main"` entry]: packages.md#packages_main_entry_point_export
27062724
[`--pending-deprecation`]: cli.md#cli_pending_deprecation
27072725
[`--throw-deprecation`]: cli.md#cli_throw_deprecation
27082726
[`--unhandled-rejections`]: cli.md#cli_unhandled_rejections_mode
@@ -2823,7 +2841,7 @@ runtime warning results no matter where the `"exports"` usage occurs.
28232841
[from_string_encoding]: buffer.md#buffer_static_method_buffer_from_string_encoding
28242842
[legacy `urlObject`]: url.md#url_legacy_urlobject
28252843
[static methods of `crypto.Certificate()`]: crypto.md#crypto_class_certificate
2826-
[subpath exports]: #packages_subpath_exports
2827-
[subpath folder mappings]: #packages_subpath_folder_mappings
2828-
[subpath imports]: #packages_subpath_imports
2829-
[subpath patterns]: #packages_subpath_patterns
2844+
[subpath exports]: packages.md#packages_subpath_exports
2845+
[subpath folder mappings]: packages.md#packages_subpath_folder_mappings
2846+
[subpath imports]: packages.md#packages_subpath_imports
2847+
[subpath patterns]: packages.md#packages_subpath_patterns

lib/internal/modules/esm/resolve.js

+55-30
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,36 @@ function emitFolderMapDeprecation(match, pjsonUrl, isExports, base) {
9090
);
9191
}
9292

93+
function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
94+
if (!pendingDeprecation)
95+
return;
96+
const { format } = defaultGetFormat(url);
97+
if (format !== 'module')
98+
return;
99+
const path = fileURLToPath(url);
100+
const pkgPath = fileURLToPath(new URL('.', packageJSONUrl));
101+
const basePath = fileURLToPath(base);
102+
if (main)
103+
process.emitWarning(
104+
`Package ${pkgPath} has a "main" field set to ${JSONStringify(main)}, ` +
105+
`excluding the full filename and extension to the resolved file at "${
106+
StringPrototypeSlice(path, pkgPath.length)}", imported from ${
107+
basePath}.\n Automatic extension resolution of the "main" field is` +
108+
'deprecated for ES modules.',
109+
'DeprecationWarning',
110+
'DEP0150'
111+
);
112+
else
113+
process.emitWarning(
114+
`No "main" or "exports" field defined in the package.json for ${pkgPath
115+
} resolving the main entry point "${
116+
StringPrototypeSlice(path, pkgPath.length)}", imported from ${basePath
117+
}.\nDefault "index" lookups for the main are deprecated for ES modules.`,
118+
'DeprecationWarning',
119+
'DEP0150'
120+
);
121+
}
122+
93123
function getConditionsSet(conditions) {
94124
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
95125
if (!ArrayIsArray(conditions)) {
@@ -209,41 +239,33 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) {
209239
if (fileExists(guess = new URL(`./${packageConfig.main}`,
210240
packageJSONUrl))) {
211241
return guess;
212-
}
213-
if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
214-
packageJSONUrl))) {
215-
return guess;
216-
}
217-
if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
218-
packageJSONUrl))) {
219-
return guess;
220-
}
221-
if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
222-
packageJSONUrl))) {
223-
return guess;
224-
}
225-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
226-
packageJSONUrl))) {
227-
return guess;
228-
}
229-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
230-
packageJSONUrl))) {
231-
return guess;
232-
}
233-
if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
234-
packageJSONUrl))) {
242+
} else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
243+
packageJSONUrl)));
244+
else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
245+
packageJSONUrl)));
246+
else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
247+
packageJSONUrl)));
248+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
249+
packageJSONUrl)));
250+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
251+
packageJSONUrl)));
252+
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
253+
packageJSONUrl)));
254+
else guess = undefined;
255+
if (guess) {
256+
emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
257+
packageConfig.main);
235258
return guess;
236259
}
237260
// Fallthrough.
238261
}
239-
if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
240-
return guess;
241-
}
262+
if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
242263
// So fs.
243-
if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
244-
return guess;
245-
}
246-
if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
264+
else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
265+
else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
266+
else guess = undefined;
267+
if (guess) {
268+
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
247269
return guess;
248270
}
249271
// Not found.
@@ -891,3 +913,6 @@ module.exports = {
891913
packageExportsResolve,
892914
packageImportsResolve
893915
};
916+
917+
// cycle
918+
const { defaultGetFormat } = require('internal/modules/esm/get_format');

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ let curWarning = 0;
66
const expectedWarnings = [
77
'"./sub/"',
88
'"./fallbackdir/"',
9-
'"./subpath/"'
9+
'"./subpath/"',
10+
'no_exports',
11+
'default_index'
1012
];
1113

1214
process.addListener('warning', mustCall((warning) => {

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

+6-1
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

+1
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

+4
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

+1
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

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)