-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
modules: runtime deprecate subpath folder mappings #35747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f258111
dda5369
8a517bd
0460eb4
4caaf60
6bf53be
e33c7ac
850772a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ const { | |
String, | ||
StringPrototypeEndsWith, | ||
StringPrototypeIndexOf, | ||
StringPrototypeLastIndexOf, | ||
StringPrototypeReplace, | ||
StringPrototypeSlice, | ||
StringPrototypeSplit, | ||
|
@@ -59,6 +60,36 @@ const userConditions = getOptionValue('--conditions'); | |
const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]); | ||
const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); | ||
|
||
const pendingDeprecation = getOptionValue('--pending-deprecation'); | ||
const emittedPackageWarnings = new SafeSet(); | ||
function emitFolderMapDeprecation(match, pjsonUrl, isExports, base) { | ||
const pjsonPath = fileURLToPath(pjsonUrl); | ||
if (!pendingDeprecation) { | ||
const nodeModulesIndex = StringPrototypeLastIndexOf(pjsonPath, | ||
'/node_modules/'); | ||
if (nodeModulesIndex !== -1) { | ||
const afterNodeModulesPath = StringPrototypeSlice(pjsonPath, | ||
nodeModulesIndex + 14, | ||
-13); | ||
try { | ||
const { packageSubpath } = parsePackageName(afterNodeModulesPath); | ||
if (packageSubpath === '.') | ||
return; | ||
} catch {} | ||
} | ||
} | ||
if (emittedPackageWarnings.has(pjsonPath + '|' + match)) | ||
return; | ||
emittedPackageWarnings.add(pjsonPath + '|' + match); | ||
process.emitWarning( | ||
`Use of deprecated folder mapping "${match}" in the ${isExports ? | ||
'"exports"' : '"imports"'} field module resolution of the package at ${ | ||
pjsonPath}${base ? ` imported from ${fileURLToPath(base)}` : ''}.\n` + | ||
`Update this package.json to use a subpath pattern like "${match}*".`, | ||
Comment on lines
+85
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be reasonable in this message to mention which node versions support subpath patterns, so package authors don't unknowingly make a breaking change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have version information in the documentation for this under the exports field. Keeping it up to date here might be tricky though, especially with a backport still in progress. It's a good point though - we could possibly delay this landing until the 12 backport is released if you'd prefer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should print version information here. It should be available in the docs and to the best of my knowledge we d not have that information available in any other deprecation warnings There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair. How likely are other deprecation warnings to cause package breakage compared to this one? Maybe we could at least say "Please check the docs for which node versions support subpath patterns" and link them there? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subpath patterns are on 14.13, 15 and will be backported to 12 very soon so I'm just not sure what value there is in muddying the advice for users here? The deprecation has a clear action and that is to move away from the feature or upgrade to subpaths. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just because node doesn’t support a version doesn’t mean package authors don’t. It’s obv fine if you don’t want to take my suggestion, but i think the risk of causing breakage is far greater than the risk of someone remaining on slash patterns due to an extra sentence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed there is a risk, but my concern is that any advice with respect to being cautious about subpaths support will be outdated by the time most users read it. Even if we weren't backporting to Node.js 12, a deprecation of a feature on 15/16 that points to using a feature only available on 14 seems fine to me and I'm not sure there is precedent for warning users from using a feature (from the web to Node.js) just because it's not available 3 versions back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (within the platform warning itself) |
||
'DeprecationWarning', | ||
'DEP0148' | ||
); | ||
} | ||
|
||
function getConditionsSet(conditions) { | ||
if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { | ||
|
@@ -507,6 +538,8 @@ function packageExportsResolve( | |
conditions); | ||
if (resolved === null || resolved === undefined) | ||
throwExportsNotFound(packageSubpath, packageJSONUrl, base); | ||
if (!pattern) | ||
emitFolderMapDeprecation(bestMatch, packageJSONUrl, true, base); | ||
return { resolved, exact: pattern }; | ||
} | ||
|
||
|
@@ -556,8 +589,11 @@ function packageImportsResolve(name, base, conditions) { | |
const resolved = resolvePackageTarget( | ||
packageJSONUrl, target, subpath, bestMatch, base, pattern, true, | ||
conditions); | ||
if (resolved !== null) | ||
if (resolved !== null) { | ||
if (!pattern) | ||
emitFolderMapDeprecation(bestMatch, packageJSONUrl, false, base); | ||
return { resolved, exact: pattern }; | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -570,13 +606,7 @@ function getPackageType(url) { | |
return packageConfig.type; | ||
} | ||
|
||
/** | ||
* @param {string} specifier | ||
* @param {URL} base | ||
* @param {Set<string>} conditions | ||
* @returns {URL} | ||
*/ | ||
function packageResolve(specifier, base, conditions) { | ||
function parsePackageName(specifier, base) { | ||
let separatorIndex = StringPrototypeIndexOf(specifier, '/'); | ||
let validPackageName = true; | ||
let isScoped = false; | ||
|
@@ -610,6 +640,19 @@ function packageResolve(specifier, base, conditions) { | |
const packageSubpath = '.' + (separatorIndex === -1 ? '' : | ||
StringPrototypeSlice(specifier, separatorIndex)); | ||
|
||
return { packageName, packageSubpath, isScoped }; | ||
} | ||
|
||
/** | ||
* @param {string} specifier | ||
* @param {URL} base | ||
* @param {Set<string>} conditions | ||
* @returns {URL} | ||
*/ | ||
function packageResolve(specifier, base, conditions) { | ||
const { packageName, packageSubpath, isScoped } = | ||
parsePackageName(specifier, base); | ||
|
||
// ResolveSelf | ||
const packageConfig = getPackageScopeConfig(base); | ||
if (packageConfig.exists) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Flags: --pending-deprecation | ||
import { mustCall } from '../common/index.mjs'; | ||
import assert from 'assert'; | ||
|
||
let curWarning = 0; | ||
const expectedWarnings = [ | ||
'"./sub/"', | ||
'"./fallbackdir/"', | ||
'"./subpath/"' | ||
]; | ||
|
||
process.addListener('warning', mustCall((warning) => { | ||
assert(warning.stack.includes(expectedWarnings[curWarning++]), warning.stack); | ||
}, expectedWarnings.length)); | ||
|
||
(async () => { | ||
await import('./test-esm-exports.mjs'); | ||
})() | ||
.catch((err) => console.error(err)); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { mustCall } from '../common/index.mjs'; | ||
import assert from 'assert'; | ||
import fixtures from '../common/fixtures.js'; | ||
import { pathToFileURL } from 'url'; | ||
|
||
const selfDeprecatedFolders = | ||
fixtures.path('/es-modules/self-deprecated-folders/main.js'); | ||
|
||
let curWarning = 0; | ||
const expectedWarnings = [ | ||
'"./" in the "exports" field', | ||
'"#self/" in the "imports" field' | ||
]; | ||
|
||
process.addListener('warning', mustCall((warning) => { | ||
assert(warning.stack.includes(expectedWarnings[curWarning++]), warning.stack); | ||
}, expectedWarnings.length)); | ||
|
||
(async () => { | ||
await import(pathToFileURL(selfDeprecatedFolders)); | ||
})() | ||
.catch((err) => console.error(err)); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import 'self/main.js'; | ||
import '#self/main.js'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "self", | ||
"type": "module", | ||
"exports": { | ||
".": "./main.js", | ||
"./": "./" | ||
}, | ||
"imports": { | ||
"#self/": "./" | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.