@guanghechen/path 1.0.3 → 2.0.1
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/LICENSE +21 -0
- package/README.md +161 -0
- package/lib/cjs/index.cjs +8 -19
- package/lib/esm/index.mjs +1 -2
- package/lib/types/index.d.ts +1 -2
- package/package.json +14 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present guanghechen (https://github.com/guanghechen)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
<a href="https://github.com/guanghechen/sora/tree/@guanghechen/path@2.0.0/packages/path#readme">@guanghechen/path</a>
|
|
4
|
+
</h1>
|
|
5
|
+
<div align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@guanghechen/path">
|
|
7
|
+
<img
|
|
8
|
+
alt="Npm Version"
|
|
9
|
+
src="https://img.shields.io/npm/v/@guanghechen/path.svg"
|
|
10
|
+
/>
|
|
11
|
+
</a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@guanghechen/path">
|
|
13
|
+
<img
|
|
14
|
+
alt="Npm Download"
|
|
15
|
+
src="https://img.shields.io/npm/dm/@guanghechen/path.svg"
|
|
16
|
+
/>
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@guanghechen/path">
|
|
19
|
+
<img
|
|
20
|
+
alt="Npm License"
|
|
21
|
+
src="https://img.shields.io/npm/l/@guanghechen/path.svg"
|
|
22
|
+
/>
|
|
23
|
+
</a>
|
|
24
|
+
<a href="#install">
|
|
25
|
+
<img
|
|
26
|
+
alt="Module Formats: cjs, esm"
|
|
27
|
+
src="https://img.shields.io/badge/module_formats-cjs%2C%20esm-green.svg"
|
|
28
|
+
/>
|
|
29
|
+
</a>
|
|
30
|
+
<a href="https://github.com/nodejs/node">
|
|
31
|
+
<img
|
|
32
|
+
alt="Node.js Version"
|
|
33
|
+
src="https://img.shields.io/node/v/@guanghechen/path"
|
|
34
|
+
/>
|
|
35
|
+
</a>
|
|
36
|
+
<a href="https://github.com/facebook/jest">
|
|
37
|
+
<img
|
|
38
|
+
alt="Tested with Jest"
|
|
39
|
+
src="https://img.shields.io/badge/tested_with-jest-9c465e.svg"
|
|
40
|
+
/>
|
|
41
|
+
</a>
|
|
42
|
+
<a href="https://github.com/prettier/prettier">
|
|
43
|
+
<img
|
|
44
|
+
alt="Code Style: prettier"
|
|
45
|
+
src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"
|
|
46
|
+
/>
|
|
47
|
+
</a>
|
|
48
|
+
</div>
|
|
49
|
+
</header>
|
|
50
|
+
<br/>
|
|
51
|
+
|
|
52
|
+
Path utilities for file system operations with safe path resolution and workspace management.
|
|
53
|
+
Provides strict path validation to prevent directory traversal attacks.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
- npm
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install --save @guanghechen/path
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- yarn
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yarn add @guanghechen/path
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
### PathResolver
|
|
72
|
+
|
|
73
|
+
Standard path resolver with safety checks:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { PathResolver, pathResolver } from '@guanghechen/path'
|
|
77
|
+
|
|
78
|
+
// Use the default singleton
|
|
79
|
+
pathResolver.normalize('/foo/bar/../baz') // '/foo/baz'
|
|
80
|
+
pathResolver.basename('/foo/bar/file.txt') // 'file.txt'
|
|
81
|
+
pathResolver.dirname('/foo/bar/file.txt') // '/foo/bar'
|
|
82
|
+
pathResolver.join('/foo', 'bar', 'baz') // '/foo/bar/baz'
|
|
83
|
+
pathResolver.relative('/foo/bar', '/foo/baz') // '../baz'
|
|
84
|
+
|
|
85
|
+
// Check if path is within a root directory (safe from traversal)
|
|
86
|
+
pathResolver.isSafeRelative('/workspace', '/workspace/src/file.ts') // true
|
|
87
|
+
pathResolver.isSafeRelative('/workspace', '/etc/passwd') // false
|
|
88
|
+
|
|
89
|
+
// Safe relative path (throws if unsafe)
|
|
90
|
+
pathResolver.safeRelative('/workspace', '/workspace/src/file.ts') // 'src/file.ts'
|
|
91
|
+
|
|
92
|
+
// Safe resolve (resolve relative path within root)
|
|
93
|
+
pathResolver.safeResolve('/workspace', './src/file.ts') // '/workspace/src/file.ts'
|
|
94
|
+
pathResolver.safeResolve('/workspace', '../outside') // throws Error
|
|
95
|
+
|
|
96
|
+
// Create custom resolver with slash preference
|
|
97
|
+
const resolver = new PathResolver({ preferSlash: true })
|
|
98
|
+
resolver.relative('/foo/bar', '/foo/baz') // '../baz' (uses forward slashes on Windows)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### WorkspacePathResolver
|
|
102
|
+
|
|
103
|
+
Workspace-scoped path operations:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { WorkspacePathResolver, pathResolver } from '@guanghechen/path'
|
|
107
|
+
|
|
108
|
+
const workspace = new WorkspacePathResolver('/project/workspace', pathResolver)
|
|
109
|
+
|
|
110
|
+
// Check if path is within workspace
|
|
111
|
+
workspace.isSafePath('/project/workspace/src/index.ts') // true
|
|
112
|
+
workspace.isSafePath('/etc/passwd') // false
|
|
113
|
+
|
|
114
|
+
// Resolve relative paths within workspace
|
|
115
|
+
workspace.resolve('./src/index.ts') // '/project/workspace/src/index.ts'
|
|
116
|
+
workspace.resolve('package.json') // '/project/workspace/package.json'
|
|
117
|
+
|
|
118
|
+
// Get relative path from workspace root
|
|
119
|
+
workspace.relative('/project/workspace/src/index.ts') // 'src/index.ts'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### UrlPathResolver
|
|
123
|
+
|
|
124
|
+
URL-style path resolution (forward slashes):
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { UrlPathResolver, urlPathResolver } from '@guanghechen/path'
|
|
128
|
+
|
|
129
|
+
urlPathResolver.normalize('/foo/bar/../baz') // '/foo/baz'
|
|
130
|
+
urlPathResolver.join('/api', 'users', '123') // '/api/users/123'
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Locate Utilities
|
|
134
|
+
|
|
135
|
+
Find files by traversing up the directory tree:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { locateNearestFilepath, findNearestFilepath } from '@guanghechen/path'
|
|
139
|
+
|
|
140
|
+
// Find nearest file by name(s)
|
|
141
|
+
const packageJson = locateNearestFilepath('/project/src/utils', 'package.json')
|
|
142
|
+
// Returns: '/project/package.json' (if exists)
|
|
143
|
+
|
|
144
|
+
// Find nearest file matching multiple names
|
|
145
|
+
const config = locateNearestFilepath('/project/src', [
|
|
146
|
+
'tsconfig.json',
|
|
147
|
+
'jsconfig.json',
|
|
148
|
+
])
|
|
149
|
+
|
|
150
|
+
// Find nearest file with custom predicate
|
|
151
|
+
const readme = findNearestFilepath('/project/src', (filepath) => {
|
|
152
|
+
return filepath.toLowerCase().endsWith('readme.md')
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
|
|
158
|
+
- [homepage][homepage]
|
|
159
|
+
|
|
160
|
+
[homepage]:
|
|
161
|
+
https://github.com/guanghechen/sora/tree/@guanghechen/path@2.0.0/packages/path#readme
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
var path = require('node:path');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
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
6
|
|
|
12
7
|
const clazz$1 = 'PathResolver';
|
|
13
8
|
class PathResolver {
|
|
@@ -18,7 +13,7 @@ class PathResolver {
|
|
|
18
13
|
basename(filepath) {
|
|
19
14
|
this.ensureAbsolute(filepath);
|
|
20
15
|
const p = this.normalize(filepath);
|
|
21
|
-
return
|
|
16
|
+
return path.basename(p);
|
|
22
17
|
}
|
|
23
18
|
ensureAbsolute(filepath, message) {
|
|
24
19
|
if (this.isAbsolute(filepath))
|
|
@@ -32,11 +27,11 @@ class PathResolver {
|
|
|
32
27
|
}
|
|
33
28
|
dirname(filepath) {
|
|
34
29
|
this.ensureAbsolute(filepath);
|
|
35
|
-
const p =
|
|
30
|
+
const p = path.dirname(filepath);
|
|
36
31
|
return this.normalize(p);
|
|
37
32
|
}
|
|
38
33
|
isAbsolute(filepath) {
|
|
39
|
-
return
|
|
34
|
+
return path.isAbsolute(filepath);
|
|
40
35
|
}
|
|
41
36
|
isSafeRelative(root, filepath) {
|
|
42
37
|
if (!this.isAbsolute(root))
|
|
@@ -51,7 +46,7 @@ class PathResolver {
|
|
|
51
46
|
throw new Error(`[${clazz$1}.join] pathPiece shouldn't be absolute path. ${pathPiece}`);
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
|
-
const p =
|
|
49
|
+
const p = path.join(filepath, ...pathPieces);
|
|
55
50
|
return this.normalize(p);
|
|
56
51
|
}
|
|
57
52
|
normalize(filepath) {
|
|
@@ -76,16 +71,16 @@ class PathResolver {
|
|
|
76
71
|
return this._internalSafeResolve(root, filepath);
|
|
77
72
|
}
|
|
78
73
|
_internalNormalize(filepath) {
|
|
79
|
-
const p =
|
|
74
|
+
const p = path
|
|
80
75
|
.normalize(filepath)
|
|
81
|
-
.replace(/[/\\]+/g,
|
|
76
|
+
.replace(/[/\\]+/g, path.sep)
|
|
82
77
|
.replace(/[/\\]+$/, '');
|
|
83
78
|
return p.length <= 0 ? '/' : p;
|
|
84
79
|
}
|
|
85
80
|
_internalRelative(from_, to_) {
|
|
86
81
|
const from = this.normalize(from_);
|
|
87
82
|
const to = this.normalize(to_);
|
|
88
|
-
const relativePath =
|
|
83
|
+
const relativePath = path.relative(from, to);
|
|
89
84
|
return relativePath;
|
|
90
85
|
}
|
|
91
86
|
_internalSafeRelative(root_, filepath_) {
|
|
@@ -95,7 +90,7 @@ class PathResolver {
|
|
|
95
90
|
return relativePath;
|
|
96
91
|
}
|
|
97
92
|
_internalSafeResolve(root, filepath_) {
|
|
98
|
-
const filepath = this.isAbsolute(filepath_) ? filepath_ :
|
|
93
|
+
const filepath = this.isAbsolute(filepath_) ? filepath_ : path.join(root, filepath_);
|
|
99
94
|
return this._internalNormalize(filepath);
|
|
100
95
|
}
|
|
101
96
|
}
|
|
@@ -268,9 +263,3 @@ exports.findNearestFilepath = findNearestFilepath;
|
|
|
268
263
|
exports.locateNearestFilepath = locateNearestFilepath;
|
|
269
264
|
exports.pathResolver = pathResolver;
|
|
270
265
|
exports.urlPathResolver = urlPathResolver;
|
|
271
|
-
Object.keys(path_types).forEach(function (k) {
|
|
272
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
273
|
-
enumerable: true,
|
|
274
|
-
get: function () { return path_types[k]; }
|
|
275
|
-
});
|
|
276
|
-
});
|
package/lib/esm/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path, { join, dirname, isAbsolute } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { readdirSync, existsSync } from 'node:fs';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
export * from '@guanghechen/path.types';
|
|
5
4
|
|
|
6
5
|
const clazz$1 = 'PathResolver';
|
|
7
6
|
class PathResolver {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { IPathResolver, IPathResolverParams, IWorkspacePathResolver } from '@guanghechen/
|
|
2
|
-
export * from '@guanghechen/path.types';
|
|
1
|
+
import { IPathResolver, IPathResolverParams, IWorkspacePathResolver } from '@guanghechen/types';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Locate a nearest filepath from the given `currentDir` which name included in the `filenames`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guanghechen/path",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Path utils.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "guanghechen",
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/guanghechen/sora/tree/@guanghechen/path@
|
|
11
|
+
"url": "https://github.com/guanghechen/sora/tree/@guanghechen/path@2.0.0",
|
|
12
12
|
"directory": "packages/path"
|
|
13
13
|
},
|
|
14
|
-
"homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/path@
|
|
14
|
+
"homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/path@2.0.0/packages/path#readme",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
18
19
|
"source": "./src/index.ts",
|
|
19
20
|
"import": "./lib/esm/index.mjs",
|
|
20
|
-
"require": "./lib/cjs/index.cjs"
|
|
21
|
-
"types": "./lib/types/index.d.ts"
|
|
21
|
+
"require": "./lib/cjs/index.cjs"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"source": "./src/index.ts",
|
|
@@ -35,7 +35,13 @@
|
|
|
35
35
|
"README.md"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@guanghechen/
|
|
38
|
+
"@guanghechen/types": "^2.0.1"
|
|
39
39
|
},
|
|
40
|
-
"
|
|
41
|
-
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "rollup -c ../../rollup.config.mjs",
|
|
42
|
+
"clean": "rimraf lib",
|
|
43
|
+
"test": "vitest run --config ../../vitest.config.ts",
|
|
44
|
+
"test:coverage": "vitest run --config ../../vitest.config.ts --coverage",
|
|
45
|
+
"test:update": "vitest run --config ../../vitest.config.ts -u"
|
|
46
|
+
}
|
|
47
|
+
}
|