Skip to content

Commit

Permalink
esm: deprecate legacy main lookup for modules
Browse files Browse the repository at this point in the history
PR-URL: #36918
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
guybedford authored and targos committed Sep 4, 2021
1 parent 8a2ce5d commit e73bfed
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 31 deletions.
18 changes: 18 additions & 0 deletions doc/api/deprecations.md
Expand Up @@ -2653,10 +2653,28 @@ In future versions of Node.js, `fs.rmdir(path, { recursive: true })` will throw
on nonexistent paths, or when given a file as a target.
Use `fs.rm(path, { recursive: true, force: true })` instead.

### DEP0XXX: Main index lookup and extension searching
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/36918
description: Documentation-only deprecation
with `--pending-deprecation` support.
-->

Type: Documentation-only (supports [`--pending-deprecation`][])

Previously, `index.js` and extension searching lookups would apply to
`import 'pkg'` main entry point resolution, even when resolving ES modules.

With this deprecation, all ES module main entry point resolutions require
an explicit [`"exports"` or `"main"` entry][] with the exact file extension.

[Legacy URL API]: url.md#url_legacy_url_api
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
[WHATWG URL API]: url.md#url_the_whatwg_url_api
[`"exports"` or `"main"` entry]: packages.md#packages_main_entry_point_export
[`--pending-deprecation`]: cli.md#cli_pending_deprecation
[`--throw-deprecation`]: cli.md#cli_throw_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.md#buffer_static_method_buffer_allocunsafeslow_size
Expand Down
86 changes: 56 additions & 30 deletions lib/internal/modules/esm/resolve.js
Expand Up @@ -59,6 +59,37 @@ const userConditions = getOptionValue('--conditions');
const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]);
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);

const pendingDeprecation = getOptionValue('--pending-deprecation');

function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
if (!pendingDeprecation)
return;
const { format } = defaultGetFormat(url);
if (format !== 'module')
return;
const path = fileURLToPath(url);
const pkgPath = fileURLToPath(new URL('.', packageJSONUrl));
const basePath = fileURLToPath(base);
if (main)
process.emitWarning(
`Package ${pkgPath} has a "main" field set to ${JSONStringify(main)}, ` +
`excluding the full filename and extension to the resolved file at "${
StringPrototypeSlice(path, pkgPath.length)}", imported from ${
basePath}.\n Automatic extension resolution of the "main" field is` +
'deprecated for ES modules.',
'DeprecationWarning',
'DEP0150'
);
else
process.emitWarning(
`No "main" or "exports" field defined in the package.json for ${pkgPath
} resolving the main entry point "${
StringPrototypeSlice(path, pkgPath.length)}", imported from ${basePath
}.\nDefault "index" lookups for the main are deprecated for ES modules.`,
'DeprecationWarning',
'DEP0150'
);
}

function getConditionsSet(conditions) {
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
Expand Down Expand Up @@ -181,41 +212,33 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) {
if (fileExists(guess = new URL(`./${packageConfig.main}`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
packageJSONUrl))) {
} else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
packageJSONUrl)));
else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
packageJSONUrl)));
else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
packageJSONUrl)));
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
packageJSONUrl)));
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
packageJSONUrl)));
else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
packageJSONUrl)));
else guess = undefined;
if (guess) {
emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
packageConfig.main);
return guess;
}
// Fallthrough.
}
if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
// So fs.
if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
return guess;
}
if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
else guess = undefined;
if (guess) {
emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
return guess;
}
// Not found.
Expand Down Expand Up @@ -864,3 +887,6 @@ module.exports = {
packageExportsResolve,
packageImportsResolve
};

// cycle
const { defaultGetFormat } = require('internal/modules/esm/get_format');
7 changes: 6 additions & 1 deletion test/es-module/test-esm-exports.mjs
Expand Up @@ -35,7 +35,7 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
['pkgexports-sugar', { default: 'main' }],
// Path patterns
['pkgexports/subpath/sub-dir1', { default: 'main' }],
['pkgexports/features/dir1', { default: 'main' }]
['pkgexports/features/dir1', { default: 'main' }],
]);

if (isRequire) {
Expand All @@ -44,6 +44,11 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js';
validSpecifiers.set('pkgexports/subpath/dir1/', { default: 'main' });
validSpecifiers.set('pkgexports/subpath/dir2', { default: 'index' });
validSpecifiers.set('pkgexports/subpath/dir2/', { default: 'index' });
} else {
// No exports or main field
validSpecifiers.set('no_exports', { default: 'index' });
// Main field without extension
validSpecifiers.set('default_index', { default: 'main' });
}

for (const [validSpecifier, expected] of validSpecifiers) {
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/node_modules/default_index/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions test/fixtures/node_modules/default_index/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/fixtures/node_modules/no_exports/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions test/fixtures/node_modules/no_exports/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e73bfed

Please sign in to comment.