@guanghechen/path 1.0.0-alpha.4 → 1.0.0-alpha.6

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