Skip to content

Commit ffb1929

Browse files
authored
esm: fix emit deprecation on legacy main resolve
PR-URL: #48664 Refs: #48325 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Rafael Gonzaga <[email protected]>
1 parent 43e5798 commit ffb1929

File tree

2 files changed

+135
-13
lines changed

2 files changed

+135
-13
lines changed

lib/internal/modules/esm/resolve.js

+14-13
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const { getOptionValue } = require('internal/options');
3030
const policy = getOptionValue('--experimental-policy') ?
3131
require('internal/process/policy') :
3232
null;
33-
const { sep, relative, toNamespacedPath } = require('path');
33+
const { sep, relative, toNamespacedPath, resolve } = require('path');
3434
const preserveSymlinks = getOptionValue('--preserve-symlinks');
3535
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
3636
const experimentalNetworkImports =
@@ -101,7 +101,7 @@ function emitInvalidSegmentDeprecation(target, request, match, pjsonUrl, interna
101101
* @param {URL} url
102102
* @param {URL} packageJSONUrl
103103
* @param {string | URL | undefined} base
104-
* @param {string} main
104+
* @param {string} [main]
105105
* @returns {void}
106106
*/
107107
function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
@@ -111,17 +111,7 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
111111
const path = fileURLToPath(url);
112112
const pkgPath = fileURLToPath(new URL('.', packageJSONUrl));
113113
const basePath = fileURLToPath(base);
114-
if (main)
115-
process.emitWarning(
116-
`Package ${pkgPath} has a "main" field set to ${JSONStringify(main)}, ` +
117-
`excluding the full filename and extension to the resolved file at "${
118-
StringPrototypeSlice(path, pkgPath.length)}", imported from ${
119-
basePath}.\n Automatic extension resolution of the "main" field is ` +
120-
'deprecated for ES modules.',
121-
'DeprecationWarning',
122-
'DEP0151',
123-
);
124-
else
114+
if (!main) {
125115
process.emitWarning(
126116
`No "main" or "exports" field defined in the package.json for ${pkgPath
127117
} resolving the main entry point "${
@@ -130,6 +120,17 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
130120
'DeprecationWarning',
131121
'DEP0151',
132122
);
123+
} else if (resolve(pkgPath, main) !== path) {
124+
process.emitWarning(
125+
`Package ${pkgPath} has a "main" field set to "${main}", ` +
126+
`excluding the full filename and extension to the resolved file at "${
127+
StringPrototypeSlice(path, pkgPath.length)}", imported from ${
128+
basePath}.\n Automatic extension resolution of the "main" field is ` +
129+
'deprecated for ES modules.',
130+
'DeprecationWarning',
131+
'DEP0151',
132+
);
133+
}
133134
}
134135

135136
const realpathCache = new SafeMap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { spawnPromisified } from '../common/index.mjs';
2+
import * as tmpdir from '../common/tmpdir.js';
3+
4+
import assert from 'node:assert';
5+
import { mkdir, writeFile } from 'node:fs/promises';
6+
import * as path from 'node:path';
7+
import { execPath } from 'node:process';
8+
import { describe, it, before } from 'node:test';
9+
10+
describe('ESM in main field', { concurrency: true }, () => {
11+
before(() => tmpdir.refresh());
12+
13+
it('should handle fully-specified relative path without any warning', async () => {
14+
const cwd = path.join(tmpdir.path, Math.random().toString());
15+
const pkgPath = path.join(cwd, './node_modules/pkg/');
16+
await mkdir(pkgPath, { recursive: true });
17+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
18+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
19+
main: './index.js',
20+
type: 'module',
21+
}));
22+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
23+
'--input-type=module',
24+
'--eval', 'import "pkg"',
25+
], { cwd });
26+
27+
assert.strictEqual(stderr, '');
28+
assert.match(stdout, /^Hello World!\r?\n$/);
29+
assert.strictEqual(code, 0);
30+
});
31+
it('should handle fully-specified absolute path without any warning', async () => {
32+
const cwd = path.join(tmpdir.path, Math.random().toString());
33+
const pkgPath = path.join(cwd, './node_modules/pkg/');
34+
await mkdir(pkgPath, { recursive: true });
35+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
36+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
37+
main: path.join(pkgPath, './index.js'),
38+
type: 'module',
39+
}));
40+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
41+
'--input-type=module',
42+
'--eval', 'import "pkg"',
43+
], { cwd });
44+
45+
assert.strictEqual(stderr, '');
46+
assert.match(stdout, /^Hello World!\r?\n$/);
47+
assert.strictEqual(code, 0);
48+
});
49+
50+
it('should emit warning when "main" and "exports" are missing', async () => {
51+
const cwd = path.join(tmpdir.path, Math.random().toString());
52+
const pkgPath = path.join(cwd, './node_modules/pkg/');
53+
await mkdir(pkgPath, { recursive: true });
54+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
55+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
56+
type: 'module',
57+
}));
58+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
59+
'--input-type=module',
60+
'--eval', 'import "pkg"',
61+
], { cwd });
62+
63+
assert.match(stderr, /\[DEP0151\]/);
64+
assert.match(stdout, /^Hello World!\r?\n$/);
65+
assert.strictEqual(code, 0);
66+
});
67+
it('should emit warning when "main" is falsy', async () => {
68+
const cwd = path.join(tmpdir.path, Math.random().toString());
69+
const pkgPath = path.join(cwd, './node_modules/pkg/');
70+
await mkdir(pkgPath, { recursive: true });
71+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
72+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
73+
type: 'module',
74+
main: '',
75+
}));
76+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
77+
'--input-type=module',
78+
'--eval', 'import "pkg"',
79+
], { cwd });
80+
81+
assert.match(stderr, /\[DEP0151\]/);
82+
assert.match(stdout, /^Hello World!\r?\n$/);
83+
assert.strictEqual(code, 0);
84+
});
85+
it('should emit warning when "main" is a relative path without extension', async () => {
86+
const cwd = path.join(tmpdir.path, Math.random().toString());
87+
const pkgPath = path.join(cwd, './node_modules/pkg/');
88+
await mkdir(pkgPath, { recursive: true });
89+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
90+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
91+
main: 'index',
92+
type: 'module',
93+
}));
94+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
95+
'--input-type=module',
96+
'--eval', 'import "pkg"',
97+
], { cwd });
98+
99+
assert.match(stderr, /\[DEP0151\]/);
100+
assert.match(stdout, /^Hello World!\r?\n$/);
101+
assert.strictEqual(code, 0);
102+
});
103+
it('should emit warning when "main" is an absolute path without extension', async () => {
104+
const cwd = path.join(tmpdir.path, Math.random().toString());
105+
const pkgPath = path.join(cwd, './node_modules/pkg/');
106+
await mkdir(pkgPath, { recursive: true });
107+
await writeFile(path.join(pkgPath, './index.js'), 'console.log("Hello World!")');
108+
await writeFile(path.join(pkgPath, './package.json'), JSON.stringify({
109+
main: pkgPath + 'index',
110+
type: 'module',
111+
}));
112+
const { code, stdout, stderr } = await spawnPromisified(execPath, [
113+
'--input-type=module',
114+
'--eval', 'import "pkg"',
115+
], { cwd });
116+
117+
assert.match(stderr, /\[DEP0151\]/);
118+
assert.match(stdout, /^Hello World!\r?\n$/);
119+
assert.strictEqual(code, 0);
120+
});
121+
});

0 commit comments

Comments
 (0)