feat(metadata|docs|site): reuse metadata from a base icon with $extends: (#4360)

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix resolving icon metadata

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix resolving icon metadata

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Eric Fennis
2026-10-29 17:29:58 +01:00
committed by GitHub
co-authored by Claude Opus 5.5 github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
parent e2c3360976
commit 7bf67d3b1c
14 changed files with 417 additions and 131 deletions
+6 -21
View File
@@ -1,10 +1,9 @@
import fs from 'fs';
import path from 'path';
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
import { readAllMetadata } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
const location = path.resolve(currentDir, '.vitepress/data', 'iconMetaData.ts');
@@ -12,29 +11,15 @@ if (fs.existsSync(location)) {
fs.unlinkSync(location);
}
const iconMetaIndexFileImports = [];
const iconMetaIndexFileExports = [];
iconJsonFiles.forEach((iconJsonFile) => {
const iconName = path.basename(iconJsonFile, '.json');
iconMetaIndexFileImports.push(
`import ${toCamelCase(iconName)}Metadata from '../../../icons/${iconName}.json';`,
);
iconMetaIndexFileExports.push(` '${iconName}': ${toCamelCase(iconName)}Metadata,`);
});
// The docs read this index raw and never re-resolve, so resolve the
// `$extends:<icon-name>` markers here and bake the inherited
// tags/categories/contributors into the generated file.
const iconMetaData = await readAllMetadata(ICONS_DIR);
try {
await fs.promises.writeFile(
location,
`\
${iconMetaIndexFileImports.join('\n')}
export default {
${iconMetaIndexFileExports.join('\n')}
}
`,
`export default ${JSON.stringify(iconMetaData, null, 2)};\n`,
'utf-8',
);