yargs-file-commands 0.0.8 → 0.0.10
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/dist/{test → lib}/buildSegmentTree.test.js +4 -4
- package/dist/lib/fileCommands.d.ts +3 -8
- package/dist/lib/fileCommands.js +35 -12
- package/dist/{test → lib}/fileCommands.test.js +2 -2
- package/dist/{test → lib}/importCommand.test.js +2 -2
- package/dist/lib/scanDirectory.d.ts +7 -4
- package/dist/lib/scanDirectory.js +26 -32
- package/dist/lib/scanDirectory.test.js +46 -0
- package/dist/lib/segmentPath.test.js +6 -6
- package/package.json +9 -9
- package/dist/test/scanDirectory.test.js +0 -41
- /package/dist/{test → lib}/buildSegmentTree.test.d.ts +0 -0
- /package/dist/{test → lib}/fileCommands.test.d.ts +0 -0
- /package/dist/{test → lib}/fixtures/commands/db/health.d.ts +0 -0
- /package/dist/{test → lib}/fixtures/commands/db/health.js +0 -0
- /package/dist/{test → lib}/fixtures/commands/db/migration/command.d.ts +0 -0
- /package/dist/{test → lib}/fixtures/commands/db/migration/command.js +0 -0
- /package/dist/{test → lib}/importCommand.test.d.ts +0 -0
- /package/dist/{test → lib}/scanDirectory.test.d.ts +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { describe, it } from 'node:test';
|
|
3
|
-
import { buildSegmentTree } from '
|
|
3
|
+
import { buildSegmentTree } from './buildSegmentTree.js';
|
|
4
4
|
describe('buildSegmentTree', async () => {
|
|
5
|
-
it('should build correct tree structure',
|
|
5
|
+
it('should build correct tree structure', () => {
|
|
6
6
|
const commands = [
|
|
7
7
|
{
|
|
8
8
|
fullPath: '/commands/db/migration/command.js',
|
|
@@ -42,11 +42,11 @@ describe('buildSegmentTree', async () => {
|
|
|
42
42
|
.sort();
|
|
43
43
|
assert.deepEqual(childSegments, ['health', 'migration'], 'Should have "health" and "migration" sub-commands');
|
|
44
44
|
});
|
|
45
|
-
it('should handle empty input',
|
|
45
|
+
it('should handle empty input', () => {
|
|
46
46
|
const tree = buildSegmentTree([]);
|
|
47
47
|
assert.equal(tree.length, 0, 'Should return empty array for empty input');
|
|
48
48
|
});
|
|
49
|
-
it('should handle single command',
|
|
49
|
+
it('should handle single command', () => {
|
|
50
50
|
const commands = [
|
|
51
51
|
{
|
|
52
52
|
fullPath: '/commands/test.ts',
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
+
import { type ScanDirectoryOptions } from './scanDirectory.js';
|
|
1
2
|
/**
|
|
2
3
|
* Configuration options for file-based command generation
|
|
3
4
|
* @interface FileCommandsOptions
|
|
4
5
|
*/
|
|
5
|
-
export type FileCommandsOptions = {
|
|
6
|
+
export type FileCommandsOptions = ScanDirectoryOptions & {
|
|
6
7
|
/** Array of directory paths to scan for command files */
|
|
7
8
|
commandDirs: string[];
|
|
8
|
-
/** File extensions to consider when scanning for command files */
|
|
9
|
-
extensions?: string[];
|
|
10
|
-
/** Regular expressions for patterns to ignore when scanning directories */
|
|
11
|
-
ignorePatterns?: RegExp[];
|
|
12
|
-
/** Logging verbosity level */
|
|
13
|
-
logLevel?: 'info' | 'debug';
|
|
14
9
|
};
|
|
15
10
|
/**
|
|
16
11
|
* Default configuration options for file-based commands
|
|
17
12
|
* @constant
|
|
18
13
|
* @type {Partial<FileCommandsOptions>}
|
|
19
14
|
*/
|
|
20
|
-
export declare const DefaultFileCommandsOptions:
|
|
15
|
+
export declare const DefaultFileCommandsOptions: Required<FileCommandsOptions>;
|
|
21
16
|
/**
|
|
22
17
|
* Generates a command tree structure from files in specified directories
|
|
23
18
|
* @async
|
package/dist/lib/fileCommands.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import { buildSegmentTree, createCommand, logCommandTree } from './buildSegmentTree.js';
|
|
2
3
|
import { importCommandFromFile } from './importCommand.js';
|
|
3
4
|
import { scanDirectory } from './scanDirectory.js';
|
|
@@ -8,6 +9,8 @@ import { segmentPath } from './segmentPath.js';
|
|
|
8
9
|
* @type {Partial<FileCommandsOptions>}
|
|
9
10
|
*/
|
|
10
11
|
export const DefaultFileCommandsOptions = {
|
|
12
|
+
/** Default directories to scan for command files */
|
|
13
|
+
commandDirs: [],
|
|
11
14
|
/** Default file extensions to process */
|
|
12
15
|
extensions: ['.js', '.ts'],
|
|
13
16
|
/** Default patterns to ignore when scanning directories */
|
|
@@ -18,7 +21,9 @@ export const DefaultFileCommandsOptions = {
|
|
|
18
21
|
/\.d\.ts$/ // TypeScript declaration files
|
|
19
22
|
],
|
|
20
23
|
/** Default logging level */
|
|
21
|
-
logLevel: 'info'
|
|
24
|
+
logLevel: 'info',
|
|
25
|
+
/** Default log prefix */
|
|
26
|
+
logPrefix: ' '
|
|
22
27
|
};
|
|
23
28
|
/**
|
|
24
29
|
* Generates a command tree structure from files in specified directories
|
|
@@ -38,23 +43,41 @@ export const DefaultFileCommandsOptions = {
|
|
|
38
43
|
* 4. Convert the tree into a command hierarchy
|
|
39
44
|
*/
|
|
40
45
|
export const fileCommands = async (options) => {
|
|
41
|
-
const fullOptions = {
|
|
46
|
+
const fullOptions = {
|
|
47
|
+
...DefaultFileCommandsOptions,
|
|
48
|
+
...options
|
|
49
|
+
};
|
|
50
|
+
// validate extensions have dots in them
|
|
51
|
+
if (fullOptions.extensions.some((ext) => !ext.startsWith('.'))) {
|
|
52
|
+
throw new Error(`Invalid extensions provided, must start with a dot: ${fullOptions.extensions.join(', ')}`);
|
|
53
|
+
}
|
|
54
|
+
// check for empty list of directories to scan
|
|
55
|
+
if (fullOptions.commandDirs.length === 0) {
|
|
56
|
+
throw new Error('No command directories provided');
|
|
57
|
+
}
|
|
42
58
|
const commands = [];
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
59
|
+
for (const commandDir of fullOptions.commandDirs) {
|
|
60
|
+
const fullPath = path.resolve(commandDir);
|
|
61
|
+
console.debug(`Scanning directory for commands: ${fullPath}`);
|
|
62
|
+
const filePaths = await scanDirectory(commandDir, commandDir, fullOptions);
|
|
63
|
+
for (const filePath of filePaths) {
|
|
46
64
|
const segments = segmentPath(filePath, commandDir);
|
|
47
|
-
|
|
65
|
+
segments.pop(); // remove extension.
|
|
66
|
+
commands.push({
|
|
48
67
|
fullPath: filePath,
|
|
49
|
-
segments
|
|
68
|
+
segments,
|
|
50
69
|
commandModule: await importCommandFromFile(filePath, segments[segments.length - 1], fullOptions)
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// check if no commands were found
|
|
74
|
+
if (commands.length === 0) {
|
|
75
|
+
throw new Error(`No commands found in specified directories: ${fullOptions.commandDirs.join(', ')}`);
|
|
76
|
+
}
|
|
55
77
|
const commandRootNodes = buildSegmentTree(commands);
|
|
56
78
|
if (fullOptions.logLevel === 'debug') {
|
|
57
|
-
|
|
79
|
+
console.debug('Command tree structure:');
|
|
80
|
+
logCommandTree(commandRootNodes, 1);
|
|
58
81
|
}
|
|
59
82
|
const rootCommands = commandRootNodes.map((node) => createCommand(node));
|
|
60
83
|
return rootCommands;
|
|
@@ -7,7 +7,7 @@ import { hideBin } from 'yargs/helpers';
|
|
|
7
7
|
import { fileCommands } from '../lib/fileCommands.js';
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
describe('fileCommands', async () => {
|
|
10
|
-
it('should load commands from directory structure', async () => {
|
|
10
|
+
await it('should load commands from directory structure', async () => {
|
|
11
11
|
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
12
12
|
const commands = await fileCommands({
|
|
13
13
|
commandDirs: [commandsDir],
|
|
@@ -27,7 +27,7 @@ describe('fileCommands', async () => {
|
|
|
27
27
|
const description = rootCommand.describe;
|
|
28
28
|
assert(typeof description === 'string' && description.includes('db'), 'Command should have correct description');
|
|
29
29
|
});
|
|
30
|
-
it('should respect ignore patterns', async () => {
|
|
30
|
+
await it('should respect ignore patterns', async () => {
|
|
31
31
|
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
32
32
|
const commands = await fileCommands({
|
|
33
33
|
commandDirs: [commandsDir],
|
|
@@ -5,14 +5,14 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { importCommandFromFile } from '../lib/importCommand.js';
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
describe('importCommandFromFile', async () => {
|
|
8
|
-
it('should import command module correctly', async () => {
|
|
8
|
+
await it('should import command module correctly', async () => {
|
|
9
9
|
const commandPath = path.join(__dirname, 'fixtures', 'commands', 'db', 'health.js');
|
|
10
10
|
const command = await importCommandFromFile(commandPath, 'health', {});
|
|
11
11
|
assert(command.describe, 'Should have describe property');
|
|
12
12
|
assert(typeof command.builder === 'function', 'Should have builder function');
|
|
13
13
|
assert(typeof command.handler === 'function', 'Should have handler function');
|
|
14
14
|
});
|
|
15
|
-
it('should handle non-existent files', async () => {
|
|
15
|
+
await it('should handle non-existent files', async () => {
|
|
16
16
|
const nonExistentPath = path.join(__dirname, 'fixtures', 'commands', 'non-existent.ts');
|
|
17
17
|
await assert.rejects(async () => {
|
|
18
18
|
await importCommandFromFile(nonExistentPath, 'non-existent', {});
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
* @interface ScanDirectoryOptions
|
|
4
4
|
*/
|
|
5
5
|
export interface ScanDirectoryOptions {
|
|
6
|
-
/**
|
|
7
|
-
ignorePatterns?: RegExp[];
|
|
8
|
-
/** File extensions to include in the scan */
|
|
6
|
+
/** File extensions to consider when scanning for command files */
|
|
9
7
|
extensions?: string[];
|
|
8
|
+
/** Regular expressions for patterns to ignore when scanning directories */
|
|
9
|
+
ignorePatterns?: RegExp[];
|
|
10
|
+
/** Logging verbosity level */
|
|
10
11
|
logLevel?: 'info' | 'debug';
|
|
12
|
+
/** Prefix for log messages */
|
|
13
|
+
logPrefix?: string;
|
|
11
14
|
}
|
|
12
15
|
/**
|
|
13
16
|
* Recursively scans a directory for command files
|
|
@@ -22,4 +25,4 @@ export interface ScanDirectoryOptions {
|
|
|
22
25
|
* - File extensions (only includes matching files)
|
|
23
26
|
* The scan is performed in parallel for better performance.
|
|
24
27
|
*/
|
|
25
|
-
export declare const scanDirectory: (dirPath: string, options?: ScanDirectoryOptions) => Promise<string[]>;
|
|
28
|
+
export declare const scanDirectory: (dirPath: string, commandDir: string, options?: ScanDirectoryOptions) => Promise<string[]>;
|
|
@@ -13,56 +13,50 @@ import path, { join } from 'path';
|
|
|
13
13
|
* - File extensions (only includes matching files)
|
|
14
14
|
* The scan is performed in parallel for better performance.
|
|
15
15
|
*/
|
|
16
|
-
export const scanDirectory = async (dirPath, options = {}) => {
|
|
17
|
-
const { ignorePatterns = [], extensions = [], logLevel = 'info' } = options;
|
|
18
|
-
const rootedDirPath = path.resolve(dirPath);
|
|
19
|
-
if (logLevel === 'debug') {
|
|
20
|
-
console.debug(`Inspecting directory for possible commands: ${rootedDirPath}`);
|
|
21
|
-
}
|
|
22
|
-
// Check if path should be ignored
|
|
23
|
-
const shouldIgnore = ignorePatterns.some((pattern) => pattern.test(dirPath));
|
|
24
|
-
if (shouldIgnore) {
|
|
25
|
-
if (logLevel === 'debug') {
|
|
26
|
-
console.debug(`Ignoring directory because of ignorePatterns match: ${rootedDirPath}, ${ignorePatterns
|
|
27
|
-
.filter((pattern) => pattern.test(dirPath))
|
|
28
|
-
.join(', ')}`);
|
|
29
|
-
}
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
16
|
+
export const scanDirectory = async (dirPath, commandDir, options = {}) => {
|
|
17
|
+
const { ignorePatterns = [], extensions = ['.js', '.ts'], logLevel = 'info', logPrefix = '' } = options;
|
|
32
18
|
try {
|
|
33
19
|
const entries = await readdir(dirPath);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
const nestedFilesPromises = entries.map(async (entry) => {
|
|
20
|
+
const commandPaths = [];
|
|
21
|
+
for (const entry of entries) {
|
|
38
22
|
const fullPath = join(dirPath, entry);
|
|
23
|
+
const localPath = fullPath.replace(commandDir, '');
|
|
39
24
|
// apply ignore pattern and early return if matched
|
|
40
|
-
const shouldIgnore = ignorePatterns.some((pattern) => pattern.test(
|
|
25
|
+
const shouldIgnore = ignorePatterns.some((pattern) => pattern.test(localPath));
|
|
41
26
|
if (shouldIgnore) {
|
|
42
27
|
if (logLevel === 'debug') {
|
|
43
|
-
console.debug(
|
|
44
|
-
.filter((pattern) => pattern.test(
|
|
28
|
+
console.debug(`${logPrefix}${localPath} - ignoring because it matches ignorePattern: ${ignorePatterns
|
|
29
|
+
.filter((pattern) => pattern.test(localPath))
|
|
45
30
|
.join(', ')}`);
|
|
46
31
|
}
|
|
47
|
-
|
|
32
|
+
continue;
|
|
48
33
|
}
|
|
49
34
|
const stats = await stat(fullPath);
|
|
50
35
|
if (stats.isDirectory()) {
|
|
51
|
-
|
|
36
|
+
if (logLevel === 'debug') {
|
|
37
|
+
console.debug(`${logPrefix}${localPath} - directory, scanning for commands:`);
|
|
38
|
+
}
|
|
39
|
+
commandPaths.push(...(await scanDirectory(fullPath, commandDir, {
|
|
40
|
+
...options,
|
|
41
|
+
logPrefix: `${logPrefix} `
|
|
42
|
+
})));
|
|
43
|
+
continue;
|
|
52
44
|
}
|
|
53
45
|
const extension = path.extname(fullPath);
|
|
54
46
|
if (!extensions.includes(extension)) {
|
|
55
|
-
|
|
47
|
+
if (logLevel === 'debug') {
|
|
48
|
+
console.debug(`${logPrefix}${localPath} - ignoring as its extension, ${extension}, doesn't match required extension: ${extensions.join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
56
51
|
}
|
|
57
52
|
if (logLevel === 'debug') {
|
|
58
|
-
console.debug(
|
|
53
|
+
console.debug(`${logPrefix}${localPath} - possible command file`);
|
|
59
54
|
}
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return nestedFiles.flat();
|
|
55
|
+
commandPaths.push(fullPath);
|
|
56
|
+
}
|
|
57
|
+
return commandPaths;
|
|
64
58
|
}
|
|
65
59
|
catch (error) {
|
|
66
|
-
throw new Error(
|
|
60
|
+
throw new Error(`${logPrefix}Failed to scan directory ${dirPath}: ${error}`);
|
|
67
61
|
}
|
|
68
62
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { scanDirectory } from '../lib/scanDirectory.js';
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
describe('scanDirectory', async () => {
|
|
8
|
+
await it('should find all command files in directory', async () => {
|
|
9
|
+
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
10
|
+
console.log('Scan Directory: ', commandsDir);
|
|
11
|
+
const files = await scanDirectory(commandsDir, commandsDir, {
|
|
12
|
+
extensions: ['.js'],
|
|
13
|
+
logLevel: 'debug'
|
|
14
|
+
});
|
|
15
|
+
assert.equal(files.length, 2, `Should find two command files, instead found: ${files.join(', ')}`);
|
|
16
|
+
assert(files.some((f) => f.includes('health.js')), `Should find health.js, instead found: ${files.join(', ')}`);
|
|
17
|
+
assert(files.some((f) => f.includes('command.js')), `Should find command.js, instead found: ${files.join(', ')}`);
|
|
18
|
+
});
|
|
19
|
+
await it('should respect ignore patterns', async () => {
|
|
20
|
+
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
21
|
+
console.log('Scan Directory: ', commandsDir);
|
|
22
|
+
const files = await scanDirectory(commandsDir, commandsDir, {
|
|
23
|
+
extensions: ['.js'],
|
|
24
|
+
ignorePatterns: [/health/],
|
|
25
|
+
logLevel: 'debug'
|
|
26
|
+
});
|
|
27
|
+
assert.equal(files.length, 1, `Should find one command file, instead found: ${files.join(', ')}`);
|
|
28
|
+
assert(files.some((f) => f.includes('command.js')), `Should find command.js, instead found: ${files.join(', ')}`);
|
|
29
|
+
assert(!files.some((f) => f.includes('health.js')), `Should not find health.js, instead found: ${files.join(', ')}`);
|
|
30
|
+
});
|
|
31
|
+
await it('should handle non-existent directories', async () => {
|
|
32
|
+
const nonExistentDir = path.join(__dirname, 'fixtures', 'non-existent');
|
|
33
|
+
try {
|
|
34
|
+
console.log('Scan Directory: ', nonExistentDir);
|
|
35
|
+
await scanDirectory(nonExistentDir, nonExistentDir, {
|
|
36
|
+
extensions: ['.js'],
|
|
37
|
+
logLevel: 'debug'
|
|
38
|
+
});
|
|
39
|
+
assert.fail('Should have thrown an error');
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
assert(error instanceof Error);
|
|
43
|
+
assert(error.message.includes('ENOENT'), 'Error should indicate directory not found');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'assert';
|
|
3
3
|
import { segmentPath } from './segmentPath.js';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
describe('segmentPath', () => {
|
|
5
|
+
it('should segment a path correctly', () => {
|
|
6
6
|
const fullPath = '/Users/username/Coding/Personal/yargs-file-commands/packages/yargs-file-commands/src/lib/segmentPath.ts';
|
|
7
7
|
const baseDir = '/Users/username/Coding/Personal/yargs-file-commands/';
|
|
8
8
|
const expected = [
|
|
@@ -16,7 +16,7 @@ await test('segmentPath', async () => {
|
|
|
16
16
|
const result = segmentPath(fullPath, baseDir);
|
|
17
17
|
assert.deepStrictEqual(result, expected);
|
|
18
18
|
});
|
|
19
|
-
|
|
19
|
+
it('should handle paths with periods correctly', () => {
|
|
20
20
|
const fullPath = '/Users/username/Coding/Personal/yargs-file-commands/packages/yargs-file-commands/src/lib/segmentPath.test.ts';
|
|
21
21
|
const baseDir = '/Users/username/Coding/Personal/yargs-file-commands/';
|
|
22
22
|
const expected = [
|
|
@@ -31,7 +31,7 @@ await test('segmentPath', async () => {
|
|
|
31
31
|
const result = segmentPath(fullPath, baseDir);
|
|
32
32
|
assert.deepStrictEqual(result, expected);
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
it('should filter out "command" segments', () => {
|
|
35
35
|
const fullPath = '/Users/username/Coding/Personal/yargs-file-commands/packages/yargs-file-commands/src/lib/commandPath.ts';
|
|
36
36
|
const baseDir = '/Users/username/Coding/Personal/yargs-file-commands/';
|
|
37
37
|
const expected = [
|
|
@@ -45,7 +45,7 @@ await test('segmentPath', async () => {
|
|
|
45
45
|
const result = segmentPath(fullPath, baseDir);
|
|
46
46
|
assert.deepStrictEqual(result, expected);
|
|
47
47
|
});
|
|
48
|
-
|
|
48
|
+
it('should handle empty segments correctly', () => {
|
|
49
49
|
const fullPath = '/Users/username/Coding/Personal/yargs-file-commands/packages/yargs-file-commands/src/lib/.hiddenFile';
|
|
50
50
|
const baseDir = '/Users/username/Coding/Personal/yargs-file-commands/';
|
|
51
51
|
const expected = [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yargs-file-commands",
|
|
3
3
|
"description": "A yargs helper function that lets you define your commands structure via directory and file naming conventions.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -19,13 +19,6 @@
|
|
|
19
19
|
"nodejs",
|
|
20
20
|
"typescript"
|
|
21
21
|
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsc",
|
|
24
|
-
"typecheck": "tsc --noEmit",
|
|
25
|
-
"test": "tsc && node --test dist/**/*.test.js",
|
|
26
|
-
"lint": "eslint --fix \"src/**/*.{ts,tsx}\"",
|
|
27
|
-
"format": "prettier \"src/**/*.{js,jsx,css,md,html,ts,tsx,json,yaml}\" --check"
|
|
28
|
-
},
|
|
29
22
|
"homepage": "https://github.com/bhouston/yargs-file-commands#readme",
|
|
30
23
|
"repository": {
|
|
31
24
|
"type": "git",
|
|
@@ -54,5 +47,12 @@
|
|
|
54
47
|
},
|
|
55
48
|
"engines": {
|
|
56
49
|
"node": ">=20.0.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "tsc && node --test dist/**/*.test.js --test-concurrency=1",
|
|
55
|
+
"lint": "eslint --fix \"src/**/*.{ts,tsx}\"",
|
|
56
|
+
"format": "prettier \"src/**/*.{js,jsx,css,md,html,ts,tsx,json,yaml}\" --check"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, it } from 'node:test';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { scanDirectory } from '../lib/scanDirectory.js';
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
describe('scanDirectory', async () => {
|
|
8
|
-
it('should find all command files in directory', async () => {
|
|
9
|
-
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
10
|
-
const files = await scanDirectory(commandsDir, {
|
|
11
|
-
extensions: ['.js'],
|
|
12
|
-
ignorePatterns: []
|
|
13
|
-
});
|
|
14
|
-
assert.equal(files.length, 2, 'Should find two command files');
|
|
15
|
-
assert(files.some((f) => f.includes('health.js')), 'Should find health.js');
|
|
16
|
-
assert(files.some((f) => f.includes('command.js')), 'Should find command.js');
|
|
17
|
-
});
|
|
18
|
-
it('should respect ignore patterns', async () => {
|
|
19
|
-
const commandsDir = path.join(__dirname, 'fixtures', 'commands');
|
|
20
|
-
const files = await scanDirectory(commandsDir, {
|
|
21
|
-
extensions: ['.js'],
|
|
22
|
-
ignorePatterns: [/health/]
|
|
23
|
-
});
|
|
24
|
-
assert.equal(files.length, 1, 'Should find one command file');
|
|
25
|
-
assert(files.some((f) => f.includes('command.js')), 'Should find command.js');
|
|
26
|
-
assert(!files.some((f) => f.includes('health.js')), 'Should not find health.js');
|
|
27
|
-
});
|
|
28
|
-
it('should handle non-existent directories', async () => {
|
|
29
|
-
const nonExistentDir = path.join(__dirname, 'fixtures', 'non-existent');
|
|
30
|
-
try {
|
|
31
|
-
await scanDirectory(nonExistentDir, {
|
|
32
|
-
extensions: ['.js']
|
|
33
|
-
});
|
|
34
|
-
assert.fail('Should have thrown an error');
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
assert(error instanceof Error);
|
|
38
|
-
assert(error.message.includes('ENOENT'), 'Error should indicate directory not found');
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|