Skip to content

Commit 255d633

Browse files
committed
esm: deprecate legacy main lookup for modules
PR-URL: #36918 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent fd02dac commit 255d633

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
@@ -2726,10 +2726,28 @@ settings set when the Node.js binary was compiled. However, the property has
27262726
been mutable by user code making it impossible to rely on. The ability to
27272727
change the value has been deprecated and will be disabled in the future.
27282728

2729+
### DEP0XXX: Main index lookup and extension searching
2730+
<!-- YAML
2731+
changes:
2732+
- version: REPLACEME
2733+
pr-url: https://github.com/nodejs/node/pull/36918
2734+
description: Documentation-only deprecation
2735+
with `--pending-deprecation` support.
2736+
-->
2737+
2738+
Type: Documentation-only (supports [`--pending-deprecation`][])
2739+
2740+
Previously, `index.js` and extension searching lookups would apply to
2741+
`import 'pkg'` main entry point resolution, even when resolving ES modules.
2742+
2743+
With this deprecation, all ES module main entry point resolutions require
2744+
an explicit [`"exports"` or `"main"` entry][] with the exact file extension.
2745+
27292746
[Legacy URL API]: url.md#url_legacy_url_api
27302747
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
27312748
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
27322749
[WHATWG URL API]: url.md#url_the_whatwg_url_api
2750+
[`"exports"` or `"main"` entry]: packages.md#packages_main_entry_point_export
27332751
[`--pending-deprecation`]: cli.md#cli_pending_deprecation
27342752
[`--throw-deprecation`]: cli.md#cli_throw_deprecation
27352753
[`--unhandled-rejections`]: cli.md#cli_unhandled_rejections_mode
@@ -2852,7 +2870,7 @@ change the value has been deprecated and will be disabled in the future.
28522870
[from_string_encoding]: buffer.md#buffer_static_method_buffer_from_string_encoding
28532871
[legacy `urlObject`]: url.md#url_legacy_urlobject
28542872
[static methods of `crypto.Certificate()`]: crypto.md#crypto_class_certificate
2855-
[subpath exports]: #packages_subpath_exports
2856-
[subpath folder mappings]: #packages_subpath_folder_mappings
2857-
[subpath imports]: #packages_subpath_imports
2858-
[subpath patterns]: #packages_subpath_patterns
2873+
[subpath exports]: packages.md#packages_subpath_exports
2874+
[subpath folder mappings]: packages.md#packages_subpath_folder_mappings
2875+
[subpath imports]: packages.md#packages_subpath_imports
2876+
[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)