Skip to content

Commit 81ce0dd

Browse files
indexzeroclaude
andauthored
feat(cli) add input dedup, --no-cache flag, and subcommand fallback (#26)
Add `uniqueNames()` to `fetch-list.js` to deduplicate package names from JSON and text inputs, handling both plain strings and `{name}` objects. Wire up the existing `--no-cache` CLI flag to actually pass `{ cache: false }` through to the packument client. Refactor `importCommand` in `index.js` to gracefully handle missing subcommands by attempting to load the command's `index.js` module before falling back to a usage error. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 24a75fc commit 81ce0dd

2 files changed

Lines changed: 62 additions & 11 deletions

File tree

cli/cli/src/cmd/packument/fetch-list.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,49 @@ function parseTextFile(filepath) {
4242
return packages;
4343
}
4444

45+
/**
46+
* Extract unique package names from a parsed list.
47+
* Handles both plain strings and {name, ...} objects.
48+
* @param {Array<string|{name: string}>} items - Raw items from input
49+
* @returns {string[]} Deduplicated array of package names
50+
*/
51+
function uniqueNames(items) {
52+
const seen = new Set();
53+
const names = [];
54+
55+
for (const item of items) {
56+
const name = typeof item === 'string' ? item
57+
: (item && typeof item.name === 'string') ? item.name
58+
: null;
59+
60+
if (!name) {
61+
console.warn(`Warning: Skipping unrecognized entry: ${JSON.stringify(item)}`);
62+
continue;
63+
}
64+
65+
if (!seen.has(name)) {
66+
seen.add(name);
67+
names.push(name);
68+
}
69+
}
70+
71+
return names;
72+
}
73+
4574
/**
4675
* Load package names from input file
4776
* @param {string} fullpath - Absolute path to input file
48-
* @returns {Promise<string[]>} Array of package names
77+
* @returns {Promise<string[]>} Deduplicated array of package names
4978
*/
5079
async function loadPackageNames(fullpath) {
5180
const ext = extname(fullpath).toLowerCase();
5281

5382
if (ext === '.json') {
5483
const { default: jsonData } = await import(fullpath, { with: { type: 'json' } });
55-
return Array.isArray(jsonData) ? jsonData : [jsonData];
84+
const items = Array.isArray(jsonData) ? jsonData : [jsonData];
85+
return uniqueNames(items);
5686
} else if (ext === '.txt' || ext === '.text' || ext === '') {
57-
return parseTextFile(fullpath);
87+
return uniqueNames(parseTextFile(fullpath));
5888
} else {
5989
throw new Error(`Unsupported file type: ${ext}. Use .json or .txt files.`);
6090
}
@@ -211,6 +241,10 @@ export const command = async cli => {
211241
env
212242
});
213243

244+
// Resolve cache flag: --no-cache sets cli.values.cache to false
245+
const useCache = cli.values.cache !== false;
246+
const requestOptions = useCache ? {} : { cache: false };
247+
214248
// Track progress with atomic counter for concurrent access
215249
let processedCount = 0;
216250
let interrupted = false;
@@ -247,7 +281,7 @@ export const command = async cli => {
247281
}
248282

249283
try {
250-
const entry = await client.request(name);
284+
const entry = await client.request(name, requestOptions);
251285
const cached = entry?.hit ?? false;
252286

253287
if (useCheckpoint) {

cli/cli/src/index.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,31 @@ async function importCommand(cmd, subcmd) {
5252
process.exit(1);
5353
}
5454

55-
const cmdpath = `${cmd}/${subcmd}`;
55+
if (subcmd) {
56+
const cmdpath = `${cmd}/${subcmd}`;
57+
try {
58+
return await import(`./cmd/${cmdpath}.js`);
59+
} catch (err) {
60+
throw error('Failed to load command', {
61+
found: cmdpath,
62+
cause: err
63+
});
64+
}
65+
}
66+
67+
// No subcommand provided: try loading the command's index module
5668
try {
57-
return await import(`./cmd/${cmdpath}.js`);
58-
} catch (err) {
59-
throw error('Failed to load command', {
60-
found: cmdpath,
61-
cause: err
62-
});
69+
return await import(`./cmd/${cmd}/index.js`);
70+
} catch {
71+
// No index.js exists for this command; return a stub so --help
72+
// can still print general CLI usage via output.js fallback
73+
return {
74+
command: () => {
75+
console.error(`Error: No subcommand provided for '${cmd}'`);
76+
console.error(cli.usage());
77+
process.exit(1);
78+
}
79+
};
6380
}
6481
}
6582

0 commit comments

Comments
 (0)