-
Notifications
You must be signed in to change notification settings - Fork 190
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
fix: corrects resolve for win32 paths #436
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -5,6 +5,8 @@ | |
|
||
"use strict"; | ||
|
||
const { transformPathToPosix } = require("./util/path"); | ||
|
||
/** @typedef {import("./Resolver")} Resolver */ | ||
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */ | ||
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */ | ||
|
@@ -77,10 +79,18 @@ module.exports = class PnpPlugin { | |
return; | ||
} | ||
|
||
if (typeof resolution === "string") { | ||
resolution = transformPathToPosix(resolution); | ||
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. Same here 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. external path that can be received from pnpapi -> we transform it to path with posix separators in case we get path with win32 separator from pnpapi |
||
} | ||
|
||
if (resolveContext.fileDependencies) { | ||
apiResolution = this.pnpApi.resolveToUnqualified("pnpapi", issuer, { | ||
considerBuiltins: false | ||
}); | ||
|
||
if (typeof apiResolution === "string") { | ||
apiResolution = transformPathToPosix(apiResolution); | ||
} | ||
} | ||
} catch (/** @type {unknown} */ error) { | ||
if ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,9 @@ | |
const versions = require("process").versions; | ||
const Resolver = require("./Resolver"); | ||
const { getType, PathType } = require("./util/path"); | ||
const { | ||
normalizeOptionsForWindows | ||
} = require("./util/win32-normalize-options"); | ||
|
||
const SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator"); | ||
|
||
|
@@ -199,7 +202,7 @@ function createOptions(options) { | |
} | ||
} | ||
|
||
return { | ||
return normalizeOptionsForWindows({ | ||
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 am afraid it will be slow on big builds... 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. Okay, I'll take a look how it integrates with other libraries, maybe I'll find better place to normalize it. Don't really sure how often resolver factory can be called. |
||
alias: normalizeAlias(options.alias), | ||
fallback: normalizeAlias(options.fallback), | ||
aliasFields: new Set(options.aliasFields), | ||
|
@@ -267,7 +270,7 @@ function createOptions(options) { | |
preferRelative: options.preferRelative || false, | ||
preferAbsolute: options.preferAbsolute || false, | ||
restrictions: new Set(options.restrictions) | ||
}; | ||
}); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ | |
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */ | ||
|
||
const slashCode = "/".charCodeAt(0); | ||
const backslashCode = "\\".charCodeAt(0); | ||
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. Can you describe? 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 can keep it like this, but I thought about removing it because internally we should handle only posix separator char |
||
|
||
/** | ||
* @param {string} path path | ||
|
@@ -20,7 +19,7 @@ const isInside = (path, parent) => { | |
if (!path.startsWith(parent)) return false; | ||
if (path.length === parent.length) return true; | ||
const charCode = path.charCodeAt(parent.length); | ||
return charCode === slashCode || charCode === backslashCode; | ||
return charCode === slashCode; | ||
}; | ||
|
||
module.exports = class RestrictionsPlugin { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,7 @@ | |
*/ | ||
module.exports = function getPaths(path) { | ||
if (path === "/") return { paths: ["/"], segments: [""] }; | ||
const parts = path.split(/(.*?[\\/]+)/); | ||
const parts = path.split(/(.*?[/]+)/); | ||
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. And this 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. Same idea as this: #436 (comment) |
||
const paths = [path]; | ||
const segments = [parts[parts.length - 1]]; | ||
let part = parts[parts.length - 1]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,6 @@ const CHAR_DOT = ".".charCodeAt(0); | |
const CHAR_COLON = ":".charCodeAt(0); | ||
|
||
const posixNormalize = path.posix.normalize; | ||
const winNormalize = path.win32.normalize; | ||
|
||
/** | ||
* @enum {number} | ||
|
@@ -134,7 +133,7 @@ const normalize = p => { | |
case PathType.Empty: | ||
return p; | ||
case PathType.AbsoluteWin: | ||
return winNormalize(p); | ||
return posixNormalize(p); | ||
case PathType.Relative: { | ||
const r = posixNormalize(p); | ||
return getType(r) === PathType.Relative ? r : `./${r}`; | ||
|
@@ -156,15 +155,15 @@ const join = (rootPath, request) => { | |
case PathType.AbsolutePosix: | ||
return posixNormalize(request); | ||
case PathType.AbsoluteWin: | ||
return winNormalize(request); | ||
return posixNormalize(request); | ||
} | ||
switch (getType(rootPath)) { | ||
case PathType.Normal: | ||
case PathType.Relative: | ||
case PathType.AbsolutePosix: | ||
return posixNormalize(`${rootPath}/${request}`); | ||
case PathType.AbsoluteWin: | ||
return winNormalize(`${rootPath}\\${request}`); | ||
return posixNormalize(`${rootPath}/${request}`); | ||
} | ||
switch (requestType) { | ||
case PathType.Empty: | ||
|
@@ -201,3 +200,16 @@ const cachedJoin = (rootPath, request) => { | |
return cacheEntry; | ||
}; | ||
exports.cachedJoin = cachedJoin; | ||
|
||
/** | ||
* @param {string} rawPath node.js path | ||
* @returns {string} normalized for windows, same for posix paths | ||
*/ | ||
const transformPathToPosix = rawPath => { | ||
if (!rawPath) return rawPath; | ||
|
||
if (path.sep === "/") | ||
return rawPath; // Don't even transform if path.sep is posix | ||
else return rawPath.split("\\").join("/"); | ||
}; | ||
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 am very worried about the performance here and that we will have different behavior on windws and nix systems 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 agree about the performance part. About the behavior: it's already different and based on os. It's possible with this PR to test how different behavior is between Unix and Windows with the following instruction:
|
||
exports.transformPathToPosix = transformPathToPosix; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
MIT License http://www.opensource.org/licenses/mit-license.php | ||
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. question: not sure, what I should write here? should I add same author ( 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. @davakh You can write own name here |
||
*/ | ||
|
||
"use strict"; | ||
|
||
const { transformPathToPosix } = require("./path"); | ||
|
||
/** @typedef {import("../AliasPlugin").AliasOption} AliasOptionEntry */ | ||
/** @typedef {import("../Resolver").ResolveOptions} ResolveOptions */ | ||
|
||
/** | ||
* @param {AliasOptionEntry[]} aliasOptions alias options | ||
* @returns {AliasOptionEntry[]} normalized for win32 aliases | ||
*/ | ||
function normalizeWindowsAliasOption(aliasOptions) { | ||
return aliasOptions.map(aliasOption => { | ||
let newAliasOption = aliasOption.alias; | ||
|
||
if (typeof newAliasOption !== "boolean") { | ||
newAliasOption = normalizeStringOrArrayOfStrings(newAliasOption); | ||
} | ||
|
||
return { | ||
...aliasOption, | ||
name: normalizeStringOrArrayOfStrings(aliasOption.name), | ||
alias: newAliasOption | ||
}; | ||
}); | ||
} | ||
|
||
/** | ||
* @param {Set<string | string[]> | Set<string> | Set<string[]>} rawSet alias | ||
* @returns {*} normalized fon win32 sets of string or string[] | ||
*/ | ||
function normalizeStringifiedSets(rawSet) { | ||
const normalizedSet = new Set(); | ||
rawSet.forEach(item => { | ||
normalizedSet.add(normalizeStringOrArrayOfStrings(item)); | ||
}); | ||
return normalizedSet; | ||
} | ||
|
||
/** | ||
* @param {string | string[]} str str | ||
* @returns {*} normalized str | ||
*/ | ||
function normalizeStringOrArrayOfStrings(str) { | ||
return Array.isArray(str) | ||
? str.map(transformPathToPosix) | ||
: transformPathToPosix(str); | ||
} | ||
|
||
/** | ||
* @param {ResolveOptions} resolveOptions input options | ||
* @returns {ResolveOptions} output options | ||
*/ | ||
function normalizeOptionsForWindows(resolveOptions) { | ||
// List of all options that can be passed with win32 path separator | ||
resolveOptions.alias = normalizeWindowsAliasOption(resolveOptions.alias); | ||
resolveOptions.fallback = normalizeWindowsAliasOption( | ||
resolveOptions.fallback | ||
); | ||
resolveOptions.aliasFields = normalizeStringifiedSets( | ||
resolveOptions.aliasFields | ||
); | ||
resolveOptions.extensionAlias = resolveOptions.extensionAlias.map( | ||
aliasOption => ({ | ||
...aliasOption, | ||
alias: normalizeStringOrArrayOfStrings(aliasOption.alias) | ||
}) | ||
); | ||
resolveOptions.conditionNames = normalizeStringifiedSets( | ||
resolveOptions.conditionNames | ||
); | ||
resolveOptions.descriptionFiles = normalizeStringOrArrayOfStrings( | ||
resolveOptions.descriptionFiles | ||
); | ||
resolveOptions.exportsFields = normalizeStringifiedSets( | ||
resolveOptions.exportsFields | ||
); | ||
resolveOptions.importsFields = normalizeStringifiedSets( | ||
resolveOptions.importsFields | ||
); | ||
resolveOptions.extensions = normalizeStringifiedSets( | ||
resolveOptions.extensions | ||
); | ||
resolveOptions.modules = Array.isArray(resolveOptions.modules) | ||
? resolveOptions.modules.map(item => normalizeStringOrArrayOfStrings(item)) | ||
: resolveOptions.modules; | ||
resolveOptions.mainFiles = normalizeStringifiedSets(resolveOptions.mainFiles); | ||
resolveOptions.roots = normalizeStringifiedSets(resolveOptions.roots); | ||
|
||
const newRestrictions = new Set(); | ||
resolveOptions.restrictions.forEach(restrict => { | ||
if (typeof restrict === "string") { | ||
newRestrictions.add(normalizeStringOrArrayOfStrings(restrict)); | ||
} else { | ||
// regexp | ||
newRestrictions.add(restrict); | ||
} | ||
}); | ||
resolveOptions.restrictions = newRestrictions; | ||
|
||
return resolveOptions; | ||
} | ||
|
||
exports.normalizeOptionsForWindows = normalizeOptionsForWindows; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need it? We have unix path internally...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it can be win32 path still. Of course, it has posix char separator, but absolute path can be resolved as
X:/something/
for example and for this case - to prevent this function to go up