yargs-file-commands 0.0.19 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +59 -13
  2. package/dist/index.js +1 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/lib/Command.js +1 -0
  5. package/dist/lib/Command.js.map +1 -0
  6. package/dist/lib/buildSegmentTree.d.ts +1 -1
  7. package/dist/lib/buildSegmentTree.js +7 -3
  8. package/dist/lib/buildSegmentTree.js.map +1 -0
  9. package/dist/lib/buildSegmentTree.test.js +279 -26
  10. package/dist/lib/buildSegmentTree.test.js.map +1 -0
  11. package/dist/lib/fileCommands.d.ts +2 -1
  12. package/dist/lib/fileCommands.js +39 -21
  13. package/dist/lib/fileCommands.js.map +1 -0
  14. package/dist/lib/fileCommands.test.js +107 -30
  15. package/dist/lib/fileCommands.test.js.map +1 -0
  16. package/dist/lib/fixtures/commands/$default.js +1 -0
  17. package/dist/lib/fixtures/commands/$default.js.map +1 -0
  18. package/dist/lib/fixtures/commands/create.js +1 -0
  19. package/dist/lib/fixtures/commands/create.js.map +1 -0
  20. package/dist/lib/fixtures/commands/db/health.d.ts +2 -1
  21. package/dist/lib/fixtures/commands/db/health.js +2 -3
  22. package/dist/lib/fixtures/commands/db/health.js.map +1 -0
  23. package/dist/lib/fixtures/commands/db/migration/command.d.ts +9 -2
  24. package/dist/lib/fixtures/commands/db/migration/command.js +6 -7
  25. package/dist/lib/fixtures/commands/db/migration/command.js.map +1 -0
  26. package/dist/lib/importCommand.d.ts +8 -4
  27. package/dist/lib/importCommand.js +66 -21
  28. package/dist/lib/importCommand.js.map +1 -0
  29. package/dist/lib/importCommand.test.js +157 -34
  30. package/dist/lib/importCommand.test.js.map +1 -0
  31. package/dist/lib/scanDirectory.js +54 -25
  32. package/dist/lib/scanDirectory.js.map +1 -0
  33. package/dist/lib/scanDirectory.test.js +148 -25
  34. package/dist/lib/scanDirectory.test.js.map +1 -0
  35. package/dist/lib/segmentPath.js +8 -6
  36. package/dist/lib/segmentPath.js.map +1 -0
  37. package/dist/lib/segmentPath.test.js +10 -38
  38. package/dist/lib/segmentPath.test.js.map +1 -0
  39. package/dist/tsconfig.tsbuildinfo +1 -0
  40. package/package.json +6 -9
  41. package/CHANGELOG.md +0 -56
  42. package/src/index.ts +0 -1
  43. package/src/lib/Command.ts +0 -16
  44. package/src/lib/buildSegmentTree.test.ts +0 -90
  45. package/src/lib/buildSegmentTree.ts +0 -149
  46. package/src/lib/fileCommands.test.ts +0 -55
  47. package/src/lib/fileCommands.ts +0 -149
  48. package/src/lib/fixtures/commands/$default.ts +0 -5
  49. package/src/lib/fixtures/commands/create.ts +0 -6
  50. package/src/lib/fixtures/commands/db/health.ts +0 -9
  51. package/src/lib/fixtures/commands/db/migration/command.ts +0 -12
  52. package/src/lib/importCommand.test.ts +0 -65
  53. package/src/lib/importCommand.ts +0 -148
  54. package/src/lib/scanDirectory.test.ts +0 -75
  55. package/src/lib/scanDirectory.ts +0 -109
  56. package/src/lib/segmentPath.test.ts +0 -71
  57. package/src/lib/segmentPath.ts +0 -38
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![NPM Package][npm]][npm-url]
4
4
  [![NPM Downloads][npm-downloads]][npmtrends-url]
5
+ [![Tests][tests-badge]][tests-url]
6
+ [![Coverage][coverage-badge]][coverage-url]
5
7
 
6
8
  This Yargs helper function lets you define all your commands as individual files and their file names and directory structure defines via implication your nested command structure.
7
9
 
@@ -111,6 +113,52 @@ db health
111
113
  studio start
