@guanghechen/path 1.0.0-alpha.4 → 1.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/lib/cjs/index.cjs +149 -64
- package/lib/esm/index.mjs +145 -62
- package/lib/types/index.d.ts +50 -21
- package/package.json +6 -11
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.0.0-alpha.6](https://github.com/guanghechen/sora/compare/@guanghechen/path@1.0.0-alpha.5...@guanghechen/path@1.0.0-alpha.6) (2023-10-16)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Performance Improvements
|
|
10
|
+
|
|
11
|
+
* :art: refactor @guanghechen/path ([8952da3](https://github.com/guanghechen/sora/commit/8952da3154a042a10e76b356da7461a1f8ad8582))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [1.0.0-alpha.5](https://github.com/guanghechen/sora/compare/@guanghechen/path@1.0.0-alpha.4...@guanghechen/path@1.0.0-alpha.5) (2023-10-16)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Performance Improvements
|
|
21
|
+
|
|
22
|
+
* :art: refactor @guanghechen/path and @guanghechen/filepath ([5952ac3](https://github.com/guanghechen/sora/commit/5952ac39fee92e807e3cccc8e4b4dfa1aba1fa34))
|
|
23
|
+
* 🔧 remove unnecessary devDependencies ([944f3ae](https://github.com/guanghechen/sora/commit/944f3aee64e68ce52ca30237c7d0240a82c9c58f))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
# [1.0.0-alpha.4](https://github.com/guanghechen/sora/compare/@guanghechen/path@1.0.0-alpha.3...@guanghechen/path@1.0.0-alpha.4) (2023-10-10)
|
|
7
30
|
|
|
8
31
|
**Note:** Version bump only for package @guanghechen/path
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var path = require('node:path');
|
|
4
|
+
var node_fs = require('node:fs');
|
|
5
|
+
var node_url = require('node:url');
|
|
4
6
|
var path_types = require('@guanghechen/path.types');
|
|
5
7
|
|
|
6
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
9
|
|
|
8
10
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
9
11
|
|
|
10
|
-
const clazz$
|
|
11
|
-
class
|
|
12
|
+
const clazz$1 = 'PathResolver';
|
|
13
|
+
class PathResolver {
|
|
12
14
|
basename(filepath) {
|
|
13
15
|
this.ensureAbsolute(filepath);
|
|
14
16
|
const p = this.normalize(filepath);
|
|
@@ -17,7 +19,12 @@ class PhysicalPathResolver {
|
|
|
17
19
|
ensureAbsolute(filepath, message) {
|
|
18
20
|
if (this.isAbsolute(filepath))
|
|
19
21
|
return;
|
|
20
|
-
throw new Error(message ?? `[${clazz$
|
|
22
|
+
throw new Error(message ?? `[${clazz$1}] not an absolute path: ${filepath}.`);
|
|
23
|
+
}
|
|
24
|
+
ensureSafeRelative(root, filepath, message) {
|
|
25
|
+
if (this.isSafeRelative(root, filepath))
|
|
26
|
+
return;
|
|
27
|
+
throw new Error(message ?? `[${clazz$1}] not under the root path: filepath ${filepath}, root: ${root}.`);
|
|
21
28
|
}
|
|
22
29
|
dirname(filepath) {
|
|
23
30
|
this.ensureAbsolute(filepath);
|
|
@@ -27,11 +34,17 @@ class PhysicalPathResolver {
|
|
|
27
34
|
isAbsolute(filepath) {
|
|
28
35
|
return path__default.default.isAbsolute(filepath);
|
|
29
36
|
}
|
|
37
|
+
isSafeRelative(root, filepath) {
|
|
38
|
+
if (!this.isAbsolute(root))
|
|
39
|
+
return false;
|
|
40
|
+
const relativePath = this._internalSafeRelative(root, filepath);
|
|
41
|
+
return !relativePath.startsWith('..');
|
|
42
|
+
}
|
|
30
43
|
join(filepath, ...pathPieces) {
|
|
31
|
-
this.ensureAbsolute(filepath, `[${clazz$
|
|
44
|
+
this.ensureAbsolute(filepath, `[${clazz$1}.join] not an absolute path: ${filepath}.`);
|
|
32
45
|
for (const pathPiece of pathPieces) {
|
|
33
46
|
if (this.isAbsolute(pathPiece)) {
|
|
34
|
-
throw new Error(`[${clazz$
|
|
47
|
+
throw new Error(`[${clazz$1}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
|
|
35
48
|
}
|
|
36
49
|
}
|
|
37
50
|
const p = path__default.default.join(filepath, ...pathPieces);
|
|
@@ -39,67 +52,48 @@ class PhysicalPathResolver {
|
|
|
39
52
|
}
|
|
40
53
|
normalize(filepath) {
|
|
41
54
|
this.ensureAbsolute(filepath);
|
|
55
|
+
return this._internalNormalize(filepath);
|
|
56
|
+
}
|
|
57
|
+
relative(from, to) {
|
|
58
|
+
this.ensureAbsolute(from, `[${clazz$1}.relative] from is not an absolute path: ${from}`);
|
|
59
|
+
this.ensureAbsolute(to, `[${clazz$1}.relative] to is not an absolute path: ${to}`);
|
|
60
|
+
return this._internalRelative(from, to);
|
|
61
|
+
}
|
|
62
|
+
safeRelative(root, filepath) {
|
|
63
|
+
this.ensureSafeRelative(root, filepath);
|
|
64
|
+
return this._internalSafeRelative(root, filepath);
|
|
65
|
+
}
|
|
66
|
+
safeResolve(root, filepath) {
|
|
67
|
+
this.ensureSafeRelative(root, filepath);
|
|
68
|
+
return this._internalSafeResolve(root, filepath);
|
|
69
|
+
}
|
|
70
|
+
_internalNormalize(filepath) {
|
|
42
71
|
const p = path__default.default
|
|
43
72
|
.normalize(filepath)
|
|
44
73
|
.replace(/[/\\]+/g, path__default.default.sep)
|
|
45
74
|
.replace(/[/\\]+$/, '');
|
|
46
75
|
return p.length <= 0 ? '/' : p;
|
|
47
76
|
}
|
|
48
|
-
|
|
49
|
-
this.ensureAbsolute(from_, `[${clazz$2}.relative] from is not an absolute path: ${from_}`);
|
|
50
|
-
this.ensureAbsolute(to_, `[${clazz$2}.relative] to is not an absolute path: ${to_}`);
|
|
77
|
+
_internalRelative(from_, to_) {
|
|
51
78
|
const from = this.normalize(from_);
|
|
52
79
|
const to = this.normalize(to_);
|
|
53
80
|
const relativePath = path__default.default.relative(from, to);
|
|
54
81
|
return relativePath;
|
|
55
82
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
pathResolver;
|
|
62
|
-
constructor(root, pathResolver) {
|
|
63
|
-
this.root = root;
|
|
64
|
-
this.pathResolver = pathResolver;
|
|
65
|
-
}
|
|
66
|
-
ensureSafePath(filepath, message) {
|
|
67
|
-
if (this.isSafePath(filepath))
|
|
68
|
-
return;
|
|
69
|
-
throw new Error(message ?? `[${clazz$1}] not an absolute path: ${filepath}.`);
|
|
70
|
-
}
|
|
71
|
-
isSafePath(filepath) {
|
|
72
|
-
const { root, pathResolver } = this;
|
|
73
|
-
if (!pathResolver.isAbsolute(filepath))
|
|
74
|
-
return true;
|
|
75
|
-
if (filepath === root)
|
|
76
|
-
return true;
|
|
77
|
-
const relativePath = pathResolver.relative(root, filepath);
|
|
78
|
-
return !relativePath.startsWith('..');
|
|
79
|
-
}
|
|
80
|
-
resolve(filepath) {
|
|
81
|
-
this.ensureSafePath(filepath);
|
|
82
|
-
const { root, pathResolver } = this;
|
|
83
|
-
if (pathResolver.isAbsolute(filepath))
|
|
84
|
-
return pathResolver.normalize(filepath);
|
|
85
|
-
return pathResolver.join(root, filepath);
|
|
86
|
-
}
|
|
87
|
-
relative(filepath_) {
|
|
88
|
-
this.ensureSafePath(filepath_);
|
|
89
|
-
const filepath = this.resolve(filepath_);
|
|
90
|
-
return this.pathResolver.relative(this.root, filepath);
|
|
83
|
+
_internalSafeRelative(root_, filepath_) {
|
|
84
|
+
const root = this._internalNormalize(root_);
|
|
85
|
+
const filepath = this._internalSafeResolve(root, filepath_);
|
|
86
|
+
const relativePath = this._internalRelative(root, filepath);
|
|
87
|
+
return relativePath;
|
|
91
88
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
constructor(root) {
|
|
96
|
-
const pathResolver = new PhysicalPathResolver();
|
|
97
|
-
super(root, pathResolver);
|
|
89
|
+
_internalSafeResolve(root, filepath_) {
|
|
90
|
+
const filepath = this.isAbsolute(filepath_) ? filepath_ : path__default.default.join(root, filepath_);
|
|
91
|
+
return this._internalNormalize(filepath);
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
94
|
|
|
101
|
-
const clazz = '
|
|
102
|
-
class
|
|
95
|
+
const clazz = 'UrlPathResolver';
|
|
96
|
+
class UrlPathResolver {
|
|
103
97
|
basename(filepath) {
|
|
104
98
|
this.ensureAbsolute(filepath);
|
|
105
99
|
const p = this.normalize(filepath);
|
|
@@ -111,6 +105,11 @@ class VirtualPathResolver {
|
|
|
111
105
|
return;
|
|
112
106
|
throw new Error(message ?? `[${clazz}] not an absolute path: ${filepath}.`);
|
|
113
107
|
}
|
|
108
|
+
ensureSafeRelative(root, filepath, message) {
|
|
109
|
+
if (this.isSafeRelative(root, filepath))
|
|
110
|
+
return;
|
|
111
|
+
throw new Error(message ?? `[${clazz}] not under the root path: filepath ${filepath}, root: ${root}.`);
|
|
112
|
+
}
|
|
114
113
|
dirname(filepath) {
|
|
115
114
|
this.ensureAbsolute(filepath);
|
|
116
115
|
const p = this.normalize(filepath);
|
|
@@ -120,6 +119,12 @@ class VirtualPathResolver {
|
|
|
120
119
|
isAbsolute(filepath) {
|
|
121
120
|
return filepath.startsWith('/') || filepath.startsWith('\\');
|
|
122
121
|
}
|
|
122
|
+
isSafeRelative(root, filepath) {
|
|
123
|
+
if (!this.isAbsolute(root))
|
|
124
|
+
return false;
|
|
125
|
+
const relativePath = this._internalSafeRelative(root, filepath);
|
|
126
|
+
return !relativePath.startsWith('..');
|
|
127
|
+
}
|
|
123
128
|
join(filepath, ...pathPieces) {
|
|
124
129
|
this.ensureAbsolute(filepath, `[${clazz}.join] not an absolute path: ${filepath}.`);
|
|
125
130
|
for (const pathPiece of pathPieces) {
|
|
@@ -127,11 +132,30 @@ class VirtualPathResolver {
|
|
|
127
132
|
throw new Error(`[${clazz}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
|
|
131
|
-
return this.normalize(p);
|
|
135
|
+
return this._internalJoin(filepath, pathPieces.join('/'));
|
|
132
136
|
}
|
|
133
137
|
normalize(filepath) {
|
|
134
138
|
this.ensureAbsolute(filepath);
|
|
139
|
+
return this._internalNormalize(filepath);
|
|
140
|
+
}
|
|
141
|
+
relative(from, to) {
|
|
142
|
+
this.ensureAbsolute(from, `[${clazz}.relative] from is not an absolute path: ${from}`);
|
|
143
|
+
this.ensureAbsolute(to, `[${clazz}.relative] to is not an absolute path: ${to}`);
|
|
144
|
+
return this._internalRelative(from, to);
|
|
145
|
+
}
|
|
146
|
+
safeRelative(root, filepath) {
|
|
147
|
+
this.ensureSafeRelative(root, filepath);
|
|
148
|
+
return this._internalSafeRelative(root, filepath);
|
|
149
|
+
}
|
|
150
|
+
safeResolve(root, filepath) {
|
|
151
|
+
this.ensureSafeRelative(root, filepath);
|
|
152
|
+
return this._internalSafeResolve(root, filepath);
|
|
153
|
+
}
|
|
154
|
+
_internalJoin(root, relativePath) {
|
|
155
|
+
const filepath = root + '/' + relativePath;
|
|
156
|
+
return this._internalNormalize(filepath);
|
|
157
|
+
}
|
|
158
|
+
_internalNormalize(filepath) {
|
|
135
159
|
const pieces = [];
|
|
136
160
|
for (const piece of filepath.split(/[/\\]+/g)) {
|
|
137
161
|
if (!piece)
|
|
@@ -146,9 +170,7 @@ class VirtualPathResolver {
|
|
|
146
170
|
}
|
|
147
171
|
return '/' + pieces.join('/');
|
|
148
172
|
}
|
|
149
|
-
|
|
150
|
-
this.ensureAbsolute(from_, `[${clazz}.relative] from is not an absolute path: ${from_}`);
|
|
151
|
-
this.ensureAbsolute(to_, `[${clazz}.relative] to is not an absolute path: ${to_}`);
|
|
173
|
+
_internalRelative(from_, to_) {
|
|
152
174
|
const from = this.normalize(from_);
|
|
153
175
|
const to = this.normalize(to_);
|
|
154
176
|
const fromPieces = from.split('/');
|
|
@@ -161,20 +183,83 @@ class VirtualPathResolver {
|
|
|
161
183
|
}
|
|
162
184
|
return '../'.repeat(fromPieces.length - ci) + toPieces.slice(ci).join('/');
|
|
163
185
|
}
|
|
186
|
+
_internalSafeRelative(root_, filepath_) {
|
|
187
|
+
const root = this._internalNormalize(root_);
|
|
188
|
+
const filepath = this._internalSafeResolve(root, filepath_);
|
|
189
|
+
const relativePath = this._internalRelative(root, filepath);
|
|
190
|
+
return relativePath;
|
|
191
|
+
}
|
|
192
|
+
_internalSafeResolve(root, filepath_) {
|
|
193
|
+
const filepath = this.isAbsolute(filepath_) ? filepath_ : this.join(root, filepath_);
|
|
194
|
+
return filepath;
|
|
195
|
+
}
|
|
164
196
|
}
|
|
165
197
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
198
|
+
function locateNearestFilepath(currentDir, filenames) {
|
|
199
|
+
return internalRecursiveLocate(currentDir.startsWith('file://') ? node_url.fileURLToPath(currentDir) : currentDir, [filenames].flat());
|
|
200
|
+
}
|
|
201
|
+
function findNearestFilepath(currentDir, predicate) {
|
|
202
|
+
return internalFindNearestFilepath(currentDir.startsWith('file://') ? node_url.fileURLToPath(currentDir) : currentDir, predicate);
|
|
203
|
+
}
|
|
204
|
+
function internalRecursiveLocate(currentDir, filenames) {
|
|
205
|
+
for (const filename of filenames) {
|
|
206
|
+
const filepath = path.join(currentDir, filename);
|
|
207
|
+
if (node_fs.existsSync(filepath))
|
|
208
|
+
return filepath;
|
|
209
|
+
}
|
|
210
|
+
const parentDir = path.dirname(currentDir);
|
|
211
|
+
if (parentDir === currentDir || !path.isAbsolute(parentDir))
|
|
212
|
+
return null;
|
|
213
|
+
return locateNearestFilepath(parentDir, filenames);
|
|
214
|
+
}
|
|
215
|
+
function internalFindNearestFilepath(currentDir, predicate) {
|
|
216
|
+
const filenames = node_fs.readdirSync(currentDir);
|
|
217
|
+
for (const filename of filenames) {
|
|
218
|
+
const filepath = path.join(currentDir, filename);
|
|
219
|
+
if (predicate(filepath))
|
|
220
|
+
return filepath;
|
|
170
221
|
}
|
|
222
|
+
const parentDir = path.dirname(currentDir);
|
|
223
|
+
if (parentDir === currentDir)
|
|
224
|
+
return null;
|
|
225
|
+
return internalFindNearestFilepath(parentDir, predicate);
|
|
171
226
|
}
|
|
172
227
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
228
|
+
class WorkspacePathResolver {
|
|
229
|
+
root;
|
|
230
|
+
pathResolver;
|
|
231
|
+
constructor(root, pathResolver) {
|
|
232
|
+
this.root = root;
|
|
233
|
+
this.pathResolver = pathResolver;
|
|
234
|
+
}
|
|
235
|
+
ensureSafePath(filepath, message) {
|
|
236
|
+
const { root, pathResolver } = this;
|
|
237
|
+
pathResolver.ensureSafeRelative(root, filepath, message);
|
|
238
|
+
}
|
|
239
|
+
isSafePath(filepath) {
|
|
240
|
+
const { root, pathResolver } = this;
|
|
241
|
+
return pathResolver.isSafeRelative(root, filepath);
|
|
242
|
+
}
|
|
243
|
+
relative(filepath) {
|
|
244
|
+
const { root, pathResolver } = this;
|
|
245
|
+
return pathResolver.safeRelative(root, filepath);
|
|
246
|
+
}
|
|
247
|
+
resolve(filepath) {
|
|
248
|
+
const { root, pathResolver } = this;
|
|
249
|
+
return pathResolver.safeResolve(root, filepath);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const pathResolver = new PathResolver();
|
|
254
|
+
const urlPathResolver = new UrlPathResolver();
|
|
255
|
+
|
|
256
|
+
exports.PathResolver = PathResolver;
|
|
257
|
+
exports.UrlPathResolver = UrlPathResolver;
|
|
177
258
|
exports.WorkspacePathResolver = WorkspacePathResolver;
|
|
259
|
+
exports.findNearestFilepath = findNearestFilepath;
|
|
260
|
+
exports.locateNearestFilepath = locateNearestFilepath;
|
|
261
|
+
exports.pathResolver = pathResolver;
|
|
262
|
+
exports.urlPathResolver = urlPathResolver;
|
|
178
263
|
Object.keys(path_types).forEach(function (k) {
|
|
179
264
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
180
265
|
enumerable: true,
|
package/lib/esm/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
1
|
+
import path, { join, dirname, isAbsolute } from 'node:path';
|
|
2
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
export * from '@guanghechen/path.types';
|
|
3
5
|
|
|
4
|
-
const clazz$
|
|
5
|
-
class
|
|
6
|
+
const clazz$1 = 'PathResolver';
|
|
7
|
+
class PathResolver {
|
|
6
8
|
basename(filepath) {
|
|
7
9
|
this.ensureAbsolute(filepath);
|
|
8
10
|
const p = this.normalize(filepath);
|
|
@@ -11,7 +13,12 @@ class PhysicalPathResolver {
|
|
|
11
13
|
ensureAbsolute(filepath, message) {
|
|
12
14
|
if (this.isAbsolute(filepath))
|
|
13
15
|
return;
|
|
14
|
-
throw new Error(message ?? `[${clazz$
|
|
16
|
+
throw new Error(message ?? `[${clazz$1}] not an absolute path: ${filepath}.`);
|
|
17
|
+
}
|
|
18
|
+
ensureSafeRelative(root, filepath, message) {
|
|
19
|
+
if (this.isSafeRelative(root, filepath))
|
|
20
|
+
return;
|
|
21
|
+
throw new Error(message ?? `[${clazz$1}] not under the root path: filepath ${filepath}, root: ${root}.`);
|
|
15
22
|
}
|
|
16
23
|
dirname(filepath) {
|
|
17
24
|
this.ensureAbsolute(filepath);
|
|
@@ -21,11 +28,17 @@ class PhysicalPathResolver {
|
|
|
21
28
|
isAbsolute(filepath) {
|
|
22
29
|
return path.isAbsolute(filepath);
|
|
23
30
|
}
|
|
31
|
+
isSafeRelative(root, filepath) {
|
|
32
|
+
if (!this.isAbsolute(root))
|
|
33
|
+
return false;
|
|
34
|
+
const relativePath = this._internalSafeRelative(root, filepath);
|
|
35
|
+
return !relativePath.startsWith('..');
|
|
36
|
+
}
|
|
24
37
|
join(filepath, ...pathPieces) {
|
|
25
|
-
this.ensureAbsolute(filepath, `[${clazz$
|
|
38
|
+
this.ensureAbsolute(filepath, `[${clazz$1}.join] not an absolute path: ${filepath}.`);
|
|
26
39
|
for (const pathPiece of pathPieces) {
|
|
27
40
|
if (this.isAbsolute(pathPiece)) {
|
|
28
|
-
throw new Error(`[${clazz$
|
|
41
|
+
throw new Error(`[${clazz$1}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
|
|
29
42
|
}
|
|
30
43
|
}
|
|
31
44
|
const p = path.join(filepath, ...pathPieces);
|
|
@@ -33,67 +46,48 @@ class PhysicalPathResolver {
|
|
|
33
46
|
}
|
|
34
47
|
normalize(filepath) {
|
|
35
48
|
this.ensureAbsolute(filepath);
|
|
49
|
+
return this._internalNormalize(filepath);
|
|
50
|
+
}
|
|
51
|
+
relative(from, to) {
|
|
52
|
+
this.ensureAbsolute(from, `[${clazz$1}.relative] from is not an absolute path: ${from}`);
|
|
53
|
+
this.ensureAbsolute(to, `[${clazz$1}.relative] to is not an absolute path: ${to}`);
|
|
54
|
+
return this._internalRelative(from, to);
|
|
55
|
+
}
|
|
56
|
+
safeRelative(root, filepath) {
|
|
57
|
+
this.ensureSafeRelative(root, filepath);
|
|
58
|
+
return this._internalSafeRelative(root, filepath);
|
|
59
|
+
}
|
|
60
|
+
safeResolve(root, filepath) {
|
|
61
|
+
this.ensureSafeRelative(root, filepath);
|
|
62
|
+
return this._internalSafeResolve(root, filepath);
|
|
63
|
+
}
|
|
64
|
+
_internalNormalize(filepath) {
|
|
36
65
|
const p = path
|
|
37
66
|
.normalize(filepath)
|
|
38
67
|
.replace(/[/\\]+/g, path.sep)
|
|
39
68
|
.replace(/[/\\]+$/, '');
|
|
40
69
|
return p.length <= 0 ? '/' : p;
|
|
41
70
|
}
|
|
42
|
-
|
|
43
|
-
this.ensureAbsolute(from_, `[${clazz$2}.relative] from is not an absolute path: ${from_}`);
|
|
44
|
-
this.ensureAbsolute(to_, `[${clazz$2}.relative] to is not an absolute path: ${to_}`);
|
|
71
|
+
_internalRelative(from_, to_) {
|
|
45
72
|
const from = this.normalize(from_);
|
|
46
73
|
const to = this.normalize(to_);
|
|
47
74
|
const relativePath = path.relative(from, to);
|
|
48
75
|
return relativePath;
|
|
49
76
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
pathResolver;
|
|
56
|
-
constructor(root, pathResolver) {
|
|
57
|
-
this.root = root;
|
|
58
|
-
this.pathResolver = pathResolver;
|
|
59
|
-
}
|
|
60
|
-
ensureSafePath(filepath, message) {
|
|
61
|
-
if (this.isSafePath(filepath))
|
|
62
|
-
return;
|
|
63
|
-
throw new Error(message ?? `[${clazz$1}] not an absolute path: ${filepath}.`);
|
|
64
|
-
}
|
|
65
|
-
isSafePath(filepath) {
|
|
66
|
-
const { root, pathResolver } = this;
|
|
67
|
-
if (!pathResolver.isAbsolute(filepath))
|
|
68
|
-
return true;
|
|
69
|
-
if (filepath === root)
|
|
70
|
-
return true;
|
|
71
|
-
const relativePath = pathResolver.relative(root, filepath);
|
|
72
|
-
return !relativePath.startsWith('..');
|
|
73
|
-
}
|
|
74
|
-
resolve(filepath) {
|
|
75
|
-
this.ensureSafePath(filepath);
|
|
76
|
-
const { root, pathResolver } = this;
|
|
77
|
-
if (pathResolver.isAbsolute(filepath))
|
|
78
|
-
return pathResolver.normalize(filepath);
|
|
79
|
-
return pathResolver.join(root, filepath);
|
|
80
|
-
}
|
|
81
|
-
relative(filepath_) {
|
|
82
|
-
this.ensureSafePath(filepath_);
|
|
83
|
-
const filepath = this.resolve(filepath_);
|
|
84
|
-
return this.pathResolver.relative(this.root, filepath);
|
|
77
|
+
_internalSafeRelative(root_, filepath_) {
|
|
78
|
+
const root = this._internalNormalize(root_);
|
|
79
|
+
const filepath = this._internalSafeResolve(root, filepath_);
|
|
80
|
+
const relativePath = this._internalRelative(root, filepath);
|
|
81
|
+
return relativePath;
|
|
85
82
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
constructor(root) {
|
|
90
|
-
const pathResolver = new PhysicalPathResolver();
|
|
91
|
-
super(root, pathResolver);
|
|
83
|
+
_internalSafeResolve(root, filepath_) {
|
|
84
|
+
const filepath = this.isAbsolute(filepath_) ? filepath_ : path.join(root, filepath_);
|
|
85
|
+
return this._internalNormalize(filepath);
|
|
92
86
|
}
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
const clazz = '
|
|
96
|
-
class
|
|
89
|
+
const clazz = 'UrlPathResolver';
|
|
90
|
+
class UrlPathResolver {
|
|
97
91
|
basename(filepath) {
|
|
98
92
|
this.ensureAbsolute(filepath);
|
|
99
93
|
const p = this.normalize(filepath);
|
|
@@ -105,6 +99,11 @@ class VirtualPathResolver {
|
|
|
105
99
|
return;
|
|
106
100
|
throw new Error(message ?? `[${clazz}] not an absolute path: ${filepath}.`);
|
|
107
101
|
}
|
|
102
|
+
ensureSafeRelative(root, filepath, message) {
|
|
103
|
+
if (this.isSafeRelative(root, filepath))
|
|
104
|
+
return;
|
|
105
|
+
throw new Error(message ?? `[${clazz}] not under the root path: filepath ${filepath}, root: ${root}.`);
|
|
106
|
+
}
|
|
108
107
|
dirname(filepath) {
|
|
109
108
|
this.ensureAbsolute(filepath);
|
|
110
109
|
const p = this.normalize(filepath);
|
|
@@ -114,6 +113,12 @@ class VirtualPathResolver {
|
|
|
114
113
|
isAbsolute(filepath) {
|
|
115
114
|
return filepath.startsWith('/') || filepath.startsWith('\\');
|
|
116
115
|
}
|
|
116
|
+
isSafeRelative(root, filepath) {
|
|
117
|
+
if (!this.isAbsolute(root))
|
|
118
|
+
return false;
|
|
119
|
+
const relativePath = this._internalSafeRelative(root, filepath);
|
|
120
|
+
return !relativePath.startsWith('..');
|
|
121
|
+
}
|
|
117
122
|
join(filepath, ...pathPieces) {
|
|
118
123
|
this.ensureAbsolute(filepath, `[${clazz}.join] not an absolute path: ${filepath}.`);
|
|
119
124
|
for (const pathPiece of pathPieces) {
|
|
@@ -121,11 +126,30 @@ class VirtualPathResolver {
|
|
|
121
126
|
throw new Error(`[${clazz}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
|
|
122
127
|
}
|
|
123
128
|
}
|
|
124
|
-
|
|
125
|
-
return this.normalize(p);
|
|
129
|
+
return this._internalJoin(filepath, pathPieces.join('/'));
|
|
126
130
|
}
|
|
127
131
|
normalize(filepath) {
|
|
128
132
|
this.ensureAbsolute(filepath);
|
|
133
|
+
return this._internalNormalize(filepath);
|
|
134
|
+
}
|
|
135
|
+
relative(from, to) {
|
|
136
|
+
this.ensureAbsolute(from, `[${clazz}.relative] from is not an absolute path: ${from}`);
|
|
137
|
+
this.ensureAbsolute(to, `[${clazz}.relative] to is not an absolute path: ${to}`);
|
|
138
|
+
return this._internalRelative(from, to);
|
|
139
|
+
}
|
|
140
|
+
safeRelative(root, filepath) {
|
|
141
|
+
this.ensureSafeRelative(root, filepath);
|
|
142
|
+
return this._internalSafeRelative(root, filepath);
|
|
143
|
+
}
|
|
144
|
+
safeResolve(root, filepath) {
|
|
145
|
+
this.ensureSafeRelative(root, filepath);
|
|
146
|
+
return this._internalSafeResolve(root, filepath);
|
|
147
|
+
}
|
|
148
|
+
_internalJoin(root, relativePath) {
|
|
149
|
+
const filepath = root + '/' + relativePath;
|
|
150
|
+
return this._internalNormalize(filepath);
|
|
151
|
+
}
|
|
152
|
+
_internalNormalize(filepath) {
|
|
129
153
|
const pieces = [];
|
|
130
154
|
for (const piece of filepath.split(/[/\\]+/g)) {
|
|
131
155
|
if (!piece)
|
|
@@ -140,9 +164,7 @@ class VirtualPathResolver {
|
|
|
140
164
|
}
|
|
141
165
|
return '/' + pieces.join('/');
|
|
142
166
|
}
|
|
143
|
-
|
|
144
|
-
this.ensureAbsolute(from_, `[${clazz}.relative] from is not an absolute path: ${from_}`);
|
|
145
|
-
this.ensureAbsolute(to_, `[${clazz}.relative] to is not an absolute path: ${to_}`);
|
|
167
|
+
_internalRelative(from_, to_) {
|
|
146
168
|
const from = this.normalize(from_);
|
|
147
169
|
const to = this.normalize(to_);
|
|
148
170
|
const fromPieces = from.split('/');
|
|
@@ -155,13 +177,74 @@ class VirtualPathResolver {
|
|
|
155
177
|
}
|
|
156
178
|
return '../'.repeat(fromPieces.length - ci) + toPieces.slice(ci).join('/');
|
|
157
179
|
}
|
|
180
|
+
_internalSafeRelative(root_, filepath_) {
|
|
181
|
+
const root = this._internalNormalize(root_);
|
|
182
|
+
const filepath = this._internalSafeResolve(root, filepath_);
|
|
183
|
+
const relativePath = this._internalRelative(root, filepath);
|
|
184
|
+
return relativePath;
|
|
185
|
+
}
|
|
186
|
+
_internalSafeResolve(root, filepath_) {
|
|
187
|
+
const filepath = this.isAbsolute(filepath_) ? filepath_ : this.join(root, filepath_);
|
|
188
|
+
return filepath;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function locateNearestFilepath(currentDir, filenames) {
|
|
193
|
+
return internalRecursiveLocate(currentDir.startsWith('file://') ? fileURLToPath(currentDir) : currentDir, [filenames].flat());
|
|
194
|
+
}
|
|
195
|
+
function findNearestFilepath(currentDir, predicate) {
|
|
196
|
+
return internalFindNearestFilepath(currentDir.startsWith('file://') ? fileURLToPath(currentDir) : currentDir, predicate);
|
|
197
|
+
}
|
|
198
|
+
function internalRecursiveLocate(currentDir, filenames) {
|
|
199
|
+
for (const filename of filenames) {
|
|
200
|
+
const filepath = join(currentDir, filename);
|
|
201
|
+
if (existsSync(filepath))
|
|
202
|
+
return filepath;
|
|
203
|
+
}
|
|
204
|
+
const parentDir = dirname(currentDir);
|
|
205
|
+
if (parentDir === currentDir || !isAbsolute(parentDir))
|
|
206
|
+
return null;
|
|
207
|
+
return locateNearestFilepath(parentDir, filenames);
|
|
208
|
+
}
|
|
209
|
+
function internalFindNearestFilepath(currentDir, predicate) {
|
|
210
|
+
const filenames = readdirSync(currentDir);
|
|
211
|
+
for (const filename of filenames) {
|
|
212
|
+
const filepath = join(currentDir, filename);
|
|
213
|
+
if (predicate(filepath))
|
|
214
|
+
return filepath;
|
|
215
|
+
}
|
|
216
|
+
const parentDir = dirname(currentDir);
|
|
217
|
+
if (parentDir === currentDir)
|
|
218
|
+
return null;
|
|
219
|
+
return internalFindNearestFilepath(parentDir, predicate);
|
|
158
220
|
}
|
|
159
221
|
|
|
160
|
-
class
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
222
|
+
class WorkspacePathResolver {
|
|
223
|
+
root;
|
|
224
|
+
pathResolver;
|
|
225
|
+
constructor(root, pathResolver) {
|
|
226
|
+
this.root = root;
|
|
227
|
+
this.pathResolver = pathResolver;
|
|
228
|
+
}
|
|
229
|
+
ensureSafePath(filepath, message) {
|
|
230
|
+
const { root, pathResolver } = this;
|
|
231
|
+
pathResolver.ensureSafeRelative(root, filepath, message);
|
|
232
|
+
}
|
|
233
|
+
isSafePath(filepath) {
|
|
234
|
+
const { root, pathResolver } = this;
|
|
235
|
+
return pathResolver.isSafeRelative(root, filepath);
|
|
236
|
+
}
|
|
237
|
+
relative(filepath) {
|
|
238
|
+
const { root, pathResolver } = this;
|
|
239
|
+
return pathResolver.safeRelative(root, filepath);
|
|
240
|
+
}
|
|
241
|
+
resolve(filepath) {
|
|
242
|
+
const { root, pathResolver } = this;
|
|
243
|
+
return pathResolver.safeResolve(root, filepath);
|
|
164
244
|
}
|
|
165
245
|
}
|
|
166
246
|
|
|
167
|
-
|
|
247
|
+
const pathResolver = new PathResolver();
|
|
248
|
+
const urlPathResolver = new UrlPathResolver();
|
|
249
|
+
|
|
250
|
+
export { PathResolver, UrlPathResolver, WorkspacePathResolver, findNearestFilepath, locateNearestFilepath, pathResolver, urlPathResolver };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,42 +1,71 @@
|
|
|
1
1
|
import { IPathResolver, IWorkspacePathResolver } from '@guanghechen/path.types';
|
|
2
2
|
export * from '@guanghechen/path.types';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Locate a nearest filepath from the given `currentDir` which name included in the give `filenames`.
|
|
6
|
+
*
|
|
7
|
+
* @param currentDir
|
|
8
|
+
* @param filenames
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
declare function locateNearestFilepath(currentDir: string, filenames: string | ReadonlyArray<string>): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Find a nearest filepath from the give `currentDir`which matched the give tester `testFilepath`.
|
|
14
|
+
*
|
|
15
|
+
* @param currentDir
|
|
16
|
+
* @param predicate
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
declare function findNearestFilepath(currentDir: string, predicate: (filepath: string) => boolean): string | null;
|
|
20
|
+
|
|
21
|
+
declare class PathResolver implements IPathResolver {
|
|
5
22
|
basename(filepath: string): string;
|
|
6
23
|
ensureAbsolute(filepath: string, message?: string | undefined): void | never;
|
|
24
|
+
ensureSafeRelative(root: string, filepath: string, message?: string | undefined): void | never;
|
|
7
25
|
dirname(filepath: string): string | never;
|
|
8
26
|
isAbsolute(filepath: string): boolean;
|
|
27
|
+
isSafeRelative(root: string, filepath: string): boolean;
|
|
9
28
|
join(filepath: string, ...pathPieces: string[]): string | never;
|
|
10
29
|
normalize(filepath: string): string | never;
|
|
11
|
-
relative(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
ensureSafePath(filepath: string, message?: string | undefined): void | never;
|
|
19
|
-
isSafePath(filepath: string): boolean;
|
|
20
|
-
resolve(filepath: string): string | never;
|
|
21
|
-
relative(filepath_: string): string | never;
|
|
30
|
+
relative(from: string, to: string): string | never;
|
|
31
|
+
safeRelative(root: string, filepath: string): string;
|
|
32
|
+
safeResolve(root: string, filepath: string): string;
|
|
33
|
+
protected _internalNormalize(filepath: string): string;
|
|
34
|
+
protected _internalRelative(from_: string, to_: string): string;
|
|
35
|
+
protected _internalSafeRelative(root_: string, filepath_: string): string;
|
|
36
|
+
protected _internalSafeResolve(root: string, filepath_: string): string;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
|
-
declare class
|
|
25
|
-
constructor(root: string);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
declare class VirtualPathResolver implements IPathResolver {
|
|
39
|
+
declare class UrlPathResolver implements IPathResolver {
|
|
29
40
|
basename(filepath: string): string;
|
|
30
41
|
ensureAbsolute(filepath: string, message?: string | undefined): void | never;
|
|
42
|
+
ensureSafeRelative(root: string, filepath: string, message?: string | undefined): void;
|
|
31
43
|
dirname(filepath: string): string | never;
|
|
32
44
|
isAbsolute(filepath: string): boolean;
|
|
45
|
+
isSafeRelative(root: string, filepath: string): boolean;
|
|
33
46
|
join(filepath: string, ...pathPieces: string[]): string | never;
|
|
34
47
|
normalize(filepath: string): string | never;
|
|
35
|
-
relative(
|
|
48
|
+
relative(from: string, to: string): string | never;
|
|
49
|
+
safeRelative(root: string, filepath: string): string;
|
|
50
|
+
safeResolve(root: string, filepath: string): string;
|
|
51
|
+
protected _internalJoin(root: string, relativePath: string): string;
|
|
52
|
+
protected _internalNormalize(filepath: string): string;
|
|
53
|
+
protected _internalRelative(from_: string, to_: string): string;
|
|
54
|
+
protected _internalSafeRelative(root_: string, filepath_: string): string;
|
|
55
|
+
protected _internalSafeResolve(root: string, filepath_: string): string;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
|
-
declare class
|
|
39
|
-
|
|
58
|
+
declare class WorkspacePathResolver implements IWorkspacePathResolver {
|
|
59
|
+
readonly root: string;
|
|
60
|
+
readonly pathResolver: IPathResolver;
|
|
61
|
+
constructor(root: string, pathResolver: IPathResolver);
|
|
62
|
+
ensureSafePath(filepath: string, message?: string | undefined): void | never;
|
|
63
|
+
isSafePath(filepath: string): boolean;
|
|
64
|
+
relative(filepath: string): string | never;
|
|
65
|
+
resolve(filepath: string): string | never;
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
|
|
68
|
+
declare const pathResolver: IPathResolver;
|
|
69
|
+
declare const urlPathResolver: IPathResolver;
|
|
70
|
+
|
|
71
|
+
export { PathResolver, UrlPathResolver, WorkspacePathResolver, findNearestFilepath, locateNearestFilepath, pathResolver, urlPathResolver };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guanghechen/path",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.6",
|
|
4
4
|
"description": "Path utils.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "guanghechen",
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/guanghechen/sora/tree/@guanghechen/path@1.0.0-alpha.
|
|
11
|
+
"url": "https://github.com/guanghechen/sora/tree/@guanghechen/path@1.0.0-alpha.5",
|
|
12
12
|
"directory": "packages/path"
|
|
13
13
|
},
|
|
14
|
-
"homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/path@1.0.0-alpha.
|
|
14
|
+
"homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/path@1.0.0-alpha.5/packages/path#readme",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -38,17 +38,12 @@
|
|
|
38
38
|
"README.md"
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
|
-
"build": "rimraf lib/ && cross-env NODE_ENV=production rollup -c ../../rollup.config.mjs",
|
|
41
|
+
"build": "../../node_modules/.bin/rimraf lib/ && ../../node_modules/.bin/cross-env NODE_ENV=production ../../node_modules/.bin/rollup -c ../../rollup.config.mjs",
|
|
42
42
|
"prepublishOnly": "yarn build",
|
|
43
43
|
"test": "node --experimental-vm-modules ../../node_modules/.bin/jest --config ../../jest.config.mjs --rootDir ."
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@guanghechen/path.types": "^1.0.0-alpha.
|
|
46
|
+
"@guanghechen/path.types": "^1.0.0-alpha.4"
|
|
47
47
|
},
|
|
48
|
-
"
|
|
49
|
-
"cross-env": "*",
|
|
50
|
-
"rimraf": "*",
|
|
51
|
-
"rollup": "*"
|
|
52
|
-
},
|
|
53
|
-
"gitHead": "208ce260c87a55fc8b52650541486be2f94e90be"
|
|
48
|
+
"gitHead": "e42cabded60b75856018c6f216d08b3d830a29b2"
|
|
54
49
|
}
|