@taterlaxmi/react-icon-library 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,32 +30,39 @@ The `dev` script uses `brand-assets/` as its input and writes components to `src
30
30
 
31
31
  **Rerunning `npm run dev` overwrites generated files** in `src/icons/`. Avoid editing generated components by hand; change the source image and regenerate instead.
32
32
 
33
- ## Use the generator in another repository
33
+ ## Use icons in another repository
34
34
 
35
- The generator package runs against image files in the React app where you invoke it. In the other React application's root, place the image in a folder such as `brand-assets/kiwi.png`, then install this GitHub package as a development dependency:
35
+ Install the package in a React application:
36
36
 
37
37
  ```bash
38
- npm install --save-dev github:taterlaxmi/react-icon-library
39
- npx react-icon-library ./brand-assets --output ./src/icons
38
+ npm install @taterlaxmi/react-icon-library
40
39
  ```
41
40
 
42
- The CLI does not overwrite existing files by default. To regenerate an existing icon set, add `--overwrite`:
41
+ Import icons directly from the package root. You do not need to copy an image or run the generator in the consuming app:
43
42
 
44
- ```bash
45
- npx react-icon-library ./brand-assets --output ./src/icons --overwrite
43
+ ```tsx
44
+ import { Kiwi } from '@taterlaxmi/react-icon-library';
45
+
46
+ export function Brand() {
47
+ return <Kiwi title="Kiwi" width={32} height={32} />;
48
+ }
46
49
  ```
47
50
 
48
- Commit the generated `src/icons/` files in the React app. Import its generated icon like this:
51
+ The package declares React as a peer dependency, so the consuming application should already have React installed. If the app uses TypeScript, `KiwiProps` is also exported:
49
52
 
50
53
  ```tsx
51
- import { Kiwi } from './icons';
54
+ import { Kiwi, type KiwiProps } from '@taterlaxmi/react-icon-library';
55
+ ```
52
56
 
53
- export function Brand() {
54
- return <Kiwi title="Kiwi" width={32} height={32} />;
55
- }
57
+ ## Generate additional icons
58
+
59
+ The package also provides a CLI and programmatic generator for your own image assets. In the consuming app, put images in a folder such as `brand-assets/`, then run:
60
+
61
+ ```bash
62
+ npx react-icon-library ./brand-assets --output ./src/icons
56
63
  ```
57
64
 
58
- The installed package provides the generator CLI and `generateIcons()` API; it does **not** currently export this repository's sample `Kiwi` component from the package root. To use Kiwi in another app, put `kiwi.png` in that app's `brand-assets/` folder and generate the component there as shown above.
65
+ The CLI does not overwrite existing generated files by default. Add `--overwrite` to regenerate them. Generated components are local source files and can be imported from the app's own `./icons` module.
59
66
 
60
67
  ## CLI options
61
68
 
@@ -0,0 +1,178 @@
1
+ // src/index.ts
2
+ import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
3
+ import path from "path";
4
+ import { randomUUID } from "crypto";
5
+ import { imageSize } from "image-size";
6
+ import { transform } from "@svgr/core";
7
+
8
+ // src/icons/Kiwi.tsx
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ var imageSource = "data:image/png;base64,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";
11
+ function Kiwi({ title, width, height, ...props }) {
12
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 447 447", width: width ?? "1em", height: height ?? "1em", role: title ? "img" : void 0, "aria-hidden": title ? void 0 : true, ...props, children: [
13
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
14
+ /* @__PURE__ */ jsx("image", { href: imageSource, x: "0", y: "0", width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet" })
15
+ ] });
16
+ }
17
+
18
+ // src/index.ts
19
+ var RASTER_MIME_TYPES = {
20
+ ".png": "image/png",
21
+ ".jpg": "image/jpeg",
22
+ ".jpeg": "image/jpeg",
23
+ ".gif": "image/gif",
24
+ ".webp": "image/webp",
25
+ ".avif": "image/avif"
26
+ };
27
+ var VECTOR_EXTENSIONS = /* @__PURE__ */ new Set([".svg"]);
28
+ function componentName(filePath) {
29
+ const baseName = path.basename(filePath, path.extname(filePath));
30
+ const words = baseName.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").match(/[A-Za-z0-9]+/g);
31
+ const name = (words ?? []).map((word) => word[0].toUpperCase() + word.slice(1)).join("");
32
+ const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;
33
+ return safeName || "Icon";
34
+ }
35
+ function isWithin(parent, child) {
36
+ const relative = path.relative(parent, child);
37
+ return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
38
+ }
39
+ function errorMessage(error) {
40
+ return error instanceof Error ? error.message : String(error);
41
+ }
42
+ async function findAssets(inputDir, outputDir, recursive) {
43
+ const assets = [];
44
+ async function visit(directory) {
45
+ const entries = await readdir(directory, { withFileTypes: true });
46
+ entries.sort((a, b) => a.name.localeCompare(b.name));
47
+ for (const entry of entries) {
48
+ const fullPath = path.join(directory, entry.name);
49
+ if (entry.isSymbolicLink()) continue;
50
+ if (entry.isDirectory()) {
51
+ if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);
52
+ continue;
53
+ }
54
+ if (!entry.isFile()) continue;
55
+ const extension = path.extname(entry.name).toLowerCase();
56
+ if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {
57
+ assets.push({ path: fullPath, name: componentName(fullPath), extension });
58
+ }
59
+ }
60
+ }
61
+ await visit(inputDir);
62
+ return assets;
63
+ }
64
+ async function rasterComponent(asset, component) {
65
+ const bytes = await readFile(asset.path);
66
+ let dimensions;
67
+ try {
68
+ dimensions = imageSize(bytes);
69
+ } catch (error) {
70
+ throw new Error(`Cannot read image dimensions for "${asset.path}": ${errorMessage(error)}`);
71
+ }
72
+ if (!dimensions.width || !dimensions.height) throw new Error(`Image "${asset.path}" does not have valid dimensions.`);
73
+ const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString("base64")}`;
74
+ return `import type { SVGProps } from 'react';
75
+
76
+ export interface ${component}Props extends SVGProps<SVGSVGElement> {
77
+ title?: string;
78
+ }
79
+
80
+ const imageSource = ${JSON.stringify(dataUri)};
81
+
82
+ export default function ${component}({ title, width, height, ...props }: ${component}Props) {
83
+ return (
84
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${dimensions.width} ${dimensions.height}" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>
85
+ {title ? <title>{title}</title> : null}
86
+ <image href={imageSource} x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
87
+ </svg>
88
+ );
89
+ }
90
+ `;
91
+ }
92
+ async function svgComponent(asset, component) {
93
+ const source = await readFile(asset.path, "utf8");
94
+ try {
95
+ return await transform(source, {
96
+ plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
97
+ typescript: true,
98
+ jsxRuntime: "automatic",
99
+ icon: true,
100
+ titleProp: true,
101
+ exportType: "default",
102
+ prettier: false
103
+ }, { componentName: component });
104
+ } catch (error) {
105
+ throw new Error(`Cannot convert SVG "${asset.path}": ${errorMessage(error)}`);
106
+ }
107
+ }
108
+ async function generateIcons(options) {
109
+ const inputDir = path.resolve(options.inputDir);
110
+ const outputDir = path.resolve(options.outputDir);
111
+ const recursive = options.recursive ?? true;
112
+ const overwrite = options.overwrite ?? false;
113
+ let inputStat;
114
+ try {
115
+ inputStat = await stat(inputDir);
116
+ } catch {
117
+ throw new Error(`Input directory does not exist: "${inputDir}"`);
118
+ }
119
+ if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: "${inputDir}"`);
120
+ const assets = await findAssets(inputDir, outputDir, recursive);
121
+ if (assets.length === 0) throw new Error(`No supported image assets found in "${inputDir}".`);
122
+ const names = /* @__PURE__ */ new Map();
123
+ for (const asset of assets) {
124
+ const normalizedName = asset.name.toLocaleLowerCase("en-US");
125
+ const previous = names.get(normalizedName);
126
+ if (previous) {
127
+ throw new Error(`Name collision: "${previous}" and "${asset.path}" both generate the component "${asset.name}". Rename one of the source files.`);
128
+ }
129
+ names.set(normalizedName, asset.path);
130
+ }
131
+ const converted = await Promise.all(assets.map(async (asset) => ({
132
+ asset,
133
+ contents: VECTOR_EXTENSIONS.has(asset.extension) ? await svgComponent(asset, asset.name) : await rasterComponent(asset, asset.name)
134
+ })));
135
+ const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join("\n")}
136
+ `;
137
+ const outputs = [
138
+ ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),
139
+ { filePath: path.join(outputDir, "index.ts"), contents: indexContents }
140
+ ];
141
+ if (!overwrite) {
142
+ const conflicts = [];
143
+ for (const output of outputs) {
144
+ try {
145
+ await access(output.filePath);
146
+ conflicts.push(output.filePath);
147
+ } catch {
148
+ }
149
+ }
150
+ if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(", ")}. Use --overwrite to replace them.`);
151
+ }
152
+ await mkdir(outputDir, { recursive: true });
153
+ for (const output of outputs) {
154
+ const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;
155
+ try {
156
+ await writeFile(temporaryPath, output.contents, "utf8");
157
+ if (overwrite) await rm(output.filePath, { force: true });
158
+ await rename(temporaryPath, output.filePath);
159
+ } catch (error) {
160
+ await rm(temporaryPath, { force: true });
161
+ throw new Error(`Failed to write "${output.filePath}": ${errorMessage(error)}`);
162
+ }
163
+ }
164
+ return {
165
+ icons: converted.map(({ asset }) => ({
166
+ name: asset.name,
167
+ source: path.relative(inputDir, asset.path),
168
+ componentFile: path.join(outputDir, `${asset.name}.tsx`)
169
+ })),
170
+ outputDir
171
+ };
172
+ }
173
+
174
+ export {
175
+ Kiwi,
176
+ generateIcons
177
+ };
178
+ //# sourceMappingURL=chunk-7UCNUTGX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/icons/Kiwi.tsx"],"sourcesContent":["import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n inputDir: string;\r\n outputDir: string;\r\n recursive?: boolean;\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}\r\n\r\nexport { default as Kiwi } from './icons/Kiwi.js';\r\nexport type { KiwiProps } from './icons/Kiwi.js';","import type { SVGProps } from 'react';\n\nexport interface KiwiProps extends SVGProps<SVGSVGElement> {\n title?: string;\n}\n\nconst imageSource = \"data:image/png;base64,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\";\n\nexport default function Kiwi({ title, width, height, ...props }: KiwiProps) {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 447 447\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\n {title ? <title>{title}</title> : null}\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\n </svg>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/cli.cjs CHANGED
@@ -32,6 +32,11 @@ var import_node_path = __toESM(require("path"), 1);
32
32
  var import_node_crypto = require("crypto");