112
114
  ```
113
115
 
116
+ ### Alternative Type-Safe Command Definition
117
+
118
+ YOu can also use this type-safe way to define commands using the `CommandModule` type from yargs directly. This is the preferred method as it provides better TypeScript support and catches potential errors at compile time rather than runtime:
119
+
120
+ ```ts
121
+ import type { ArgumentsCamelCase, CommandModule } from 'yargs';
122
+
123
+ type TriageArgs = {
124
+ owner: string;
125
+ repo: string;
126
+ issue: number;
127
+ };
128
+
129
+ export const command: CommandModule<object, TriageArgs> = {
130
+ command: 'triage <owner> <repo> <issue>',
131
+ describe: 'Triage a GitHub issue',
132
+ builder: {
133
+ owner: {
134
+ type: 'string',
135
+ description: 'GitHub repository owner',
136
+ demandOption: true
137
+ },
138
+ repo: {
139
+ type: 'string',
140
+ description: 'GitHub repository name',
141
+ demandOption: true
142
+ },
143
+ issue: {
144
+ type: 'number',
145
+ description: 'Issue number',
146
+ demandOption: true
147
+ }
148
+ },
149
+ handler: async (argv: ArgumentsCamelCase<TriageArgs>) => {
150
+ // Implementation
151
+ }
152
+ };
153
+ ```
154
+
155
+ This approach has several advantages:
156
+
157
+ - Full TypeScript support with proper type inference
158
+ - Compile-time checking of command structure
159
+ - No risk of misspelling exports
160
+ - Better IDE support with autocompletion
161
+
114
162
  ## Options
115
163
 
116
164
  The "fileCommands" method takes the following options:
@@ -141,25 +189,19 @@ If you want to contribute, just check out [this git project](https://github.com/
141
189
 
142
190
  ```sh
143
191
  # install dependencies
144
- npm install
192
+ pnpm install
145
193
 
146
194
  # build everything
147
- npm run build
148
-
149
- # prettify
150
- npm run format
195
+ pnpm run build
151
196
 
152
- # eslint
153
- npm run lint
197
+ # biome
198
+ pnpm run chec
154
199
 
155
- # build and run tests
156
- npm run test
200
+ # tests
201
+ pnpm vitest
157
202
 
158
203
  # clean everything, should be like doing a fresh git checkout of the repo.
159
- npm run clean
160
-
161
- # publish the npm package
162
- npm run publish
204
+ pnpm clean
163
205
 
164
206
  # run example cli
165
207
  npx example-cli
@@ -171,3 +213,7 @@ Underneath the hood, we are using [NX](https://nx.dev) to manage the monorepo an
171
213
  [npm-url]: https://www.npmjs.com/package/yargs-file-commands
172
214
  [npm-downloads]: https://img.shields.io/npm/dw/yargs-file-commands
173
215
  [npmtrends-url]: https://www.npmtrends.com/yargs-file-commands
216
+ [tests-badge]: https://github.com/bhouston/yargs-file-commands/workflows/Tests/badge.svg
217
+ [tests-url]: https://github.com/bhouston/yargs-file-commands/actions/workflows/test.yml
218
+ [coverage-badge]: https://codecov.io/gh/bhouston/yargs-file-commands/branch/main/graph/badge.svg
219
+ [coverage-url]: https://codecov.io/gh/bhouston/yargs-file-commands
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from './lib/fileCommands.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=Command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Command.js","sourceRoot":"","sources":["../../src/lib/Command.ts"],"names":[],"mappings":""}
@@ -38,6 +38,6 @@ export declare const buildSegmentTree: (commands: Command[]) => CommandTreeNode[
38
38
  * For leaf nodes, returns the actual command implementation.
39
39
  * For internal nodes, creates a parent command that manages subcommands.
40
40
  */
41
- export declare const createCommand: (treeNode: CommandTreeNode) => CommandModule;
41
+ export declare const createCommand: (treeNode: CommandTreeNode) => CommandModule<{}, {}>;
42
42
  export declare const logCommandTree: (commands: CommandTreeNode[], level?: number) => void;
43
43
  export {};
@@ -27,6 +27,9 @@ function insertIntoTree(treeNodes, command, depth) {
27
27
  return;
28
28
  }
29
29
  const currentSegmentName = command.segments[depth];
30
+ if (currentSegmentName === undefined) {
31
+ return;
32
+ }
30
33
  let currentSegment = treeNodes.find((s) => s.segmentName === currentSegmentName);
31
34
  // If this is the last segment, create a leaf node
32
35
  if (depth === command.segments.length - 1) {
@@ -34,7 +37,7 @@ function insertIntoTree(treeNodes, command, depth) {
34
37
  treeNodes.push({
35
38
  type: 'leaf',
36
39
  segmentName: currentSegmentName,
37
- command
40
+ command,
38
41
  });
39
42
  }
40
43
  else if (currentSegment.type === 'internal') {
@@ -47,7 +50,7 @@ function insertIntoTree(treeNodes, command, depth) {
47
50
  currentSegment = {
48
51
  type: 'internal',
49
52
  segmentName: currentSegmentName,
50
- children: []
53
+ children: [],
51
54
  };
52
55
  treeNodes.push(currentSegment);
53
56
  }
@@ -85,7 +88,7 @@ export const createCommand = (treeNode) => {
85
88
  },
86
89
  handler: async () => {
87
90
  // Internal nodes don't need handlers as they'll demand subcommands
88
- }
91
+ },
89
92
  };
90
93
  return command;
91
94
  };
@@ -97,3 +100,4 @@ export const logCommandTree = (commands, level = 0) => {
97
100
  }
98
101
  });
