@reliverse/pathkit 1.1.0 → 1.1.2
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/README.md +11 -13
- package/bin/mod.d.ts +63 -1
- package/bin/mod.js +297 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
## Key Features
|
|
8
8
|
|
|
9
|
-
- 🔹 **drop in** and replace `node:path` and `unjs/pathe` instantly
|
|
10
|
-
- ➕ **`unjs/pathe` on steroids** – alias resolution, import parsing, and more
|
|
11
|
-
- 🌀 **always `/`** – posix separators 100% of the time (buh‑bye `\\`)
|
|
12
|
-
- ⚙️ **node.js api compatible** – familiar methods, no learning curve
|
|
13
|
-
- 🚀 **modern & fast** – typescript, pure esm, bun & node‑ready
|
|
14
|
-
- 🧠 **predictable & testable** – deterministic output across windows / macos / linux
|
|
15
|
-
- 🧼 **no dependencies** – just
|
|
9
|
+
- 🔹 **drop in** and replace `node:path` and `unjs/pathe` instantly
|
|
10
|
+
- ➕ **`unjs/pathe` on steroids** – alias resolution, import parsing, and more
|
|
11
|
+
- 🌀 **always `/`** – posix separators 100% of the time (buh‑bye `\\`)
|
|
12
|
+
- ⚙️ **node.js api compatible** – familiar methods, no learning curve
|
|
13
|
+
- 🚀 **modern & fast** – typescript, pure esm, bun & node‑ready
|
|
14
|
+
- 🧠 **predictable & testable** – deterministic output across windows / macos / linux
|
|
15
|
+
- 🧼 **no dependencies** – just better path api + couple of cool utilities = [4.2 kB](https://bundlephobia.com/package/@reliverse/pathkit@latest)
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -24,18 +24,18 @@ bun add @reliverse/pathkit
|
|
|
24
24
|
**Migrate**:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
bun rm pathe
|
|
28
27
|
# soon:
|
|
29
28
|
# bun add -D @reliverse/dler
|
|
29
|
+
# bun dler migrate --lib path-to-pathkit
|
|
30
30
|
# bun dler migrate --lib pathe-to-pathkit
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### `pathe` vs
|
|
33
|
+
### `unjs/pathe` vs `@reliverse/pathkit`
|
|
34
34
|
|
|
35
35
|
| Package | What you get | When to use |
|
|
36
36
|
|---------|--------------|-------------|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
37
|
+
| **`pathe`** | Path API only (with POSIX everywhere) | You only need a drop‑in for `node:path` |
|
|
38
|
+
| **`pathkit`** | Everything in `pathe` **+** advanced utilities | You need alias resolution, import transforms, etc. |
|
|
39
39
|
|
|
40
40
|
## Why Pathkit? — The Problem with Native Paths
|
|
41
41
|
|
|
@@ -196,13 +196,11 @@ console.log(reverseResolveAlias("/src/utils", aliases)); // "@/utils"
|
|
|
196
196
|
```ts
|
|
197
197
|
import {
|
|
198
198
|
filename, // Strip extension
|
|
199
|
-
extractPackageName, // Get package name from import
|
|
200
199
|
normalizeQuotes, // Standardize quote style
|
|
201
200
|
matchesGlob // Test glob patterns
|
|
202
201
|
} from "@reliverse/pathkit";
|
|
203
202
|
|
|
204
203
|
console.log(filename("/path/component.vue")); // "component"
|
|
205
|
-
console.log(extractPackageName("@scope/pkg")); // "@scope/pkg"
|
|
206
204
|
console.log(normalizeQuotes("import 'pkg'")); // 'import "pkg"'
|
|
207
205
|
console.log(matchesGlob("file.ts", "**/*.ts")); // true
|
|
208
206
|
```
|
package/bin/mod.d.ts
CHANGED
|
@@ -130,6 +130,68 @@ declare function convertImportsExt({ targetDir, extFrom, extTo, }: {
|
|
|
130
130
|
to: string;
|
|
131
131
|
}[];
|
|
132
132
|
}[]>;
|
|
133
|
+
/**
|
|
134
|
+
* strips a specified number of segments from the beginning of a path
|
|
135
|
+
* @param path - The path to strip segments from
|
|
136
|
+
* @param count - Number of segments to strip (default: 1)
|
|
137
|
+
* @param alias - Optional alias to preserve (e.g. "@", "~")
|
|
138
|
+
* @returns The path with the specified number of segments removed
|
|
139
|
+
*/
|
|
140
|
+
declare function stripPathSegments(path: string, count?: number, alias?: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* recursively processes files in a directory to strip path segments from their contents
|
|
143
|
+
* @param targetDir - The directory to process
|
|
144
|
+
* @param segmentsToStrip - Number of segments to strip from paths
|
|
145
|
+
* @param alias - Optional alias to preserve (e.g. "@", "~")
|
|
146
|
+
* @param extensionsToProcess - Array of file extensions to process (default: EXTENSIONS)
|
|
147
|
+
* @returns Array of processed files and their changes
|
|
148
|
+
*/
|
|
149
|
+
declare function stripPathSegmentsInDirectory({ targetDir, segmentsToStrip, alias, extensionsToProcess, }: {
|
|
150
|
+
targetDir: string;
|
|
151
|
+
segmentsToStrip: number;
|
|
152
|
+
alias?: string;
|
|
153
|
+
extensionsToProcess?: string[];
|
|
154
|
+
}): Promise<{
|
|
155
|
+
file: string;
|
|
156
|
+
changes: {
|
|
157
|
+
from: string;
|
|
158
|
+
to: string;
|
|
159
|
+
}[];
|
|
160
|
+
}[]>;
|
|
161
|
+
/**
|
|
162
|
+
* attaches path segments to an existing path
|
|
163
|
+
* @param path - The base path to attach segments to
|
|
164
|
+
* @param segments - The segments to attach
|
|
165
|
+
* @param options - Configuration options
|
|
166
|
+
* @returns The path with segments attached
|
|
167
|
+
*/
|
|
168
|
+
declare function attachPathSegments(path: string, segments: string | string[], options?: {
|
|
169
|
+
position?: "before" | "after";
|
|
170
|
+
normalize?: boolean;
|
|
171
|
+
ensureSlash?: boolean;
|
|
172
|
+
preserveRoot?: boolean;
|
|
173
|
+
preserveAlias?: string;
|
|
174
|
+
}): string;
|
|
175
|
+
/**
|
|
176
|
+
* recursively processes files in a directory to attach path segments to import statements
|
|
177
|
+
* @param targetDir - The directory to process
|
|
178
|
+
* @param segments - The segments to attach
|
|
179
|
+
* @param options - Configuration options for path segment attachment
|
|
180
|
+
* @param extensionsToProcess - Array of file extensions to process (default: EXTENSIONS)
|
|
181
|
+
* @returns Array of processed files and their changes
|
|
182
|
+
*/
|
|
183
|
+
declare function attachPathSegmentsInDirectory({ targetDir, segments, options, extensionsToProcess, }: {
|
|
184
|
+
targetDir: string;
|
|
185
|
+
segments: string | string[];
|
|
186
|
+
options?: Parameters<typeof attachPathSegments>[2];
|
|
187
|
+
extensionsToProcess?: string[];
|
|
188
|
+
}): Promise<{
|
|
189
|
+
file: string;
|
|
190
|
+
changes: {
|
|
191
|
+
from: string;
|
|
192
|
+
to: string;
|
|
193
|
+
}[];
|
|
194
|
+
}[]>;
|
|
133
195
|
declare const _pathBase: {
|
|
134
196
|
sep: string;
|
|
135
197
|
normalize: (path: string) => string;
|
|
@@ -166,5 +228,5 @@ declare const path: PlatformPath & {
|
|
|
166
228
|
declare const win32: PlatformPath;
|
|
167
229
|
declare const delimiter: string;
|
|
168
230
|
export type { PlatformPath, PathExtFilter, ImportExtType };
|
|
169
|
-
export { _pathBase as posix, win32, basename, delimiter, dirname, extname, filename, format, isAbsolute, join, normalize, parse, relative, resolve, sep, toNamespacedPath, normalizeAliases, resolveAlias, reverseResolveAlias, normalizeWindowsPath, cleanDirs, copyDir, convertStringAliasRelative, convertImportsAliasToRelative, convertImportsExt, };
|
|
231
|
+
export { _pathBase as posix, win32, basename, delimiter, dirname, extname, filename, format, isAbsolute, join, normalize, parse, relative, resolve, sep, toNamespacedPath, normalizeAliases, resolveAlias, reverseResolveAlias, normalizeWindowsPath, cleanDirs, copyDir, convertStringAliasRelative, convertImportsAliasToRelative, convertImportsExt, stripPathSegments, stripPathSegmentsInDirectory, attachPathSegments, attachPathSegmentsInDirectory, };
|
|
170
232
|
export default path;
|
package/bin/mod.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
+
const log = (msg) => console.log(`\x1B[2m${msg}\x1B[0m`);
|
|
3
|
+
const logInternal = (msg) => console.log(`\x1B[36;2m${msg}\x1B[0m`);
|
|
2
4
|
const EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
3
5
|
const SLASH = "/";
|
|
4
6
|
const BACK_SLASH = "\\";
|
|
@@ -13,13 +15,12 @@ const IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
|
13
15
|
const ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
14
16
|
const PATH_ROOT_RE = /^[/\\]|^[a-zA-Z]:[/\\]/;
|
|
15
17
|
const IMPORT_REGEX = /(?:import\s+(?:[\s\S]*?)\s+from\s+|import\s*\(\s*)\s*(['"])((?:@)[^'"]+)\1/g;
|
|
16
|
-
const log = (msg) => console.log(`\x1B[2m${msg}\x1B[0m`);
|
|
17
18
|
async function cleanDirs(dirs) {
|
|
18
19
|
await Promise.all(
|
|
19
20
|
dirs.map(async (d) => {
|
|
20
21
|
try {
|
|
21
22
|
await fs.rm(d, { recursive: true, force: true });
|
|
22
|
-
|
|
23
|
+
logInternal(`\u2713 cleaned: ${d}`);
|
|
23
24
|
} catch (error) {
|
|
24
25
|
log(
|
|
25
26
|
`\u2717 error cleaning ${d}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -29,7 +30,7 @@ async function cleanDirs(dirs) {
|
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
32
|
async function copyDir(src, dest) {
|
|
32
|
-
|
|
33
|
+
logInternal(`\u2713 copying: ${src} \u2192 ${dest}`);
|
|
33
34
|
try {
|
|
34
35
|
await fs.mkdir(dest, { recursive: true });
|
|
35
36
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
@@ -41,7 +42,7 @@ async function copyDir(src, dest) {
|
|
|
41
42
|
return copyDir(srcPath, destPath);
|
|
42
43
|
}
|
|
43
44
|
await fs.copyFile(srcPath, destPath);
|
|
44
|
-
|
|
45
|
+
logInternal(` copied: ${srcPath} \u2192 ${destPath}`);
|
|
45
46
|
})
|
|
46
47
|
);
|
|
47
48
|
} catch (error) {
|
|
@@ -376,6 +377,11 @@ function reverseResolveAlias(path2, aliases) {
|
|
|
376
377
|
return matches.sort((a, b) => b.length - a.length);
|
|
377
378
|
}
|
|
378
379
|
const findAliasMatch = (importPath, paths) => {
|
|
380
|
+
const firstPathKey = Object.keys(paths)[0];
|
|
381
|
+
const baseAlias = firstPathKey.replace("/*", "");
|
|
382
|
+
if (importPath.startsWith("@") && !importPath.startsWith(baseAlias)) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
379
385
|
if (paths[importPath]?.[0]) {
|
|
380
386
|
return {
|
|
381
387
|
key: importPath,
|
|
@@ -447,6 +453,10 @@ async function convertStringAliasRelative({
|
|
|
447
453
|
pathPattern,
|
|
448
454
|
targetDir
|
|
449
455
|
}) {
|
|
456
|
+
const baseAlias = pathPattern.replace("/*", "");
|
|
457
|
+
if (importPath.startsWith("@") && !importPath.startsWith(baseAlias)) {
|
|
458
|
+
return importPath;
|
|
459
|
+
}
|
|
450
460
|
const paths = { [pathPattern]: ["./*"] };
|
|
451
461
|
const importerDir = dirname(importerFile);
|
|
452
462
|
const match = findAliasMatch(importPath, paths);
|
|
@@ -478,9 +488,16 @@ async function processFile(filePath, aliasToReplace, targetDir, pathExtFilter) {
|
|
|
478
488
|
const changes = [];
|
|
479
489
|
const matches = Array.from(content.matchAll(IMPORT_REGEX));
|
|
480
490
|
const normalizedAlias = aliasToReplace.endsWith("/*") ? aliasToReplace : `${aliasToReplace}/*`;
|
|
491
|
+
const baseAlias = aliasToReplace.replace("/*", "");
|
|
481
492
|
for (const match of matches) {
|
|
482
493
|
const originalQuote = match[1];
|
|
483
494
|
const importPath = match[2];
|
|
495
|
+
if (!importPath.startsWith(baseAlias) && !importPath.startsWith("@")) {
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
if (importPath.startsWith("@") && !importPath.startsWith(baseAlias)) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
484
501
|
const importExt = extname(importPath);
|
|
485
502
|
const shouldProcess = pathExtFilter === "js" && importExt === ".js" || pathExtFilter === "ts" && importExt === ".ts" || pathExtFilter === "none" && importExt === "" || pathExtFilter === "js-ts-none";
|
|
486
503
|
if (!shouldProcess) continue;
|
|
@@ -499,7 +516,7 @@ async function processFile(filePath, aliasToReplace, targetDir, pathExtFilter) {
|
|
|
499
516
|
}
|
|
500
517
|
if (content !== updated) {
|
|
501
518
|
await fs.writeFile(filePath, updated);
|
|
502
|
-
|
|
519
|
+
logInternal(`\u2713 processed: ${filePath}`);
|
|
503
520
|
}
|
|
504
521
|
return changes;
|
|
505
522
|
}
|
|
@@ -517,8 +534,7 @@ async function processAllFiles({
|
|
|
517
534
|
entries.map(async (entry) => {
|
|
518
535
|
const fullPath = join(srcDir, entry.name);
|
|
519
536
|
if (entry.isDirectory()) {
|
|
520
|
-
if (entry.name === "node_modules"
|
|
521
|
-
return;
|
|
537
|
+
if (entry.name === "node_modules") return;
|
|
522
538
|
const subdirResults = await processAllFiles({
|
|
523
539
|
srcDir: fullPath,
|
|
524
540
|
aliasToReplace,
|
|
@@ -537,6 +553,8 @@ async function processAllFiles({
|
|
|
537
553
|
if (changes.length > 0) {
|
|
538
554
|
results.push({ file: fullPath, changes });
|
|
539
555
|
}
|
|
556
|
+
} else {
|
|
557
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
540
558
|
}
|
|
541
559
|
})
|
|
542
560
|
);
|
|
@@ -559,7 +577,7 @@ async function convertImportsAliasToRelative({
|
|
|
559
577
|
`Converting aliased imports starting with '${aliasToReplace}' to relative paths in "${targetDir}"...`
|
|
560
578
|
);
|
|
561
579
|
log(` (Assuming "${normalizedAlias}" resolves relative to "${targetDir}")`);
|
|
562
|
-
|
|
580
|
+
logInternal(` (Using extension mode: ${pathExtFilter})`);
|
|
563
581
|
const results = await processAllFiles({
|
|
564
582
|
srcDir: targetDir,
|
|
565
583
|
aliasToReplace: normalizedAlias,
|
|
@@ -586,6 +604,9 @@ async function convertImportsExt({
|
|
|
586
604
|
extFrom,
|
|
587
605
|
extTo
|
|
588
606
|
}) {
|
|
607
|
+
logInternal(
|
|
608
|
+
`Converting import extensions from '${extFrom}' to '${extTo}' in "${targetDir}"...`
|
|
609
|
+
);
|
|
589
610
|
const fromExtStr = extFrom === "none" ? "" : `.${extFrom}`;
|
|
590
611
|
const toExtStr = extTo === "none" ? "" : `.${extTo}`;
|
|
591
612
|
const importRegex = new RegExp(
|
|
@@ -599,9 +620,7 @@ async function convertImportsExt({
|
|
|
599
620
|
entries.map(async (entry) => {
|
|
600
621
|
const fullPath = join(targetDir, entry.name);
|
|
601
622
|
if (entry.isDirectory()) {
|
|
602
|
-
if (entry.name === "node_modules"
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
623
|
+
if (entry.name === "node_modules") return;
|
|
605
624
|
const subdirResults = await convertImportsExt({
|
|
606
625
|
targetDir: fullPath,
|
|
607
626
|
extFrom,
|
|
@@ -632,11 +651,13 @@ async function convertImportsExt({
|
|
|
632
651
|
}
|
|
633
652
|
if (content !== updated) {
|
|
634
653
|
await fs.writeFile(fullPath, updated);
|
|
635
|
-
|
|
654
|
+
logInternal(`\u2713 processed: ${fullPath}`);
|
|
636
655
|
if (changes.length > 0) {
|
|
637
656
|
results.push({ file: fullPath, changes });
|
|
638
657
|
}
|
|
639
658
|
}
|
|
659
|
+
} else {
|
|
660
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
640
661
|
}
|
|
641
662
|
})
|
|
642
663
|
);
|
|
@@ -651,6 +672,265 @@ async function convertImportsExt({
|
|
|
651
672
|
}
|
|
652
673
|
} else {
|
|
653
674
|
}
|
|
675
|
+
logInternal("Extension conversion complete.");
|
|
676
|
+
return results;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
log(
|
|
679
|
+
`error processing directory ${targetDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
680
|
+
);
|
|
681
|
+
return [];
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function stripPathSegments(path2, count = 1, alias = "") {
|
|
685
|
+
if (typeof path2 !== "string" || path2.length === 0) return path2;
|
|
686
|
+
if (count <= 0) return path2;
|
|
687
|
+
logInternal(`[stripPathSegments] Processing path: ${path2}`);
|
|
688
|
+
logInternal(` - count: ${count}, alias: ${alias}`);
|
|
689
|
+
const normalizedPath = normalizeWindowsPath(path2);
|
|
690
|
+
logInternal(` - normalized: ${normalizedPath}`);
|
|
691
|
+
const parsed = parse(normalizedPath);
|
|
692
|
+
logInternal(` - parsed: ${JSON.stringify(parsed)}`);
|
|
693
|
+
let pathSegments = [];
|
|
694
|
+
if (parsed.dir && parsed.dir !== parsed.root) {
|
|
695
|
+
let dirRelativeToRoot = parsed.dir;
|
|
696
|
+
if (parsed.root && parsed.dir.startsWith(parsed.root)) {
|
|
697
|
+
dirRelativeToRoot = parsed.dir.substring(parsed.root.length);
|
|
698
|
+
}
|
|
699
|
+
pathSegments.push(...dirRelativeToRoot.split(SLASH));
|
|
700
|
+
}
|
|
701
|
+
if (parsed.base) {
|
|
702
|
+
pathSegments.push(parsed.base);
|
|
703
|
+
}
|
|
704
|
+
pathSegments = pathSegments.filter(Boolean);
|
|
705
|
+
logInternal(` - initial segments: ${JSON.stringify(pathSegments)}`);
|
|
706
|
+
const leadingPreservedSegments = [];
|
|
707
|
+
if (alias && pathSegments.length > 0 && pathSegments[0].startsWith(alias)) {
|
|
708
|
+
const preserved = pathSegments.shift();
|
|
709
|
+
leadingPreservedSegments.push(preserved);
|
|
710
|
+
logInternal(` - preserved alias segment: ${preserved}`);
|
|
711
|
+
}
|
|
712
|
+
while (pathSegments.length > 0 && (pathSegments[0] === DOT || pathSegments[0] === DOUBLE_DOT)) {
|
|
713
|
+
const preserved = pathSegments.shift();
|
|
714
|
+
leadingPreservedSegments.push(preserved);
|
|
715
|
+
logInternal(` - preserved relative segment: ${preserved}`);
|
|
716
|
+
}
|
|
717
|
+
const numToStrip = Math.min(count, pathSegments.length);
|
|
718
|
+
const remainingBodySegments = pathSegments.slice(numToStrip);
|
|
719
|
+
logInternal(
|
|
720
|
+
` - stripping ${numToStrip} segments from: ${JSON.stringify(pathSegments)}`
|
|
721
|
+
);
|
|
722
|
+
logInternal(
|
|
723
|
+
` - remaining body segments: ${JSON.stringify(remainingBodySegments)}`
|
|
724
|
+
);
|
|
725
|
+
const pathRoot = parsed.root;
|
|
726
|
+
const effectiveSegments = [
|
|
727
|
+
...leadingPreservedSegments,
|
|
728
|
+
...remainingBodySegments
|
|
729
|
+
];
|
|
730
|
+
logInternal(` - effective segments: ${JSON.stringify(effectiveSegments)}`);
|
|
731
|
+
let result;
|
|
732
|
+
if (effectiveSegments.length === 0) {
|
|
733
|
+
result = normalize(pathRoot || DOT);
|
|
734
|
+
} else if (pathRoot) {
|
|
735
|
+
result = join(pathRoot, ...effectiveSegments);
|
|
736
|
+
} else {
|
|
737
|
+
result = join(...effectiveSegments);
|
|
738
|
+
}
|
|
739
|
+
logInternal(` - final result: ${result}`);
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
async function stripPathSegmentsInDirectory({
|
|
743
|
+
targetDir,
|
|
744
|
+
segmentsToStrip,
|
|
745
|
+
alias = "",
|
|
746
|
+
extensionsToProcess = EXTENSIONS
|
|
747
|
+
}) {
|
|
748
|
+
log(`[stripPathSegmentsInDirectory] Processing directory: ${targetDir}`);
|
|
749
|
+
log(` - segmentsToStrip: ${segmentsToStrip}, alias: ${alias}`);
|
|
750
|
+
logInternal(` - extensions: ${JSON.stringify(extensionsToProcess)}`);
|
|
751
|
+
try {
|
|
752
|
+
const entries = await fs.readdir(targetDir, { withFileTypes: true });
|
|
753
|
+
const results = [];
|
|
754
|
+
await Promise.all(
|
|
755
|
+
entries.map(async (entry) => {
|
|
756
|
+
const fullPath = join(targetDir, entry.name);
|
|
757
|
+
if (entry.isDirectory()) {
|
|
758
|
+
if (entry.name === "node_modules") return;
|
|
759
|
+
logInternal(` - recursing into directory: ${entry.name}`);
|
|
760
|
+
const subdirResults = await stripPathSegmentsInDirectory({
|
|
761
|
+
targetDir: fullPath,
|
|
762
|
+
segmentsToStrip,
|
|
763
|
+
alias,
|
|
764
|
+
extensionsToProcess
|
|
765
|
+
});
|
|
766
|
+
results.push(...subdirResults);
|
|
767
|
+
} else if (extensionsToProcess.includes(extname(entry.name))) {
|
|
768
|
+
logInternal(` Processing file: ${entry.name}`);
|
|
769
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
770
|
+
let updated = content;
|
|
771
|
+
const changes = [];
|
|
772
|
+
const matches = Array.from(content.matchAll(IMPORT_REGEX));
|
|
773
|
+
logInternal(` - found ${matches.length} import statements`);
|
|
774
|
+
for (const match of matches) {
|
|
775
|
+
const originalQuote = match[1];
|
|
776
|
+
const importPath = match[2];
|
|
777
|
+
if (!importPath.includes(SLASH)) {
|
|
778
|
+
logInternal(` - skipping non-path import: ${importPath}`);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (importPath.startsWith("@") && !importPath.startsWith(alias.replace("/*", ""))) {
|
|
782
|
+
logInternal(` - skipping npm package import: ${importPath}`);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
logInternal(` Processing import: ${importPath}`);
|
|
786
|
+
const strippedPath = stripPathSegments(
|
|
787
|
+
importPath,
|
|
788
|
+
segmentsToStrip,
|
|
789
|
+
alias
|
|
790
|
+
);
|
|
791
|
+
if (importPath === strippedPath) {
|
|
792
|
+
logInternal(" - no changes needed");
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
changes.push({ from: importPath, to: strippedPath });
|
|
796
|
+
logInternal(` - transformed: ${importPath} \u2192 ${strippedPath}`);
|
|
797
|
+
const searchStr = `${originalQuote}${importPath}${originalQuote}`;
|
|
798
|
+
const replaceStr = `${originalQuote}${strippedPath}${originalQuote}`;
|
|
799
|
+
updated = replaceAllInString(updated, searchStr, replaceStr);
|
|
800
|
+
}
|
|
801
|
+
if (content !== updated) {
|
|
802
|
+
await fs.writeFile(fullPath, updated);
|
|
803
|
+
logInternal(" \u2713 wrote changes to file");
|
|
804
|
+
if (changes.length > 0) {
|
|
805
|
+
results.push({ file: fullPath, changes });
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
logInternal(" - no changes made to file");
|
|
809
|
+
}
|
|
810
|
+
} else {
|
|
811
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
812
|
+
}
|
|
813
|
+
})
|
|
814
|
+
);
|
|
815
|
+
if (results.length > 0) {
|
|
816
|
+
log("[stripPathSegmentsInDirectory] Summary of changes:");
|
|
817
|
+
for (const { file, changes } of results) {
|
|
818
|
+
const displayPath = relative(targetDir, file) || basename(file);
|
|
819
|
+
log(` in ${displayPath}:`);
|
|
820
|
+
for (const { from, to } of changes) {
|
|
821
|
+
log(` - ${from} \u2192 ${to}`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
} else {
|
|
825
|
+
logInternal(" No changes were made in any files");
|
|
826
|
+
}
|
|
827
|
+
return results;
|
|
828
|
+
} catch (error) {
|
|
829
|
+
log(
|
|
830
|
+
`error processing directory ${targetDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
831
|
+
);
|
|
832
|
+
return [];
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
function attachPathSegments(path2, segments, options = {}) {
|
|
836
|
+
if (typeof path2 !== "string" || path2.length === 0) return path2;
|
|
837
|
+
const {
|
|
838
|
+
position = "after",
|
|
839
|
+
normalize: shouldNormalize = true,
|
|
840
|
+
ensureSlash = true,
|
|
841
|
+
preserveRoot = true,
|
|
842
|
+
preserveAlias
|
|
843
|
+
} = options;
|
|
844
|
+
const segmentsArray = Array.isArray(segments) ? segments : [segments];
|
|
845
|
+
const validSegments = segmentsArray.filter((s) => s.length > 0);
|
|
846
|
+
if (validSegments.length === 0) return path2;
|
|
847
|
+
const basePath = shouldNormalize ? normalizeWindowsPath(path2) : path2;
|
|
848
|
+
let alias = "";
|
|
849
|
+
let pathWithoutAlias = basePath;
|
|
850
|
+
if (preserveAlias && position === "before") {
|
|
851
|
+
const aliasMatch = new RegExp(`^${preserveAlias.replace("*", ".*")}`).exec(
|
|
852
|
+
basePath
|
|
853
|
+
);
|
|
854
|
+
if (aliasMatch) {
|
|
855
|
+
alias = aliasMatch[0];
|
|
856
|
+
pathWithoutAlias = basePath.slice(alias.length);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
const isAbsolute2 = IS_ABSOLUTE_RE.test(pathWithoutAlias);
|
|
860
|
+
const root = preserveRoot && isAbsolute2 ? SLASH : "";
|
|
861
|
+
const pathWithoutRoot = isAbsolute2 ? pathWithoutAlias.slice(1) : pathWithoutAlias;
|
|
862
|
+
const joinedSegments = validSegments.join(SLASH);
|
|
863
|
+
let result;
|
|
864
|
+
if (position === "before") {
|
|
865
|
+
result = ensureSlash ? `${alias}${root}${joinedSegments}${SLASH}${pathWithoutRoot}` : `${alias}${root}${joinedSegments}${pathWithoutRoot}`;
|
|
866
|
+
} else {
|
|
867
|
+
result = ensureSlash ? `${alias}${root}${pathWithoutRoot}${SLASH}${joinedSegments}` : `${alias}${root}${pathWithoutRoot}${joinedSegments}`;
|
|
868
|
+
}
|
|
869
|
+
return shouldNormalize ? normalize(result) : result;
|
|
870
|
+
}
|
|
871
|
+
async function attachPathSegmentsInDirectory({
|
|
872
|
+
targetDir,
|
|
873
|
+
segments,
|
|
874
|
+
options = {},
|
|
875
|
+
extensionsToProcess = EXTENSIONS
|
|
876
|
+
}) {
|
|
877
|
+
try {
|
|
878
|
+
const entries = await fs.readdir(targetDir, { withFileTypes: true });
|
|
879
|
+
const results = [];
|
|
880
|
+
await Promise.all(
|
|
881
|
+
entries.map(async (entry) => {
|
|
882
|
+
const fullPath = join(targetDir, entry.name);
|
|
883
|
+
if (entry.isDirectory()) {
|
|
884
|
+
if (entry.name === "node_modules") return;
|
|
885
|
+
const subdirResults = await attachPathSegmentsInDirectory({
|
|
886
|
+
targetDir: fullPath,
|
|
887
|
+
segments,
|
|
888
|
+
options,
|
|
889
|
+
extensionsToProcess
|
|
890
|
+
});
|
|
891
|
+
results.push(...subdirResults);
|
|
892
|
+
} else if (extensionsToProcess.includes(extname(entry.name))) {
|
|
893
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
894
|
+
let updated = content;
|
|
895
|
+
const changes = [];
|
|
896
|
+
const matches = Array.from(content.matchAll(IMPORT_REGEX));
|
|
897
|
+
for (const match of matches) {
|
|
898
|
+
const originalQuote = match[1];
|
|
899
|
+
const importPath = match[2];
|
|
900
|
+
if (!importPath.includes(SLASH)) continue;
|
|
901
|
+
const modifiedPath = attachPathSegments(
|
|
902
|
+
importPath,
|
|
903
|
+
segments,
|
|
904
|
+
options
|
|
905
|
+
);
|
|
906
|
+
if (importPath === modifiedPath) continue;
|
|
907
|
+
changes.push({ from: importPath, to: modifiedPath });
|
|
908
|
+
const searchStr = `${originalQuote}${importPath}${originalQuote}`;
|
|
909
|
+
const replaceStr = `${originalQuote}${modifiedPath}${originalQuote}`;
|
|
910
|
+
updated = replaceAllInString(updated, searchStr, replaceStr);
|
|
911
|
+
}
|
|
912
|
+
if (content !== updated) {
|
|
913
|
+
await fs.writeFile(fullPath, updated);
|
|
914
|
+
logInternal(`\u2713 processed: ${fullPath}`);
|
|
915
|
+
if (changes.length > 0) {
|
|
916
|
+
results.push({ file: fullPath, changes });
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
} else {
|
|
920
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
921
|
+
}
|
|
922
|
+
})
|
|
923
|
+
);
|
|
924
|
+
if (results.length > 0) {
|
|
925
|
+
log("\n[attachPathSegmentsInDirectory] Summary of changes:");
|
|
926
|
+
for (const { file, changes } of results) {
|
|
927
|
+
const displayPath = relative(targetDir, file) || basename(file);
|
|
928
|
+
log(` in ${displayPath}:`);
|
|
929
|
+
for (const { from, to } of changes) {
|
|
930
|
+
log(` - ${from} \u2192 ${to}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
654
934
|
return results;
|
|
655
935
|
} catch (error) {
|
|
656
936
|
log(
|
|
@@ -743,6 +1023,10 @@ export {
|
|
|
743
1023
|
copyDir,
|
|
744
1024
|
convertStringAliasRelative,
|
|
745
1025
|
convertImportsAliasToRelative,
|
|
746
|
-
convertImportsExt
|
|
1026
|
+
convertImportsExt,
|
|
1027
|
+
stripPathSegments,
|
|
1028
|
+
stripPathSegmentsInDirectory,
|
|
1029
|
+
attachPathSegments,
|
|
1030
|
+
attachPathSegmentsInDirectory
|
|
747
1031
|
};
|
|
748
1032
|
export default path;
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"name": "@reliverse/pathkit",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"version": "1.1.
|
|
8
|
+
"version": "1.1.2",
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@biomejs/biome": "1.9.4",
|
|
11
11
|
"@eslint/js": "^9.27.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"eslint": "^9.27.0",
|
|
20
20
|
"eslint-plugin-no-relative-import-paths": "^1.6.1",
|
|
21
21
|
"eslint-plugin-perfectionist": "^4.13.0",
|
|
22
|
-
"knip": "^5.57.
|
|
22
|
+
"knip": "^5.57.1",
|
|
23
23
|
"magic-string": "^0.30.17",
|
|
24
24
|
"p-map": "^7.0.3",
|
|
25
25
|
"typescript": "^5.8.3",
|