/rust/registry/src/index.crates.io-1949cf8c6b5b557f/oxc_resolver-11.19.1/src/path.rs
Line | Count | Source |
1 | | //! Path Utilities |
2 | | //! |
3 | | //! Code adapted from the following libraries |
4 | | //! * [path-absolutize](https://docs.rs/path-absolutize) |
5 | | //! * [normalize_path](https://docs.rs/normalize-path) |
6 | | use std::path::{Component, Path, PathBuf}; |
7 | | |
8 | | pub const SLASH_START: &[char; 2] = &['/', '\\']; |
9 | | |
10 | | /// Extension trait to add path normalization to std's [`Path`]. |
11 | | pub trait PathUtil { |
12 | | /// Normalize this path without performing I/O. |
13 | | /// |
14 | | /// All redundant separator and up-level references are collapsed. |
15 | | /// |
16 | | /// However, this does not resolve links. |
17 | | fn normalize(&self) -> PathBuf; |
18 | | |
19 | | /// Like `normalize`, but don't require the path to be absolute. |
20 | | fn normalize_relative(&self) -> PathBuf; |
21 | | |
22 | | /// Normalize with subpath assuming this path is normalized without performing I/O. |
23 | | /// |
24 | | /// All redundant separator and up-level references are collapsed. |
25 | | /// |
26 | | /// However, this does not resolve links. |
27 | | fn normalize_with<P: AsRef<Path>>(&self, subpath: P) -> PathBuf; |
28 | | |
29 | | /// Defined in ESM PACKAGE_TARGET_RESOLVE |
30 | | /// If target split on "/" or "\" contains any "", ".", "..", or "node_modules" segments after the first "." segment, case insensitive and including percent encoded variants |
31 | | fn is_invalid_exports_target(&self) -> bool; |
32 | | } |
33 | | |
34 | | impl PathUtil for Path { |
35 | | // https://github.com/parcel-bundler/parcel/blob/e0b99c2a42e9109a9ecbd6f537844a1b33e7faf5/packages/utils/node-resolver-rs/src/path.rs#L7 |
36 | 0 | fn normalize(&self) -> PathBuf { |
37 | 0 | let mut components = self.components().peekable(); |
38 | 0 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek() { |
39 | 0 | let buf = PathBuf::from(c.as_os_str()); |
40 | 0 | components.next(); |
41 | 0 | buf |
42 | | } else { |
43 | 0 | PathBuf::new() |
44 | | }; |
45 | | |
46 | 0 | for component in components { |
47 | 0 | match component { |
48 | 0 | Component::Prefix(..) => unreachable!("Path {:?}", self), |
49 | 0 | Component::RootDir => { |
50 | 0 | ret.push(component.as_os_str()); |
51 | 0 | } |
52 | 0 | Component::CurDir => {} |
53 | 0 | Component::ParentDir => { |
54 | 0 | ret.pop(); |
55 | 0 | } |
56 | 0 | Component::Normal(c) => { |
57 | 0 | ret.push(c); |
58 | 0 | } |
59 | | } |
60 | | } |
61 | | |
62 | 0 | ret |
63 | 0 | } |
64 | | |
65 | 0 | fn normalize_relative(&self) -> PathBuf { |
66 | 0 | let mut normalized = PathBuf::new(); |
67 | 0 | for comp in self.components() { |
68 | 0 | match comp { |
69 | | Component::ParentDir => { |
70 | 0 | if !normalized.pop() { |
71 | 0 | normalized.push(Component::ParentDir); |
72 | 0 | } |
73 | | } |
74 | 0 | Component::CurDir => {} |
75 | 0 | comp => normalized.push(comp), |
76 | | } |
77 | | } |
78 | 0 | normalized |
79 | 0 | } |
80 | | |
81 | | // https://github.com/parcel-bundler/parcel/blob/e0b99c2a42e9109a9ecbd6f537844a1b33e7faf5/packages/utils/node-resolver-rs/src/path.rs#L37 |
82 | 0 | fn normalize_with<B: AsRef<Self>>(&self, subpath: B) -> PathBuf { |
83 | 0 | let subpath = subpath.as_ref(); |
84 | | |
85 | 0 | let mut components = subpath.components(); |
86 | | |
87 | 0 | let Some(head) = components.next() else { return subpath.to_path_buf() }; |
88 | | |
89 | 0 | if matches!(head, Component::Prefix(..) | Component::RootDir) { |
90 | 0 | return subpath.to_path_buf(); |
91 | 0 | } |
92 | | |
93 | 0 | let mut ret = self.to_path_buf(); |
94 | 0 | for component in std::iter::once(head).chain(components) { |
95 | 0 | match component { |
96 | 0 | Component::CurDir => {} |
97 | 0 | Component::ParentDir => { |
98 | 0 | ret.pop(); |
99 | 0 | } |
100 | 0 | Component::Normal(c) => { |
101 | 0 | ret.push(c); |
102 | 0 | } |
103 | | Component::Prefix(..) | Component::RootDir => { |
104 | 0 | unreachable!("Path {:?} Subpath {:?}", self, subpath) |
105 | | } |
106 | | } |
107 | | } |
108 | | |
109 | 0 | ret |
110 | 0 | } Unexecuted instantiation: <std::path::Path as oxc_resolver::path::PathUtil>::normalize_with::<&std::path::Path> Unexecuted instantiation: <std::path::Path as oxc_resolver::path::PathUtil>::normalize_with::<std::path::PathBuf> Unexecuted instantiation: <std::path::Path as oxc_resolver::path::PathUtil>::normalize_with::<&std::path::PathBuf> Unexecuted instantiation: <std::path::Path as oxc_resolver::path::PathUtil>::normalize_with::<&&mut std::path::PathBuf> Unexecuted instantiation: <std::path::Path as oxc_resolver::path::PathUtil>::normalize_with::<&str> |
111 | | |
112 | 0 | fn is_invalid_exports_target(&self) -> bool { |
113 | 0 | self.components().enumerate().any(|(index, c)| match c { |
114 | 0 | Component::ParentDir => true, |
115 | 0 | Component::CurDir => index > 0, |
116 | 0 | Component::Normal(c) => c.eq_ignore_ascii_case("node_modules"), |
117 | 0 | _ => false, |
118 | 0 | }) |
119 | 0 | } |
120 | | } |
121 | | |
122 | | // https://github.com/webpack/enhanced-resolve/blob/main/test/path.test.js |
123 | | #[test] |
124 | | fn is_invalid_exports_target() { |
125 | | let test_cases = [ |
126 | | "../a.js", |
127 | | "../", |
128 | | "./a/b/../../../c.js", |
129 | | "./a/b/../../../", |
130 | | "./../../c.js", |
131 | | "./../../", |
132 | | "./a/../b/../../c.js", |
133 | | "./a/../b/../../", |
134 | | "./././../", |
135 | | ]; |
136 | | |
137 | | for case in test_cases { |
138 | | assert!(Path::new(case).is_invalid_exports_target(), "{case}"); |
139 | | } |
140 | | |
141 | | assert!(!Path::new("C:").is_invalid_exports_target()); |
142 | | assert!(!Path::new("/").is_invalid_exports_target()); |
143 | | } |
144 | | |
145 | | #[test] |
146 | | fn normalize() { |
147 | | assert_eq!(Path::new("/foo/.././foo/").normalize(), Path::new("/foo")); |
148 | | assert_eq!(Path::new("C://").normalize(), Path::new("C://")); |
149 | | assert_eq!(Path::new("C:").normalize(), Path::new("C:")); |
150 | | assert_eq!(Path::new(r"\\server\share").normalize(), Path::new(r"\\server\share")); |
151 | | } |
152 | | |
153 | | #[test] |
154 | | fn normalize_relative() { |
155 | | assert_eq!(Path::new("foo/../../foo/").normalize_relative(), Path::new("../foo")); |
156 | | assert_eq!(Path::new("foo/.././foo/").normalize_relative(), Path::new("foo")); |
157 | | assert_eq!(Path::new("foo../../..").normalize_relative(), Path::new("..")); |
158 | | assert_eq!(Path::new("jest-runner-../../").normalize_relative(), Path::new("")); |
159 | | } |