99
102
  };
103
+ //# sourceMappingURL=buildSegmentTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildSegmentTree.js","sourceRoot":"","sources":["../../src/lib/buildSegmentTree.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import assert from 'node:assert/strict';
2
- import { describe, it } from 'node:test';
3
- import { buildSegmentTree } from './buildSegmentTree.js';
4
- describe('buildSegmentTree', async () => {
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { buildSegmentTree, createCommand, logCommandTree } from './buildSegmentTree.js';
3
+ describe('buildSegmentTree', () => {
5
4
  it('should build correct tree structure', () => {
6
5
  const commands = [
7
6
  {
@@ -12,8 +11,8 @@ describe('buildSegmentTree', async () => {
12
11
  describe: 'Migration command',
13
12
  handler: async () => {
14
13
  // Test handler
15
- }
16
- }
14
+ },
15
+ },
17
16
  },
18
17
  {
19
18
  fullPath: '/commands/db/health.js',
@@ -23,28 +22,29 @@ describe('buildSegmentTree', async () => {
23
22
  describe: 'Health command',
24
23
  handler: async () => {
25
24
  // Test handler
26
- }
27
- }
28
- }
25
+ },
26
+ },
27
+ },
29
28
  ];
30
29
  const tree = buildSegmentTree(commands);
31
- assert.equal(tree.length, 1, 'Should have one root node');
30
+ expect(tree.length).toBe(1);
32
31
  const rootNode = tree[0];
33
- assert(rootNode, 'Root node should exist');
34
- assert.equal(rootNode.segmentName, 'db', 'Root node should be "db"');
35
- assert.equal(rootNode.type, 'internal', 'Root node should be internal type');
32
+ expect(rootNode).toBeDefined();
33
+ if (!rootNode) {
34
+ throw new Error('Root node should exist');
35
+ }
36
+ expect(rootNode.segmentName).toBe('db');
37
+ expect(rootNode.type).toBe('internal');
36
38
  if (rootNode.type !== 'internal') {
37
39
  throw new Error('Expected internal node');
38
40
  }
39
- assert.equal(rootNode.children.length, 2, 'Should have two child nodes');
40
- const childSegments = rootNode.children
41
- .map((child) => child.segmentName)
42
- .sort();
43
- assert.deepEqual(childSegments, ['health', 'migration'], 'Should have "health" and "migration" sub-commands');
41
+ expect(rootNode.children.length).toBe(2);
42
+ const childSegments = rootNode.children.map((child) => child.segmentName).sort();
43
+ expect(childSegments).toEqual(['health', 'migration']);
44
44
  });
45
45
  it('should handle empty input', () => {
46
46
  const tree = buildSegmentTree([]);
47
- assert.equal(tree.length, 0, 'Should return empty array for empty input');
47
+ expect(tree.length).toBe(0);
48
48
  });
49
49
  it('should handle single command', () => {
50
50
  const commands = [
@@ -56,15 +56,268 @@ describe('buildSegmentTree', async () => {
56
56
  describe: 'Test command',
57
57
  handler: async () => {
58
58
  // Test handler
59
- }
60
- }
61
- }
59
+ },
60
+ },
61
+ },
62
62
  ];
