diff --git a/.github/workflows/lucide-lab.yml b/.github/workflows/lucide-lab.yml new file mode 100644 index 000000000..f4c62c39c --- /dev/null +++ b/.github/workflows/lucide-lab.yml @@ -0,0 +1,30 @@ +name: Lucide Lab checks + +on: + pull_request: + paths: + - packages/lab/** + - packages/shared/** + - tools/build-icons/** + - tools/rollup-plugins/** + - pnpm-lock.yaml + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + cache: 'pnpm' + node-version-file: 'package.json' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm --filter @lucide/lab build diff --git a/.gitignore b/.gitignore index a590afca9..0ad3cb108 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,3 @@ docs/.vercel docs/.nitro docs/public/og/** !docs/public/og/general.png -.gitignore diff --git a/packages/lab/.gitignore b/packages/lab/.gitignore new file mode 100644 index 000000000..ce6fc2062 --- /dev/null +++ b/packages/lab/.gitignore @@ -0,0 +1,2 @@ +icons +icon-nodes.json diff --git a/packages/lab/package.json b/packages/lab/package.json index e6d41809f..425343541 100644 --- a/packages/lab/package.json +++ b/packages/lab/package.json @@ -27,17 +27,17 @@ "scripts": { "copy:license": "cp ../../LICENSE ./LICENSE", "build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:bundle && pnpm build:lib", - "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --iconFileExtension=.ts --withAliases --exportModuleNameCasing=camel --renderUniqueKey --aliasNamesOnly --aliasesFileExtension=.ts --exportFileName=index.ts", - "build:lib": "node ./scripts/buildLib.mjs", + "build:icons": "build-icons --input=../../lab --output=./src --templateSrc=./scripts/exportTemplate.mts --iconFileExtension=.ts --exportModuleNameCasing=camel --renderUniqueKey --exportFileName=index.ts", + "build:lib": "node ./scripts/buildLib.mts", "build:bundle": "rollup -c rollup.config.mjs", - "clean": "rm -f src/icons/*.ts && rm -rf dist", + "clean": "rm -f src/icons/*.ts && rm -rf dist && rm -rf icons && rm -f icon-nodes.json", "version": "pnpm version --git-tag-version=false" }, "devDependencies": { "@lucide/build-icons": "workspace:*", "@lucide/helpers": "workspace:*", "@lucide/rollup-plugins": "workspace:*", - "minimist": "^1.2.8", + "@types/node": "^22.20.1", "prettier": "^2.3.2", "rollup": "^4.9.2", "rollup-plugin-dts": "^6.1.0", diff --git a/packages/lab/rollup.config.mjs b/packages/lab/rollup.config.mjs index b5d8a521c..2a43e7061 100644 --- a/packages/lab/rollup.config.mjs +++ b/packages/lab/rollup.config.mjs @@ -1,6 +1,6 @@ import plugins from '@lucide/rollup-plugins'; import dts from 'rollup-plugin-dts'; -import pkg from './package.json' assert { type: 'json' }; +import pkg from './package.json' with { type: 'json' }; const outputFileName = 'lucide-lab'; const outputDir = 'dist'; diff --git a/packages/lab/scripts/buildLib.mjs b/packages/lab/scripts/buildLib.mjs deleted file mode 100644 index a35bb3326..000000000 --- a/packages/lab/scripts/buildLib.mjs +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import fs from 'fs'; -import path from 'path'; -import getArgumentOptions from 'minimist'; -import { parseSync } from 'svgson'; - -import { readSvgDirectory, getCurrentDirPath } from '@lucide/helpers'; -import readSvgs from './readSvgs.mjs'; -import generateIconNodes from './generateIconNodes.mjs'; -import copyIcons from './copyIcons.mjs'; - -import pkg from '../package.json' assert { type: 'json' }; - -const cliArguments = getArgumentOptions(process.argv.slice(2)); -const createDirectory = (dir) => { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } -}; - -const currentDir = getCurrentDirPath(import.meta.url); - -const PACKAGE_DIR = path.resolve(currentDir, '../'); -const ICONS_DIR = path.join(PACKAGE_DIR, '../../icons'); - -const license = `@license ${pkg.name} v${pkg.version} - ${pkg.license}`; - -const svgFiles = await readSvgDirectory(ICONS_DIR); -const svgs = await readSvgs(svgFiles, ICONS_DIR); - -const parsedSvgs = svgs.map(({ name, contents }) => ({ - name, - contents, - parsedSvg: parseSync(contents), -})); - -await Promise.all([ - generateIconNodes(parsedSvgs, PACKAGE_DIR), - copyIcons(parsedSvgs, PACKAGE_DIR, license), -]); diff --git a/packages/lab/scripts/buildLib.mts b/packages/lab/scripts/buildLib.mts new file mode 100644 index 000000000..02712892f --- /dev/null +++ b/packages/lab/scripts/buildLib.mts @@ -0,0 +1,20 @@ +import path from 'path'; + +import { readSvgDirectory, getCurrentDirPath } from '@lucide/helpers'; +import readSvgs from './readSvgs.mts'; +import generateIconNodes from './generateIconNodes.mts'; +import copyIcons from './copyIcons.mts'; + +import pkg from '../package.json' with { type: 'json' }; + +const currentDir = getCurrentDirPath(import.meta.url); + +const PACKAGE_DIR = path.resolve(currentDir, '../'); +const ICONS_DIR = path.join(PACKAGE_DIR, '../../lab'); + +const license = `@license ${pkg.name} v${pkg.version} - ${pkg.license}`; + +const svgFiles = await readSvgDirectory(ICONS_DIR); +const svgs = await readSvgs(svgFiles, ICONS_DIR); + +await Promise.all([generateIconNodes(svgs, PACKAGE_DIR), copyIcons(svgs, PACKAGE_DIR, license)]); diff --git a/packages/lab/scripts/copyIcons.mjs b/packages/lab/scripts/copyIcons.mts similarity index 76% rename from packages/lab/scripts/copyIcons.mjs rename to packages/lab/scripts/copyIcons.mts index a55b20812..069330687 100644 --- a/packages/lab/scripts/copyIcons.mjs +++ b/packages/lab/scripts/copyIcons.mts @@ -1,13 +1,16 @@ import { writeFile } from 'fs/promises'; import { existsSync, unlinkSync, mkdirSync } from 'fs'; +import { type SVGFile } from './readSvgs.mts'; -export default async function copyIcons(parsedSvgs, packageDir, license) { +export default async function copyIcons( + parsedSvgs: SVGFile[], + packageDir: string, + license: string, +) { const iconsDirectory = `${packageDir}/icons`; if (existsSync(iconsDirectory)) { - try { - unlinkSync(iconsDirectory); - } catch (error) {} + unlinkSync(iconsDirectory); } if (!existsSync(iconsDirectory)) { diff --git a/packages/lab/scripts/exportTemplate.mjs b/packages/lab/scripts/exportTemplate.mts similarity index 52% rename from packages/lab/scripts/exportTemplate.mjs rename to packages/lab/scripts/exportTemplate.mts index fb4b9e1fb..b8157eea8 100644 --- a/packages/lab/scripts/exportTemplate.mjs +++ b/packages/lab/scripts/exportTemplate.mts @@ -1,12 +1,13 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs'; +import base64SVG from '@lucide/build-icons/utils/base64SVG'; +import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate'; import { toCamelCase } from '@lucide/helpers'; -export default ({ componentName, iconName, children, getSvg, deprecated }) => { - const svgContents = getSvg(); - const svgBase64 = base64SVG(svgContents); +export default defineExportTemplate( + async ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => { + const svgContents = await getSvg(); + const svgBase64 = base64SVG(svgContents); - return ` + return ` import type { IconNode } from '../types'; /** @@ -17,10 +18,11 @@ import type { IconNode } from '../types'; * @see https://lucide.dev/guide/packages/lucide - Documentation * * @returns {Array} - * ${deprecated ? '@deprecated' : ''} + * ${deprecated ? `@deprecated ${deprecationReason}` : ''} */ const ${toCamelCase(componentName)}: IconNode = ${JSON.stringify(children)}; export default ${toCamelCase(componentName)}; `; -}; + }, +); diff --git a/packages/lab/scripts/generateIconNodes.mjs b/packages/lab/scripts/generateIconNodes.mts similarity index 62% rename from packages/lab/scripts/generateIconNodes.mjs rename to packages/lab/scripts/generateIconNodes.mts index 5b34a2cea..a7b098dcb 100644 --- a/packages/lab/scripts/generateIconNodes.mjs +++ b/packages/lab/scripts/generateIconNodes.mts @@ -1,11 +1,12 @@ import { writeFile } from '@lucide/helpers'; +import { type SVGFile } from './readSvgs.mts'; -export default async function generateIconNodes(parsedSvgs, packageDir) { +export default async function generateIconNodes(parsedSvgs: SVGFile[], packageDir: string) { const iconNodes = parsedSvgs.reduce((acc, { name, parsedSvg }) => { acc[name] = parsedSvg.children.map(({ name, attributes }) => [name, attributes]); return acc; - }, {}); + }, {} as Record | undefined][]>); const iconNodesStringified = JSON.stringify(iconNodes, null, 2); diff --git a/packages/lab/scripts/readSvgs.mjs b/packages/lab/scripts/readSvgs.mts similarity index 55% rename from packages/lab/scripts/readSvgs.mjs rename to packages/lab/scripts/readSvgs.mts index 5d789308f..51d240f10 100644 --- a/packages/lab/scripts/readSvgs.mjs +++ b/packages/lab/scripts/readSvgs.mts @@ -1,6 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ import { basename } from 'path'; import { readSvg } from '@lucide/helpers'; +import { type INode, parseSync } from 'svgson'; /** * Build an object in the format: `{ : }`. @@ -8,13 +9,23 @@ import { readSvg } from '@lucide/helpers'; * @param {Function} getSvg - A function that returns the contents of an SVG file given a filename. * @returns {Object} */ -export default async function readSVGs(svgFiles, iconsDirectory) { - const svgReadPromises = svgFiles.map(async (svgFile) => { +export default function readSVGs(svgFiles: string[], iconsDirectory: string) { + const SVGReadPromises = svgFiles.map(async (svgFile) => { const name = basename(svgFile, '.svg'); const contents = await readSvg(svgFile, iconsDirectory); - return { name, contents }; + return { + name, + contents, + parsedSvg: parseSync(contents), + }; }); - return Promise.all(svgReadPromises); + return Promise.all(SVGReadPromises); } + +export type SVGFile = { + name: string; + contents: string; + parsedSvg: INode; +}; diff --git a/packages/lab/src/lucide-lab.ts b/packages/lab/src/lucide-lab.ts index 098bba469..838008a0b 100644 --- a/packages/lab/src/lucide-lab.ts +++ b/packages/lab/src/lucide-lab.ts @@ -1,2 +1 @@ export * from './icons'; -export * from './aliases'; diff --git a/packages/lab/tsconfig.json b/packages/lab/tsconfig.json index 7fdaedbf6..6e7572cfe 100644 --- a/packages/lab/tsconfig.json +++ b/packages/lab/tsconfig.json @@ -11,6 +11,7 @@ "allowJs": true, "lib": ["esnext", "dom"], "resolveJsonModule": true, + "allowImportingTsExtensions": true, "sourceMap": true, "outDir": "./dist", }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d227abbb0..b16c93de4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -406,9 +406,9 @@ importers: '@lucide/rollup-plugins': specifier: workspace:* version: link:../../tools/rollup-plugins - minimist: - specifier: ^1.2.8 - version: 1.2.8 + '@types/node': + specifier: ^22.20.1 + version: 22.20.1 prettier: specifier: ^2.3.2 version: 2.8.8 diff --git a/tools/build-icons/cli.ts b/tools/build-icons/cli.ts index b2b4c6e9d..6eeb78f0a 100755 --- a/tools/build-icons/cli.ts +++ b/tools/build-icons/cli.ts @@ -34,11 +34,12 @@ interface CliArguments { useDefaultExports?: boolean; pretty?: boolean; output: string | undefined; + input: string | undefined; } const cliArguments = getArgumentOptions(process.argv.slice(2)) as unknown as CliArguments; -const ICONS_DIR = path.resolve(process.cwd(), '../../icons'); +const ICONS_DIR = path.resolve(process.cwd(), cliArguments.input || '../../icons'); const OUTPUT_DIR = path.resolve(process.cwd(), cliArguments.output || '../build'); if (!fs.existsSync(OUTPUT_DIR)) {