@guanghechen/path 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +20 -0
- package/lib/cjs/index.cjs +154 -53
- package/lib/esm/index.mjs +152 -55
- package/lib/types/index.d.ts +41 -4
- package/package.json +6 -11
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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.5](https://github.com/guanghechen/sora/compare/@guanghechen/path@1.0.0-alpha.4...@guanghechen/path@1.0.0-alpha.5) (2023-10-16)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Performance Improvements
|
|
10
|
+
|
|
11
|
+
* :art: refactor @guanghechen/path and @guanghechen/filepath ([5952ac3](https://github.com/guanghechen/sora/commit/5952ac39fee92e807e3cccc8e4b4dfa1aba1fa34))
|
|
12
|
+
* 🔧 remove unnecessary devDependencies ([944f3ae](https://github.com/guanghechen/sora/commit/944f3aee64e68ce52ca30237c7d0240a82c9c58f))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# [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)
|
|
19
|
+
|
|
20
|
+
**Note:** Version bump only for package @guanghechen/path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
# [1.0.0-alpha.3](https://github.com/guanghechen/sora/compare/@guanghechen/path@1.0.0-alpha.2...@guanghechen/path@1.0.0-alpha.3) (2023-10-09)
|
|
7
27
|
|
|
8
28
|
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
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$
|
|
12
|
+
const clazz$1 = 'PhysicalPathResolver';
|
|
11
13
|
class PhysicalPathResolver {
|
|
12
14
|
basename(filepath) {
|
|
13
15
|
this.ensureAbsolute(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,62 +52,43 @@ 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);
|
|
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;
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
return this.pathResolver.relative(this.root, filepath);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
class PhysicalWorkspacePathResolver extends WorkspacePathResolver {
|
|
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
|
|
|
@@ -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,6 +183,78 @@ 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
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
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;
|
|
221
|
+
}
|
|
222
|
+
const parentDir = path.dirname(currentDir);
|
|
223
|
+
if (parentDir === currentDir)
|
|
224
|
+
return null;
|
|
225
|
+
return internalFindNearestFilepath(parentDir, predicate);
|
|
226
|
+
}
|
|
227
|
+
|
|
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
|
+
class PhysicalWorkspacePathResolver extends WorkspacePathResolver {
|
|
254
|
+
constructor(root) {
|
|
255
|
+
const pathResolver = new PhysicalPathResolver();
|
|
256
|
+
super(root, pathResolver);
|
|
257
|
+
}
|
|
164
258
|
}
|
|
165
259
|
|
|
166
260
|
class VirtualWorkspacePathResolver extends WorkspacePathResolver {
|
|
@@ -170,11 +264,18 @@ class VirtualWorkspacePathResolver extends WorkspacePathResolver {
|
|
|
170
264
|
}
|
|
171
265
|
}
|
|
172
266
|
|
|
267
|
+
const physicalPathResolver = new PhysicalPathResolver();
|
|
268
|
+
const virtualPathResolver = new VirtualPathResolver();
|
|
269
|
+
|
|
173
270
|
exports.PhysicalPathResolver = PhysicalPathResolver;
|
|
174
271
|
exports.PhysicalWorkspacePathResolver = PhysicalWorkspacePathResolver;
|
|
175
272
|
exports.VirtualPathResolver = VirtualPathResolver;
|
|
176
273
|
exports.VirtualWorkspacePathResolver = VirtualWorkspacePathResolver;
|
|
177
274
|
exports.WorkspacePathResolver = WorkspacePathResolver;
|
|
275
|
+
exports.findNearestFilepath = findNearestFilepath;
|
|
276
|
+
exports.locateNearestFilepath = locateNearestFilepath;
|
|
277
|
+
exports.physicalPathResolver = physicalPathResolver;
|
|
278
|
+
exports.virtualPathResolver = virtualPathResolver;
|
|
178
279
|
Object.keys(path_types).forEach(function (k) {
|
|
179
280
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
180
281
|
enumerable: true,
|
package/lib/esm/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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$
|
|
6
|
+
const clazz$1 = 'PhysicalPathResolver';
|
|
5
7
|
class PhysicalPathResolver {
|
|
6
8
|
basename(filepath) {
|
|
7
9
|
this.ensureAbsolute(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,62 +46,43 @@ 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);
|
|
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;
|
|
80
82
|
}
|
|
81
|
-
|
|
82
|
-
this.
|
|
83
|
-
|
|
84
|
-
return this.pathResolver.relative(this.root, filepath);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
class PhysicalWorkspacePathResolver extends WorkspacePathResolver {
|
|
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
|
|
|
@@ -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,6 +177,78 @@ 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);
|
|
220
|
+
}
|
|
221
|
+
|
|
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);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class PhysicalWorkspacePathResolver extends WorkspacePathResolver {
|
|
248
|
+
constructor(root) {
|
|
249
|
+
const pathResolver = new PhysicalPathResolver();
|
|
250
|
+
super(root, pathResolver);
|
|
251
|
+
}
|
|
158
252
|
}
|
|
159
253
|
|
|
160
254
|
class VirtualWorkspacePathResolver extends WorkspacePathResolver {
|
|
@@ -164,4 +258,7 @@ class VirtualWorkspacePathResolver extends WorkspacePathResolver {
|
|
|
164
258
|
}
|
|
165
259
|
}
|
|
166
260
|
|
|
167
|
-
|
|
261
|
+
const physicalPathResolver = new PhysicalPathResolver();
|
|
262
|
+
const virtualPathResolver = new VirtualPathResolver();
|
|
263
|
+
|
|
264
|
+
export { PhysicalPathResolver, PhysicalWorkspacePathResolver, VirtualPathResolver, VirtualWorkspacePathResolver, WorkspacePathResolver, findNearestFilepath, locateNearestFilepath, physicalPathResolver, virtualPathResolver };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import { IPathResolver, IWorkspacePathResolver } from '@guanghechen/path.types';
|
|
2
2
|
export * from '@guanghechen/path.types';
|
|
3
3
|
|
|
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
|
+
|
|
4
21
|
declare class PhysicalPathResolver 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(
|
|
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;
|
|
12
37
|
}
|
|
13
38
|
|
|
14
39
|
declare class WorkspacePathResolver implements IWorkspacePathResolver {
|
|
@@ -17,8 +42,8 @@ declare class WorkspacePathResolver implements IWorkspacePathResolver {
|
|
|
17
42
|
constructor(root: string, pathResolver: IPathResolver);
|
|
18
43
|
ensureSafePath(filepath: string, message?: string | undefined): void | never;
|
|
19
44
|
isSafePath(filepath: string): boolean;
|
|
45
|
+
relative(filepath: string): string | never;
|
|
20
46
|
resolve(filepath: string): string | never;
|
|
21
|
-
relative(filepath_: string): string | never;
|
|
22
47
|
}
|
|
23
48
|
|
|
24
49
|
declare class PhysicalWorkspacePathResolver extends WorkspacePathResolver implements IWorkspacePathResolver {
|
|
@@ -28,15 +53,27 @@ declare class PhysicalWorkspacePathResolver extends WorkspacePathResolver implem
|
|
|
28
53
|
declare class VirtualPathResolver implements IPathResolver {
|
|
29
54
|
basename(filepath: string): string;
|
|
30
55
|
ensureAbsolute(filepath: string, message?: string | undefined): void | never;
|
|
56
|
+
ensureSafeRelative(root: string, filepath: string, message?: string | undefined): void;
|
|
31
57
|
dirname(filepath: string): string | never;
|
|
32
58
|
isAbsolute(filepath: string): boolean;
|
|
59
|
+
isSafeRelative(root: string, filepath: string): boolean;
|
|
33
60
|
join(filepath: string, ...pathPieces: string[]): string | never;
|
|
34
61
|
normalize(filepath: string): string | never;
|
|
35
|
-
relative(
|
|
62
|
+
relative(from: string, to: string): string | never;
|
|
63
|
+
safeRelative(root: string, filepath: string): string;
|
|
64
|
+
safeResolve(root: string, filepath: string): string;
|
|
65
|
+
protected _internalJoin(root: string, relativePath: string): string;
|
|
66
|
+
protected _internalNormalize(filepath: string): string;
|
|
67
|
+
protected _internalRelative(from_: string, to_: string): string;
|
|
68
|
+
protected _internalSafeRelative(root_: string, filepath_: string): string;
|
|
69
|
+
protected _internalSafeResolve(root: string, filepath_: string): string;
|
|
36
70
|
}
|
|
37
71
|
|
|
38
72
|
declare class VirtualWorkspacePathResolver extends WorkspacePathResolver implements IWorkspacePathResolver {
|
|
39
73
|
constructor(root: string);
|
|
40
74
|
}
|
|
41
75
|
|
|
42
|
-
|
|
76
|
+
declare const physicalPathResolver: IPathResolver;
|
|
77
|
+
declare const virtualPathResolver: IPathResolver;
|
|
78
|
+
|
|
79
|
+
export { PhysicalPathResolver, PhysicalWorkspacePathResolver, VirtualPathResolver, VirtualWorkspacePathResolver, WorkspacePathResolver, findNearestFilepath, locateNearestFilepath, physicalPathResolver, virtualPathResolver };
|
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.5",
|
|
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.4",
|
|
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.4/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": "283e59d85998ff630abddc0224f6dd2bf9546ca2"
|
|
48
|
+
"gitHead": "5970cec8f9021212fd2009f40f65f0abf5ba77ec"
|
|
54
49
|
}
|