@taterlaxmi/react-icon-library 1.0.0 → 1.0.1
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 +20 -13
- package/dist/chunk-7UCNUTGX.js +178 -0
- package/dist/chunk-7UCNUTGX.js.map +1 -0
- package/dist/cli.cjs +7 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +15 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.js +3 -1
- package/package.json +17 -3
- package/dist/chunk-N5DDYT6F.js +0 -174
- package/dist/chunk-N5DDYT6F.js.map +0 -1
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
|
|
33
|
+
## Use icons in another repository
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Install the package in a React application:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npm install
|
|
39
|
-
npx react-icon-library ./brand-assets --output ./src/icons
|
|
38
|
+
npm install @taterlaxmi/react-icon-library
|
|
40
39
|
```
|
|
41
40
|
|
|
42
|
-
|
|
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
|
-
```
|
|
45
|
-
|
|
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
|
-
|
|
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 '
|
|
54
|
+
import { Kiwi, type KiwiProps } from '@taterlaxmi/react-icon-library';
|
|
55
|
+
```
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
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}
|
|
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":"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","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-
|
|
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}
|
|
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}
|
|
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":"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","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,iVBORw0KGgoAAAANSUhEUgAAAb8AAAG/CAMAAAD/zSlAAAAAYFBMVEWBvlb///99vFB/vVP5/Pf9/vx2uUSx15eo04qv1pR6u0v1+vKZyneHwV3v9+rx+O2Rxmzg79Xq9OSNxGbU6Ma626KhzoPA36qXy3SezX7D37Hm8t7I4rmx1pqo0YzP5r8mtUOlAAARCElEQVR4nO3d6YKiyBIFYDICXFJBNhVxe/+3HKq6e6pUVJYIU/B89++dbuQ0ELl7pqHA99N8O99kWeiBkqzYnbbn1A+CprF4jbJbpfm64IklYtc/cdyYieyEi3UerRpl2CC/KDnsPIvkXojJhrMyWfXPz08Om9Aiu5f7ivBUJn6//JJjweT6p3wsouKYP36NPswv2WSMR88l5myTd8wvnXlIz7kqgmXUIb/V1OLF+R6I1ncrmTv5BXmB9N4HxfmdQqY+v3TrIb53wrROm+eXLJHeu6Fl0jS/Q4z43g/Fh0b5+WWIqvMdcVg2yG9x9BDfe2LvuHiWXzSzri8T7rKz66bgVX7RCfG9M3uKHuW3wNP35uxs8SC/I+J7d/bo382vdH1x0EB5L78DGg4DwOGhPr8kRnxDwHFSl1+KTrOBoGV6m5+/RnxDQdvgJr8c8Q1Ifp3fCh+/AaFidZUf3p6DQtPL/CLENyw2vcgPtefA0Ox3frnry4HWkp/8gg2Kl6HhzU9+eYb8hoaz5F9+/hHxDQ9/D0R85ZcUyG94uEj+5lei+BwiPvzJb3VCfkNEm+g7vwQLoocpTL7yC0pMmhgmewiq/FYzvD6HiXerKr8IsyYGK6ryy/H6HCqbGy/AyNFg0TrwfDTeB4sL3/MR33Cx76UT1xcBnU1S74zyZbhs7m1RvgwXbT10fg4Yzb0d6pfh4o2H5sOQZdX/YLiQ3rBh6A8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAjMfWDs0RdYopP8z6muwkSdIbDgx/0tA8RoDNr098e+TnCWSKQ32KDAN3geIH8Bow3AvGZVYb83GCJz59Jrevf8an4LJFfjvwc4UgivyO5/h0fijOJ+AJ8/hyhmUR+Pl6fjthSIr898nNkspfIr0R+jpAvkd8M5YsbXEjEZ9B97QiJtN4jPH6O2FwivwT5OWJTify2yM8NLiQGH8wMnz83aC5RfvoF8nPDloFAfil6zxyxEmPvJg9d/44PxZlI78vW9e/4VDRbSeQ3R/npht1KlC+rHT5/bsiMvScx8nOCQ5QvQ8ZLmcEjfP7coJNE+eJj7osjtBWIz0SYuutIKFO+oPfFDY5lyhfXv+NT8U6k9Y65L47wXCK+FXpfXBEpX9IlPn9uhCJzJ7D01hHORFY+YOWKI7yUiC/A3BdHeCqR3wJTdx2hg0h+aL07QiKd15i664xEfChfXJEpXzB11xWZwQeDqZ+OyCz8C9B6d0Rm4V80cf07PhTHEvGZM8oXN0hk8AFTP12xIq13lC+uTEQW/vmIzw2ORcbeE+TnBp1EFm6WyM8NmYV/5oT83JBZ+Gcwd8INDmUGH7ByxQ3eyMydwMoVN+goU764/h2fSqj1PkXvixsyUwdXWLnihtDCvz3KTzdkFv6ZHHOX3JDZddAcXP+OjyVSvmDhrSOcyZQvJ+TnBC9FWu8JBv/coJNEfOh9cUaofMHcFzdk9i3w1/j8OSG062C00cuv74HKtdSu9sWEpg6maoNHbJdTccfjWD7XLFO+qJ2ZQ2EuMjngUjqaXRJJ5MijQGnfEPZOIq/3q6s9h6N5f1qR++PrtN4pk5mZcylde2N5+qRWPiw0Wu/Mc5mJOZfy5WgePqkTG81C4ZZUD5/IvICrKy3H8+70xFY+JPKfP9olCu/O5MTjeXd6UkceyS+8ZbtWePiCw9gGKScyd0l47J2FpjReWZ1oZPFxJvOOkl14y+FapKq6FOSj+vJ9o5NIfivJhbfsLWVe6lfXuLYje/g8samDkvuGsHcUWc12KUgUO2jdkWm9G8HBB4pzlVZDNsb4OBSZembEehPZzhQePpOeRtTj8ovQpsli5QvxWaFwMYexrqwhkSOPzF7maph2Gg/fYjO2VsP/hFrvZ5GLoWyr0OESJKP88v0h1Eo+Cvz7ZtpodFZH2xHvCcVLmfeVQPlSPXwyn+JLyXjfnd5X57XIPRMYPKKdxih7UMbjfXd6X/sWiNym3mfmsLcWmUN8ZT8fZ6vhfzIL/8yh51wgCs8KD5/Jx9pq+IcLmYqh38oV5plGm29xHNdAXw1eiuTn99p0nmOZLthLQVJ8wHxwiiWqhj4LbzmcqzTZyzGXnT8oLvu/u3osvKX4oNBZXRUun7KRLNO6dxOi+8JbqzG50ywO4241XOJdz3vYeeUKkcrDFx1H3ONSg+N+rYiuR/5ZmW0vruXxyBt9N5h6fQT3nXpfqia7RqvBnEY4SeIpe+pRBCYdWu8s1HS5Euw/odVQw+66385z+9cnhWuNVsOq/Kwv3y/UeRVuh/KFVOaXjW5qdSscdhy/WbXtfWFeaxQuwaH4oFbDLeZug0ltF95SpjLFZXEa3+TctuIuH8F9q9vGVmWKi0niz+gwe4ipfXdo0GrfEA41OqvNYo30vk3mbd9tbbY943Cj0mRPxrQgsx9qOxzgN5/7QplAZ/mtxQFfvh9UtFs1uWj6BzPPNNZjmv0R787fuN2Q0r7hSA17KvPLgjPenVfYO7YIsGH5YrNE4925Wn9sj8t9zHHzcZ1GrXe2c42Roq89KxFfjaoh0fQWNvj3X/170OisNv52gvTqsdfwIxg8//xxONWY3Bmks88ca2iEvWmjhsTzhbd2edAoOxfnES9LkUCzJi+9Z/uGMM1Vmuzp8dNG2VujuMGQ0pPypfozVAqX82i2D1TE4frpm+9x+WJ1+svMHK2GJpif7S6SPvzPPY31mN8jVoivmarZ/fBWPlh4qzTFxfgl0muOsodDSuu7t5KzNr04ze1HtwWWLubtgwrk3onFzBuVKS5+/qETzHrg+y2AxZ2VK1Xlo9FkN3u0Gjqwdz9k+/q5LxTnKu/OUW2c+0Kc3VlnXbvwlidzlYfP345t786XYa9+M9Vtzf+XPJHzXG7sd6g7u7O1O43crlxhT2ZLi2v+wcO7sw+qGQO6PQSj5eB9YxFaDX0x3XRlJlflC9NJp8mex2g19Ha7Scv5snyh8KBSuIx6C6wXYu9qSKm8+CTRRmV+mck3aPQJuZwIcTF1l1llfpkxmKEkqHqH/jxjv8uXuvJGwgLrGmTZ0/8F5s/CWwqnKmVngFaDOBv/6w7N/95b5qVOkz2a4uGTx8Wf3s1/R/5RqLHf/1erAZMkVFD4vWv24k/vCxUq6zHNao0JZko4nFUNveh73xCapyqthv0O7049VKQmrR4PshrnY1bGedzG+2Aqc8tUqHS4YKzhBTjmWOfhG9Ppwm+MeauRnjEp4nsJjg8qlad/wMdPHWVfdajOHMEALT9lbKdfgw8sdPzjjRQzlTRRmAd/Bh94o1OBLrC4T82fOS5/x97pyQz7zrAljxIqvqdW/3s+nsyw7w6j7hqqh++7t/pn35DGa63byoUPJYevp+1vm+H8831ierREogfsiyXs1wSz6e9byzojSCbtuDU61KKw/H+Oy+Wm5dT3EII7/CPKUDG/N/z3r4oL1jkMoHrOP3E/eQ2X3Z03m86zp7JNSFXFoAwVwN7lWXO3Z+b0PEXivutZ3tAeZ8fLfpZpzT0lpRmg+9Prf/C48O56Qeam7pmwM50ytOsJPfCN+ea0jai+aU0bpQBLlKGdkXe7dee9E4tZ52QOE+ToDe2G7abmq3b3xGIWOhL5Rs54h3bAWW2z4MG+L6QzKG8ijOm2xl79AZnBo4JictLpDU1nr/vh40BFWR/F7cLp3+xJZ0w3WmMhYAts7+4A+qRJTT1Oo3vE/5BTpUX87qy+dn5yZCMXOgFiZmhjD8/xKJ/dxTtlT38YEmyE6figCFk8P/OI+aiSn9kv0ZR/5tmG/2mTiQ1WaWrhAnvYPfF0NfS93pdLSif9VY1PBPgIPV0NnTT8g5T2NAiwuOw+9qbPRtIbH9nIS6WphXmMAOtR9nzD/wbly1+sc+Jf9QbYIcA6k1n0/Im5M3hUhz2lNWbRFAHeIG7UaktbzEhhnukE6GNHwivsNSwYk4ZHNv5hM53eUFNiYtNvjb9VQcvBcF7udaoYnGD1g2nTtNiv3lzt/myt7cyxQOJ/FJaN33J+6/l8lCntMrJv+09ppGjXYuvORft71uQkrE4iBPj17mx12kbSoQOZeacToL/9+J1iKGs35ejZkY13/padzuReU372Agn2Ti1vbMeigbRmpjXrTR8n5uLQ9n51bXax1kLrz92sgsOnndU3Fq1a7xd/m1ZvaHr6zBElijts3fn8xOm7mB+N6vew+shNzmnTpWNk3edlRXOdJ/ADZ6ax3Xa6l09OnH72tyottA6Sz3qFMsUdZzf0rPa0BuVbjYoMHmddj4beP5n6+ZTafj/p8lMeQebuE6SfTd1t8rd3e3E/9Skz0yjcdh+SqzuysSW1/X4WH7Hlli36vMDunTjdCk91phb6+ej7YphuFkO3ce/E6ZasUhlqkpG3I2zWbyxVajMPvnumdU/7bMTz6/ufTtu/fPl3KaHSCpfFbLSPIGWtO6uvlWIXo7bQenEcZxVzc3xmF42n7jZgjzoz04LDGKsYiu8shm5jtZEcqrFKm2ePcGYa80nidNq97Iyv5pPeWkqKcQVInsDDV5FuX3GsNCif7sZUhtpYaArtQfqftdp+P9F0NAEyz4Vuit9r8K/+4kipNzQox9Ebyp7cnmRRv8G/enaj0xtqyjGUoZy1mtz52L7QuCNa+/2YfPgTm2gnOdrWcOF0+4vU6kwb+GYVbHt1Vt84K90OtUH5vWR/w8tRKHs0tN9t6nUDHJ+VekPVLlkdW+ndcBca5cvfi/XWstf6jz/Uc7C4f2f1tUizoLMznSdQvM/hJe7t3NnLXrUasEqHWQ+xM01nxaTyLuIUKw3Kp7NhPYFMG41eKaG5Ew+uu1BaaL2YDukj2Gt+2QOl+j2gUGmFy5BmptHydr9/Ca84iIhDpTI0OA+liuGuM6sfe9GsBPaWOk+gyYfRnc3xfKYgs6+q4ShLAl9BEA3jHCwmDS/86WwnKj57nTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAe6HrC4BeMtcXAD1kyG/IuPA2OJt1uHjnzV91ujPIo5O3RX7DRVsvt64vAjqzZy+duL4I6GySej7ql+Fi3/MLBDhUXPhesEYBM1S0DjyDAmawbG48k7q+CugqjKr8Vjt8AIeJZqsqv+CAF+gw2TKo8jMJhpCGKUzMV37RBhXoEH29Pr/yMwd8AIeISvMnvwRN+AHiIvmbn39EfsPDR/9vfibJEODQcJabf/kZDMIPDm+Cn/wS11cDrX0/fn/zMzM0IYaFluZ3fin6YIaFoov8zBQP4JDQ2lzmtyoQ4HBwvLrKz+QeatDBoNxc5xdgIuFg0Nq/yc+kSwQ4DLRMzW1+JonxBh0CjhNTl585hAjw/XFYmvr8TOn62qCB3/Fd5ucf0Yx/d/Zo7uZnFjME+N7sbPEgPxOdEOA7s6fIPMrPRHgC35idXcV3k59ZHNER86bYOy6u47rJr6pC0Yx4S1XDwb8JqyY/c4jRE/N+KD7UZFWXn0nQlfZ2aJnURVWbn0nXhHfoOyFvndYmVZ+f8XO8Q98IFXlQH9Sd/IxZrQkJvgey09W9mO7mVzUFlx7jLepaFcGs/tX5LD9j8k2GBJ1izja1dUuj/EyQHwu8Rp0hLo4P03uWX1XIJOUstIjw9diGm0Ny22Jvl19l9SdCvEhfh8l6u0Ny3dnZLb/qNbqK8nXBE0uE76EuJrITLtZ5tLrTYuiQ33eGvp+et6ddgQ1D1YRZtplv89T3G2X35T//QvkmZnEE7QAAAABJRU5ErkJggg==\";\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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taterlaxmi/react-icon-library",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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"
|
package/dist/chunk-N5DDYT6F.js
DELETED
|
@@ -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":"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","names":[]}
|