From 588113e359113fd562077e72b73d5865e5ff877a Mon Sep 17 00:00:00 2001 From: Vugar Bakhishov Date: Mon, 2 Nov 2026 21:00:04 +0400 Subject: [PATCH] test(build-icons): cover Windows file URL imports (#4862) * fix(dev): import icon templates with file URLs Convert the resolved template path to an encoded file URL so the Node.js ESM loader accepts Windows paths and URL characters. * fix(dev): import icon metadata with file URLs Encode metadata file paths before dynamic JSON import, including Windows drive paths and URL characters. * test(dev): cover icon build assets in paths with URL characters Run the icon CLI in a Node process with a small SVG and metadata fixture; verify generation and metadata in a path containing spaces, # and %. * test(build-icons): cover Windows file URL imports --------- Co-authored-by: Vugar Bakhishov --- .../lucide-react/tests/build-icons.spec.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 packages/lucide-react/tests/build-icons.spec.ts diff --git a/packages/lucide-react/tests/build-icons.spec.ts b/packages/lucide-react/tests/build-icons.spec.ts new file mode 100644 index 000000000..3fcfc9ef7 --- /dev/null +++ b/packages/lucide-react/tests/build-icons.spec.ts @@ -0,0 +1,59 @@ +// @vitest-environment node + +import { afterEach, expect, it } from 'vitest'; +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const fixtureDirectories: string[] = []; + +afterEach(() => { + fixtureDirectories.splice(0).forEach((directory) => { + rmSync(directory, { recursive: true, force: true }); + }); +}); + +it('builds icons with templates and metadata in paths containing URL characters', () => { + const directory = mkdtempSync(path.join(tmpdir(), 'lucide-build-icons #%-')); + fixtureDirectories.push(directory); + const input = path.join(directory, 'input'); + const output = path.join(directory, 'output'); + const template = path.join(directory, 'template.mjs'); + mkdirSync(input); + writeFileSync( + path.join(input, 'sample.svg'), + '', + ); + writeFileSync(path.join(input, 'sample.json'), JSON.stringify({ aliases: ['sample-alias'] })); + writeFileSync( + template, + 'export default ({ iconData }) => "export default " + JSON.stringify(iconData);', + ); + + const cli = fileURLToPath(new URL('../../../tools/build-icons/cli.ts', import.meta.url)); + const result = spawnSync( + process.execPath, + [ + cli, + `--input=${input}`, + `--output=${output}`, + `--templateSrc=${template}`, + '--pretty=false', + '--silent', + ], + { encoding: 'utf8', timeout: 30_000 }, + ); + + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(0); + expect(result.stderr).toBe(''); + const generatedIcon = readFileSync(path.join(output, 'icons', 'sample.js'), 'utf8'); + expect(JSON.parse(generatedIcon.replace(/^export default /, ''))).toMatchObject({ + name: 'sample', + aliases: ['sample-alias'], + node: [['path', { d: 'M1 1h2' }]], + }); + expect(readFileSync(path.join(output, 'icons', 'index.js'), 'utf8')).toContain('sample'); +});