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