@reliverse/pathkit 1.1.0 → 1.1.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/README.md +11 -13
- package/bin/mod.d.ts +63 -1
- package/bin/mod.js +280 -8
- 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) {
|
|
@@ -499,7 +500,7 @@ async function processFile(filePath, aliasToReplace, targetDir, pathExtFilter) {
|
|
|
499
500
|
}
|
|
500
501
|
if (content !== updated) {
|
|
501
502
|
await fs.writeFile(filePath, updated);
|
|
502
|
-
|
|
503
|
+
logInternal(`\u2713 processed: ${filePath}`);
|
|
503
504
|
}
|
|
504
505
|
return changes;
|
|
505
506
|
}
|
|
@@ -537,6 +538,8 @@ async function processAllFiles({
|
|
|
537
538
|
if (changes.length > 0) {
|
|
538
539
|
results.push({ file: fullPath, changes });
|
|
539
540
|
}
|
|
541
|
+
} else {
|
|
542
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
540
543
|
}
|
|
541
544
|
})
|
|
542
545
|
);
|
|
@@ -559,7 +562,7 @@ async function convertImportsAliasToRelative({
|
|
|
559
562
|
`Converting aliased imports starting with '${aliasToReplace}' to relative paths in "${targetDir}"...`
|
|
560
563
|
);
|
|
561
564
|
log(` (Assuming "${normalizedAlias}" resolves relative to "${targetDir}")`);
|
|
562
|
-
|
|
565
|
+
logInternal(` (Using extension mode: ${pathExtFilter})`);
|
|
563
566
|
const results = await processAllFiles({
|
|
564
567
|
srcDir: targetDir,
|
|
565
568
|
aliasToReplace: normalizedAlias,
|
|
@@ -586,6 +589,9 @@ async function convertImportsExt({
|
|
|
586
589
|
extFrom,
|
|
587
590
|
extTo
|
|
588
591
|
}) {
|
|
592
|
+
logInternal(
|
|
593
|
+
`Converting import extensions from '${extFrom}' to '${extTo}' in "${targetDir}"...`
|
|
594
|
+
);
|
|
589
595
|
const fromExtStr = extFrom === "none" ? "" : `.${extFrom}`;
|
|
590
596
|
const toExtStr = extTo === "none" ? "" : `.${extTo}`;
|
|
591
597
|
const importRegex = new RegExp(
|
|
@@ -632,11 +638,13 @@ async function convertImportsExt({
|
|
|
632
638
|
}
|
|
633
639
|
if (content !== updated) {
|
|
634
640
|
await fs.writeFile(fullPath, updated);
|
|
635
|
-
|
|
641
|
+
logInternal(`\u2713 processed: ${fullPath}`);
|
|
636
642
|
if (changes.length > 0) {
|
|
637
643
|
results.push({ file: fullPath, changes });
|
|
638
644
|
}
|
|
639
645
|
}
|
|
646
|
+
} else {
|
|
647
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
640
648
|
}
|
|
641
649
|
})
|
|
642
650
|
);
|
|
@@ -651,6 +659,266 @@ async function convertImportsExt({
|
|
|
651
659
|
}
|
|
652
660
|
} else {
|
|
653
661
|
}
|
|
662
|
+
logInternal("Extension conversion complete.");
|
|
663
|
+
return results;
|
|
664
|
+
} catch (error) {
|
|
665
|
+
log(
|
|
666
|
+
`error processing directory ${targetDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
667
|
+
);
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function stripPathSegments(path2, count = 1, alias = "") {
|
|
672
|
+
if (typeof path2 !== "string" || path2.length === 0) return path2;
|
|
673
|
+
if (count <= 0) return path2;
|
|
674
|
+
logInternal(`[stripPathSegments] Processing path: ${path2}`);
|
|
675
|
+
logInternal(` - count: ${count}, alias: ${alias}`);
|
|
676
|
+
const normalizedPath = normalizeWindowsPath(path2);
|
|
677
|
+
logInternal(` - normalized: ${normalizedPath}`);
|
|
678
|
+
const parsed = parse(normalizedPath);
|
|
679
|
+
logInternal(` - parsed: ${JSON.stringify(parsed)}`);
|
|
680
|
+
let pathSegments = [];
|
|
681
|
+
if (parsed.dir && parsed.dir !== parsed.root) {
|
|
682
|
+
let dirRelativeToRoot = parsed.dir;
|
|
683
|
+
if (parsed.root && parsed.dir.startsWith(parsed.root)) {
|
|
684
|
+
dirRelativeToRoot = parsed.dir.substring(parsed.root.length);
|
|
685
|
+
}
|
|
686
|
+
pathSegments.push(...dirRelativeToRoot.split(SLASH));
|
|
687
|
+
}
|
|
688
|
+
if (parsed.base) {
|
|
689
|
+
pathSegments.push(parsed.base);
|
|
690
|
+
}
|
|
691
|
+
pathSegments = pathSegments.filter(Boolean);
|
|
692
|
+
logInternal(` - initial segments: ${JSON.stringify(pathSegments)}`);
|
|
693
|
+
const leadingPreservedSegments = [];
|
|
694
|
+
if (alias && pathSegments.length > 0 && pathSegments[0].startsWith(alias)) {
|
|
695
|
+
const preserved = pathSegments.shift();
|
|
696
|
+
leadingPreservedSegments.push(preserved);
|
|
697
|
+
logInternal(` - preserved alias segment: ${preserved}`);
|
|
698
|
+
}
|
|
699
|
+
while (pathSegments.length > 0 && (pathSegments[0] === DOT || pathSegments[0] === DOUBLE_DOT)) {
|
|
700
|
+
const preserved = pathSegments.shift();
|
|
701
|
+
leadingPreservedSegments.push(preserved);
|
|
702
|
+
logInternal(` - preserved relative segment: ${preserved}`);
|
|
703
|
+
}
|
|
704
|
+
const numToStrip = Math.min(count, pathSegments.length);
|
|
705
|
+
const remainingBodySegments = pathSegments.slice(numToStrip);
|
|
706
|
+
logInternal(
|
|
707
|
+
` - stripping ${numToStrip} segments from: ${JSON.stringify(pathSegments)}`
|
|
708
|
+
);
|
|
709
|
+
logInternal(
|
|
710
|
+
` - remaining body segments: ${JSON.stringify(remainingBodySegments)}`
|
|
711
|
+
);
|
|
712
|
+
const pathRoot = parsed.root;
|
|
713
|
+
const effectiveSegments = [
|
|
714
|
+
...leadingPreservedSegments,
|
|
715
|
+
...remainingBodySegments
|
|
716
|
+
];
|
|
717
|
+
logInternal(` - effective segments: ${JSON.stringify(effectiveSegments)}`);
|
|
718
|
+
let result;
|
|
719
|
+
if (effectiveSegments.length === 0) {
|
|
720
|
+
result = normalize(pathRoot || DOT);
|
|
721
|
+
} else if (pathRoot) {
|
|
722
|
+
result = join(pathRoot, ...effectiveSegments);
|
|
723
|
+
} else {
|
|
724
|
+
result = join(...effectiveSegments);
|
|
725
|
+
}
|
|
726
|
+
logInternal(` - final result: ${result}`);
|
|
727
|
+
return result;
|
|
728
|
+
}
|
|
729
|
+
async function stripPathSegmentsInDirectory({
|
|
730
|
+
targetDir,
|
|
731
|
+
segmentsToStrip,
|
|
732
|
+
alias = "",
|
|
733
|
+
extensionsToProcess = EXTENSIONS
|
|
734
|
+
}) {
|
|
735
|
+
log(`[stripPathSegmentsInDirectory] Processing directory: ${targetDir}`);
|
|
736
|
+
log(` - segmentsToStrip: ${segmentsToStrip}, alias: ${alias}`);
|
|
737
|
+
logInternal(` - extensions: ${JSON.stringify(extensionsToProcess)}`);
|
|
738
|
+
try {
|
|
739
|
+
const entries = await fs.readdir(targetDir, { withFileTypes: true });
|
|
740
|
+
const results = [];
|
|
741
|
+
await Promise.all(
|
|
742
|
+
entries.map(async (entry) => {
|
|
743
|
+
const fullPath = join(targetDir, entry.name);
|
|
744
|
+
if (entry.isDirectory()) {
|
|
745
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
746
|
+
logInternal(` - skipping directory: ${entry.name}`);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
logInternal(` - recursing into directory: ${entry.name}`);
|
|
750
|
+
const subdirResults = await stripPathSegmentsInDirectory({
|
|
751
|
+
targetDir: fullPath,
|
|
752
|
+
segmentsToStrip,
|
|
753
|
+
alias,
|
|
754
|
+
extensionsToProcess
|
|
755
|
+
});
|
|
756
|
+
results.push(...subdirResults);
|
|
757
|
+
} else if (extensionsToProcess.includes(extname(entry.name))) {
|
|
758
|
+
logInternal(` Processing file: ${entry.name}`);
|
|
759
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
760
|
+
let updated = content;
|
|
761
|
+
const changes = [];
|
|
762
|
+
const matches = Array.from(content.matchAll(IMPORT_REGEX));
|
|
763
|
+
logInternal(` - found ${matches.length} import statements`);
|
|
764
|
+
for (const match of matches) {
|
|
765
|
+
const originalQuote = match[1];
|
|
766
|
+
const importPath = match[2];
|
|
767
|
+
if (!importPath.includes(SLASH)) {
|
|
768
|
+
logInternal(` - skipping non-path import: ${importPath}`);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
logInternal(` Processing import: ${importPath}`);
|
|
772
|
+
const strippedPath = stripPathSegments(
|
|
773
|
+
importPath,
|
|
774
|
+
segmentsToStrip,
|
|
775
|
+
alias
|
|
776
|
+
);
|
|
777
|
+
if (importPath === strippedPath) {
|
|
778
|
+
logInternal(" - no changes needed");
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
changes.push({ from: importPath, to: strippedPath });
|
|
782
|
+
logInternal(` - transformed: ${importPath} \u2192 ${strippedPath}`);
|
|
783
|
+
const searchStr = `${originalQuote}${importPath}${originalQuote}`;
|
|
784
|
+
const replaceStr = `${originalQuote}${strippedPath}${originalQuote}`;
|
|
785
|
+
updated = replaceAllInString(updated, searchStr, replaceStr);
|
|
786
|
+
}
|
|
787
|
+
if (content !== updated) {
|
|
788
|
+
await fs.writeFile(fullPath, updated);
|
|
789
|
+
logInternal(" \u2713 wrote changes to file");
|
|
790
|
+
if (changes.length > 0) {
|
|
791
|
+
results.push({ file: fullPath, changes });
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
logInternal(" - no changes made to file");
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
798
|
+
}
|
|
799
|
+
})
|
|
800
|
+
);
|
|
801
|
+
if (results.length > 0) {
|
|
802
|
+
log("[stripPathSegmentsInDirectory] Summary of changes:");
|
|
803
|
+
for (const { file, changes } of results) {
|
|
804
|
+
const displayPath = relative(targetDir, file) || basename(file);
|
|
805
|
+
log(` in ${displayPath}:`);
|
|
806
|
+
for (const { from, to } of changes) {
|
|
807
|
+
log(` - ${from} \u2192 ${to}`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
} else {
|
|
811
|
+
logInternal(" No changes were made in any files");
|
|
812
|
+
}
|
|
813
|
+
return results;
|
|
814
|
+
} catch (error) {
|
|
815
|
+
log(
|
|
816
|
+
`error processing directory ${targetDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
817
|
+
);
|
|
818
|
+
return [];
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function attachPathSegments(path2, segments, options = {}) {
|
|
822
|
+
if (typeof path2 !== "string" || path2.length === 0) return path2;
|
|
823
|
+
const {
|
|
824
|
+
position = "after",
|
|
825
|
+
normalize: shouldNormalize = true,
|
|
826
|
+
ensureSlash = true,
|
|
827
|
+
preserveRoot = true,
|
|
828
|
+
preserveAlias
|
|
829
|
+
} = options;
|
|
830
|
+
const segmentsArray = Array.isArray(segments) ? segments : [segments];
|
|
831
|
+
const validSegments = segmentsArray.filter((s) => s.length > 0);
|
|
832
|
+
if (validSegments.length === 0) return path2;
|
|
833
|
+
const basePath = shouldNormalize ? normalizeWindowsPath(path2) : path2;
|
|
834
|
+
let alias = "";
|
|
835
|
+
let pathWithoutAlias = basePath;
|
|
836
|
+
if (preserveAlias && position === "before") {
|
|
837
|
+
const aliasMatch = new RegExp(`^${preserveAlias.replace("*", ".*")}`).exec(
|
|
838
|
+
basePath
|
|
839
|
+
);
|
|
840
|
+
if (aliasMatch) {
|
|
841
|
+
alias = aliasMatch[0];
|
|
842
|
+
pathWithoutAlias = basePath.slice(alias.length);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const isAbsolute2 = IS_ABSOLUTE_RE.test(pathWithoutAlias);
|
|
846
|
+
const root = preserveRoot && isAbsolute2 ? SLASH : "";
|
|
847
|
+
const pathWithoutRoot = isAbsolute2 ? pathWithoutAlias.slice(1) : pathWithoutAlias;
|
|
848
|
+
const joinedSegments = validSegments.join(SLASH);
|
|
849
|
+
let result;
|
|
850
|
+
if (position === "before") {
|
|
851
|
+
result = ensureSlash ? `${alias}${root}${joinedSegments}${SLASH}${pathWithoutRoot}` : `${alias}${root}${joinedSegments}${pathWithoutRoot}`;
|
|
852
|
+
} else {
|
|
853
|
+
result = ensureSlash ? `${alias}${root}${pathWithoutRoot}${SLASH}${joinedSegments}` : `${alias}${root}${pathWithoutRoot}${joinedSegments}`;
|
|
854
|
+
}
|
|
855
|
+
return shouldNormalize ? normalize(result) : result;
|
|
856
|
+
}
|
|
857
|
+
async function attachPathSegmentsInDirectory({
|
|
858
|
+
targetDir,
|
|
859
|
+
segments,
|
|
860
|
+
options = {},
|
|
861
|
+
extensionsToProcess = EXTENSIONS
|
|
862
|
+
}) {
|
|
863
|
+
try {
|
|
864
|
+
const entries = await fs.readdir(targetDir, { withFileTypes: true });
|
|
865
|
+
const results = [];
|
|
866
|
+
await Promise.all(
|
|
867
|
+
entries.map(async (entry) => {
|
|
868
|
+
const fullPath = join(targetDir, entry.name);
|
|
869
|
+
if (entry.isDirectory()) {
|
|
870
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const subdirResults = await attachPathSegmentsInDirectory({
|
|
874
|
+
targetDir: fullPath,
|
|
875
|
+
segments,
|
|
876
|
+
options,
|
|
877
|
+
extensionsToProcess
|
|
878
|
+
});
|
|
879
|
+
results.push(...subdirResults);
|
|
880
|
+
} else if (extensionsToProcess.includes(extname(entry.name))) {
|
|
881
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
882
|
+
let updated = content;
|
|
883
|
+
const changes = [];
|
|
884
|
+
const matches = Array.from(content.matchAll(IMPORT_REGEX));
|
|
885
|
+
for (const match of matches) {
|
|
886
|
+
const originalQuote = match[1];
|
|
887
|
+
const importPath = match[2];
|
|
888
|
+
if (!importPath.includes(SLASH)) continue;
|
|
889
|
+
const modifiedPath = attachPathSegments(
|
|
890
|
+
importPath,
|
|
891
|
+
segments,
|
|
892
|
+
options
|
|
893
|
+
);
|
|
894
|
+
if (importPath === modifiedPath) continue;
|
|
895
|
+
changes.push({ from: importPath, to: modifiedPath });
|
|
896
|
+
const searchStr = `${originalQuote}${importPath}${originalQuote}`;
|
|
897
|
+
const replaceStr = `${originalQuote}${modifiedPath}${originalQuote}`;
|
|
898
|
+
updated = replaceAllInString(updated, searchStr, replaceStr);
|
|
899
|
+
}
|
|
900
|
+
if (content !== updated) {
|
|
901
|
+
await fs.writeFile(fullPath, updated);
|
|
902
|
+
logInternal(`\u2713 processed: ${fullPath}`);
|
|
903
|
+
if (changes.length > 0) {
|
|
904
|
+
results.push({ file: fullPath, changes });
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
} else {
|
|
908
|
+
logInternal(` - skipping non-matching file: ${entry.name}`);
|
|
909
|
+
}
|
|
910
|
+
})
|
|
911
|
+
);
|
|
912
|
+
if (results.length > 0) {
|
|
913
|
+
log("\n[attachPathSegmentsInDirectory] Summary of changes:");
|
|
914
|
+
for (const { file, changes } of results) {
|
|
915
|
+
const displayPath = relative(targetDir, file) || basename(file);
|
|
916
|
+
log(` in ${displayPath}:`);
|
|
917
|
+
for (const { from, to } of changes) {
|
|
918
|
+
log(` - ${from} \u2192 ${to}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
654
922
|
return results;
|
|
655
923
|
} catch (error) {
|
|
656
924
|
log(
|
|
@@ -743,6 +1011,10 @@ export {
|
|
|
743
1011
|
copyDir,
|
|
744
1012
|
convertStringAliasRelative,
|
|
745
1013
|
convertImportsAliasToRelative,
|
|
746
|
-
convertImportsExt
|
|
1014
|
+
convertImportsExt,
|
|
1015
|
+
stripPathSegments,
|
|
1016
|
+
stripPathSegmentsInDirectory,
|
|
1017
|
+
attachPathSegments,
|
|
1018
|
+
attachPathSegmentsInDirectory
|
|
747
1019
|
};
|
|
748
1020
|
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.1",
|
|
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",
|