63
63
  const tree = buildSegmentTree(commands);
64
- assert.equal(tree.length, 1, 'Should have one node');
64
+ expect(tree.length).toBe(1);
65
65
  const node = tree[0];
66
- assert(node, 'Node should exist');
67
- assert.equal(node.segmentName, 'test', 'Should have correct segment');
68
- assert.equal(node.type, 'leaf', 'Should be a leaf node');
66
+ expect(node).toBeDefined();
67
+ if (!node) {
68
+ throw new Error('Node should exist');
69
+ }
70
+ expect(node.segmentName).toBe('test');
71
+ expect(node.type).toBe('leaf');
72
+ });
73
+ it('should throw error when directory conflicts with command name (directory first)', () => {
74
+ const commands = [
75
+ {
76
+ fullPath: '/commands/db/migration/command.js',
77
+ segments: ['db', 'migration'],
78
+ commandModule: {
79
+ command: 'migration',
80
+ describe: 'Migration command',
81
+ handler: async () => { },
82
+ },
83
+ },
84
+ {
85
+ fullPath: '/commands/db.js',
86
+ segments: ['db'],
87
+ commandModule: {
88
+ command: 'db',
89
+ describe: 'DB command',
90
+ handler: async () => { },
91
+ },
92
+ },
93
+ ];
94
+ expect(() => buildSegmentTree(commands)).toThrow(/Conflict: db is both a directory and a command/);
95
+ });
96
+ it('should throw error when directory conflicts with command name (command first)', () => {
97
+ const commands = [
98
+ {
99
+ fullPath: '/commands/db.js',
100
+ segments: ['db'],
101
+ commandModule: {
102
+ command: 'db',
103
+ describe: 'DB command',
104
+ handler: async () => { },
105
+ },
106
+ },
107
+ {
108
+ fullPath: '/commands/db/migration/command.js',
109
+ segments: ['db', 'migration'],
110
+ commandModule: {
111
+ command: 'migration',
112
+ describe: 'Migration command',
113
+ handler: async () => { },
114
+ },
115
+ },
116
+ ];
117
+ expect(() => buildSegmentTree(commands)).toThrow(/Conflict: db is both a directory and a command/);
118
+ });
119
+ it('should handle multiple root commands', () => {
120
+ const commands = [
121
+ {
122
+ fullPath: '/commands/hello.ts',
123
+ segments: ['hello'],
124
+ commandModule: {
125
+ command: 'hello',
126
+ describe: 'Hello command',
127
+ handler: async () => { },
128
+ },
129
+ },
130
+ {
131
+ fullPath: '/commands/world.ts',
132
+ segments: ['world'],
133
+ commandModule: {
134
+ command: 'world',
135
+ describe: 'World command',
136
+ handler: async () => { },
137
+ },
138
+ },
139
+ ];
140
+ const tree = buildSegmentTree(commands);
141
+ expect(tree.length).toBe(2);
142
+ expect(tree.map((n) => n.segmentName).sort()).toEqual(['hello', 'world']);
143
+ });
144
+ it('should handle nested commands at different depths', () => {
145
+ const commands = [
146
+ {
147
+ fullPath: '/commands/a/b/c.ts',
148
+ segments: ['a', 'b', 'c'],
149
+ commandModule: {
150
+ command: 'c',
151
+ describe: 'C command',
152
+ handler: async () => { },
153
+ },
154
+ },
155
+ {
156
+ fullPath: '/commands/a/d.ts',
157
+ segments: ['a', 'd'],
158
+ commandModule: {
159
+ command: 'd',
160
+ describe: 'D command',
161
+ handler: async () => { },
162
+ },
163
+ },
164
+ ];
165
+ const tree = buildSegmentTree(commands);
166
+ expect(tree.length).toBe(1);
167
+ expect(tree[0]?.segmentName).toBe('a');
168
+ if (tree[0]?.type === 'internal') {
169
+ expect(tree[0].children.length).toBe(2);
170
+ const childNames = tree[0].children.map((c) => c.segmentName).sort();
171
+ expect(childNames).toEqual(['b', 'd']);
172
+ const bNode = tree[0].children.find((c) => c.segmentName === 'b');
173
+ if (bNode?.type === 'internal') {
174
+ expect(bNode.children.length).toBe(1);
175
+ expect(bNode.children[0]?.segmentName).toBe('c');
176
+ expect(bNode.children[0]?.type).toBe('leaf');
177
+ }
178
+ }
179
+ });
180
+ });
181
+ describe('createCommand', () => {
182
+ it('should create command module from leaf node', () => {
183
+ const command = {
184
+ fullPath: '/commands/test.ts',
185
+ segments: ['test'],
186
+ commandModule: {
187
+ command: 'test',
188
+ describe: 'Test command',
189
+ handler: async () => {
190
+ // Test handler
191
+ },
192
+ },
193
+ };
194
+ const treeNode = {
195
+ type: 'leaf',
196
+ segmentName: 'test',
197
+ command,
198
+ };
199
+ const commandModule = createCommand(treeNode);
200
+ expect(commandModule.command).toBe('test');
201
+ expect(commandModule.describe).toBe('Test command');
202
+ expect(commandModule.handler).toBe(command.commandModule.handler);
203
+ });
204
+ it('should create command module from internal node with children', () => {
205
+ const childCommand = {
206
+ fullPath: '/commands/db/health.ts',
207
+ segments: ['db', 'health'],
208
+ commandModule: {
209
+ command: 'health',
210
+ describe: 'Health check',
211
+ handler: async () => { },
212
+ },
213
+ };
214
+ const childTreeNode = {
215
+ type: 'leaf',
216
+ segmentName: 'health',
217
+ command: childCommand,
218
+ };
219
+ const internalTreeNode = {
220
+ type: 'internal',
221
+ segmentName: 'db',
222
+ children: [childTreeNode],
223
+ };
224
+ const commandModule = createCommand(internalTreeNode);
225
+ expect(commandModule.command).toBe('db');
226
+ expect(commandModule.describe).toBe('db commands');
227
+ expect(commandModule.builder).toBeDefined();
228
+ expect(commandModule.handler).toBeDefined();
229
+ });
230
+ it('should create nested command structure with builder', () => {
231
+ const healthCommand = {
232
+ fullPath: '/commands/db/health.ts',
233
+ segments: ['db', 'health'],
234
+ commandModule: {
235
+ command: 'health',
236
+ describe: 'Health check',
237
+ handler: async () => { },
238
+ },
239
+ };
240
+ const migrationCommand = {
241
+ fullPath: '/commands/db/migration.ts',
242
+ segments: ['db', 'migration'],
243
+ commandModule: {
244
+ command: 'migration',
245
+ describe: 'Migration',
246
+ handler: async () => { },
247
+ },
248
+ };
249
+ const tree = buildSegmentTree([healthCommand, migrationCommand]);
250
+ const dbNode = tree[0];
251
+ if (!dbNode || dbNode.type !== 'internal') {
252
+ throw new Error('Expected internal node');
253
+ }
254
+ const commandModule = createCommand(dbNode);
255
+ expect(commandModule.command).toBe('db');
256
+ expect(commandModule.builder).toBeDefined();
257
+ // Test the builder function
258
+ if (commandModule.builder && typeof commandModule.builder === 'function') {
259
+ const mockYargs = {
260
+ command: vi.fn().mockReturnThis(),
261
+ demandCommand: vi.fn().mockReturnThis(),
262
+ };
263
+ commandModule.builder(mockYargs);
264
+ expect(mockYargs.command).toHaveBeenCalledTimes(1);
265
+ expect(mockYargs.demandCommand).toHaveBeenCalledWith(1, 'You must specify a db subcommand');
266
+ }
267
+ });
268
+ });
269
+ describe('logCommandTree', () => {
270
+ it('should log command tree structure', () => {
271
+ const consoleSpy = vi.spyOn(console, 'debug').mockImplementation(() => { });
272
+ const commands = [
273
+ {
274
+ fullPath: '/commands/db/health.ts',
275
+ segments: ['db', 'health'],
276
+ commandModule: {
277
+ command: 'health',
278
+ describe: 'Health',
279
+ handler: async () => { },
280
+ },
281
+ },
282
+ {
283
+ fullPath: '/commands/hello.ts',
284
+ segments: ['hello'],
285
+ commandModule: {
286
+ command: 'hello',
287
+ describe: 'Hello',
288
+ handler: async () => { },
289
+ },
290
+ },
291
+ ];
292
+ const tree = buildSegmentTree(commands);
293
+ logCommandTree(tree);
294
+ expect(consoleSpy).toHaveBeenCalled();
295
+ const calls = consoleSpy.mock.calls.map((call) => call[0]);
296
+ expect(calls.some((call) => call.includes('db'))).toBe(true);
297
+ expect(calls.some((call) => call.includes('health'))).toBe(true);
298
+ expect(calls.some((call) => call.includes('hello'))).toBe(true);
299
+ consoleSpy.mockRestore();
300
+ });
301
+ it('should log command tree with correct indentation levels', () => {
302
+ const consoleSpy = vi.spyOn(console, 'debug').mockImplementation(() => { });
303
+ const commands = [
304
+ {
305
+ fullPath: '/commands/a/b/c.ts',
306
+ segments: ['a', 'b', 'c'],
307
+ commandModule: {
308
+ command: 'c',
309
+ describe: 'C',
310
+ handler: async () => { },
311
+ },
312
+ },
313
+ ];
314
+ const tree = buildSegmentTree(commands);
315
+ logCommandTree(tree, 2);
316
+ expect(consoleSpy).toHaveBeenCalled();
317
+ const calls = consoleSpy.mock.calls.map((call) => call[0]);
318
+ // Check for indentation (initial level 2 = 4 spaces, then children add more)
319
+ expect(calls.length).toBeGreaterThan(0);
320
+ consoleSpy.mockRestore();
69
321
  });
70
322
  });
