GeezerVison is showing github.com — path: fix bugs and inconsistencies · nodejs/node@efbba60
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +13-2Lines changed: 13 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -702,7 +702,7 @@ Module._findPath = function(request, paths, isMain) { | 702 | 702 |
| 703 | 703 | let exts; | 704 | 704 | const trailingSlash = request.length > 0 && | 705 | | - (StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_FORWARD_SLASH || ( | | 705 | + ((StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_FORWARD_SLASH || ( | 706 | 706 | StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_DOT && | 707 | 707 | ( | 708 | 708 | request.length === 1 || | @@ -712,7 +712,18 @@ Module._findPath = function(request, paths, isMain) { | 712 | 712 | StringPrototypeCharCodeAt(request, request.length - 3) === CHAR_FORWARD_SLASH | 713 | 713 | )) | 714 | 714 | ) | 715 | | - )); | | 715 | + )) || (isWindows && ( | | 716 | + StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_BACKWARD_SLASH || ( | | 717 | + StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_DOT && | | 718 | + ( | | 719 | + request.length === 1 || | | 720 | + StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_BACKWARD_SLASH || | | 721 | + (StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_DOT && ( | | 722 | + request.length === 2 || | | 723 | + StringPrototypeCharCodeAt(request, request.length - 3) === CHAR_BACKWARD_SLASH | | 724 | + )) | | 725 | + ) | | 726 | + )))); | 716 | 727 |
| 717 | 728 | const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT && | 718 | 729 | ( | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/url.js -9Lines changed: 0 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -74,9 +74,7 @@ const { | 74 | 74 | } = require('internal/errors'); | 75 | 75 | const { | 76 | 76 | CHAR_AMPERSAND, | 77 | | - CHAR_BACKWARD_SLASH, | 78 | 77 | CHAR_EQUAL, | 79 | | - CHAR_FORWARD_SLASH, | 80 | 78 | CHAR_LOWERCASE_A, | 81 | 79 | CHAR_LOWERCASE_Z, | 82 | 80 | CHAR_PERCENT, | @@ -1569,13 +1567,6 @@ function pathToFileURL(filepath, options = kEmptyObject) { | 1569 | 1567 | return outURL; | 1570 | 1568 | } | 1571 | 1569 | let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); | 1572 | | - // path.resolve strips trailing slashes so we must add them back | 1573 | | - const filePathLast = StringPrototypeCharCodeAt(filepath, | 1574 | | - filepath.length - 1); | 1575 | | - if ((filePathLast === CHAR_FORWARD_SLASH || | 1576 | | - ((windows ?? isWindows) && filePathLast === CHAR_BACKWARD_SLASH)) && | 1577 | | - resolved[resolved.length - 1] !== path.sep) | 1578 | | - resolved += '/'; | 1579 | 1570 |
| 1580 | 1571 | // Call encodePathChars first to avoid encoding % again for ? and #. | 1581 | 1572 | resolved = encodePathChars(resolved, { windows }); | |
Collapse file Copy file name to clipboardExpand all lines: lib/path.js +98-26Lines changed: 98 additions & 26 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -190,6 +190,7 @@ const win32 = { | 190 | 190 | let resolvedDevice = ''; | 191 | 191 | let resolvedTail = ''; | 192 | 192 | let resolvedAbsolute = false; | | 193 | + let slashCheck = false; | 193 | 194 |
| 194 | 195 | for (let i = args.length - 1; i >= -1; i--) { | 195 | 196 | let path; | @@ -221,6 +222,10 @@ const win32 = { | 221 | 222 | } | 222 | 223 | } | 223 | 224 |
| | 225 | + if (i === args.length - 1 && | | 226 | + isPathSeparator(StringPrototypeCharCodeAt(path, path.length - 1))) { | | 227 | + slashCheck = true; | | 228 | + } | 224 | 229 | const len = path.length; | 225 | 230 | let rootEnd = 0; | 226 | 231 | let device = ''; | @@ -268,10 +273,16 @@ const win32 = { | 268 | 273 | j++; | 269 | 274 | } | 270 | 275 | if (j === len || j !== last) { | 271 | | - // We matched a UNC root | 272 | | - device = | 273 | | - `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; | 274 | | - rootEnd = j; | | 276 | + if (firstPart !== '.' && firstPart !== '?') { | | 277 | + // We matched a UNC root | | 278 | + device = | | 279 | + `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; | | 280 | + rootEnd = j; | | 281 | + } else { | | 282 | + // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) | | 283 | + device = `\\\\${firstPart}`; | | 284 | + rootEnd = 4; | | 285 | + } | 275 | 286 | } | 276 | 287 | } | 277 | 288 | } | @@ -323,9 +334,21 @@ const win32 = { | 323 | 334 | resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', | 324 | 335 | isPathSeparator); | 325 | 336 |
| 326 | | - return resolvedAbsolute ? | 327 | | - `${resolvedDevice}\\${resolvedTail}` : | 328 | | - `${resolvedDevice}${resolvedTail}` || '.'; | | 337 | + if (!resolvedAbsolute) { | | 338 | + return `${resolvedDevice}${resolvedTail}` || '.'; | | 339 | + } | | 340 | + | | 341 | + if (resolvedTail.length === 0) { | | 342 | + return slashCheck ? `${resolvedDevice}\\` : resolvedDevice; | | 343 | + } | | 344 | + | | 345 | + if (slashCheck) { | | 346 | + return resolvedTail === '\\' ? | | 347 | + `${resolvedDevice}\\` : | | 348 | + `${resolvedDevice}\\${resolvedTail}\\`; | | 349 | + } | | 350 | + | | 351 | + return `${resolvedDevice}\\${resolvedTail}`; | 329 | 352 | }, | 330 | 353 |
| 331 | 354 | /** | @@ -381,17 +404,22 @@ const win32 = { | 381 | 404 | !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { | 382 | 405 | j++; | 383 | 406 | } | 384 | | - if (j === len) { | 385 | | - // We matched a UNC root only | 386 | | - // Return the normalized version of the UNC root since there | 387 | | - // is nothing left to process | 388 | | - return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`; | 389 | | - } | 390 | | - if (j !== last) { | 391 | | - // We matched a UNC root with leftovers | 392 | | - device = | 393 | | - `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; | 394 | | - rootEnd = j; | | 407 | + if (j === len || j !== last) { | | 408 | + if (firstPart === '.' || firstPart === '?') { | | 409 | + // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) | | 410 | + device = `\\\\${firstPart}`; | | 411 | + rootEnd = 4; | | 412 | + } else if (j === len) { | | 413 | + // We matched a UNC root only | | 414 | + // Return the normalized version of the UNC root since there | | 415 | + // is nothing left to process | | 416 | + return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`; | | 417 | + } else { | | 418 | + // We matched a UNC root with leftovers | | 419 | + device = | | 420 | + `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; | | 421 | + rootEnd = j; | | 422 | + } | 395 | 423 | } | 396 | 424 | } | 397 | 425 | } | @@ -1162,6 +1190,7 @@ const posix = { | 1162 | 1190 | resolve(...args) { | 1163 | 1191 | let resolvedPath = ''; | 1164 | 1192 | let resolvedAbsolute = false; | | 1193 | + let slashCheck = false; | 1165 | 1194 |
| 1166 | 1195 | for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { | 1167 | 1196 | const path = i >= 0 ? args[i] : posixCwd(); | @@ -1171,8 +1200,17 @@ const posix = { | 1171 | 1200 | if (path.length === 0) { | 1172 | 1201 | continue; | 1173 | 1202 | } | | 1203 | + if (i === args.length - 1 && | | 1204 | + isPosixPathSeparator(StringPrototypeCharCodeAt(path, | | 1205 | + path.length - 1))) { | | 1206 | + slashCheck = true; | | 1207 | + } | 1174 | 1208 |
| 1175 | | - resolvedPath = `${path}/${resolvedPath}`; | | 1209 | + if (resolvedPath.length !== 0) { | | 1210 | + resolvedPath = `${path}/${resolvedPath}`; | | 1211 | + } else { | | 1212 | + resolvedPath = path; | | 1213 | + } | 1176 | 1214 | resolvedAbsolute = | 1177 | 1215 | StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; | 1178 | 1216 | } | @@ -1184,10 +1222,20 @@ const posix = { | 1184 | 1222 | resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', | 1185 | 1223 | isPosixPathSeparator); | 1186 | 1224 |
| 1187 | | - if (resolvedAbsolute) { | 1188 | | - return `/${resolvedPath}`; | | 1225 | + if (!resolvedAbsolute) { | | 1226 | + if (resolvedPath.length === 0) { | | 1227 | + return '.'; | | 1228 | + } | | 1229 | + if (slashCheck) { | | 1230 | + return `${resolvedPath}/`; | | 1231 | + } | | 1232 | + return resolvedPath; | | 1233 | + } | | 1234 | + | | 1235 | + if (resolvedPath.length === 0 || resolvedPath === '/') { | | 1236 | + return '/'; | 1189 | 1237 | } | 1190 | | - return resolvedPath.length > 0 ? resolvedPath : '.'; | | 1238 | + return slashCheck ? `/${resolvedPath}/` : `/${resolvedPath}`; | 1191 | 1239 | }, | 1192 | 1240 |
| 1193 | 1241 | /** | @@ -1271,11 +1319,35 @@ const posix = { | 1271 | 1319 | if (from === to) | 1272 | 1320 | return ''; | 1273 | 1321 |
| 1274 | | - const fromStart = 1; | 1275 | | - const fromEnd = from.length; | | 1322 | + // Trim any leading slashes | | 1323 | + let fromStart = 0; | | 1324 | + while (fromStart < from.length && | | 1325 | + StringPrototypeCharCodeAt(from, fromStart) === CHAR_FORWARD_SLASH) { | | 1326 | + fromStart++; | | 1327 | + } | | 1328 | + // Trim trailing slashes | | 1329 | + let fromEnd = from.length; | | 1330 | + while ( | | 1331 | + fromEnd - 1 > fromStart && | | 1332 | + StringPrototypeCharCodeAt(from, fromEnd - 1) === CHAR_FORWARD_SLASH | | 1333 | + ) { | | 1334 | + fromEnd--; | | 1335 | + } | 1276 | 1336 | const fromLen = fromEnd - fromStart; | 1277 | | - const toStart = 1; | 1278 | | - const toLen = to.length - toStart; | | 1337 | + | | 1338 | + // Trim any leading slashes | | 1339 | + let toStart = 0; | | 1340 | + while (toStart < to.length && | | 1341 | + StringPrototypeCharCodeAt(to, toStart) === CHAR_FORWARD_SLASH) { | | 1342 | + toStart++; | | 1343 | + } | | 1344 | + // Trim trailing slashes | | 1345 | + let toEnd = to.length; | | 1346 | + while (toEnd - 1 > toStart && | | 1347 | + StringPrototypeCharCodeAt(to, toEnd - 1) === CHAR_FORWARD_SLASH) { | | 1348 | + toEnd--; | | 1349 | + } | | 1350 | + const toLen = toEnd - toStart; | 1279 | 1351 |
| 1280 | 1352 | // Compare paths to find the longest common path from root | 1281 | 1353 | const length = (fromLen < toLen ? fromLen : toLen); | |
Collapse file Copy file name to clipboardExpand all lines: src/path.cc +48-13Lines changed: 48 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -97,6 +97,7 @@ std::string PathResolve(Environment* env, | 97 | 97 | std::string resolvedDevice = ""; | 98 | 98 | std::string resolvedTail = ""; | 99 | 99 | bool resolvedAbsolute = false; | | 100 | + bool slashCheck = false; | 100 | 101 | const size_t numArgs = paths.size(); | 101 | 102 | auto cwd = env->GetCwd(env->exec_path()); | 102 | 103 |
| @@ -126,6 +127,10 @@ std::string PathResolve(Environment* env, | 126 | 127 | } | 127 | 128 | } | 128 | 129 |
| | 130 | + if (static_cast<size_t>(i) == numArgs - 1 && | | 131 | + IsPathSeparator(path[path.length() - 1])) { | | 132 | + slashCheck = true; | | 133 | + } | 129 | 134 | const size_t len = path.length(); | 130 | 135 | int rootEnd = 0; | 131 | 136 | std::string device = ""; | @@ -170,9 +175,16 @@ std::string PathResolve(Environment* env, | 170 | 175 | j++; | 171 | 176 | } | 172 | 177 | if (j == len || j != last) { | 173 | | - // We matched a UNC root | 174 | | - device = "\\\\" + firstPart + "\\" + path.substr(last, j - last); | 175 | | - rootEnd = j; | | 178 | + if (firstPart != "." && firstPart != "?") { | | 179 | + // We matched a UNC root | | 180 | + device = | | 181 | + "\\\\" + firstPart + "\\" + path.substr(last, j - last); | | 182 | + rootEnd = j; | | 183 | + } else { | | 184 | + // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) | | 185 | + device = "\\\\" + firstPart; | | 186 | + rootEnd = 4; | | 187 | + } | 176 | 188 | } | 177 | 189 | } | 178 | 190 | } | @@ -220,15 +232,27 @@ std::string PathResolve(Environment* env, | 220 | 232 | // Normalize the tail path | 221 | 233 | resolvedTail = NormalizeString(resolvedTail, !resolvedAbsolute, "\\"); | 222 | 234 |
| 223 | | - if (resolvedAbsolute) { | 224 | | - return resolvedDevice + "\\" + resolvedTail; | | 235 | + if (!resolvedAbsolute) { | | 236 | + if (!resolvedDevice.empty() || !resolvedTail.empty()) { | | 237 | + return resolvedDevice + resolvedTail; | | 238 | + } | | 239 | + return "."; | 225 | 240 | } | 226 | 241 |
| 227 | | - if (!resolvedDevice.empty() || !resolvedTail.empty()) { | 228 | | - return resolvedDevice + resolvedTail; | | 242 | + if (resolvedTail.empty()) { | | 243 | + if (slashCheck) { | | 244 | + return resolvedDevice + "\\"; | | 245 | + } | | 246 | + return resolvedDevice; | 229 | 247 | } | 230 | 248 |
| 231 | | - return "."; | | 249 | + if (slashCheck) { | | 250 | + if (resolvedTail == "\\") { | | 251 | + return resolvedDevice + "\\"; | | 252 | + } | | 253 | + return resolvedDevice + "\\" + resolvedTail + "\\"; | | 254 | + } | | 255 | + return resolvedDevice + "\\" + resolvedTail; | 232 | 256 | } | 233 | 257 | #else // _WIN32 | 234 | 258 | std::string PathResolve(Environment* env, | @@ -237,10 +261,15 @@ std::string PathResolve(Environment* env, | 237 | 261 | bool resolvedAbsolute = false; | 238 | 262 | auto cwd = env->GetCwd(env->exec_path()); | 239 | 263 | const size_t numArgs = paths.size(); | | 264 | + bool slashCheck = false; | 240 | 265 |
| 241 | 266 | for (int i = numArgs - 1; i >= -1 && !resolvedAbsolute; i--) { | 242 | 267 | const std::string& path = (i >= 0) ? std::string(paths[i]) : cwd; | 243 | 268 |
| | 269 | + if (static_cast<size_t>(i) == numArgs - 1 && path.back() == '/') { | | 270 | + slashCheck = true; | | 271 | + } | | 272 | + | 244 | 273 | if (!path.empty()) { | 245 | 274 | resolvedPath = std::string(path) + "/" + resolvedPath; | 246 | 275 |
| @@ -254,15 +283,21 @@ std::string PathResolve(Environment* env, | 254 | 283 | // Normalize the path | 255 | 284 | auto normalizedPath = NormalizeString(resolvedPath, !resolvedAbsolute, "/"); | 256 | 285 |
| 257 | | - if (resolvedAbsolute) { | 258 | | - return "/" + normalizedPath; | | 286 | + if (!resolvedAbsolute) { | | 287 | + if (normalizedPath.empty()) { | | 288 | + return "."; | | 289 | + } | | 290 | + if (slashCheck) { | | 291 | + return normalizedPath + "/"; | | 292 | + } | | 293 | + return normalizedPath; | 259 | 294 | } | 260 | 295 |
| 261 | | - if (normalizedPath.empty()) { | 262 | | - return "."; | | 296 | + if (normalizedPath.empty() || normalizedPath == "/") { | | 297 | + return "/"; | 263 | 298 | } | 264 | 299 |
| 265 | | - return normalizedPath; | | 300 | + return slashCheck ? "/" + normalizedPath + "/" : "/" + normalizedPath; | 266 | 301 | } | 267 | 302 | #endif // _WIN32 | 268 | 303 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/cctest/test_path.cc +11-9Lines changed: 11 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -25,26 +25,28 @@ TEST_F(PathTest, PathResolve) { | 25 | 25 | "d:\\e.exe"); | 26 | 26 | EXPECT_EQ(PathResolve(*env, {"c:/ignore", "c:/some/file"}), "c:\\some\\file"); | 27 | 27 | EXPECT_EQ(PathResolve(*env, {"d:/ignore", "d:some/dir//"}), | 28 | | - "d:\\ignore\\some\\dir"); | | 28 | + "d:\\ignore\\some\\dir\\"); | 29 | 29 | EXPECT_EQ(PathResolve(*env, {"."}), cwd); | 30 | 30 | EXPECT_EQ(PathResolve(*env, {"//server/share", "..", "relative\\"}), | 31 | | - "\\\\server\\share\\relative"); | | 31 | + "\\\\server\\share\\relative\\"); | 32 | 32 | EXPECT_EQ(PathResolve(*env, {"c:/", "//"}), "c:\\"); | 33 | 33 | EXPECT_EQ(PathResolve(*env, {"c:/", "//dir"}), "c:\\dir"); | 34 | | - EXPECT_EQ(PathResolve(*env, {"c:/", "//server/share"}), | 35 | | - "\\\\server\\share\\"); | 36 | | - EXPECT_EQ(PathResolve(*env, {"c:/", "//server//share"}), | 37 | | - "\\\\server\\share\\"); | | 34 | + EXPECT_EQ(PathResolve(*env, {"c:/", "//server/share"}), "\\\\server\\share"); | | 35 | + EXPECT_EQ(PathResolve(*env, {"c:/", "//server//share"}), "\\\\server\\share"); | 38 | 36 | EXPECT_EQ(PathResolve(*env, {"c:/", "///some//dir"}), "c:\\some\\dir"); | 39 | 37 | EXPECT_EQ( | 40 | 38 | PathResolve(*env, {"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}), | 41 | 39 | "C:\\foo\\tmp.3\\cycles\\root.js"); | | 40 | + EXPECT_EQ(PathResolve(*env, {"\\\\.\\PHYSICALDRIVE0"}), | | 41 | + "\\\\.\\PHYSICALDRIVE0"); | | 42 | + EXPECT_EQ(PathResolve(*env, {"\\\\?\\PHYSICALDRIVE0"}), | | 43 | + "\\\\?\\PHYSICALDRIVE0"); | 42 | 44 | #else | 43 | | - EXPECT_EQ(PathResolve(*env, {"/var/lib", "../", "file/"}), "/var/file"); | 44 | | - EXPECT_EQ(PathResolve(*env, {"/var/lib", "/../", "file/"}), "/file"); | | 45 | + EXPECT_EQ(PathResolve(*env, {"/var/lib", "../", "file/"}), "/var/file/"); | | 46 | + EXPECT_EQ(PathResolve(*env, {"/var/lib", "/../", "file/"}), "/file/"); | 45 | 47 | EXPECT_EQ(PathResolve(*env, {"a/b/c/", "../../.."}), cwd); | 46 | 48 | EXPECT_EQ(PathResolve(*env, {"."}), cwd); | 47 | | - EXPECT_EQ(PathResolve(*env, {"/some/dir", ".", "/absolute/"}), "/absolute"); | | 49 | + EXPECT_EQ(PathResolve(*env, {"/some/dir", ".", "/absolute/"}), "/absolute/"); | 48 | 50 | EXPECT_EQ(PathResolve(*env, {"/foo/tmp.3/", "../tmp.3/cycles/root.js"}), | 49 | 51 | "/foo/tmp.3/cycles/root.js"); | 50 | 52 | #endif | |
|