@reliverse/pathkit 1.2.0 → 1.2.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 CHANGED
@@ -1,247 +1,510 @@
1
- # pathkit • cross‑platform path manipulation
2
-
3
- > @reliverse/pathkit is a slash‑consistent, cross‑platform path manipulation, with POSIX forward slash, drop‑in for node:path and unjs/pathe. This library extends the node:path module with a set of functions for manipulating file paths.
4
-
5
- [sponsor](https://github.com/sponsors/blefnk) • [discord](https://discord.gg/Pb8uKbwpsJ) • [npm](https://npmjs.com/package/@reliverse/pathkit) • [repo](https://github.com/reliverse/pathkit)
6
-
7
- ## Key Features
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 better path api + couple of cool utilities = [4kB](https://bundlephobia.com/package/@reliverse/pathkit@latest)
16
-
17
- ## Installation
18
-
19
- ```bash
20
- # bun • pnpm • yarn • npm
21
- bun add @reliverse/pathkit
22
- ```
23
-
24
- **Migrate**:
25
-
26
- ```bash
27
- # soon:
28
- # bun add -D @reliverse/dler
29
- # bun dler migrate --lib path-to-pathkit
30
- # bun dler migrate --lib pathe-to-pathkit
31
- ```
32
-
33
- ### `unjs/pathe` vs `@reliverse/pathkit`
34
-
35
- | Package | What you get | When to use |
36
- |---------|--------------|-------------|
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
-
40
- ## Why Pathkit? — The Problem with Native Paths
41
-
42
- Native `node:path` flips behavior between operating systems, spurring subtle bugs and OS checks.
43
-
44
- ```js
45
- // With node:path – the same call may yield different separators on each OS
46
- import path from "node:path";
47
-
48
- const project = "users/blefnk/project";
49
- const full = path.join("C:\\", project);
50
- console.log(full); // "C:\\users\\blefnk\\project" (Windows) vs ??? (others)
51
- ```
52
-
53
- ### ✅ The `pathkit` Fix
54
-
55
- ```js
56
- import { join } from "@reliverse/pathkit";
57
-
58
- const full = join("C:", "users", "blefnk", "project");
59
- console.log(full); // "C:/users/blefnk/project" on **every** OS 🎉
60
- ```
61
-
62
- | Pain Point | `@reliverse/pathkit` Solution |
63
- | :----------------------------- | :--------------------------- |
64
- | Inconsistent separators | ✅ Always `/` |
65
- | OS‑specific work‑arounds | ✅ One code path |
66
- | Needs TypeScript + ESM | ✅ Built‑in |
67
- | Works in Bun / Deno / Node | ✅ Out of the box |
68
-
69
- ## Quick Start
70
-
71
- ```ts
72
- import { resolve, join, normalize } from "@reliverse/pathkit";
73
-
74
- // Mixed slashes & dot‑segments? No problem.
75
- const messy = "src\\..\\./dist///file.js";
76
- console.log(resolve(messy)); // → "dist/file.js"
77
-
78
- // Join is predictable everywhere:
79
- console.log(join("users", "blefnk")); // → "users/blefnk"
80
- ```
81
-
82
- **Side‑by‑Side Demo**:
83
-
84
- | Code | Windows Output | macOS / Linux Output |
85
- |------|----------------|----------------------|
86
- | `join("a", "b")` | `a/b` | `a/b` |
87
- | `resolve("..", "x")` | `x` | `x` |
88
-
89
- Say goodbye to `process.platform` conditionals 👋.
90
-
91
- ## pathkit advanced features
92
-
93
- `@reliverse/pathkit` extends the core functionality of `node:path` with powerful utilities for working with imports, aliases, and more.
94
-
95
- ### Import/Export Analysis
96
-
97
- The `getFileImportsExports` function provides detailed analysis of ES module imports and exports:
98
-
99
- ```ts
100
- import { getFileImportsExports } from "@reliverse/pathkit";
101
-
102
- const code = `
103
- import { ref } from "vue";
104
- import utils from "@/utils";
105
- import type { Config } from "./types";
106
- export { default as MyComponent } from "./MyComponent";
107
- `;
108
-
109
- const analysis = getFileImportsExports(code, {
110
- kind: "all", // "import" | "export" | "all"
111
- pathTypes: ["alias"], // Filter by path types
112
- limitPerType: 2 // Limit results per type
113
- });
114
- ```
115
-
116
- The analysis provides rich information about each import/export:
117
-
118
- ```ts
119
- interface ImportExportInfo {
120
- statement: string; // Full original statement
121
- type: "static" | "dynamic"; // Import type
122
- kind: "import" | "export"; // Statement kind
123
- source?: string; // Import/export source path
124
- pathType?: "alias" | "relative" | "absolute" | "bare" | "module";
125
- pathTypeSymbol?: string; // Path prefix (e.g., "@/", "~/")
126
- isTypeOnly?: boolean; // Type-only import/export
127
- specifiers?: { // Imported/exported items
128
- type: "named" | "default" | "namespace" | "all";
129
- name: string;
130
- alias?: string;
131
- isType?: boolean;
132
- }[];
133
- start: number; // Position in source
134
- end: number;
135
- }
136
- ```
137
-
138
- Features:
139
-
140
- - ✨ Handles all import/export syntax variants
141
- - 🔍 Type imports/exports support
142
- - 💬 Preserves comments
143
- - 🎯 Path type detection
144
- - 🔄 Multi-line statement support
145
-
146
- ### Path Transformation
147
-
148
- Convert between different path formats:
149
-
150
- ```ts
151
- import { convertImportPaths } from "@reliverse/pathkit";
152
-
153
- await convertImportPaths({
154
- baseDir: "./src",
155
- fromType: "relative", // "./components/Button"
156
- toType: "alias", // "@/components/Button"
157
- aliasPrefix: "@/",
158
- generateSourceMap: true
159
- });
160
- ```
161
-
162
- ### Extension Conversion
163
-
164
- ```ts
165
- import { convertImportExtensionsJsToTs } from "@reliverse/pathkit";
166
-
167
- await convertImportExtensionsJsToTs({
168
- dirPath: "./src",
169
- generateSourceMap: true
170
- });
171
- ```
172
-
173
- ### Alias Resolution
174
-
175
- ```ts
176
- import {
177
- normalizeAliases,
178
- resolveAlias,
179
- reverseResolveAlias
180
- } from "@reliverse/pathkit";
181
-
182
- const aliases = { "@/": "/src/", "~/": "/home/user/" };
183
-
184
- // Normalize alias config
185
- console.log(normalizeAliases(aliases));
186
-
187
- // Resolve alias to absolute path
188
- console.log(resolveAlias("@/components", aliases)); // "/src/components"
189
-
190
- // Convert absolute path back to alias
191
- console.log(reverseResolveAlias("/src/utils", aliases)); // "@/utils"
192
- ```
193
-
194
- ### Utility Functions
195
-
196
- ```ts
197
- import {
198
- filename, // Strip extension
199
- normalizeQuotes, // Standardize quote style
200
- matchesGlob // Test glob patterns
201
- } from "@reliverse/pathkit";
202
-
203
- console.log(filename("/path/component.vue")); // "component"
204
- console.log(normalizeQuotes("import 'pkg'")); // 'import "pkg"'
205
- console.log(matchesGlob("file.ts", "**/*.ts")); // true
206
- ```
207
-
208
- ## Use Cases / Ideal For
209
-
210
- - 🛠️ **CLI tools**
211
- - 🌍 **Cross‑platform dev environments**
212
- - 🔄 **Bundlers, linters, compilers**
213
- - 🏗️ **Framework & library authors**
214
- - 📜 **Scripts / test runners**
215
- - …anywhere file‑paths roam!
216
-
217
- ## Examples & Contributing
218
-
219
- ```bash
220
- git clone https://github.com/reliverse/pathkit.git
221
- cd pathkit
222
- bun install
223
- bun dev
224
- ```
225
-
226
- Bug reports & PRs are warmly welcome—come on in!
227
-
228
- ## Related
229
-
230
- - [`@reliverse/rempts`](https://npmjs.com/package/@reliverse/rempts) – Terminal Prompts Engine
231
-
232
- ## Community
233
-
234
- - **Star** the repo if this helped you.
235
- - 💖 **Sponsor** [@blefnk](https://github.com/sponsors/blefnk) to keep the lights on.
236
- - 💬 **Chat** with us on [Discord](https://discord.gg/Pb8uKbwpsJ).
237
-
238
- ## License
239
-
240
- [MIT](LICENSE) © [Nazar Kornienko (blefnk)](https://github.com/blefnk), [Reliverse](https://github.com/reliverse)
241
-
242
- ## Badges
243
-
244
- [![npm](https://img.shields.io/npm/v/@reliverse/pathkit?label=npm%20v)](https://npmjs.com/package/@reliverse/pathkit)
245
- [![downloads](https://img.shields.io/npm/dm/@reliverse/pathkit.svg?color=brightgreen)](https://npmjs.com/package/@reliverse/pathkit)
246
- [![typescript](https://img.shields.io/badge/typed-%E2%9C%85-blue)](https://github.com/reliverse/pathkit)
247
- [![license](https://img.shields.io/npm/l/@reliverse/pathkit.svg)](LICENSE)
1
+ # pathkit • cross‑platform path manipulation
2
+
3
+ > @reliverse/pathkit is a slash‑consistent, cross‑platform path manipulation, with POSIX forward slash, drop‑in for node:path and unjs/pathe. This library extends the node:path module with a set of functions for manipulating file paths.
4
+
5
+ [sponsor](https://github.com/sponsors/blefnk) • [discord](https://discord.gg/Pb8uKbwpsJ) • [npm](https://npmjs.com/package/@reliverse/pathkit) • [repo](https://github.com/reliverse/pathkit)
6
+
7
+ ## Key Features
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 better path api + couple of cool utilities = [4kB](https://bundlephobia.com/package/@reliverse/pathkit@latest)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # bun • pnpm • yarn • npm
21
+ bun add @reliverse/pathkit
22
+ ```
23
+
24
+ **Migrate**:
25
+
26
+ ```bash
27
+ # soon:
28
+ # bun add -D @reliverse/dler
29
+ # bun dler migrate --lib path-to-pathkit
30
+ # bun dler migrate --lib pathe-to-pathkit
31
+ ```
32
+
33
+ ### `unjs/pathe` vs `@reliverse/pathkit`
34
+
35
+ | Package | What you get | When to use |
36
+ |---------|--------------|-------------|
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
+
40
+ ## Why Pathkit? — The Problem with Native Paths
41
+
42
+ Native `node:path` flips behavior between operating systems, spurring subtle bugs and OS checks.
43
+
44
+ ```js
45
+ // With node:path – the same call may yield different separators on each OS
46
+ import path from "node:path";
47
+
48
+ const project = "users/blefnk/project";
49
+ const full = path.join("C:\\", project);
50
+ console.log(full); // "C:\\users\\blefnk\\project" (Windows) vs ??? (others)
51
+ ```
52
+
53
+ ### ✅ The `pathkit` Fix
54
+
55
+ ```js
56
+ import { join } from "@reliverse/pathkit";
57
+
58
+ const full = join("C:", "users", "blefnk", "project");
59
+ console.log(full); // "C:/users/blefnk/project" on **every** OS 🎉
60
+ ```
61
+
62
+ | Pain Point | `@reliverse/pathkit` Solution |
63
+ | :----------------------------- | :--------------------------- |
64
+ | Inconsistent separators | ✅ Always `/` |
65
+ | OS‑specific work‑arounds | ✅ One code path |
66
+ | Needs TypeScript + ESM | ✅ Built‑in |
67
+ | Works in Bun / Deno / Node | ✅ Out of the box |
68
+
69
+ ## Quick Start
70
+
71
+ ```ts
72
+ import { resolve, join, normalize } from "@reliverse/pathkit";
73
+
74
+ // Mixed slashes & dot‑segments? No problem.
75
+ const messy = "src\\..\\./dist///file.js";
76
+ console.log(resolve(messy)); // → "dist/file.js"
77
+
78
+ // Join is predictable everywhere:
79
+ console.log(join("users", "blefnk")); // → "users/blefnk"
80
+ ```
81
+
82
+ **Side‑by‑Side Demo**:
83
+
84
+ | Code | Windows Output | macOS / Linux Output |
85
+ |------|----------------|----------------------|
86
+ | `join("a", "b")` | `a/b` | `a/b` |
87
+ | `resolve("..", "x")` | `x` | `x` |
88
+
89
+ Say goodbye to `process.platform` conditionals 👋.
90
+
91
+ ## pathkit advanced features
92
+
93
+ `@reliverse/pathkit` extends the core functionality of `node:path` with powerful utilities for working with imports, aliases, and more.
94
+
95
+ ### Import/Export Analysis
96
+
97
+ The `getFileImportsExports` function provides detailed analysis of ES module imports and exports in your code:
98
+
99
+ ```ts
100
+ import { getFileImportsExports } from "@reliverse/pathkit";
101
+
102
+ const code = `
103
+ import { ref } from "vue";
104
+ import utils from "@/utils";
105
+ import type { Config } from "./types";
106
+ import * as React from "react";
107
+ import { Button as UIButton } from "./components";
108
+ export { default as MyComponent } from "./MyComponent";
109
+ export type { Props } from "./types";
110
+ `;
111
+
112
+ const analysis = getFileImportsExports(code, {
113
+ kind: "all", // "import" | "export" | "all"
114
+ pathTypes: ["alias"], // Filter by path types: "alias" | "relative" | "absolute" | "bare" | "module"
115
+ limitPerType: 2 // Limit results per type
116
+ });
117
+ ```
118
+
119
+ The analysis provides rich information about each import/export statement:
120
+
121
+ ```ts
122
+ interface ImportExportInfo {
123
+ statement: string; // Full original statement
124
+ type: "static" | "dynamic"; // Import type (static or dynamic import())
125
+ kind: "import" | "export"; // Statement kind
126
+ source?: string; // Import/export source path
127
+ pathType?: "alias" | "relative" | "absolute" | "bare" | "module";
128
+ pathTypeSymbol?: string; // Path prefix (e.g., "@/", "~/")
129
+ isTypeOnly?: boolean; // Type-only import/export
130
+ specifiers?: { // Imported/exported items
131
+ type: "named" | "default" | "namespace" | "all";
132
+ name: string;
133
+ alias?: string;
134
+ isType?: boolean;
135
+ }[];
136
+ start: number; // Position in source
137
+ end: number;
138
+ importExt?: string; // Extension as written in import/export statement
139
+ realFileExt?: string; // Likely actual file extension (e.g., .ts for .js imports)
140
+ }
141
+ ```
142
+
143
+ Features:
144
+
145
+ - ✨ **Comprehensive Syntax Support**
146
+ - Static imports (`import x from "y"`)
147
+ - Dynamic imports (`import("y")`)
148
+ - Named imports/exports (`import { x } from "y"`)
149
+ - Default imports/exports (`import x from "y"`)
150
+ - Namespace imports (`import * as x from "y"`)
151
+ - Re-exports (`export * from "y"`)
152
+ - Type imports/exports (`import type { x } from "y"`)
153
+
154
+ - 🔍 **Path Analysis**
155
+ - Detects path types (alias, relative, absolute, bare, module)
156
+ - Extracts path prefixes (e.g., `@/`, `~/`)
157
+ - Preserves original path format
158
+ - Tracks both import statement extensions and likely real file extensions
159
+ - Handles TypeScript/JavaScript extension conversion (e.g., `.js` → `.ts`)
160
+
161
+ - 🎯 **Specifier Details**
162
+ - Named imports/exports with aliases
163
+ - Default imports/exports
164
+ - Namespace imports
165
+ - Type-only imports/exports
166
+ - Mixed type and value imports
167
+
168
+ - 📊 **Filtering Options**
169
+ - Filter by statement kind (import/export)
170
+ - Filter by path types
171
+ - Limit results per type
172
+ - Preserve statement order
173
+
174
+ - 🛡️ **Type Safety**
175
+ - Full TypeScript support
176
+ - Detailed type definitions
177
+ - Null-safe operations
178
+
179
+ Example output:
180
+
181
+ ```ts
182
+ [
183
+ {
184
+ statement: 'import { ref } from "vue"',
185
+ type: "static",
186
+ kind: "import",
187
+ source: "vue",
188
+ pathType: "bare",
189
+ specifiers: [{
190
+ type: "named",
191
+ name: "ref"
192
+ }],
193
+ start: 0,
194
+ end: 24,
195
+ importExt: "",
196
+ realFileExt: ""
197
+ },
198
+ {
199
+ statement: 'import type { Config } from "./types.js"',
200
+ type: "static",
201
+ kind: "import",
202
+ source: "./types.js",
203
+ pathType: "relative",
204
+ isTypeOnly: true,
205
+ specifiers: [{
206
+ type: "named",
207
+ name: "Config",
208
+ isType: true
209
+ }],
210
+ start: 45,
211
+ end: 85,
212
+ importExt: ".js",
213
+ realFileExt: ".ts"
214
+ }
215
+ ]
216
+ ```
217
+
218
+ ### Path Transformation
219
+
220
+ Convert between different path formats:
221
+
222
+ ```ts
223
+ import { convertImportPaths } from "@reliverse/pathkit";
224
+
225
+ await convertImportPaths({
226
+ baseDir: "./src",
227
+ fromType: "relative", // "./components/Button"
228
+ toType: "alias", // "@/components/Button"
229
+ aliasPrefix: "@/",
230
+ generateSourceMap: true
231
+ });
232
+ ```
233
+
234
+ ### Extension Conversion
235
+
236
+ ```ts
237
+ import { convertImportsExt } from "@reliverse/pathkit";
238
+
239
+ // Basic usage - convert all relative imports to .ts
240
+ await convertImportsExt({
241
+ targetDir: "./src",
242
+ extFrom: "none",
243
+ extTo: "ts"
244
+ });
245
+
246
+ // Convert .js to .ts
247
+ await convertImportsExt({
248
+ targetDir: "./src",
249
+ extFrom: "js",
250
+ extTo: "ts"
251
+ });
252
+
253
+ // Remove extensions
254
+ await convertImportsExt({
255
+ targetDir: "./src",
256
+ extFrom: "ts",
257
+ extTo: "none"
258
+ });
259
+
260
+ // Handle alias paths (e.g. @/components)
261
+ await convertImportsExt({
262
+ targetDir: "./src",
263
+ extFrom: "none",
264
+ extTo: "ts",
265
+ alias: "@" // or "@/*"
266
+ });
267
+ ```
268
+
269
+ The function intelligently handles different import types:
270
+
271
+ - ✅ Relative imports (`./file`, `../file`)
272
+ - ✅ Alias imports (when alias is specified)
273
+ - ✅ Package imports (`lodash`, `@scope/pkg`)
274
+ - ✅ Node built-ins (`node:path`, `node:fs`)
275
+ - ✅ URLs (`http://`, `https://`)
276
+ - ✅ Already processed paths
277
+
278
+ Features:
279
+
280
+ - 🔄 Recursively processes directories
281
+ - 🎯 Preserves package imports
282
+ - 🛡️ Safe for code generation
283
+ - 📝 Detailed change logging
284
+ - 🎨 Supports custom aliases
285
+
286
+ ### Alias Resolution
287
+
288
+ Advanced alias handling and resolution:
289
+
290
+ ```ts
291
+ import {
292
+ normalizeAliases,
293
+ resolveAlias,
294
+ reverseResolveAlias
295
+ } from "@reliverse/pathkit";
296
+
297
+ const aliases = { "@/": "/src/", "~/": "/home/user/" };
298
+
299
+ // Normalize alias config
300
+ console.log(normalizeAliases(aliases));
301
+
302
+ // Resolve alias to absolute path
303
+ console.log(resolveAlias("@/components", aliases)); // "/src/components"
304
+
305
+ // Convert absolute path back to alias
306
+ console.log(reverseResolveAlias("/src/utils", aliases)); // "@/utils"
307
+ ```
308
+
309
+ ```ts
310
+ import {
311
+ normalizeAliases,
312
+ resolveAlias,
313
+ reverseResolveAlias,
314
+ findAliasMatch
315
+ } from "@reliverse/pathkit";
316
+
317
+ // Normalize and optimize alias configurations
318
+ const aliases = {
319
+ "@/": "/src/",
320
+ "~/": "/home/user/",
321
+ "@/components/": "/src/components/" // Nested alias
322
+ };
323
+ const normalized = normalizeAliases(aliases);
324
+
325
+ // Resolve aliased paths
326
+ resolveAlias("@/components/Button", aliases); // "/src/components/Button"
327
+
328
+ // Convert absolute paths back to aliases
329
+ reverseResolveAlias("/src/utils", aliases); // ["@/utils"]
330
+
331
+ // Find matching alias in tsconfig-style paths
332
+ const paths = {
333
+ "@/*": ["./src/*"],
334
+ "~/*": ["./home/*"]
335
+ };
336
+ findAliasMatch("@/components/Button", paths);
337
+ ```
338
+
339
+ ### Path Conversion
340
+
341
+ Convert between different path formats:
342
+
343
+ ```ts
344
+ import {
345
+ convertStringAliasRelative,
346
+ convertImportsAliasToRelative
347
+ } from "@reliverse/pathkit";
348
+
349
+ // Convert a single aliased path to relative
350
+ await convertStringAliasRelative({
351
+ importPath: "@/components/Button",
352
+ importerFile: "src/pages/Home.tsx",
353
+ pathPattern: "@/*",
354
+ targetDir: "src"
355
+ });
356
+
357
+ // Convert all aliased imports to relative in a directory
358
+ await convertImportsAliasToRelative({
359
+ targetDir: "./src",
360
+ aliasToReplace: "@",
361
+ pathExtFilter: "js-ts-none" // "js" | "ts" | "none" | "js-ts-none"
362
+ });
363
+ ```
364
+
365
+ ### Platform-Specific Features
366
+
367
+ Handle platform-specific path operations:
368
+
369
+ ```ts
370
+ import { posix, win32 } from "@reliverse/pathkit";
371
+
372
+ // Use platform-specific path handling
373
+ const path = process.platform === "win32" ? win32 : posix;
374
+
375
+ // Windows-specific features
376
+ win32.toNamespacedPath("C:\\path\\to\\file"); // "\\\\?\\C:\\path\\to\\file"
377
+ win32.delimiter; // ";"
378
+
379
+ // POSIX-specific features
380
+ posix.delimiter; // ":"
381
+ ```
382
+
383
+ ### Utility Functions
384
+
385
+ ```ts
386
+ import {
387
+ filename, // Strip extension
388
+ normalizeQuotes, // Standardize quote style
389
+ matchesGlob // Test glob patterns
390
+ } from "@reliverse/pathkit";
391
+
392
+ console.log(filename("/path/component.vue")); // "component"
393
+ console.log(normalizeQuotes("import 'pkg'")); // 'import "pkg"'
394
+ console.log(matchesGlob("file.ts", "**/*.ts")); // true
395
+ ```
396
+
397
+ ```ts
398
+ import {
399
+ filename,
400
+ normalizeWindowsPath,
401
+ replaceAllInString
402
+ } from "@reliverse/pathkit";
403
+
404
+ // Get filename without extension
405
+ filename("/path/to/file.ts"); // "file"
406
+
407
+ // Normalize Windows paths
408
+ normalizeWindowsPath("C:\\path\\to\\file"); // "C:/path/to/file"
409
+
410
+ // Replace strings while tracking position
411
+ replaceAllInString("import x from 'y'", "'y'", "'z'");
412
+ ```
413
+
414
+ ### Supported File Extensions
415
+
416
+ The library supports the following file extensions by default:
417
+
418
+ ```ts
419
+ const EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
420
+ ```
421
+
422
+ ### Type Definitions
423
+
424
+ ```ts
425
+ type PathExtFilter = "js" | "ts" | "none" | "js-ts-none";
426
+ type ImportExtType = "js" | "ts" | "none";
427
+ ```
428
+
429
+ ## Use Cases / Ideal For
430
+
431
+ - 🛠️ **CLI tools**
432
+ - 🌍 **Cross‑platform dev environments**
433
+ - 🔄 **Bundlers, linters, compilers**
434
+ - 🏗️ **Framework & library authors**
435
+ - 📜 **Scripts / test runners**
436
+ - …anywhere file‑paths roam!
437
+
438
+ ## Examples & Contributing
439
+
440
+ ```bash
441
+ git clone https://github.com/reliverse/pathkit.git
442
+ cd pathkit
443
+ bun install
444
+ bun dev
445
+ ```
446
+
447
+ Bug reports & PRs are warmly welcome—come on in!
448
+
449
+ ## Related
450
+
451
+ - [`@reliverse/rempts`](https://npmjs.com/package/@reliverse/rempts) – Terminal Prompts Engine
452
+
453
+ ## Community
454
+
455
+ - ⭐ **Star** the repo if this helped you.
456
+ - 💖 **Sponsor** [@blefnk](https://github.com/sponsors/blefnk) to keep the lights on.
457
+ - 💬 **Chat** with us on [Discord](https://discord.gg/Pb8uKbwpsJ).
458
+
459
+ ## License
460
+
461
+ [MIT](LICENSE) © [Nazar Kornienko (blefnk)](https://github.com/blefnk), [Reliverse](https://github.com/reliverse)
462
+
463
+ ## Badges
464
+
465
+ [![npm](https://img.shields.io/npm/v/@reliverse/pathkit?label=npm%20v)](https://npmjs.com/package/@reliverse/pathkit)
466
+ [![downloads](https://img.shields.io/npm/dm/@reliverse/pathkit.svg?color=brightgreen)](https://npmjs.com/package/@reliverse/pathkit)
467
+ [![typescript](https://img.shields.io/badge/typed-%E2%9C%85-blue)](https://github.com/reliverse/pathkit)
468
+ [![license](https://img.shields.io/npm/l/@reliverse/pathkit.svg)](LICENSE)
469
+
470
+ ### Path Segment Manipulation
471
+
472
+ Manipulate path segments in import statements:
473
+
474
+ ```ts
475
+ import {
476
+ stripPathSegments,
477
+ stripPathSegmentsInDirectory,
478
+ attachPathSegments,
479
+ attachPathSegmentsInDirectory
480
+ } from "@reliverse/pathkit";
481
+
482
+ // Strip segments from a path
483
+ stripPathSegments("src/components/Button.tsx", 1); // "components/Button.tsx"
484
+
485
+ // Strip segments from imports in a directory
486
+ await stripPathSegmentsInDirectory({
487
+ targetDir: "./src",
488
+ segmentsToStrip: 1,
489
+ alias: "@" // Optional: preserve alias prefix
490
+ });
491
+
492
+ // Attach segments to a path
493
+ attachPathSegments("Button.tsx", "components", {
494
+ position: "before", // "before" | "after"
495
+ normalize: true, // Normalize the path
496
+ ensureSlash: true, // Ensure slash between segments
497
+ preserveRoot: true, // Preserve root in absolute paths
498
+ preserveAlias: "@" // Optional: preserve alias prefix
499
+ });
500
+
501
+ // Attach segments to imports in a directory
502
+ await attachPathSegmentsInDirectory({
503
+ targetDir: "./src",
504
+ segments: "components",
505
+ options: {
506
+ position: "before",
507
+ preserveAlias: "@"
508
+ }
509
+ });
510
+ ```
@@ -15,6 +15,8 @@ export interface ImportExportInfo {
15
15
  specifiers?: ImportExportSpecifier[];
16
16
  start: number;
17
17
  end: number;
18
+ importExt?: string;
19
+ realFileExt?: string;
18
20
  }
19
21
  export interface GetFileImportsExportsOptions {
20
22
  kind?: "import" | "export" | "all";
@@ -5,6 +5,35 @@ export function getFileImportsExports(content, options = {}) {
5
5
  limitPerType
6
6
  } = options;
7
7
  const results = [];
8
+ function getImportExtension(path) {
9
+ if (!path) return "";
10
+ const lastDotIndex = path.lastIndexOf(".");
11
+ if (lastDotIndex <= 0 || lastDotIndex === path.length - 1) {
12
+ return "";
13
+ }
14
+ const extension = path.slice(lastDotIndex);
15
+ if (path.endsWith(".d.ts")) {
16
+ return ".d.ts";
17
+ }
18
+ return extension;
19
+ }
20
+ function getRealFileExtension(path, pathType) {
21
+ if (pathType === "bare" || pathType === "alias") {
22
+ return "";
23
+ }
24
+ if (pathType === "module") {
25
+ return getImportExtension(path);
26
+ }
27
+ const pathExt = getImportExtension(path);
28
+ if (!pathExt) return "";
29
+ if (pathExt === ".js" || pathExt === ".jsx") {
30
+ return pathExt === ".jsx" ? ".tsx" : ".ts";
31
+ }
32
+ if (pathExt === ".ts" || pathExt === ".tsx") {
33
+ return pathExt;
34
+ }
35
+ return pathExt;
36
+ }
8
37
  const patterns = {
9
38
  // import statements with from clause
10
39
  staticImport: /import\s+(?:type\s+)?(?:(?:\w+)(?:\s*,\s*)?)?(?:\{[^}]*\}|\*\s+as\s+\w+)?\s+from\s+['"]([^'"]+)['"]|import\s+(?:type\s+)?(\w+)\s+from\s+['"]([^'"]+)['"]/g,
@@ -92,7 +121,9 @@ export function getFileImportsExports(content, options = {}) {
92
121
  isTypeOnly: match[0].includes("import type"),
93
122
  specifiers: extractSpecifiers(match[0]),
94
123
  start: match.index ?? 0,
95
- end: (match.index ?? 0) + match[0].length
124
+ end: (match.index ?? 0) + match[0].length,
125
+ importExt: getImportExtension(source),
126
+ realFileExt: getRealFileExtension(source, pathType)
96
127
  };
97
128
  results.push(info);
98
129
  }
@@ -112,7 +143,9 @@ export function getFileImportsExports(content, options = {}) {
112
143
  isTypeOnly: false,
113
144
  specifiers: [],
114
145
  start: match.index ?? 0,
115
- end: (match.index ?? 0) + match[0].length
146
+ end: (match.index ?? 0) + match[0].length,
147
+ importExt: getImportExtension(source),
148
+ realFileExt: getRealFileExtension(source, pathType)
116
149
  };
117
150
  results.push(info);
118
151
  }
@@ -134,7 +167,9 @@ export function getFileImportsExports(content, options = {}) {
134
167
  isTypeOnly: match[0].includes("export type"),
135
168
  specifiers: extractSpecifiers(match[0]),
136
169
  start: match.index ?? 0,
137
- end: (match.index ?? 0) + match[0].length
170
+ end: (match.index ?? 0) + match[0].length,
171
+ importExt: getImportExtension(source),
172
+ realFileExt: getRealFileExtension(source, pathType)
138
173
  };
139
174
  results.push(info);
140
175
  }
@@ -150,7 +185,9 @@ export function getFileImportsExports(content, options = {}) {
150
185
  isTypeOnly: false,
151
186
  specifiers: [{ type: "default", name: "default" }],
152
187
  start: match.index,
153
- end: match.index + match[0].length
188
+ end: match.index + match[0].length,
189
+ importExt: void 0,
190
+ realFileExt: void 0
154
191
  };
155
192
  results.push(info);
156
193
  }
@@ -168,7 +205,9 @@ export function getFileImportsExports(content, options = {}) {
168
205
  isTypeOnly: match[0].includes("export type"),
169
206
  specifiers: [{ type: "named", name }],
170
207
  start: match.index,
171
- end: match.index + match[0].length
208
+ end: match.index + match[0].length,
209
+ importExt: void 0,
210
+ realFileExt: void 0
172
211
  };
173
212
  results.push(info);
174
213
  }
package/bin/mod.js CHANGED
@@ -600,6 +600,7 @@ async function convertImportsExt({
600
600
  return true;
601
601
  if (path2.startsWith("http://") || path2.startsWith("https://")) return true;
602
602
  if (extTo !== "none" && path2.endsWith(`.${extTo}`)) return true;
603
+ if (extFrom === "none" && /\.[^/]+$/.test(path2)) return true;
603
604
  return false;
604
605
  }
605
606
  try {
@@ -633,7 +634,7 @@ async function convertImportsExt({
633
634
  }
634
635
  let replacementPath;
635
636
  if (extFrom === "none") {
636
- replacementPath = importPath + toExtStr;
637
+ replacementPath = /\.[^/]+$/.test(importPath) ? importPath : importPath + toExtStr;
637
638
  } else if (extTo === "none") {
638
639
  replacementPath = importPath.slice(0, -fromExtStr.length);
639
640
  } else {
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "license": "MIT",
6
6
  "name": "@reliverse/pathkit",
7
7
  "type": "module",
8
- "version": "1.2.0",
8
+ "version": "1.2.1",
9
9
  "devDependencies": {},
10
10
  "exports": {
11
11
  ".": "./bin/mod.js"