@tkeron/tools 0.1.1 → 0.2.0

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/bun.lock CHANGED
@@ -7,18 +7,20 @@
7
7
  "@types/bun": "latest",
8
8
  },
9
9
  "peerDependencies": {
10
- "typescript": "^5.0.0",
10
+ "typescript": "^5.7.3",
11
11
  },
12
12
  },
13
13
  },
14
14
  "packages": {
15
- "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="],
15
+ "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
16
16
 
17
17
  "@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
18
18
 
19
- "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
19
+ "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
20
20
 
21
- "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
21
+ "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
22
+
23
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
22
24
 
23
25
  "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
24
26
 
package/package.json CHANGED
@@ -1,21 +1,26 @@
1
1
  {
2
2
  "name": "@tkeron/tools",
3
- "version": "0.1.1",
4
- "description": "Useful JavaScript utilities",
3
+ "version": "0.2.0",
4
+ "description": "Useful JavaScript utilities for Bun runtime",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
7
7
  "type": "module",
8
8
  "author": "tkeron",
9
9
  "license": "MIT",
10
+ "engines": {
11
+ "bun": ">=1.0.0"
12
+ },
10
13
  "devDependencies": {
11
14
  "@types/bun": "latest"
12
15
  },
13
16
  "peerDependencies": {
14
- "typescript": "^5.0.0"
17
+ "typescript": "^5.7.3"
15
18
  },
16
19
  "keywords": [
20
+ "bun",
17
21
  "utilities",
18
- "utils"
22
+ "utils",
23
+ "tools"
19
24
  ],
20
25
  "repository": {
21
26
  "url": "git@github.com:tkeron/tools.git"
@@ -0,0 +1,492 @@
1
+ import { describe, test, expect, beforeAll, afterAll, it } from "bun:test";
2
+ import { mkdirSync, writeFileSync, rmSync } from "fs";
3
+ import { join } from "path";
4
+ import { getPaths, getFilePaths, getDirectoryPaths } from "./getPaths";
5
+
6
+ const testDir = join(process.cwd(), "test-temp");
7
+
8
+ describe("getPaths Tests", () => {
9
+
10
+ beforeAll(() => {
11
+ mkdirSync(testDir, { recursive: true });
12
+
13
+ writeFileSync(join(testDir, "file1.txt"), "content");
14
+ writeFileSync(join(testDir, "file2.js"), "content");
15
+ writeFileSync(join(testDir, "README.md"), "content");
16
+
17
+ mkdirSync(join(testDir, "src"), { recursive: true });
18
+ writeFileSync(join(testDir, "src", "index.ts"), "content");
19
+ writeFileSync(join(testDir, "src", "utils.ts"), "content");
20
+
21
+ mkdirSync(join(testDir, "docs"), { recursive: true });
22
+ writeFileSync(join(testDir, "docs", "guide.md"), "content");
23
+
24
+ mkdirSync(join(testDir, "empty-dir"), { recursive: true });
25
+
26
+ mkdirSync(join(testDir, "src", "components"), { recursive: true });
27
+ writeFileSync(join(testDir, "src", "components", "Button.tsx"), "content");
28
+
29
+ writeFileSync(join(testDir, ".env"), "content");
30
+ mkdirSync(join(testDir, ".git"), { recursive: true });
31
+ writeFileSync(join(testDir, ".git", "config"), "content");
32
+
33
+ writeFileSync(join(testDir, "file with spaces.txt"), "content");
34
+ writeFileSync(join(testDir, "file-no-extension"), "content");
35
+ writeFileSync(join(testDir, "UPPERCASE.TXT"), "content");
36
+ writeFileSync(join(testDir, "números-ñ-ü.js"), "content");
37
+
38
+ writeFileSync(join(testDir, "config.json"), "content");
39
+ writeFileSync(join(testDir, "styles.css"), "content");
40
+ writeFileSync(join(testDir, "script.jsx"), "content");
41
+
42
+ mkdirSync(join(testDir, "deep", "very", "nested", "structure"), { recursive: true });
43
+ writeFileSync(join(testDir, "deep", "very", "nested", "structure", "deep-file.txt"), "content");
44
+
45
+ mkdirSync(join(testDir, "node_modules"), { recursive: true });
46
+ writeFileSync(join(testDir, "node_modules", "package.json"), "content");
47
+ mkdirSync(join(testDir, "build"), { recursive: true });
48
+ writeFileSync(join(testDir, "build", "output.js"), "content");
49
+ });
50
+
51
+ afterAll(() => {
52
+ rmSync(testDir, { recursive: true, force: true });
53
+ });
54
+
55
+ describe("getPaths", () => {
56
+ test("should return both files and directories when includeDirectories is 'yes'", () => {
57
+ const result = getPaths(testDir, "**/*", "yes");
58
+
59
+ expect(result.length).toBeGreaterThan(0);
60
+ expect(result.some(path => path.includes("file1.txt"))).toBe(true);
61
+ expect(result.some(path => path.includes("src"))).toBe(true);
62
+ expect(result.some(path => path.includes("docs"))).toBe(true);
63
+ });
64
+
65
+ test("should return only files when includeDirectories is 'no'", () => {
66
+ const result = getPaths(testDir, "**/*", "no");
67
+
68
+ expect(result.length).toBeGreaterThan(0);
69
+ expect(result.some(path => path.includes("file1.txt"))).toBe(true);
70
+ expect(result.some(path => path.includes("index.ts"))).toBe(true);
71
+ expect(result.some(path => path.endsWith("src"))).toBe(false);
72
+ expect(result.some(path => path.endsWith("docs"))).toBe(false);
73
+ });
74
+
75
+ test("should return only directories when includeDirectories is 'onlyDirectories'", () => {
76
+ const result = getPaths(testDir, "**/*", "onlyDirectories");
77
+
78
+ expect(result.length).toBeGreaterThan(0);
79
+ expect(result.some(path => path.endsWith("src"))).toBe(true);
80
+ expect(result.some(path => path.endsWith("docs"))).toBe(true);
81
+ expect(result.some(path => path.endsWith("empty-dir"))).toBe(true);
82
+ expect(result.some(path => path.includes("file1.txt"))).toBe(false);
83
+ expect(result.some(path => path.includes("index.ts"))).toBe(false);
84
+ });
85
+
86
+ test("should use default includeDirectories value ('no')", () => {
87
+ const result = getPaths(testDir, "**/*");
88
+ const filesOnlyResult = getPaths(testDir, "**/*", "no");
89
+
90
+ expect(result).toEqual(filesOnlyResult);
91
+ });
92
+
93
+ test("should work with specific patterns", () => {
94
+ const result = getPaths(testDir, "*.txt", "no");
95
+
96
+ expect(result.some(path => path.includes("file1.txt"))).toBe(true);
97
+ expect(result.some(path => path.includes("file2.js"))).toBe(false);
98
+ expect(result.some(path => path.includes("README.md"))).toBe(false);
99
+ });
100
+
101
+ test("should include hidden files and directories", () => {
102
+ const allResult = getPaths(testDir, "**/*", "yes");
103
+
104
+ expect(allResult.some(path => path.includes(".env"))).toBe(true);
105
+ expect(allResult.some(path => path.includes(".git"))).toBe(true);
106
+ });
107
+
108
+ test("should return empty array for non-existent path", () => {
109
+ const result = getPaths("/non/existent/path", "**/*", "yes");
110
+
111
+ expect(result).toEqual([]);
112
+ });
113
+ });
114
+
115
+ describe("getFilePaths", () => {
116
+ test("should return only files", () => {
117
+ const result = getFilePaths(testDir, "**/*");
118
+
119
+ expect(result.length).toBeGreaterThan(0);
120
+ expect(result.some(path => path.includes("file1.txt"))).toBe(true);
121
+ expect(result.some(path => path.includes("index.ts"))).toBe(true);
122
+ expect(result.some(path => path.includes("Button.tsx"))).toBe(true);
123
+ expect(result.some(path => path.endsWith("src"))).toBe(false);
124
+ expect(result.some(path => path.endsWith("docs"))).toBe(false);
125
+ });
126
+
127
+ test("should work with specific file patterns", () => {
128
+ const jsFiles = getFilePaths(testDir, "**/*.js");
129
+ const tsFiles = getFilePaths(testDir, "**/*.ts");
130
+
131
+ expect(jsFiles.some(path => path.includes("file2.js"))).toBe(true);
132
+ expect(jsFiles.some(path => path.includes("file1.txt"))).toBe(false);
133
+
134
+ expect(tsFiles.some(path => path.includes("index.ts"))).toBe(true);
135
+ expect(tsFiles.some(path => path.includes("utils.ts"))).toBe(true);
136
+ });
137
+
138
+ test("should include hidden files", () => {
139
+ const result = getFilePaths(testDir, "**/*");
140
+
141
+ expect(result.some(path => path.includes(".env"))).toBe(true);
142
+ expect(result.some(path => path.includes(".git/config"))).toBe(true);
143
+ });
144
+
145
+ test("should return empty array for non-existent path", () => {
146
+ const result = getFilePaths("/non/existent/path", "**/*");
147
+
148
+ expect(result).toEqual([]);
149
+ });
150
+ });
151
+
152
+ describe("getDirectoryPaths", () => {
153
+ test("should return only directories", () => {
154
+ const result = getDirectoryPaths(testDir, "**/*");
155
+
156
+ expect(result.length).toBeGreaterThan(0);
157
+ expect(result.some(path => path.endsWith("src"))).toBe(true);
158
+ expect(result.some(path => path.endsWith("docs"))).toBe(true);
159
+ expect(result.some(path => path.endsWith("empty-dir"))).toBe(true);
160
+ expect(result.some(path => path.endsWith("components"))).toBe(true);
161
+ expect(result.some(path => path.includes("file1.txt"))).toBe(false);
162
+ expect(result.some(path => path.includes("index.ts"))).toBe(false);
163
+ });
164
+
165
+ test("should include hidden directories", () => {
166
+ const result = getDirectoryPaths(testDir, "**/*");
167
+
168
+ expect(result.some(path => path.includes(".git"))).toBe(true);
169
+ });
170
+
171
+ test("should work with directory patterns", () => {
172
+ const result = getDirectoryPaths(testDir, "src/**");
173
+
174
+ expect(result.some(path => path.endsWith("components"))).toBe(true);
175
+ expect(result.some(path => path.endsWith("docs"))).toBe(false);
176
+ });
177
+
178
+ test("should return empty array for non-existent path", () => {
179
+ const result = getDirectoryPaths("/non/existent/path", "**/*");
180
+
181
+ expect(result).toEqual([]);
182
+ });
183
+ });
184
+
185
+ describe("Edge cases", () => {
186
+ test("should handle empty directory", () => {
187
+ const emptyDirPath = join(testDir, "empty-dir");
188
+ const files = getFilePaths(emptyDirPath, "**/*");
189
+ const dirs = getDirectoryPaths(emptyDirPath, "**/*");
190
+
191
+ expect(files).toEqual([]);
192
+ expect(dirs).toEqual([]);
193
+ });
194
+
195
+ test("should handle invalid glob patterns gracefully", () => {
196
+ const result = getPaths(testDir, "[invalid", "yes");
197
+
198
+ expect(Array.isArray(result)).toBe(true);
199
+ });
200
+
201
+ test("all functions should return absolute paths by default", () => {
202
+ const files = getFilePaths(testDir, "**/*");
203
+ const dirs = getDirectoryPaths(testDir, "**/*");
204
+ const all = getPaths(testDir, "**/*", "yes");
205
+
206
+ files.forEach(path => {
207
+ expect(path).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
208
+ });
209
+
210
+ dirs.forEach(path => {
211
+ expect(path).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
212
+ });
213
+
214
+ all.forEach(path => {
215
+ expect(path).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
216
+ });
217
+ });
218
+
219
+ test("all functions should return relative paths when absolute is false", () => {
220
+ const files = getFilePaths(testDir, "**/*", false);
221
+ const dirs = getDirectoryPaths(testDir, "**/*", false);
222
+ const all = getPaths(testDir, "**/*", "yes", false);
223
+
224
+ files.forEach(path => {
225
+ expect(path).not.toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
226
+ expect(path.length).toBeGreaterThan(0);
227
+ });
228
+
229
+ dirs.forEach(path => {
230
+ expect(path).not.toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
231
+ expect(path.length).toBeGreaterThan(0);
232
+ });
233
+
234
+ all.forEach(path => {
235
+ expect(path).not.toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
236
+ expect(path.length).toBeGreaterThan(0);
237
+ });
238
+ });
239
+
240
+ test("relative paths should contain expected file and directory names", () => {
241
+ const files = getFilePaths(testDir, "**/*", false);
242
+ const dirs = getDirectoryPaths(testDir, "**/*", false);
243
+
244
+ expect(files.some(path => path === "file1.txt")).toBe(true);
245
+ expect(files.some(path => path === ".env")).toBe(true);
246
+ expect(files.some(path => path === "src/index.ts")).toBe(true);
247
+ expect(files.some(path => path === "src/components/Button.tsx")).toBe(true);
248
+
249
+ const nestedFiles = files.filter(path => path.includes("/"));
250
+ expect(nestedFiles.length).toBeGreaterThan(0);
251
+
252
+ expect(dirs.some(path => path === "src")).toBe(true);
253
+ expect(dirs.some(path => path === ".git")).toBe(true);
254
+
255
+ expect(dirs.length).toBeGreaterThan(0);
256
+ });
257
+
258
+ test("absolute parameter should work consistently across all function variants", () => {
259
+ const pattern = "src/**";
260
+
261
+ // Test with absolute = true
262
+ const absoluteFiles = getFilePaths(testDir, pattern, true);
263
+ const absoluteDirs = getDirectoryPaths(testDir, pattern, true);
264
+ const absoluteAll = getPaths(testDir, pattern, "yes", true);
265
+
266
+ // Test with absolute = false
267
+ const relativeFiles = getFilePaths(testDir, pattern, false);
268
+ const relativeDirs = getDirectoryPaths(testDir, pattern, false);
269
+ const relativeAll = getPaths(testDir, pattern, "yes", false);
270
+
271
+ expect(absoluteFiles.length).toBe(relativeFiles.length);
272
+ expect(absoluteAll.length).toBe(relativeAll.length);
273
+
274
+ expect(Array.isArray(absoluteDirs)).toBe(true);
275
+ expect(Array.isArray(relativeDirs)).toBe(true);
276
+
277
+ absoluteFiles.forEach(path => {
278
+ expect(path).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
279
+ });
280
+
281
+ relativeFiles.forEach(path => {
282
+ expect(path).not.toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
283
+ });
284
+ });
285
+ });
286
+
287
+ describe("Advanced Glob Patterns", () => {
288
+ test("should work with multiple extension patterns", () => {
289
+ const result = getFilePaths(testDir, "*.{js,ts,json}");
290
+
291
+ expect(result.some(path => path.includes("file2.js"))).toBe(true);
292
+ expect(result.some(path => path.includes("config.json"))).toBe(true);
293
+ expect(result.some(path => path.includes("números-ñ-ü.js"))).toBe(true);
294
+ expect(result.some(path => path.includes("file1.txt"))).toBe(false);
295
+ expect(result.some(path => path.includes("styles.css"))).toBe(false);
296
+ });
297
+
298
+ test("should work with exclusion patterns", () => {
299
+ const allFiles = getFilePaths(testDir, "**/*");
300
+ expect(allFiles.some(path => path.includes("node_modules"))).toBe(true);
301
+ expect(allFiles.some(path => path.includes("build"))).toBe(true);
302
+ });
303
+
304
+ test("should work with specific directory depth patterns", () => {
305
+ const topLevel = getFilePaths(testDir, "*");
306
+ expect(topLevel.some(path => path.includes("file1.txt"))).toBe(true);
307
+ expect(topLevel.some(path => path.includes("index.ts"))).toBe(false);
308
+
309
+ const srcFiles = getFilePaths(testDir, "src/*");
310
+ expect(srcFiles.some(path => path.includes("index.ts"))).toBe(true);
311
+ expect(srcFiles.some(path => path.includes("Button.tsx"))).toBe(false);
312
+ });
313
+
314
+ test("should work with character class patterns", () => {
315
+ const result = getFilePaths(testDir, "*[Ee]*");
316
+
317
+ expect(result.some(path => path.includes("README.md"))).toBe(true);
318
+ expect(result.some(path => path.includes(".env"))).toBe(true);
319
+ });
320
+
321
+ test("should work with case sensitive patterns", () => {
322
+ const upperFiles = getFilePaths(testDir, "*.TXT");
323
+ const lowerFiles = getFilePaths(testDir, "*.txt");
324
+
325
+ expect(upperFiles.some(path => path.includes("UPPERCASE.TXT"))).toBe(true);
326
+ expect(lowerFiles.some(path => path.includes("file1.txt"))).toBe(true);
327
+
328
+ expect(upperFiles).not.toEqual(lowerFiles);
329
+ });
330
+
331
+ test("should handle patterns that match nothing", () => {
332
+ const result = getFilePaths(testDir, "*.nonexistent");
333
+ expect(result).toEqual([]);
334
+
335
+ const dirs = getDirectoryPaths(testDir, "nonexistent-*");
336
+ expect(dirs).toEqual([]);
337
+ });
338
+
339
+ test("should work with deep nested patterns", () => {
340
+ const deepFiles = getFilePaths(testDir, "**/very/**/*.txt");
341
+ expect(deepFiles.some(path => path.includes("deep-file.txt"))).toBe(true);
342
+
343
+ const deepDirs = getDirectoryPaths(testDir, "**/very/**");
344
+ expect(deepDirs.some(path => path.endsWith("nested"))).toBe(true);
345
+ expect(deepDirs.some(path => path.endsWith("structure"))).toBe(true);
346
+ });
347
+ });
348
+
349
+ describe("Function Consistency", () => {
350
+ test("getPaths('yes') should equal getFilePaths() + getDirectoryPaths()", () => {
351
+ const pattern = "**/*";
352
+
353
+ const allPaths = getPaths(testDir, pattern, "yes");
354
+ const files = getFilePaths(testDir, pattern);
355
+ const dirs = getDirectoryPaths(testDir, pattern);
356
+
357
+ const combined = [...files, ...dirs].sort();
358
+ const allSorted = allPaths.sort();
359
+
360
+ expect(allSorted).toEqual(combined);
361
+ });
362
+
363
+ test("should not have duplicates in any function", () => {
364
+ const files = getFilePaths(testDir, "**/*");
365
+ const dirs = getDirectoryPaths(testDir, "**/*");
366
+ const all = getPaths(testDir, "**/*", "yes");
367
+
368
+ expect(files.length).toBe(new Set(files).size);
369
+ expect(dirs.length).toBe(new Set(dirs).size);
370
+ expect(all.length).toBe(new Set(all).size);
371
+ });
372
+
373
+ test("files and directories should be mutually exclusive", () => {
374
+ const files = getFilePaths(testDir, "**/*");
375
+ const dirs = getDirectoryPaths(testDir, "**/*");
376
+
377
+ const intersection = files.filter(file => dirs.includes(file));
378
+ expect(intersection).toEqual([]);
379
+ });
380
+
381
+ test("different patterns should be consistent across functions", () => {
382
+ const patterns = ["*.js", "src/**", "**/*.md", "**/components/*"];
383
+
384
+ patterns.forEach(pattern => {
385
+ const allPaths = getPaths(testDir, pattern, "yes");
386
+ const files = getFilePaths(testDir, pattern);
387
+ const dirs = getDirectoryPaths(testDir, pattern);
388
+
389
+ files.forEach(file => {
390
+ expect(allPaths).toContain(file);
391
+ });
392
+
393
+ dirs.forEach(dir => {
394
+ expect(allPaths).toContain(dir);
395
+ });
396
+ });
397
+ });
398
+
399
+ test("should maintain consistent behavior with default parameters", () => {
400
+ const result1 = getPaths(testDir);
401
+ const result2 = getPaths(testDir, "**/*");
402
+ const result3 = getPaths(testDir, "**/*", "no");
403
+ const result4 = getFilePaths(testDir);
404
+ const result5 = getFilePaths(testDir, "**/*");
405
+
406
+ expect(result1).toEqual(result2);
407
+ expect(result2).toEqual(result3);
408
+ expect(result3).toEqual(result4);
409
+ expect(result4).toEqual(result5);
410
+ });
411
+ });
412
+
413
+ describe("Special File Names", () => {
414
+ test("should handle files with spaces", () => {
415
+ const files = getFilePaths(testDir, "**/*");
416
+ expect(files.some(path => path.includes("file with spaces.txt"))).toBe(true);
417
+
418
+ const specific = getFilePaths(testDir, "*with*");
419
+ expect(specific.some(path => path.includes("file with spaces.txt"))).toBe(true);
420
+ });
421
+
422
+ test("should handle files without extensions", () => {
423
+ const files = getFilePaths(testDir, "**/*");
424
+ expect(files.some(path => path.includes("file-no-extension"))).toBe(true);
425
+
426
+ const withExt = getFilePaths(testDir, "*.txt");
427
+ expect(withExt.some(path => path.includes("file-no-extension"))).toBe(false);
428
+ });
429
+
430
+ test("should handle files with unicode characters", () => {
431
+ const files = getFilePaths(testDir, "**/*");
432
+ expect(files.some(path => path.includes("números-ñ-ü.js"))).toBe(true);
433
+
434
+ const jsFiles = getFilePaths(testDir, "*.js");
435
+ expect(jsFiles.some(path => path.includes("números-ñ-ü.js"))).toBe(true);
436
+ });
437
+
438
+ test("should handle uppercase/lowercase variations", () => {
439
+ const files = getFilePaths(testDir, "**/*");
440
+ expect(files.some(path => path.includes("UPPERCASE.TXT"))).toBe(true);
441
+ expect(files.some(path => path.includes("file1.txt"))).toBe(true);
442
+
443
+ // Should be case sensitive
444
+ const upper = getFilePaths(testDir, "*UPPER*");
445
+ const lower = getFilePaths(testDir, "*upper*");
446
+ expect(upper.length).toBeGreaterThan(0);
447
+ expect(lower).toEqual([]);
448
+ });
449
+
450
+ test("should handle various file extensions", () => {
451
+ const extensions = ["js", "ts", "tsx", "json", "css", "jsx", "md", "txt"];
452
+
453
+ extensions.forEach(ext => {
454
+ const files = getFilePaths(testDir, `*.${ext}`);
455
+ expect(Array.isArray(files)).toBe(true);
456
+ });
457
+
458
+ expect(getFilePaths(testDir, "*.css").some(path => path.includes("styles.css"))).toBe(true);
459
+ expect(getFilePaths(testDir, "*.jsx").some(path => path.includes("script.jsx"))).toBe(true);
460
+ });
461
+
462
+ test("should handle deeply nested paths correctly", () => {
463
+ const deepFiles = getFilePaths(testDir, "**/structure/*");
464
+ expect(deepFiles.some(path => path.includes("deep-file.txt"))).toBe(true);
465
+
466
+ const deepDirs = getDirectoryPaths(testDir, "**/very/*");
467
+ expect(deepDirs.some(path => path.endsWith("nested"))).toBe(true);
468
+
469
+ const allDeep = getPaths(testDir, "deep/**", "yes");
470
+ expect(allDeep.length).toBeGreaterThanOrEqual(4);
471
+ });
472
+
473
+ test("should normalize paths consistently", () => {
474
+ const files = getFilePaths(testDir, "**/*");
475
+ const dirs = getDirectoryPaths(testDir, "**/*");
476
+
477
+ files.forEach(file => {
478
+ expect(file).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
479
+ expect(file).not.toMatch(/[\/\\]$/);
480
+ });
481
+
482
+ dirs.forEach(dir => {
483
+ expect(dir).toMatch(/^[\/\\]|^[a-zA-Z]:[\/\\]/);
484
+ });
485
+ });
486
+ });
487
+
488
+
489
+ })
490
+
491
+
492
+
@@ -0,0 +1,112 @@
1
+ import { Glob } from "bun";
2
+ import { statSync } from "fs";
3
+
4
+ /**
5
+ * Gets file paths, directory paths, or both based on the specified parameters
6
+ * @param path - The base directory path to search in
7
+ * @param pattern - The glob pattern to match files/directories (default: **\/*)
8
+ * @param includeDirectories - Controls what to include: yes (files + dirs), no (files only), onlyDirectories (dirs only)
9
+ * @param absolute - Whether to return absolute paths (default: true)
10
+ * @returns Array of paths matching the criteria
11
+ */
12
+ export const getPaths = (
13
+ path: string,
14
+ pattern: string = "**/*",
15
+ includeDirectories: "yes" | "no" | "onlyDirectories" = "no",
16
+ absolute: boolean = true,
17
+ ): string[] => {
18
+ try {
19
+ if (includeDirectories === "no") return getFilePaths(path, pattern, absolute);
20
+ if (includeDirectories === "onlyDirectories") return getDirectoryPaths(path, pattern, absolute);
21
+
22
+ const paths: string[] = [];
23
+
24
+ const glob = new Glob(pattern);
25
+
26
+ const files = glob.scanSync({
27
+ cwd: path,
28
+ onlyFiles: false,
29
+ absolute: absolute,
30
+ dot: true,
31
+ followSymlinks: false,
32
+ });
33
+
34
+ paths.push(...files);
35
+
36
+ return paths;
37
+ } catch (error) {
38
+ console.error(`Error getting paths from ${path}:`, error);
39
+ return [];
40
+ }
41
+ };
42
+
43
+
44
+ /**
45
+ * Gets only file paths (excluding directories) matching the pattern
46
+ * @param path - The base directory path to search in
47
+ * @param pattern - The glob pattern to match files (default: **\/*)
48
+ * @param absolute - Whether to return absolute paths (default: true)
49
+ * @returns Array of file paths
50
+ */
51
+ export const getFilePaths = (path: string, pattern: string = "**/*", absolute: boolean = true): string[] => {
52
+ try {
53
+ const paths: string[] = [];
54
+
55
+ const glob = new Glob(pattern);
56
+
57
+ const files = glob.scanSync({
58
+ cwd: path,
59
+ onlyFiles: true,
60
+ absolute: absolute,
61
+ dot: true,
62
+ followSymlinks: false,
63
+ });
64
+
65
+ paths.push(...files);
66
+
67
+ return paths;
68
+ } catch (error) {
69
+ console.error(`Error getting file paths from ${path}:`, error);
70
+ return [];
71
+ }
72
+ };
73
+
74
+ /**
75
+ * Gets only directory paths (excluding files) matching the pattern
76
+ * @param path - The base directory path to search in
77
+ * @param pattern - The glob pattern to match directories (default: **\/*)
78
+ * @param absolute - Whether to return absolute paths (default: true)
79
+ * @returns Array of directory paths
80
+ */
81
+ export const getDirectoryPaths = (path: string, pattern: string = "**/*", absolute: boolean = true): string[] => {
82
+ try {
83
+ const paths: string[] = [];
84
+
85
+ const glob = new Glob(pattern);
86
+
87
+ const allItems = [...glob.scanSync({
88
+ cwd: path,
89
+ onlyFiles: false,
90
+ absolute: absolute,
91
+ dot: true,
92
+ followSymlinks: false,
93
+ })];
94
+
95
+ const directories = allItems.filter(item => {
96
+ try {
97
+ return statSync(item).isDirectory();
98
+ } catch {
99
+ return false;
100
+ }
101
+ });
102
+
103
+ paths.push(...directories);
104
+
105
+ return paths;
106
+ } catch (error) {
107
+ console.error(`Error getting directory paths from ${path}:`, error);
108
+ return [];
109
+ }
110
+ };
111
+
112
+
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./random";
2
2
  export * from "./stack";
3
+ export * from "./getPaths";
package/changelog.md DELETED
@@ -1,3 +0,0 @@
1
- # v0.1.0
2
-
3
- - initial repo with useful tools.