323
+ //# sourceMappingURL=buildSegmentTree.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildSegmentTree.test.js","sourceRoot":"","sources":["../../src/lib/buildSegmentTree.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { CommandModule } from 'yargs';
1
2
  import { type ScanDirectoryOptions } from './scanDirectory.js';
2
3
  /**
3
4
  * Configuration options for file-based command generation
@@ -30,4 +31,4 @@ export declare const DefaultFileCommandsOptions: Required<FileCommandsOptions>;
30
31
  * 3. Build a tree structure based on file paths
31
32
  * 4. Convert the tree into a command hierarchy
32
33
  */
33
- export declare const fileCommands: (options: FileCommandsOptions) => Promise<import("yargs").CommandModule<{}, {}>[]>;
34
+ export declare const fileCommands: (options: FileCommandsOptions) => Promise<CommandModule<{}, {}>[]>;
@@ -1,4 +1,4 @@
1
- import path from 'path';
1
+ import path from 'node:path';
2
2
  import { buildSegmentTree, createCommand, logCommandTree } from './buildSegmentTree.js';
3
3
  import { importCommandFromFile } from './importCommand.js';
4
4
  import { scanDirectory } from './scanDirectory.js';
@@ -13,17 +13,19 @@ export const DefaultFileCommandsOptions = {
13
13
  commandDirs: [],
14
14
  /** Default file extensions to process */
15
15
  extensions: ['.js', '.ts'],
16
- /** Default patterns to ignore when scanning directories */
16
+ /** Default patterns to ignore when scanning directories.
17
+ * Note: System files (files starting with '.') are ALWAYS ignored regardless of these patterns.
18
+ * These defaults can be overridden by providing your own ignorePatterns.
19
+ */
17
20
  ignorePatterns: [
18
- /^[.|_].*/, // Hidden files and underscore files
19
21
  /\.(?:test|spec)\.[jt]s$/, // Test files
20
22
  /__(?:test|spec)__/, // Test directories
21
- /\.d\.ts$/ // TypeScript declaration files
23
+ /\.d\.ts$/, // TypeScript declaration files
22
24
  ],
23
25
  /** Default logging level */
24
26
  logLevel: 'info',
25
27
  /** Default log prefix */
26
- logPrefix: ' '
28
+ logPrefix: ' ',
27
29
  };
