Skip to content

Commit 773fe09

Browse files
committed
feat: add support for win32 paths
1 parent 58464fc commit 773fe09

10 files changed

+147
-36
lines changed

lib/DescriptionFilePlugin.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ module.exports = class DescriptionFilePlugin {
6363
);
6464
return callback();
6565
}
66-
const relativePath =
67-
"." + path.slice(result.directory.length).replace(/\\/g, "/");
66+
const relativePath = "." + path.slice(result.directory.length);
6867
/** @type {ResolveRequest} */
6968
const obj = {
7069
...request,

lib/DescriptionFileUtils.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"use strict";
77

8+
const path = require("path");
89
const forEachBail = require("./forEachBail");
910

1011
/** @typedef {import("./Resolver")} Resolver */
@@ -185,12 +186,10 @@ function getField(content, field) {
185186
* @returns {string|null} parent directory or null
186187
*/
187188
function cdUp(directory) {
188-
if (directory === "/") return null;
189-
const i = directory.lastIndexOf("/"),
190-
j = directory.lastIndexOf("\\");
191-
const p = i < 0 ? j : j < 0 ? i : i < j ? j : i;
192-
if (p < 0) return null;
193-
return directory.slice(0, p || 1);
189+
if (directory === path.sep) return null;
190+
const i = directory.lastIndexOf(path.sep);
191+
if (i < 0) return null;
192+
return directory.slice(0, i || 1);
194193
}
195194

196195
exports.loadDescriptionFile = loadDescriptionFile;

lib/Resolver.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"use strict";
77

8+
const nodePath = require("path");
89
const { AsyncSeriesBailHook, AsyncSeriesHook, SyncHook } = require("tapable");
910
const createInnerContext = require("./createInnerContext");
1011
const { parseIdentifier } = require("./util/identifier");
@@ -577,7 +578,7 @@ class Resolver {
577578
* @returns {boolean} true, if the path is a directory path
578579
*/
579580
isDirectory(path) {
580-
return path.endsWith("/");
581+
return path.endsWith(nodePath.sep);
581582
}
582583

583584
/**

lib/ResolverFactory.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ function createOptions(options) {
235235
: ["node_modules"],
236236
item => {
237237
const type = getType(item);
238-
return type === PathType.Normal || type === PathType.Relative;
238+
return (
239+
type === PathType.Normal ||
240+
type === PathType.RelativePosix ||
241+
type === PathType.RelativeWin
242+
);
239243
}
240244
),
241245
mainFields,

lib/RestrictionsPlugin.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55

66
"use strict";
77

8+
const path = require("path");
9+
810
/** @typedef {import("./Resolver")} Resolver */
911
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
1012

11-
const slashCode = "/".charCodeAt(0);
12-
const backslashCode = "\\".charCodeAt(0);
13+
const separatorCode = path.sep.charCodeAt(0);
1314

1415
/**
1516
* @param {string} path path
@@ -20,7 +21,7 @@ const isInside = (path, parent) => {
2021
if (!path.startsWith(parent)) return false;
2122
if (path.length === parent.length) return true;
2223
const charCode = path.charCodeAt(parent.length);
23-
return charCode === slashCode || charCode === backslashCode;
24+
return charCode === separatorCode;
2425
};
2526

2627
module.exports = class RestrictionsPlugin {

lib/RootsPlugin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
"use strict";
77

8+
const path = require("path");
89
const forEachBail = require("./forEachBail");
910

1011
/** @typedef {import("./Resolver")} Resolver */
@@ -35,7 +36,7 @@ class RootsPlugin {
3536
.tapAsync("RootsPlugin", (request, resolveContext, callback) => {
3637
const req = request.request;
3738
if (!req) return callback();
38-
if (!req.startsWith("/")) return callback();
39+
if (!req.startsWith(path.sep)) return callback();
3940

4041
forEachBail(
4142
this.roots,

lib/SelfReferencePlugin.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
"use strict";
77

8+
const path = require("path");
89
const DescriptionFileUtils = require("./DescriptionFileUtils");
910

1011
/** @typedef {import("./Resolver")} Resolver */
1112
/** @typedef {import("./Resolver").JsonObject} JsonObject */
1213
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
1314
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
1415

15-
const slashCode = "/".charCodeAt(0);
16+
const separatorCode = path.sep.charCodeAt(0);
1617

1718
module.exports = class SelfReferencePlugin {
1819
/**
@@ -56,7 +57,7 @@ module.exports = class SelfReferencePlugin {
5657
if (
5758
req.startsWith(name) &&
5859
(req.length === name.length ||
59-
req.charCodeAt(name.length) === slashCode)
60+
req.charCodeAt(name.length) === separatorCode)
6061
) {
6162
const remainingRequest = `.${req.slice(name.length)}`;
6263
/** @type {ResolveRequest} */

lib/getPaths.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55

66
"use strict";
77

8+
const nodePath = require("path");
9+
810
/**
911
* @param {string} path path
1012
* @returns {{paths: string[], segments: string[]}}} paths and segments
1113
*/
1214
module.exports = function getPaths(path) {
13-
if (path === "/") return { paths: ["/"], segments: [""] };
15+
if (path === nodePath.sep) return { paths: [nodePath.sep], segments: [""] };
1416
const parts = path.split(/(.*?[\\/]+)/);
1517
const paths = [path];
1618
const segments = [parts[parts.length - 1]];
@@ -19,7 +21,7 @@ module.exports = function getPaths(path) {
1921
for (let i = parts.length - 2; i > 2; i -= 2) {
2022
paths.push(path);
2123
part = parts[i];
22-
path = path.substring(0, path.length - part.length) || "/";
24+
path = path.substring(0, path.length - part.length) || nodePath.sep;
2325
segments.push(part.slice(0, -1));
2426
}
2527
part = parts[1];
@@ -36,10 +38,8 @@ module.exports = function getPaths(path) {
3638
* @returns {string|null} basename or null
3739
*/
3840
module.exports.basename = function basename(path) {
39-
const i = path.lastIndexOf("/"),
40-
j = path.lastIndexOf("\\");
41-
const p = i < 0 ? j : j < 0 ? i : i < j ? j : i;
42-
if (p < 0) return null;
43-
const s = path.slice(p + 1);
41+
const i = path.lastIndexOf(nodePath.sep);
42+
if (i < 0) return null;
43+
const s = path.slice(i + 1);
4444
return s;
4545
};

lib/util/path.js

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const winNormalize = path.win32.normalize;
2626
const PathType = Object.freeze({
2727
Empty: 0,
2828
Normal: 1,
29-
Relative: 2,
29+
RelativeWin: 6,
30+
RelativePosix: 2,
3031
AbsoluteWin: 3,
3132
AbsolutePosix: 4,
3233
Internal: 5
@@ -45,7 +46,9 @@ const getType = p => {
4546
const c0 = p.charCodeAt(0);
4647
switch (c0) {
4748
case CHAR_DOT:
48-
return PathType.Relative;
49+
return path.sep.charCodeAt(0) === CHAR_SLASH
50+
? PathType.RelativePosix
51+
: PathType.RelativeWin;
4952
case CHAR_SLASH:
5053
return PathType.AbsolutePosix;
5154
case CHAR_HASH:
@@ -60,8 +63,13 @@ const getType = p => {
6063
const c1 = p.charCodeAt(1);
6164
switch (c1) {
6265
case CHAR_DOT:
66+
return path.sep.charCodeAt(0) === CHAR_SLASH
67+
? PathType.RelativePosix
68+
: PathType.RelativeWin;
6369
case CHAR_SLASH:
64-
return PathType.Relative;
70+
return PathType.RelativePosix;
71+
case CHAR_BACKSLASH:
72+
return PathType.RelativeWin;
6573
}
6674
return PathType.Normal;
6775
}
@@ -88,10 +96,15 @@ const getType = p => {
8896
const c1 = p.charCodeAt(1);
8997
switch (c1) {
9098
case CHAR_SLASH:
91-
return PathType.Relative;
99+
return PathType.RelativePosix;
100+
case CHAR_BACKSLASH:
101+
return PathType.RelativeWin;
92102
case CHAR_DOT: {
93103
const c2 = p.charCodeAt(2);
94-
if (c2 === CHAR_SLASH) return PathType.Relative;
104+
105+
if (c2 === CHAR_SLASH) return PathType.RelativePosix;
106+
if (c2 === CHAR_BACKSLASH) return PathType.RelativeWin;
107+
95108
return PathType.Normal;
96109
}
97110
}
@@ -127,12 +140,16 @@ const normalize = p => {
127140
return p;
128141
case PathType.AbsoluteWin:
129142
return winNormalize(p);
130-
case PathType.Relative: {
143+
case PathType.RelativePosix: {
131144
const r = posixNormalize(p);
132-
return getType(r) === PathType.Relative ? r : `./${r}`;
145+
return getType(r) === PathType.RelativePosix ? r : `./${r}`;
146+
}
147+
case PathType.RelativeWin: {
148+
const r = winNormalize(p);
149+
return getType(r) === PathType.RelativeWin ? r : `.\\${r}`;
133150
}
134151
}
135-
return posixNormalize(p);
152+
return path.normalize(p);
136153
};
137154
exports.normalize = normalize;
138155

@@ -152,21 +169,29 @@ const join = (rootPath, request) => {
152169
}
153170
switch (getType(rootPath)) {
154171
case PathType.Normal:
155-
case PathType.Relative:
172+
return path.sep.charCodeAt(0) === CHAR_SLASH
173+
? posixNormalize(`${rootPath}/${request}`)
174+
: winNormalize(`${rootPath}\\${request}`);
175+
case PathType.RelativePosix:
156176
case PathType.AbsolutePosix:
157177
return posixNormalize(`${rootPath}/${request}`);
178+
case PathType.RelativeWin:
158179
case PathType.AbsoluteWin:
159180
return winNormalize(`${rootPath}\\${request}`);
160181
}
161182
switch (requestType) {
162183
case PathType.Empty:
163184
return rootPath;
164-
case PathType.Relative: {
185+
case PathType.RelativePosix: {
186+
const r = posixNormalize(rootPath);
187+
return getType(r) === PathType.RelativePosix ? r : `./${r}`;
188+
}
189+
case PathType.RelativeWin: {
165190
const r = posixNormalize(rootPath);
166-
return getType(r) === PathType.Relative ? r : `./${r}`;
191+
return getType(r) === PathType.RelativeWin ? r : `.\\${r}`;
167192
}
168193
}
169-
return posixNormalize(rootPath);
194+
return path.normalize(rootPath);
170195
};
171196
exports.join = join;
172197

test/path.test.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
const { checkImportsExportsFieldTarget } = require("../lib/util/path");
1+
const path = require("path");
2+
const {
3+
checkImportsExportsFieldTarget,
4+
PathType,
5+
getType,
6+
normalize
7+
} = require("../lib/util/path");
28

39
describe("checkImportsExportsFieldTarget", () => {
410
/**
@@ -27,3 +33,77 @@ describe("checkImportsExportsFieldTarget", () => {
2733
});
2834
});
2935
});
36+
37+
describe("getPath", () => {
38+
let pathSepDefault = path.sep;
39+
40+
afterAll(() => {
41+
path.sep = pathSepDefault;
42+
});
43+
44+
["win32", "posix"].forEach(platform => {
45+
const relativePathType =
46+
platform === "win32" ? "RelativeWin" : "RelativePosix";
47+
const separator = platform === "win32" ? "\\" : "/";
48+
49+
it(`should resolve PathType.${relativePathType} for paths if path.sep is ${platform} (${separator})`, () => {
50+
path.sep = separator;
51+
52+
expect(getType(".")).toBe(PathType[relativePathType]);
53+
expect(getType("..")).toBe(PathType[relativePathType]);
54+
expect(getType(`..${path.sep}`)).toBe(PathType[relativePathType]);
55+
expect(getType(`..${path.sep}test${path.sep}index.js`)).toBe(
56+
PathType[relativePathType]
57+
);
58+
});
59+
});
60+
});
61+
62+
describe("normalize", () => {
63+
let pathSepDefault = path.sep;
64+
65+
afterEach(() => {
66+
path.sep = pathSepDefault;
67+
});
68+
69+
["win32", "posix"].forEach(platform => {
70+
const separator = platform === "win32" ? "\\" : "/";
71+
72+
it(`should correctly normalize for relative/empty paths if path.sep is ${platform} (${separator})`, () => {
73+
path.sep = separator;
74+
75+
expect(normalize("")).toBe("");
76+
expect(
77+
normalize(
78+
`..${path.sep}hello${path.sep}world${path.sep}..${path.sep}test.js`
79+
)
80+
).toBe(`..${path.sep}hello${path.sep}test.js`);
81+
});
82+
});
83+
84+
it("should correctly normalize for PathType.AbsoluteWin", () => {
85+
path.sep = "\\";
86+
87+
expect(
88+
normalize(
89+
`..${path.sep}hello${path.sep}world${path.sep}..${path.sep}test.js`
90+
)
91+
).toBe(`..${path.sep}hello${path.sep}test.js`);
92+
});
93+
94+
it("should correctly normalize for PathType.AbsolutePosix", () => {
95+
path.sep = "/";
96+
97+
expect(
98+
normalize(
99+
`..${path.sep}hello${path.sep}world${path.sep}..${path.sep}test.js`
100+
)
101+
).toBe(`..${path.sep}hello${path.sep}test.js`);
102+
});
103+
104+
it("should correctly normalize for PathType.Normal", () => {
105+
expect(normalize("enhancedResolve/lib/util/../index")).toBe(
106+
"enhancedResolve/lib/index"
107+
);
108+
});
109+
});

0 commit comments

Comments
 (0)