mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-13 13:48:26 +00:00
feat(scripts): add --output to write icon usage as JSON
Writes { [icon]: [...usage] } to a file: grouped use cases with --describe,
otherwise every individual usage context. Implies --context.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q2D9GYbnSV56MAVVhHPRhH
This commit is contained in:
@@ -26,6 +26,12 @@ pnpm icon-usage --icons-file exports.txt --describe --max-contexts 30
|
||||
|
||||
`--describe` does the same, then sends the collected contexts to an OpenAI model (`OPENAI_API_KEY` required; `--model` or `OPENAI_MODEL` selects the model, `gpt-5-mini` by default). The model groups them into several use cases per icon, each with a short lowercase `useCase` phrase in the style of the `use-cases` field in `icons/*.json`, a description of where the icon appears in the UI, its approximate share of the sample, and example links. The icon's existing `use-cases` are included in the prompt. Responses are cached in the cache directory.
|
||||
|
||||
Use `--output <file>` (or `-o`) to write the results per icon to a JSON file shaped as `{ [icon]: [...usage] }`. With `--describe`, each entry is a use case (`useCase`, `description`, `sampleShare`, `examples`). Without it, each entry is an individual usage (`description`, `repository`, `url`, `kind`, `parents`, `component`, `labels`, `handlers`, `links`). `--output` implies `--context`, and the normal terminal, `--json` or `--csv` output is still printed.
|
||||
|
||||
```sh
|
||||
pnpm icon-usage --icons-file exports.txt --describe --output usage.json
|
||||
```
|
||||
|
||||
To keep runs bounded, searching for an icon stops once `--max-contexts` contexts (default 40) are collected, and at most two contexts are taken from each repository. When the limit is hit, the repository counts for that icon are a lower bound, and a warning says so. JSON output includes every context per match under `repositories.*.matches[].contexts`, and per-icon summaries and descriptions under `usage`.
|
||||
|
||||
Context extraction uses heuristics, not a full parser. It works best for JSX (React, Preact, Solid), Vue, Svelte and Astro templates in the same file as the import. Angular templates in separate `.html` files are not fetched.
|
||||
|
||||
@@ -156,6 +156,7 @@ type CliOptions = {
|
||||
maxContexts: number;
|
||||
model: string;
|
||||
iconsFiles: string[];
|
||||
output: string | null;
|
||||
};
|
||||
|
||||
type SearchItem = {
|
||||
@@ -293,6 +294,7 @@ const parseArgs = (argv: string[]): CliOptions => {
|
||||
maxContexts: DEFAULT_MAX_CONTEXTS,
|
||||
model: process.env.OPENAI_MODEL ?? DEFAULT_DESCRIBE_MODEL,
|
||||
iconsFiles: [],
|
||||
output: null,
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
@@ -406,6 +408,18 @@ const parseArgs = (argv: string[]): CliOptions => {
|
||||
options.model = arg.slice('--model='.length);
|
||||
continue;
|
||||
}
|
||||
if (arg === '--output' || arg === '-o') {
|
||||
const file = argv[++index];
|
||||
if (!file) throw new Error('--output requires a path.');
|
||||
options.output = path.resolve(file);
|
||||
options.context = true;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith('--output=')) {
|
||||
options.output = path.resolve(arg.slice('--output='.length));
|
||||
options.context = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === '--icons-file') {
|
||||
const file = argv[++index];
|
||||
if (!file) throw new Error('--icons-file requires a path.');
|
||||
@@ -529,6 +543,9 @@ Options:
|
||||
--max-files-per-query <n>
|
||||
Max candidate files to validate per query slice (default: ${DEFAULT_MAX_FILES_PER_QUERY})
|
||||
--thorough Shortcut for --max-pages ${THOROUGH_MAX_PAGES} --max-files-per-query ${THOROUGH_MAX_FILES_PER_QUERY}
|
||||
--output, -o <path> Write usage per icon to a JSON file as { [icon]: [...usage] }:
|
||||
use cases with --describe, otherwise individual usages
|
||||
(implies --context)
|
||||
--icons-file <path> Read icon names (one per line, kebab-case or export names
|
||||
like Trash2) from a file; repeatable
|
||||
--context Extract how each icon is used in context (parent element,
|
||||
@@ -1411,6 +1428,39 @@ const printUsage = (usage: Record<string, IconUsage>) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes `{ [icon]: [...usage] }`: the grouped use cases when `--describe` ran,
|
||||
* otherwise every individual usage context found for the icon.
|
||||
*/
|
||||
const writeOutput = async (file: string, result: AnalysisResult, options: CliOptions) => {
|
||||
const output = Object.fromEntries(
|
||||
options.icons.map((icon) => {
|
||||
if (options.describe) return [icon, result.usage?.[icon]?.descriptions ?? []];
|
||||
const usages = Object.entries(result.repositories).flatMap(([repository, record]) =>
|
||||
record.matches
|
||||
.filter((match) => match.icon === icon)
|
||||
.flatMap((match) =>
|
||||
(match.contexts ?? []).map((context) => ({
|
||||
description: context.description,
|
||||
repository,
|
||||
url: `${match.htmlUrl}#L${context.line}`,
|
||||
kind: context.kind,
|
||||
parents: context.ancestors,
|
||||
component: context.component,
|
||||
labels: [...context.labels, ...context.i18nKeys],
|
||||
handlers: context.handlers,
|
||||
links: context.links,
|
||||
})),
|
||||
),
|
||||
);
|
||||
return [icon, usages];
|
||||
}),
|
||||
);
|
||||
await fs.mkdir(path.dirname(file), { recursive: true });
|
||||
await fs.writeFile(file, `${JSON.stringify(output, null, 2)}\n`);
|
||||
status(`wrote usage for ${options.icons.length} icon(s) to ${file}`);
|
||||
};
|
||||
|
||||
const printCsv = (result: AnalysisResult) => {
|
||||
const rows = [['repository', 'icons', 'packages', 'matches', 'contexts']];
|
||||
for (const [repo, record] of Object.entries(result.repositories)) {
|
||||
@@ -1438,6 +1488,7 @@ try {
|
||||
...(await readIconsFiles(options.iconsFiles)),
|
||||
]);
|
||||
const result = await analyze(options);
|
||||
if (options.output) await writeOutput(options.output, result, options);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else if (options.csv) {
|
||||
|
||||
Reference in New Issue
Block a user