@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 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$2 = 'PhysicalPathResolver';
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$2}] not an absolute path: ${filepath}.`);
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$2}.join] not an absolute path: ${filepath}.`);
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$2}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
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
- relative(from_, to_) {
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 clazz$1 = 'WorkspacePathResolver';
59
- class WorkspacePathResolver {
60
- root;
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
- relative(filepath_) {
88
- this.ensureSafePath(filepath_);
89
- const filepath = this.resolve(filepath_);
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
- const p = filepath + '/' + pathPieces.join('/');
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
- relative(from_, to_) {
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$2 = 'PhysicalPathResolver';
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$2}] not an absolute path: ${filepath}.`);
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$2}.join] not an absolute path: ${filepath}.`);
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$2}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
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
- relative(from_, to_) {
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 clazz$1 = 'WorkspacePathResolver';
53
- class WorkspacePathResolver {
54
- root;
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
- relative(filepath_) {
82
- this.ensureSafePath(filepath_);
83
- const filepath = this.resolve(filepath_);
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
- const p = filepath + '/' + pathPieces.join('/');
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
- 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_}`);
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
- export { PhysicalPathResolver, PhysicalWorkspacePathResolver, VirtualPathResolver, VirtualWorkspacePathResolver, WorkspacePathResolver };
261
+ const physicalPathResolver = new PhysicalPathResolver();
262
+ const virtualPathResolver = new VirtualPathResolver();
263
+
264
+ export { PhysicalPathResolver, PhysicalWorkspacePathResolver, VirtualPathResolver, VirtualWorkspacePathResolver, WorkspacePathResolver, findNearestFilepath, locateNearestFilepath, physicalPathResolver, virtualPathResolver };
@@ -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(from_: string, to_: 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;
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(from_: string, to_: string): string | never;
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
- export { PhysicalPathResolver, PhysicalWorkspacePathResolver, VirtualPathResolver, VirtualWorkspacePathResolver, WorkspacePathResolver };
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",
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.2",
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.2/packages/path#readme",
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.2"
46
+ "@guanghechen/path.types": "^1.0.0-alpha.4"
47
47
  },
48
- "devDependencies": {
49
- "cross-env": "*",
50
- "rimraf": "*",
51
- "rollup": "*"
52
- },
53
- "gitHead": "283e59d85998ff630abddc0224f6dd2bf9546ca2"
48
+ "gitHead": "5970cec8f9021212fd2009f40f65f0abf5ba77ec"
54
49
  }