33
33
  var import_image_size = require("image-size");
34
34
  var import_core = require("@svgr/core");
35
+
36
+ // src/icons/Kiwi.tsx
37
+ var import_jsx_runtime = require("react/jsx-runtime");
38
+
39
+ // src/index.ts
35
40
  var RASTER_MIME_TYPES = {
36
41
  ".png": "image/png",
37
42
  ".jpg": "image/jpeg",
@@ -90,7 +95,6 @@ async function rasterComponent(asset, component) {
90
95
  return `import type { SVGProps } from 'react';
91
96
 
92
97
  export interface ${component}Props extends SVGProps<SVGSVGElement> {
93
- /** Accessible label. Omit for a decorative icon. */
94
98
  title?: string;
95
99
  }
96
100
 
@@ -98,15 +102,7 @@ const imageSource = ${JSON.stringify(dataUri)};
98
102
 
99
103
  export default function ${component}({ title, width, height, ...props }: ${component}Props) {
100
104
  return (
101
- <svg
102
- xmlns="http://www.w3.org/2000/svg"
103
- viewBox="0 0 ${dimensions.width} ${dimensions.height}"
104
- width={width ?? '1em'}
105
- height={height ?? '1em'}
106
- role={title ? 'img' : undefined}
107
- aria-hidden={title ? undefined : true}
108
- {...props}
109
- >
105
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${dimensions.width} ${dimensions.height}" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>
110
106
  {title ? <title>{title}</title> : null}
111
107
  <image href={imageSource} x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
112
108
  </svg>
@@ -207,7 +203,7 @@ program.name("react-icon-library").description("Generate reusable React icon com
207
203
  ...options.overwrite === void 0 ? {} : { overwrite: options.overwrite }
208
204
  });
209
205
  console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);
210
- for (const icon of result.icons) console.log(` ${icon.name} \u2190 ${icon.source}`);
206
+ for (const icon of result.icons) console.log(` ${icon.name} <- ${icon.source}`);
211
207
  } catch (error) {
212
208
  console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
213
209
  process.exitCode = 1;
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command } from 'commander';\r\nimport { generateIcons } from './index.js';\r\n\r\nconst program = new Command();\r\nprogram\r\n .name('react-icon-library')\r\n .description('Generate reusable React icon components from SVG and raster image files.')\r\n .argument('[input-dir]', 'directory containing image assets', 'icons')\r\n .option('-o, --output <directory>', 'output directory for generated components', 'src/icons')\r\n .option('--no-recursive', 'only read images directly inside the input directory')\r\n .option('--overwrite', 'replace existing generated component files')\r\n .action(async (inputDir: string, options: { output: string; recursive: boolean; overwrite?: boolean }) => {\r\n try {\r\n const result = await generateIcons({\r\n inputDir,\r\n outputDir: options.output,\r\n recursive: options.recursive,\r\n ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),\r\n });\r\n console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);\r\n for (const icon of result.icons) console.log(` ${icon.name} ← ${icon.source}`);\r\n } catch (error) {\r\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\r\n process.exitCode = 1;\r\n }\r\n });\r\n\r\nprogram.parseAsync();","import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n /** Directory containing source image assets. */\r\n inputDir: string;\r\n /** Directory where generated React components and their barrel are written. */\r\n outputDir: string;\r\n /** Include nested folders. Enabled by default. */\r\n recursive?: boolean;\r\n /** Replace generated files that already exist. Disabled by default. */\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n /** Accessible label. Omit for a decorative icon. */\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\"\r\n width={width ?? '1em'}\r\n height={height ?? '1em'}\r\n role={title ? 'img' : undefined}\r\n aria-hidden={title ? undefined : true}\r\n {...props}\r\n >\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\n/** Generate typed React components and a named-export barrel from image files. */\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n // Convert all inputs before writing so malformed assets do not leave partial output.\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uBAAwB;;;ACDxB,sBAA8E;AAC9E,uBAAiB;AACjB,yBAA2B;AAC3B,wBAA0B;AAC1B,kBAA0B;AAE1B,IAAM,oBAAsD;AAAA,EAC1D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AACA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,CAAC;AA8B1C,SAAS,cAAc,UAA0B;AAC/C,QAAM,WAAW,iBAAAA,QAAK,SAAS,UAAU,iBAAAA,QAAK,QAAQ,QAAQ,CAAC;AAC/D,QAAM,QAAQ,SACX,UAAU,MAAM,EAChB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,sBAAsB,OAAO,EACrC,MAAM,eAAe;AACxB,QAAM,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,CAAC,EAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE;AACxF,QAAM,WAAW,cAAc,KAAK,IAAI,IAAI,OAAO,OAAO,IAAI;AAC9D,SAAO,YAAY;AACrB;AAEA,SAAS,SAAS,QAAgB,OAAwB;AACxD,QAAM,WAAW,iBAAAA,QAAK,SAAS,QAAQ,KAAK;AAC5C,SAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAK,iBAAAA,QAAK,GAAG,EAAE,KAAK,aAAa,QAAQ,CAAC,iBAAAA,QAAK,WAAW,QAAQ;AACpH;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,eAAe,WAAW,UAAkB,WAAmB,WAA4C;AACzG,QAAM,SAAwB,CAAC;AAE/B,iBAAe,MAAM,WAAkC;AACrD,UAAM,UAAU,UAAM,yBAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AAChE,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACnD,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,iBAAAA,QAAK,KAAK,WAAW,MAAM,IAAI;AAChD,UAAI,MAAM,eAAe,EAAG;AAC5B,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,aAAa,EAAE,cAAc,YAAY,SAAS,UAAU,SAAS,GAAI,OAAM,MAAM,QAAQ;AACjG;AAAA,MACF;AACA,UAAI,CAAC,MAAM,OAAO,EAAG;AACrB,YAAM,YAAY,iBAAAA,QAAK,QAAQ,MAAM,IAAI,EAAE,YAAY;AACvD,UAAI,kBAAkB,IAAI,SAAS,KAAK,aAAa,mBAAmB;AACtE,eAAO,KAAK,EAAE,MAAM,UAAU,MAAM,cAAc,QAAQ,GAAG,UAAU,CAAC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ;AACpB,SAAO;AACT;AAEA,eAAe,gBAAgB,OAAoB,WAAoC;AACrF,QAAM,QAAQ,UAAM,0BAAS,MAAM,IAAI;AACvC,MAAI;AACJ,MAAI;AACF,qBAAa,6BAAU,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,qCAAqC,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC5F;AACA,MAAI,CAAC,WAAW,SAAS,CAAC,WAAW,OAAQ,OAAM,IAAI,MAAM,UAAU,MAAM,IAAI,mCAAmC;AAEpH,QAAM,UAAU,QAAQ,kBAAkB,MAAM,SAAS,CAAC,WAAW,MAAM,SAAS,QAAQ,CAAC;AAC7F,SAAO;AAAA;AAAA,mBAEU,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKN,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA,0BAEnB,SAAS,wCAAwC,SAAS;AAAA;AAAA;AAAA;AAAA,qBAI/D,WAAW,KAAK,IAAI,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa1D;AAEA,eAAe,aAAa,OAAoB,WAAoC;AAClF,QAAM,SAAS,UAAM,0BAAS,MAAM,MAAM,MAAM;AAChD,MAAI;AACF,WAAO,UAAM,uBAAU,QAAQ;AAAA,MAC7B,SAAS,CAAC,qBAAqB,kBAAkB;AAAA,MACjD,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ,GAAG,EAAE,eAAe,UAAU,CAAC;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,uBAAuB,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC9E;AACF;AAGA,eAAsB,cAAc,SAA6D;AAC/F,QAAM,WAAW,iBAAAA,QAAK,QAAQ,QAAQ,QAAQ;AAC9C,QAAM,YAAY,iBAAAA,QAAK,QAAQ,QAAQ,SAAS;AAChD,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,QAAQ,aAAa;AAEvC,MAAI;AACJ,MAAI;AACF,gBAAY,UAAM,sBAAK,QAAQ;AAAA,EACjC,QAAQ;AACN,UAAM,IAAI,MAAM,oCAAoC,QAAQ,GAAG;AAAA,EACjE;AACA,MAAI,CAAC,UAAU,YAAY,EAAG,OAAM,IAAI,MAAM,mCAAmC,QAAQ,GAAG;AAE5F,QAAM,SAAS,MAAM,WAAW,UAAU,WAAW,SAAS;AAC9D,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAI;AAE5F,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,SAAS,QAAQ;AAC1B,UAAM,iBAAiB,MAAM,KAAK,kBAAkB,OAAO;AAC3D,UAAM,WAAW,MAAM,IAAI,cAAc;AACzC,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,oBAAoB,QAAQ,UAAU,MAAM,IAAI,kCAAkC,MAAM,IAAI,oCAAoC;AAAA,IAClJ;AACA,UAAM,IAAI,gBAAgB,MAAM,IAAI;AAAA,EACtC;AAGA,QAAM,YAAY,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,WAAW;AAAA,IAC/D;AAAA,IACA,UAAU,kBAAkB,IAAI,MAAM,SAAS,IAC3C,MAAM,aAAa,OAAO,MAAM,IAAI,IACpC,MAAM,gBAAgB,OAAO,MAAM,IAAI;AAAA,EAC7C,EAAE,CAAC;AAEH,QAAM,gBAAgB,GAAG,UAAU,IAAI,CAAC,EAAE,MAAM,MAAM,uBAAuB,MAAM,IAAI,cAAc,MAAM,IAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA;AAClI,QAAM,UAAU;AAAA,IACd,GAAG,UAAU,IAAI,CAAC,EAAE,OAAO,SAAS,OAAO,EAAE,UAAU,iBAAAA,QAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,GAAG,SAAS,EAAE;AAAA,IAC7G,EAAE,UAAU,iBAAAA,QAAK,KAAK,WAAW,UAAU,GAAG,UAAU,cAAc;AAAA,EACxE;AAEA,MAAI,CAAC,WAAW;AACd,UAAM,YAAsB,CAAC;AAC7B,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,kBAAM,wBAAO,OAAO,QAAQ;AAC5B,kBAAU,KAAK,OAAO,QAAQ;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,UAAU,OAAQ,OAAM,IAAI,MAAM,qDAAqD,UAAU,KAAK,IAAI,CAAC,oCAAoC;AAAA,EACrJ;AAEA,YAAM,uBAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C,aAAW,UAAU,SAAS;AAC5B,UAAM,gBAAgB,GAAG,OAAO,QAAQ,QAAI,+BAAW,CAAC;AACxD,QAAI;AACF,gBAAM,2BAAU,eAAe,OAAO,UAAU,MAAM;AACtD,UAAI,UAAW,WAAM,oBAAG,OAAO,UAAU,EAAE,OAAO,KAAK,CAAC;AACxD,gBAAM,wBAAO,eAAe,OAAO,QAAQ;AAAA,IAC7C,SAAS,OAAO;AACd,gBAAM,oBAAG,eAAe,EAAE,OAAO,KAAK,CAAC;AACvC,YAAM,IAAI,MAAM,oBAAoB,OAAO,QAAQ,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,UAAU,IAAI,CAAC,EAAE,MAAM,OAAO;AAAA,MACnC,MAAM,MAAM;AAAA,MACZ,QAAQ,iBAAAA,QAAK,SAAS,UAAU,MAAM,IAAI;AAAA,MAC1C,eAAe,iBAAAA,QAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM;AAAA,IACzD,EAAE;AAAA,IACF;AAAA,EACF;AACF;;;AD1NA,IAAM,UAAU,IAAI,yBAAQ;AAC5B,QACG,KAAK,oBAAoB,EACzB,YAAY,0EAA0E,EACtF,SAAS,eAAe,qCAAqC,OAAO,EACpE,OAAO,4BAA4B,6CAA6C,WAAW,EAC3F,OAAO,kBAAkB,sDAAsD,EAC/E,OAAO,eAAe,4CAA4C,EAClE,OAAO,OAAO,UAAkB,YAAyE;AACxG,MAAI;AACF,UAAM,SAAS,MAAM,cAAc;AAAA,MACjC;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,GAAI,QAAQ,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,IAC5E,CAAC;AACD,YAAQ,IAAI,aAAa,OAAO,MAAM,MAAM,qBAAqB,OAAO,SAAS,EAAE;AACnF,eAAW,QAAQ,OAAO,MAAO,SAAQ,IAAI,KAAK,KAAK,IAAI,aAAQ,KAAK,MAAM,EAAE;AAAA,EAClF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,YAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEH,QAAQ,WAAW;","names":["path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/index.ts","../src/icons/Kiwi.tsx"],"sourcesContent":["#!/usr/bin/env node\r\n/// <reference types=\"node\" />\r\nimport { Command } from 'commander';\r\nimport { generateIcons } from './index.js';\r\n\r\nconst program = new Command();\r\nprogram\r\n .name('react-icon-library')\r\n .description('Generate reusable React icon components from SVG and raster image files.')\r\n .argument('[input-dir]', 'directory containing image assets', 'icons')\r\n .option('-o, --output <directory>', 'output directory for generated components', 'src/icons')\r\n .option('--no-recursive', 'only read images directly inside the input directory')\r\n .option('--overwrite', 'replace existing generated component files')\r\n .action(async (inputDir: string, options: { output: string; recursive: boolean; overwrite?: boolean }) => {\r\n try {\r\n const result = await generateIcons({\r\n inputDir,\r\n outputDir: options.output,\r\n recursive: options.recursive,\r\n ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),\r\n });\r\n console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);\r\n for (const icon of result.icons) console.log(` ${icon.name} <- ${icon.source}`);\r\n } catch (error) {\r\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\r\n process.exitCode = 1;\r\n }\r\n });\r\n\r\nprogram.parseAsync();","import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n inputDir: string;\r\n outputDir: string;\r\n recursive?: boolean;\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}\r\n\r\nexport { default as Kiwi } from './icons/Kiwi.js';\r\nexport type { KiwiProps } from './icons/Kiwi.js';","import type { SVGProps } from 'react';\n\nexport interface KiwiProps extends SVGProps<SVGSVGElement> {\n title?: string;\n}\n\nconst imageSource = \"data:image/png;base64,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\";\n\nexport default function Kiwi({ title, width, height, ...props }: KiwiProps) {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 447 447\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\n {title ? <title>{title}</title> : null}\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["path"]}
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  generateIcons
4
- } from "./chunk-N5DDYT6F.js";
4
+ } from "./chunk-7UCNUTGX.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -15,7 +15,7 @@ program.name("react-icon-library").description("Generate reusable React icon com
15
15
  ...options.overwrite === void 0 ? {} : { overwrite: options.overwrite }
16
16
  });
17
17
  console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);
18
- for (const icon of result.icons) console.log(` ${icon.name} \u2190 ${icon.source}`);
18
+ for (const icon of result.icons) console.log(` ${icon.name} <- ${icon.source}`);
19
19
  } catch (error) {
20
20
  console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
21
21
  process.exitCode = 1;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command } from 'commander';\r\nimport { generateIcons } from './index.js';\r\n\r\nconst program = new Command();\r\nprogram\r\n .name('react-icon-library')\r\n .description('Generate reusable React icon components from SVG and raster image files.')\r\n .argument('[input-dir]', 'directory containing image assets', 'icons')\r\n .option('-o, --output <directory>', 'output directory for generated components', 'src/icons')\r\n .option('--no-recursive', 'only read images directly inside the input directory')\r\n .option('--overwrite', 'replace existing generated component files')\r\n .action(async (inputDir: string, options: { output: string; recursive: boolean; overwrite?: boolean }) => {\r\n try {\r\n const result = await generateIcons({\r\n inputDir,\r\n outputDir: options.output,\r\n recursive: options.recursive,\r\n ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),\r\n });\r\n console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);\r\n for (const icon of result.icons) console.log(` ${icon.name} ← ${icon.source}`);\r\n } catch (error) {\r\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\r\n process.exitCode = 1;\r\n }\r\n });\r\n\r\nprogram.parseAsync();"],"mappings":";;;;;;AACA,SAAS,eAAe;AAGxB,IAAM,UAAU,IAAI,QAAQ;AAC5B,QACG,KAAK,oBAAoB,EACzB,YAAY,0EAA0E,EACtF,SAAS,eAAe,qCAAqC,OAAO,EACpE,OAAO,4BAA4B,6CAA6C,WAAW,EAC3F,OAAO,kBAAkB,sDAAsD,EAC/E,OAAO,eAAe,4CAA4C,EAClE,OAAO,OAAO,UAAkB,YAAyE;AACxG,MAAI;AACF,UAAM,SAAS,MAAM,cAAc;AAAA,MACjC;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,GAAI,QAAQ,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,IAC5E,CAAC;AACD,YAAQ,IAAI,aAAa,OAAO,MAAM,MAAM,qBAAqB,OAAO,SAAS,EAAE;AACnF,eAAW,QAAQ,OAAO,MAAO,SAAQ,IAAI,KAAK,KAAK,IAAI,aAAQ,KAAK,MAAM,EAAE;AAAA,EAClF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,YAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEH,QAAQ,WAAW;","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\n/// <reference types=\"node\" />\r\nimport { Command } from 'commander';\r\nimport { generateIcons } from './index.js';\r\n\r\nconst program = new Command();\r\nprogram\r\n .name('react-icon-library')\r\n .description('Generate reusable React icon components from SVG and raster image files.')\r\n .argument('[input-dir]', 'directory containing image assets', 'icons')\r\n .option('-o, --output <directory>', 'output directory for generated components', 'src/icons')\r\n .option('--no-recursive', 'only read images directly inside the input directory')\r\n .option('--overwrite', 'replace existing generated component files')\r\n .action(async (inputDir: string, options: { output: string; recursive: boolean; overwrite?: boolean }) => {\r\n try {\r\n const result = await generateIcons({\r\n inputDir,\r\n outputDir: options.output,\r\n recursive: options.recursive,\r\n ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),\r\n });\r\n console.log(`Generated ${result.icons.length} React icon(s) in ${result.outputDir}`);\r\n for (const icon of result.icons) console.log(` ${icon.name} <- ${icon.source}`);\r\n } catch (error) {\r\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\r\n process.exitCode = 1;\r\n }\r\n });\r\n\r\nprogram.parseAsync();"],"mappings":";;;;;;AAEA,SAAS,eAAe;AAGxB,IAAM,UAAU,IAAI,QAAQ;AAC5B,QACG,KAAK,oBAAoB,EACzB,YAAY,0EAA0E,EACtF,SAAS,eAAe,qCAAqC,OAAO,EACpE,OAAO,4BAA4B,6CAA6C,WAAW,EAC3F,OAAO,kBAAkB,sDAAsD,EAC/E,OAAO,eAAe,4CAA4C,EAClE,OAAO,OAAO,UAAkB,YAAyE;AACxG,MAAI;AACF,UAAM,SAAS,MAAM,cAAc;AAAA,MACjC;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,GAAI,QAAQ,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,IAC5E,CAAC;AACD,YAAQ,IAAI,aAAa,OAAO,MAAM,MAAM,qBAAqB,OAAO,SAAS,EAAE;AACnF,eAAW,QAAQ,OAAO,MAAO,SAAQ,IAAI,KAAK,KAAK,IAAI,SAAS,KAAK,MAAM,EAAE;AAAA,EACnF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,YAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEH,QAAQ,WAAW;","names":[]}
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ Kiwi: () => Kiwi,
33
34
  generateIcons: () => generateIcons
34
35
  });
35
36
  module.exports = __toCommonJS(index_exports);
@@ -38,6 +39,18 @@ var import_node_path = __toESM(require("path"), 1);
38
39
  var import_node_crypto = require("crypto");
39
40
  var import_image_size = require("image-size");
40
41
  var import_core = require("@svgr/core");
42
+
43
+ // src/icons/Kiwi.tsx
44
+ var import_jsx_runtime = require("react/jsx-runtime");
45
+ var imageSource = "data:image/png;base64,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";
46
+ function Kiwi({ title, width, height, ...props }) {
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 447 447", width: width ?? "1em", height: height ?? "1em", role: title ? "img" : void 0, "aria-hidden": title ? void 0 : true, ...props, children: [
48
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: title }) : null,
49
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("image", { href: imageSource, x: "0", y: "0", width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet" })
50
+ ] });
51
+ }
52
+
53
+ // src/index.ts
41
54
  var RASTER_MIME_TYPES = {
42
55
  ".png": "image/png",
43
56
  ".jpg": "image/jpeg",
@@ -96,7 +109,6 @@ async function rasterComponent(asset, component) {
96
109
  return `import type { SVGProps } from 'react';
97
110
 
98
111
  export interface ${component}Props extends SVGProps<SVGSVGElement> {
99
- /** Accessible label. Omit for a decorative icon. */
100
112
  title?: string;
101
113
  }
102
114
 
@@ -104,15 +116,7 @@ const imageSource = ${JSON.stringify(dataUri)};
104
116
 
105
117
  export default function ${component}({ title, width, height, ...props }: ${component}Props) {
106
118
  return (
107
- <svg
108
- xmlns="http://www.w3.org/2000/svg"
109
- viewBox="0 0 ${dimensions.width} ${dimensions.height}"
110
- width={width ?? '1em'}
111
- height={height ?? '1em'}
112
- role={title ? 'img' : undefined}
113
- aria-hidden={title ? undefined : true}
114
- {...props}
115
- >
119
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${dimensions.width} ${dimensions.height}" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>
116
120
  {title ? <title>{title}</title> : null}
117
121
  <image href={imageSource} x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
118
122
  </svg>
@@ -203,6 +207,7 @@ async function generateIcons(options) {
203
207
  }
204
208
  // Annotate the CommonJS export names for ESM import in node:
205
209
  0 && (module.exports = {
210
+ Kiwi,
206
211
  generateIcons
207
212
  });
208
213
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n /** Directory containing source image assets. */\r\n inputDir: string;\r\n /** Directory where generated React components and their barrel are written. */\r\n outputDir: string;\r\n /** Include nested folders. Enabled by default. */\r\n recursive?: boolean;\r\n /** Replace generated files that already exist. Disabled by default. */\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n /** Accessible label. Omit for a decorative icon. */\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\"\r\n width={width ?? '1em'}\r\n height={height ?? '1em'}\r\n role={title ? 'img' : undefined}\r\n aria-hidden={title ? undefined : true}\r\n {...props}\r\n >\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\n/** Generate typed React components and a named-export barrel from image files. */\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n // Convert all inputs before writing so malformed assets do not leave partial output.\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAA8E;AAC9E,uBAAiB;AACjB,yBAA2B;AAC3B,wBAA0B;AAC1B,kBAA0B;AAE1B,IAAM,oBAAsD;AAAA,EAC1D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AACA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,CAAC;AA8B1C,SAAS,cAAc,UAA0B;AAC/C,QAAM,WAAW,iBAAAA,QAAK,SAAS,UAAU,iBAAAA,QAAK,QAAQ,QAAQ,CAAC;AAC/D,QAAM,QAAQ,SACX,UAAU,MAAM,EAChB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,sBAAsB,OAAO,EACrC,MAAM,eAAe;AACxB,QAAM,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,CAAC,EAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE;AACxF,QAAM,WAAW,cAAc,KAAK,IAAI,IAAI,OAAO,OAAO,IAAI;AAC9D,SAAO,YAAY;AACrB;AAEA,SAAS,SAAS,QAAgB,OAAwB;AACxD,QAAM,WAAW,iBAAAA,QAAK,SAAS,QAAQ,KAAK;AAC5C,SAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAK,iBAAAA,QAAK,GAAG,EAAE,KAAK,aAAa,QAAQ,CAAC,iBAAAA,QAAK,WAAW,QAAQ;AACpH;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,eAAe,WAAW,UAAkB,WAAmB,WAA4C;AACzG,QAAM,SAAwB,CAAC;AAE/B,iBAAe,MAAM,WAAkC;AACrD,UAAM,UAAU,UAAM,yBAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AAChE,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACnD,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,iBAAAA,QAAK,KAAK,WAAW,MAAM,IAAI;AAChD,UAAI,MAAM,eAAe,EAAG;AAC5B,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,aAAa,EAAE,cAAc,YAAY,SAAS,UAAU,SAAS,GAAI,OAAM,MAAM,QAAQ;AACjG;AAAA,MACF;AACA,UAAI,CAAC,MAAM,OAAO,EAAG;AACrB,YAAM,YAAY,iBAAAA,QAAK,QAAQ,MAAM,IAAI,EAAE,YAAY;AACvD,UAAI,kBAAkB,IAAI,SAAS,KAAK,aAAa,mBAAmB;AACtE,eAAO,KAAK,EAAE,MAAM,UAAU,MAAM,cAAc,QAAQ,GAAG,UAAU,CAAC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ;AACpB,SAAO;AACT;AAEA,eAAe,gBAAgB,OAAoB,WAAoC;AACrF,QAAM,QAAQ,UAAM,0BAAS,MAAM,IAAI;AACvC,MAAI;AACJ,MAAI;AACF,qBAAa,6BAAU,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,qCAAqC,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC5F;AACA,MAAI,CAAC,WAAW,SAAS,CAAC,WAAW,OAAQ,OAAM,IAAI,MAAM,UAAU,MAAM,IAAI,mCAAmC;AAEpH,QAAM,UAAU,QAAQ,kBAAkB,MAAM,SAAS,CAAC,WAAW,MAAM,SAAS,QAAQ,CAAC;AAC7F,SAAO;AAAA;AAAA,mBAEU,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKN,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA,0BAEnB,SAAS,wCAAwC,SAAS;AAAA;AAAA;AAAA;AAAA,qBAI/D,WAAW,KAAK,IAAI,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa1D;AAEA,eAAe,aAAa,OAAoB,WAAoC;AAClF,QAAM,SAAS,UAAM,0BAAS,MAAM,MAAM,MAAM;AAChD,MAAI;AACF,WAAO,UAAM,uBAAU,QAAQ;AAAA,MAC7B,SAAS,CAAC,qBAAqB,kBAAkB;AAAA,MACjD,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ,GAAG,EAAE,eAAe,UAAU,CAAC;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,uBAAuB,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC9E;AACF;AAGA,eAAsB,cAAc,SAA6D;AAC/F,QAAM,WAAW,iBAAAA,QAAK,QAAQ,QAAQ,QAAQ;AAC9C,QAAM,YAAY,iBAAAA,QAAK,QAAQ,QAAQ,SAAS;AAChD,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,QAAQ,aAAa;AAEvC,MAAI;AACJ,MAAI;AACF,gBAAY,UAAM,sBAAK,QAAQ;AAAA,EACjC,QAAQ;AACN,UAAM,IAAI,MAAM,oCAAoC,QAAQ,GAAG;AAAA,EACjE;AACA,MAAI,CAAC,UAAU,YAAY,EAAG,OAAM,IAAI,MAAM,mCAAmC,QAAQ,GAAG;AAE5F,QAAM,SAAS,MAAM,WAAW,UAAU,WAAW,SAAS;AAC9D,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAI;AAE5F,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,SAAS,QAAQ;AAC1B,UAAM,iBAAiB,MAAM,KAAK,kBAAkB,OAAO;AAC3D,UAAM,WAAW,MAAM,IAAI,cAAc;AACzC,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,oBAAoB,QAAQ,UAAU,MAAM,IAAI,kCAAkC,MAAM,IAAI,oCAAoC;AAAA,IAClJ;AACA,UAAM,IAAI,gBAAgB,MAAM,IAAI;AAAA,EACtC;AAGA,QAAM,YAAY,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,WAAW;AAAA,IAC/D;AAAA,IACA,UAAU,kBAAkB,IAAI,MAAM,SAAS,IAC3C,MAAM,aAAa,OAAO,MAAM,IAAI,IACpC,MAAM,gBAAgB,OAAO,MAAM,IAAI;AAAA,EAC7C,EAAE,CAAC;AAEH,QAAM,gBAAgB,GAAG,UAAU,IAAI,CAAC,EAAE,MAAM,MAAM,uBAAuB,MAAM,IAAI,cAAc,MAAM,IAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA;AAClI,QAAM,UAAU;AAAA,IACd,GAAG,UAAU,IAAI,CAAC,EAAE,OAAO,SAAS,OAAO,EAAE,UAAU,iBAAAA,QAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,GAAG,SAAS,EAAE;AAAA,IAC7G,EAAE,UAAU,iBAAAA,QAAK,KAAK,WAAW,UAAU,GAAG,UAAU,cAAc;AAAA,EACxE;AAEA,MAAI,CAAC,WAAW;AACd,UAAM,YAAsB,CAAC;AAC7B,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,kBAAM,wBAAO,OAAO,QAAQ;AAC5B,kBAAU,KAAK,OAAO,QAAQ;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,UAAU,OAAQ,OAAM,IAAI,MAAM,qDAAqD,UAAU,KAAK,IAAI,CAAC,oCAAoC;AAAA,EACrJ;AAEA,YAAM,uBAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C,aAAW,UAAU,SAAS;AAC5B,UAAM,gBAAgB,GAAG,OAAO,QAAQ,QAAI,+BAAW,CAAC;AACxD,QAAI;AACF,gBAAM,2BAAU,eAAe,OAAO,UAAU,MAAM;AACtD,UAAI,UAAW,WAAM,oBAAG,OAAO,UAAU,EAAE,OAAO,KAAK,CAAC;AACxD,gBAAM,wBAAO,eAAe,OAAO,QAAQ;AAAA,IAC7C,SAAS,OAAO;AACd,gBAAM,oBAAG,eAAe,EAAE,OAAO,KAAK,CAAC;AACvC,YAAM,IAAI,MAAM,oBAAoB,OAAO,QAAQ,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,UAAU,IAAI,CAAC,EAAE,MAAM,OAAO;AAAA,MACnC,MAAM,MAAM;AAAA,MACZ,QAAQ,iBAAAA,QAAK,SAAS,UAAU,MAAM,IAAI;AAAA,MAC1C,eAAe,iBAAAA,QAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM;AAAA,IACzD,EAAE;AAAA,IACF;AAAA,EACF;AACF;","names":["path"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/icons/Kiwi.tsx"],"sourcesContent":["import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n inputDir: string;\r\n outputDir: string;\r\n recursive?: boolean;\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}\r\n\r\nexport { default as Kiwi } from './icons/Kiwi.js';\r\nexport type { KiwiProps } from './icons/Kiwi.js';","import type { SVGProps } from 'react';\n\nexport interface KiwiProps extends SVGProps<SVGSVGElement> {\n title?: string;\n}\n\nconst imageSource = \"data:image/png;base64,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\";\n\nexport default function Kiwi({ title, width, height, ...props }: KiwiProps) {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 447 447\" width={width ?? '1em'} height={height ?? '1em'} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} {...props}>\n {title ? <title>{title}</title> : null}\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["path"]}
package/dist/index.d.cts CHANGED
@@ -1,11 +1,15 @@
1
+ import * as react from 'react';
2
+ import { SVGProps } from 'react';
3
+
4
+ interface KiwiProps extends SVGProps<SVGSVGElement> {
5
+ title?: string;
6
+ }
7
+ declare function Kiwi({ title, width, height, ...props }: KiwiProps): react.JSX.Element;
8
+
1
9
  interface GenerateIconsOptions {
2
- /** Directory containing source image assets. */
3
10
  inputDir: string;
4
- /** Directory where generated React components and their barrel are written. */
5
11
  outputDir: string;
6
- /** Include nested folders. Enabled by default. */
7
12
  recursive?: boolean;
8
- /** Replace generated files that already exist. Disabled by default. */
9
13
  overwrite?: boolean;
10
14
  }
11
15
  interface GeneratedIcon {
@@ -17,7 +21,6 @@ interface GenerateIconsResult {
17
21
  icons: GeneratedIcon[];
18
22
  outputDir: string;
19
23
  }
20
- /** Generate typed React components and a named-export barrel from image files. */
21
24
  declare function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult>;
22
25
 
23
- export { type GenerateIconsOptions, type GenerateIconsResult, type GeneratedIcon, generateIcons };
26
+ export { type GenerateIconsOptions, type GenerateIconsResult, type GeneratedIcon, Kiwi, type KiwiProps, generateIcons };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,15 @@
1
+ import * as react from 'react';
2
+ import { SVGProps } from 'react';
3
+
4
+ interface KiwiProps extends SVGProps<SVGSVGElement> {
5
+ title?: string;
6
+ }
7
+ declare function Kiwi({ title, width, height, ...props }: KiwiProps): react.JSX.Element;
8
+
1
9
  interface GenerateIconsOptions {
2
- /** Directory containing source image assets. */
3
10
  inputDir: string;
4
- /** Directory where generated React components and their barrel are written. */
5
11
  outputDir: string;
6
- /** Include nested folders. Enabled by default. */
7
12
  recursive?: boolean;
8
- /** Replace generated files that already exist. Disabled by default. */
9
13
  overwrite?: boolean;
10
14
  }
11
15
  interface GeneratedIcon {
@@ -17,7 +21,6 @@ interface GenerateIconsResult {
17
21
  icons: GeneratedIcon[];
18
22
  outputDir: string;
19
23
  }
20
- /** Generate typed React components and a named-export barrel from image files. */
21
24
  declare function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult>;
22
25
 
23
- export { type GenerateIconsOptions, type GenerateIconsResult, type GeneratedIcon, generateIcons };
26
+ export { type GenerateIconsOptions, type GenerateIconsResult, type GeneratedIcon, Kiwi, type KiwiProps, generateIcons };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
+ Kiwi,
2
3
  generateIcons
3
- } from "./chunk-N5DDYT6F.js";
4
+ } from "./chunk-7UCNUTGX.js";
4
5
  export {
6
+ Kiwi,
5
7
  generateIcons
6
8
  };
7
9
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taterlaxmi/react-icon-library",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -22,7 +22,7 @@
22
22
  }
23
23
  },
24
24
  "bin": {
25
- "react-icon-library": "dist/cli.js"
25
+ "react-icon-library": "./dist/cli.js"
26
26
  },
27
27
  "files": [
28
28
  "dist",
@@ -30,8 +30,12 @@
30
30
  "LICENSE"
31
31
  ],
32
32
  "scripts": {
33
+ "build": "tsup src/index.ts src/cli.ts --format esm,cjs --dts --clean --sourcemap",
33
34
  "dev": "node ./dist/cli.js ./brand-assets --output ./src/icons --overwrite",
34
- "generate": "node ./dist/cli.js"
35
+ "generate": "node ./dist/cli.js",
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "node --test",
38
+ "prepublishOnly": "npm run build && npm run typecheck && npm test"
35
39
  },
36
40
  "dependencies": {
37
41
  "@svgr/core": "^8.1.0",
@@ -40,6 +44,16 @@
40
44
  "commander": "^13.1.0",
41
45
  "image-size": "^2.0.2"
42
46
  },
47
+ "peerDependencies": {
48
+ "react": ">=18"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.20.5",
52
+ "@types/react": "^19.0.10",
53
+ "react": "^19.0.0",
54
+ "tsup": "^8.5.1",
55
+ "typescript": "^5.9.3"
56
+ },
43
57
  "repository": {
44
58
  "type": "git",
45
59
  "url": "git+https://github.com/taterlaxmi/react-icon-library.git"
@@ -1,174 +0,0 @@
1
- // src/index.ts
2
- import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
3
- import path from "path";
4
- import { randomUUID } from "crypto";
5
- import { imageSize } from "image-size";
6
- import { transform } from "@svgr/core";
7
- var RASTER_MIME_TYPES = {
8
- ".png": "image/png",
9
- ".jpg": "image/jpeg",
10
- ".jpeg": "image/jpeg",
11
- ".gif": "image/gif",
12
- ".webp": "image/webp",
13
- ".avif": "image/avif"
14
- };
15
- var VECTOR_EXTENSIONS = /* @__PURE__ */ new Set([".svg"]);
16
- function componentName(filePath) {
17
- const baseName = path.basename(filePath, path.extname(filePath));
18
- const words = baseName.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").match(/[A-Za-z0-9]+/g);
19
- const name = (words ?? []).map((word) => word[0].toUpperCase() + word.slice(1)).join("");
20
- const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;
21
- return safeName || "Icon";
22
- }
23
- function isWithin(parent, child) {
24
- const relative = path.relative(parent, child);
25
- return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
26
- }
27
- function errorMessage(error) {
28
- return error instanceof Error ? error.message : String(error);
29
- }
30
- async function findAssets(inputDir, outputDir, recursive) {
31
- const assets = [];
32
- async function visit(directory) {
33
- const entries = await readdir(directory, { withFileTypes: true });
34
- entries.sort((a, b) => a.name.localeCompare(b.name));
35
- for (const entry of entries) {
36
- const fullPath = path.join(directory, entry.name);
37
- if (entry.isSymbolicLink()) continue;
38
- if (entry.isDirectory()) {
39
- if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);
40
- continue;
41
- }
42
- if (!entry.isFile()) continue;
43
- const extension = path.extname(entry.name).toLowerCase();
44
- if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {
45
- assets.push({ path: fullPath, name: componentName(fullPath), extension });
46
- }
47
- }
48
- }
49
- await visit(inputDir);
50
- return assets;
51
- }
52
- async function rasterComponent(asset, component) {
53
- const bytes = await readFile(asset.path);
54
- let dimensions;
55
- try {
56
- dimensions = imageSize(bytes);
57
- } catch (error) {
58
- throw new Error(`Cannot read image dimensions for "${asset.path}": ${errorMessage(error)}`);
59
- }
60
- if (!dimensions.width || !dimensions.height) throw new Error(`Image "${asset.path}" does not have valid dimensions.`);
61
- const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString("base64")}`;
62
- return `import type { SVGProps } from 'react';
63
-
64
- export interface ${component}Props extends SVGProps<SVGSVGElement> {
65
- /** Accessible label. Omit for a decorative icon. */
66
- title?: string;
67
- }
68
-
69
- const imageSource = ${JSON.stringify(dataUri)};
70
-
71
- export default function ${component}({ title, width, height, ...props }: ${component}Props) {
72
- return (
73
- <svg
74
- xmlns="http://www.w3.org/2000/svg"
75
- viewBox="0 0 ${dimensions.width} ${dimensions.height}"
76
- width={width ?? '1em'}
77
- height={height ?? '1em'}
78
- role={title ? 'img' : undefined}
79
- aria-hidden={title ? undefined : true}
80
- {...props}
81
- >
82
- {title ? <title>{title}</title> : null}
83
- <image href={imageSource} x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
84
- </svg>
85
- );
86
- }
87
- `;
88
- }
89
- async function svgComponent(asset, component) {
90
- const source = await readFile(asset.path, "utf8");
91
- try {
92
- return await transform(source, {
93
- plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
94
- typescript: true,
95
- jsxRuntime: "automatic",
96
- icon: true,
97
- titleProp: true,
98
- exportType: "default",
99
- prettier: false
100
- }, { componentName: component });
101
- } catch (error) {
102
- throw new Error(`Cannot convert SVG "${asset.path}": ${errorMessage(error)}`);
103
- }
104
- }
105
- async function generateIcons(options) {
106
- const inputDir = path.resolve(options.inputDir);
107
- const outputDir = path.resolve(options.outputDir);
108
- const recursive = options.recursive ?? true;
109
- const overwrite = options.overwrite ?? false;
110
- let inputStat;
111
- try {
112
- inputStat = await stat(inputDir);
113
- } catch {
114
- throw new Error(`Input directory does not exist: "${inputDir}"`);
115
- }
116
- if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: "${inputDir}"`);
117
- const assets = await findAssets(inputDir, outputDir, recursive);
118
- if (assets.length === 0) throw new Error(`No supported image assets found in "${inputDir}".`);
119
- const names = /* @__PURE__ */ new Map();
120
- for (const asset of assets) {
121
- const normalizedName = asset.name.toLocaleLowerCase("en-US");
122
- const previous = names.get(normalizedName);
123
- if (previous) {
124
- throw new Error(`Name collision: "${previous}" and "${asset.path}" both generate the component "${asset.name}". Rename one of the source files.`);
125
- }
126
- names.set(normalizedName, asset.path);
127
- }
128
- const converted = await Promise.all(assets.map(async (asset) => ({
129
- asset,
130
- contents: VECTOR_EXTENSIONS.has(asset.extension) ? await svgComponent(asset, asset.name) : await rasterComponent(asset, asset.name)
131
- })));
132
- const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join("\n")}
133
- `;
134
- const outputs = [
135
- ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),
136
- { filePath: path.join(outputDir, "index.ts"), contents: indexContents }
137
- ];
138
- if (!overwrite) {
139
- const conflicts = [];
140
- for (const output of outputs) {
141
- try {
142
- await access(output.filePath);
143
- conflicts.push(output.filePath);
144
- } catch {
145
- }
146
- }
147
- if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(", ")}. Use --overwrite to replace them.`);
148
- }
149
- await mkdir(outputDir, { recursive: true });
150
- for (const output of outputs) {
151
- const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;
152
- try {
153
- await writeFile(temporaryPath, output.contents, "utf8");
154
- if (overwrite) await rm(output.filePath, { force: true });
155
- await rename(temporaryPath, output.filePath);
156
- } catch (error) {
157
- await rm(temporaryPath, { force: true });
158
- throw new Error(`Failed to write "${output.filePath}": ${errorMessage(error)}`);
159
- }
160
- }
161
- return {
162
- icons: converted.map(({ asset }) => ({
163
- name: asset.name,
164
- source: path.relative(inputDir, asset.path),
165
- componentFile: path.join(outputDir, `${asset.name}.tsx`)
166
- })),
167
- outputDir
168
- };
169
- }
170
-
171
- export {
172
- generateIcons
173
- };
174
- //# sourceMappingURL=chunk-N5DDYT6F.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { access, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { imageSize } from 'image-size';\r\nimport { transform } from '@svgr/core';\r\n\r\nconst RASTER_MIME_TYPES: Readonly<Record<string, string>> = {\r\n '.png': 'image/png',\r\n '.jpg': 'image/jpeg',\r\n '.jpeg': 'image/jpeg',\r\n '.gif': 'image/gif',\r\n '.webp': 'image/webp',\r\n '.avif': 'image/avif',\r\n};\r\nconst VECTOR_EXTENSIONS = new Set(['.svg']);\r\n\r\nexport interface GenerateIconsOptions {\r\n /** Directory containing source image assets. */\r\n inputDir: string;\r\n /** Directory where generated React components and their barrel are written. */\r\n outputDir: string;\r\n /** Include nested folders. Enabled by default. */\r\n recursive?: boolean;\r\n /** Replace generated files that already exist. Disabled by default. */\r\n overwrite?: boolean;\r\n}\r\n\r\nexport interface GeneratedIcon {\r\n name: string;\r\n source: string;\r\n componentFile: string;\r\n}\r\n\r\nexport interface GenerateIconsResult {\r\n icons: GeneratedIcon[];\r\n outputDir: string;\r\n}\r\n\r\ninterface SourceAsset {\r\n path: string;\r\n name: string;\r\n extension: string;\r\n}\r\n\r\nfunction componentName(filePath: string): string {\r\n const baseName = path.basename(filePath, path.extname(filePath));\r\n const words = baseName\r\n .normalize('NFKD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\r\n .match(/[A-Za-z0-9]+/g);\r\n const name = (words ?? []).map((word) => word[0]!.toUpperCase() + word.slice(1)).join('');\r\n const safeName = /^[A-Za-z_$]/.test(name) ? name : `Icon${name}`;\r\n return safeName || 'Icon';\r\n}\r\n\r\nfunction isWithin(parent: string, child: string): boolean {\r\n const relative = path.relative(parent, child);\r\n return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\r\n}\r\n\r\nfunction errorMessage(error: unknown): string {\r\n return error instanceof Error ? error.message : String(error);\r\n}\r\n\r\nasync function findAssets(inputDir: string, outputDir: string, recursive: boolean): Promise<SourceAsset[]> {\r\n const assets: SourceAsset[] = [];\r\n\r\n async function visit(directory: string): Promise<void> {\r\n const entries = await readdir(directory, { withFileTypes: true });\r\n entries.sort((a, b) => a.name.localeCompare(b.name));\r\n for (const entry of entries) {\r\n const fullPath = path.join(directory, entry.name);\r\n if (entry.isSymbolicLink()) continue;\r\n if (entry.isDirectory()) {\r\n if (recursive && !(directory === inputDir && isWithin(fullPath, outputDir))) await visit(fullPath);\r\n continue;\r\n }\r\n if (!entry.isFile()) continue;\r\n const extension = path.extname(entry.name).toLowerCase();\r\n if (VECTOR_EXTENSIONS.has(extension) || extension in RASTER_MIME_TYPES) {\r\n assets.push({ path: fullPath, name: componentName(fullPath), extension });\r\n }\r\n }\r\n }\r\n\r\n await visit(inputDir);\r\n return assets;\r\n}\r\n\r\nasync function rasterComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const bytes = await readFile(asset.path);\r\n let dimensions: ReturnType<typeof imageSize>;\r\n try {\r\n dimensions = imageSize(bytes);\r\n } catch (error) {\r\n throw new Error(`Cannot read image dimensions for \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n if (!dimensions.width || !dimensions.height) throw new Error(`Image \"${asset.path}\" does not have valid dimensions.`);\r\n\r\n const dataUri = `data:${RASTER_MIME_TYPES[asset.extension]};base64,${bytes.toString('base64')}`;\r\n return `import type { SVGProps } from 'react';\r\n\r\nexport interface ${component}Props extends SVGProps<SVGSVGElement> {\r\n /** Accessible label. Omit for a decorative icon. */\r\n title?: string;\r\n}\r\n\r\nconst imageSource = ${JSON.stringify(dataUri)};\r\n\r\nexport default function ${component}({ title, width, height, ...props }: ${component}Props) {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 ${dimensions.width} ${dimensions.height}\"\r\n width={width ?? '1em'}\r\n height={height ?? '1em'}\r\n role={title ? 'img' : undefined}\r\n aria-hidden={title ? undefined : true}\r\n {...props}\r\n >\r\n {title ? <title>{title}</title> : null}\r\n <image href={imageSource} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"xMidYMid meet\" />\r\n </svg>\r\n );\r\n}\r\n`;\r\n}\r\n\r\nasync function svgComponent(asset: SourceAsset, component: string): Promise<string> {\r\n const source = await readFile(asset.path, 'utf8');\r\n try {\r\n return await transform(source, {\r\n plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],\r\n typescript: true,\r\n jsxRuntime: 'automatic',\r\n icon: true,\r\n titleProp: true,\r\n exportType: 'default',\r\n prettier: false,\r\n }, { componentName: component });\r\n } catch (error) {\r\n throw new Error(`Cannot convert SVG \"${asset.path}\": ${errorMessage(error)}`);\r\n }\r\n}\r\n\r\n/** Generate typed React components and a named-export barrel from image files. */\r\nexport async function generateIcons(options: GenerateIconsOptions): Promise<GenerateIconsResult> {\r\n const inputDir = path.resolve(options.inputDir);\r\n const outputDir = path.resolve(options.outputDir);\r\n const recursive = options.recursive ?? true;\r\n const overwrite = options.overwrite ?? false;\r\n\r\n let inputStat;\r\n try {\r\n inputStat = await stat(inputDir);\r\n } catch {\r\n throw new Error(`Input directory does not exist: \"${inputDir}\"`);\r\n }\r\n if (!inputStat.isDirectory()) throw new Error(`Input path is not a directory: \"${inputDir}\"`);\r\n\r\n const assets = await findAssets(inputDir, outputDir, recursive);\r\n if (assets.length === 0) throw new Error(`No supported image assets found in \"${inputDir}\".`);\r\n\r\n const names = new Map<string, string>();\r\n for (const asset of assets) {\r\n const normalizedName = asset.name.toLocaleLowerCase('en-US');\r\n const previous = names.get(normalizedName);\r\n if (previous) {\r\n throw new Error(`Name collision: \"${previous}\" and \"${asset.path}\" both generate the component \"${asset.name}\". Rename one of the source files.`);\r\n }\r\n names.set(normalizedName, asset.path);\r\n }\r\n\r\n // Convert all inputs before writing so malformed assets do not leave partial output.\r\n const converted = await Promise.all(assets.map(async (asset) => ({\r\n asset,\r\n contents: VECTOR_EXTENSIONS.has(asset.extension)\r\n ? await svgComponent(asset, asset.name)\r\n : await rasterComponent(asset, asset.name),\r\n })));\r\n\r\n const indexContents = `${converted.map(({ asset }) => `export { default as ${asset.name} } from './${asset.name}.js';`).join('\\n')}\\n`;\r\n const outputs = [\r\n ...converted.map(({ asset, contents }) => ({ filePath: path.join(outputDir, `${asset.name}.tsx`), contents })),\r\n { filePath: path.join(outputDir, 'index.ts'), contents: indexContents },\r\n ];\r\n\r\n if (!overwrite) {\r\n const conflicts: string[] = [];\r\n for (const output of outputs) {\r\n try {\r\n await access(output.filePath);\r\n conflicts.push(output.filePath);\r\n } catch {\r\n // A missing destination is expected for a fresh generation.\r\n }\r\n }\r\n if (conflicts.length) throw new Error(`Refusing to overwrite existing generated file(s): ${conflicts.join(', ')}. Use --overwrite to replace them.`);\r\n }\r\n\r\n await mkdir(outputDir, { recursive: true });\r\n for (const output of outputs) {\r\n const temporaryPath = `${output.filePath}.${randomUUID()}.tmp`;\r\n try {\r\n await writeFile(temporaryPath, output.contents, 'utf8');\r\n if (overwrite) await rm(output.filePath, { force: true });\r\n await rename(temporaryPath, output.filePath);\r\n } catch (error) {\r\n await rm(temporaryPath, { force: true });\r\n throw new Error(`Failed to write \"${output.filePath}\": ${errorMessage(error)}`);\r\n }\r\n }\r\n\r\n return {\r\n icons: converted.map(({ asset }) => ({\r\n name: asset.name,\r\n source: path.relative(inputDir, asset.path),\r\n componentFile: path.join(outputDir, `${asset.name}.tsx`),\r\n })),\r\n outputDir,\r\n };\r\n}"],"mappings":";AAAA,SAAS,QAAQ,OAAO,UAAU,SAAS,QAAQ,IAAI,MAAM,iBAAiB;AAC9E,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAE1B,IAAM,oBAAsD;AAAA,EAC1D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AACA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,CAAC;AA8B1C,SAAS,cAAc,UAA0B;AAC/C,QAAM,WAAW,KAAK,SAAS,UAAU,KAAK,QAAQ,QAAQ,CAAC;AAC/D,QAAM,QAAQ,SACX,UAAU,MAAM,EAChB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,sBAAsB,OAAO,EACrC,MAAM,eAAe;AACxB,QAAM,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,CAAC,EAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE;AACxF,QAAM,WAAW,cAAc,KAAK,IAAI,IAAI,OAAO,OAAO,IAAI;AAC9D,SAAO,YAAY;AACrB;AAEA,SAAS,SAAS,QAAgB,OAAwB;AACxD,QAAM,WAAW,KAAK,SAAS,QAAQ,KAAK;AAC5C,SAAO,aAAa,MAAO,CAAC,SAAS,WAAW,KAAK,KAAK,GAAG,EAAE,KAAK,aAAa,QAAQ,CAAC,KAAK,WAAW,QAAQ;AACpH;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,eAAe,WAAW,UAAkB,WAAmB,WAA4C;AACzG,QAAM,SAAwB,CAAC;AAE/B,iBAAe,MAAM,WAAkC;AACrD,UAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AAChE,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACnD,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,KAAK,KAAK,WAAW,MAAM,IAAI;AAChD,UAAI,MAAM,eAAe,EAAG;AAC5B,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,aAAa,EAAE,cAAc,YAAY,SAAS,UAAU,SAAS,GAAI,OAAM,MAAM,QAAQ;AACjG;AAAA,MACF;AACA,UAAI,CAAC,MAAM,OAAO,EAAG;AACrB,YAAM,YAAY,KAAK,QAAQ,MAAM,IAAI,EAAE,YAAY;AACvD,UAAI,kBAAkB,IAAI,SAAS,KAAK,aAAa,mBAAmB;AACtE,eAAO,KAAK,EAAE,MAAM,UAAU,MAAM,cAAc,QAAQ,GAAG,UAAU,CAAC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ;AACpB,SAAO;AACT;AAEA,eAAe,gBAAgB,OAAoB,WAAoC;AACrF,QAAM,QAAQ,MAAM,SAAS,MAAM,IAAI;AACvC,MAAI;AACJ,MAAI;AACF,iBAAa,UAAU,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,qCAAqC,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC5F;AACA,MAAI,CAAC,WAAW,SAAS,CAAC,WAAW,OAAQ,OAAM,IAAI,MAAM,UAAU,MAAM,IAAI,mCAAmC;AAEpH,QAAM,UAAU,QAAQ,kBAAkB,MAAM,SAAS,CAAC,WAAW,MAAM,SAAS,QAAQ,CAAC;AAC7F,SAAO;AAAA;AAAA,mBAEU,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,sBAKN,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA,0BAEnB,SAAS,wCAAwC,SAAS;AAAA;AAAA;AAAA;AAAA,qBAI/D,WAAW,KAAK,IAAI,WAAW,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa1D;AAEA,eAAe,aAAa,OAAoB,WAAoC;AAClF,QAAM,SAAS,MAAM,SAAS,MAAM,MAAM,MAAM;AAChD,MAAI;AACF,WAAO,MAAM,UAAU,QAAQ;AAAA,MAC7B,SAAS,CAAC,qBAAqB,kBAAkB;AAAA,MACjD,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ,GAAG,EAAE,eAAe,UAAU,CAAC;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,uBAAuB,MAAM,IAAI,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,EAC9E;AACF;AAGA,eAAsB,cAAc,SAA6D;AAC/F,QAAM,WAAW,KAAK,QAAQ,QAAQ,QAAQ;AAC9C,QAAM,YAAY,KAAK,QAAQ,QAAQ,SAAS;AAChD,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,QAAQ,aAAa;AAEvC,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,KAAK,QAAQ;AAAA,EACjC,QAAQ;AACN,UAAM,IAAI,MAAM,oCAAoC,QAAQ,GAAG;AAAA,EACjE;AACA,MAAI,CAAC,UAAU,YAAY,EAAG,OAAM,IAAI,MAAM,mCAAmC,QAAQ,GAAG;AAE5F,QAAM,SAAS,MAAM,WAAW,UAAU,WAAW,SAAS;AAC9D,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAI;AAE5F,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,SAAS,QAAQ;AAC1B,UAAM,iBAAiB,MAAM,KAAK,kBAAkB,OAAO;AAC3D,UAAM,WAAW,MAAM,IAAI,cAAc;AACzC,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,oBAAoB,QAAQ,UAAU,MAAM,IAAI,kCAAkC,MAAM,IAAI,oCAAoC;AAAA,IAClJ;AACA,UAAM,IAAI,gBAAgB,MAAM,IAAI;AAAA,EACtC;AAGA,QAAM,YAAY,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,WAAW;AAAA,IAC/D;AAAA,IACA,UAAU,kBAAkB,IAAI,MAAM,SAAS,IAC3C,MAAM,aAAa,OAAO,MAAM,IAAI,IACpC,MAAM,gBAAgB,OAAO,MAAM,IAAI;AAAA,EAC7C,EAAE,CAAC;AAEH,QAAM,gBAAgB,GAAG,UAAU,IAAI,CAAC,EAAE,MAAM,MAAM,uBAAuB,MAAM,IAAI,cAAc,MAAM,IAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA;AAClI,QAAM,UAAU;AAAA,IACd,GAAG,UAAU,IAAI,CAAC,EAAE,OAAO,SAAS,OAAO,EAAE,UAAU,KAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,GAAG,SAAS,EAAE;AAAA,IAC7G,EAAE,UAAU,KAAK,KAAK,WAAW,UAAU,GAAG,UAAU,cAAc;AAAA,EACxE;AAEA,MAAI,CAAC,WAAW;AACd,UAAM,YAAsB,CAAC;AAC7B,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,OAAO,OAAO,QAAQ;AAC5B,kBAAU,KAAK,OAAO,QAAQ;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,UAAU,OAAQ,OAAM,IAAI,MAAM,qDAAqD,UAAU,KAAK,IAAI,CAAC,oCAAoC;AAAA,EACrJ;AAEA,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C,aAAW,UAAU,SAAS;AAC5B,UAAM,gBAAgB,GAAG,OAAO,QAAQ,IAAI,WAAW,CAAC;AACxD,QAAI;AACF,YAAM,UAAU,eAAe,OAAO,UAAU,MAAM;AACtD,UAAI,UAAW,OAAM,GAAG,OAAO,UAAU,EAAE,OAAO,KAAK,CAAC;AACxD,YAAM,OAAO,eAAe,OAAO,QAAQ;AAAA,IAC7C,SAAS,OAAO;AACd,YAAM,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC;AACvC,YAAM,IAAI,MAAM,oBAAoB,OAAO,QAAQ,MAAM,aAAa,KAAK,CAAC,EAAE;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,UAAU,IAAI,CAAC,EAAE,MAAM,OAAO;AAAA,MACnC,MAAM,MAAM;AAAA,MACZ,QAAQ,KAAK,SAAS,UAAU,MAAM,IAAI;AAAA,MAC1C,eAAe,KAAK,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM;AAAA,IACzD,EAAE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}