28
30
  /**
29
31
  * Generates a command tree structure from files in specified directories
@@ -45,7 +47,7 @@ export const DefaultFileCommandsOptions = {
45
47
  export const fileCommands = async (options) => {
46
48
  const fullOptions = {
47
49
  ...DefaultFileCommandsOptions,
48
- ...options
50
+ ...options,
49
51
  };
50
52
  // validate extensions have dots in them
51
53
  if (fullOptions.extensions.some((ext) => !ext.startsWith('.'))) {
@@ -61,29 +63,44 @@ export const fileCommands = async (options) => {
61
63
  throw new Error(`Command directories must be absolute paths: ${nonAbsoluteDirs.join(', ')}`);
62
64
  }
63
65
  const commands = [];
64
- for (const commandDir of fullOptions.commandDirs) {
66
+ // Process all command directories in parallel
67
+ const directoryResults = await Promise.all(fullOptions.commandDirs.map(async (commandDir) => {
65
68
  const fullPath = path.resolve(commandDir);
66
69
  if (fullOptions.logLevel === 'debug') {
67
70
  console.debug(`Scanning directory for commands: ${fullPath}`);
68
71
  }
69
72
  const filePaths = await scanDirectory(commandDir, commandDir, fullOptions);
73
+ return { commandDir, filePaths };
74
+ }));
75
+ if (fullOptions.logLevel === 'debug') {
76
+ console.debug(`Importing found commands:`);
77
+ }
78
+ // Process all files in parallel
79
+ const fileResults = await Promise.all(directoryResults.flatMap(({ commandDir, filePaths }) => filePaths.map(async (filePath) => {
80
+ const localPath = path.relative(commandDir, filePath);
81
+ const segments = segmentPath(filePath, commandDir);
82
+ // Remove extension (last segment) if there are multiple segments
83
+ // If there's only one segment, it means the file has no name (e.g., .js)
84
+ if (segments.length > 1) {
85
+ segments.pop(); // remove extension.
86
+ }
87
+ else if (segments.length === 0) {
88
+ throw new Error(`No segments found for file: ${filePath}`);
89
+ }
70
90
  if (fullOptions.logLevel === 'debug') {
71
- console.debug(`Importing found commands:`);
91
+ console.debug(` ${localPath} - importing command module`);
72
92
  }
73
- for (const filePath of filePaths) {
74
- const localPath = path.relative(commandDir, filePath);
75
- const segments = segmentPath(filePath, commandDir);
76
- segments.pop(); // remove extension.
77
- if (fullOptions.logLevel === 'debug') {
78
- console.debug(` ${localPath} - importing command module`);
79
- }
80
- commands.push({
81
- fullPath: filePath,
82
- segments,
83
- commandModule: await importCommandFromFile(filePath, segments[segments.length - 1], fullOptions)
84
- });
93
+ const lastSegment = segments[segments.length - 1];
94
+ if (lastSegment === undefined) {
95
+ throw new Error(`No segments found for file: ${filePath}`);
85
96
  }
86
- }
97
+ return {
98
+ fullPath: filePath,
99
+ segments,
100
+ commandModule: await importCommandFromFile(filePath, lastSegment, fullOptions),
101
+ };
102
+ })));
103
+ commands.push(...fileResults);
87
104
  // check if no commands were found
88
105
  if (commands.length === 0) {
89
106
  throw new Error(`No commands found in specified directories: ${fullOptions.commandDirs.join(', ')}`);
@@ -96,3 +113,4 @@ export const fileCommands = async (options) => {
96
113
  const rootCommands = commandRootNodes.map((node) => createCommand(node));
97
114
  return rootCommands;
98
115
  };
116
+ //# sourceMappingURL=fileCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileCommands.js","sourceRoot":"","sources":["../../src/lib/fileCommands.ts"],"names":[],"mappings":"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"}