@iconia/react 0.1.0-dev.00e4942
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/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/cli/index.js +26 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wyesoftware
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @iconia/react
|
|
2
|
+
|
|
3
|
+
CLI and React component library for [iconia.io](https://iconia.io) — manage SVG icon collections and import them as typed React components at build time.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @iconia/react
|
|
9
|
+
# or
|
|
10
|
+
bun add @iconia/react
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @iconia/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @iconia/react init # create iconia.config.ts
|
|
19
|
+
npx @iconia/react add my-icons # add a collection and download icons
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Shorter commands after install
|
|
24
|
+
|
|
25
|
+
Once `@iconia/react` is installed in your project you can drop the package name and use `iconia` directly:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
iconia init
|
|
29
|
+
iconia add my-icons
|
|
30
|
+
iconia sync
|
|
31
|
+
iconia pull
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
`iconia.config.ts` in your project root:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import type { IconiaConfig } from "@iconia/react";
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
apiKey: process.env.ICONIA_API_KEY,
|
|
43
|
+
collections: ["my-icons", "team-brand"],
|
|
44
|
+
} satisfies IconiaConfig;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Set `ICONIA_API_KEY` to an API key generated in your [iconia.io dashboard](https://iconia.io/settings/api-keys).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## CLI commands
|
|
52
|
+
|
|
53
|
+
### `iconia init`
|
|
54
|
+
|
|
55
|
+
Create a starter `iconia.config.ts` in the current directory.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx @iconia/react init
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### `iconia add <slug>`
|
|
64
|
+
|
|
65
|
+
Add a collection to your project: validates it exists on the server, updates `iconia.config.ts`, downloads icons and generates React components.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx @iconia/react add my-icons
|
|
69
|
+
npx @iconia/react add team-brand
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### `iconia remove <slug>`
|
|
75
|
+
|
|
76
|
+
Remove a collection: deletes generated files and removes the slug from `iconia.config.ts`.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx @iconia/react remove my-icons
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### `iconia pull`
|
|
85
|
+
|
|
86
|
+
Full re-download and regeneration of all (or one) collection. Replaces existing generated files.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx @iconia/react pull # all collections from config
|
|
90
|
+
npx @iconia/react pull --collection my-icons # one collection only
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### `iconia sync`
|
|
96
|
+
|
|
97
|
+
Incremental update: compares server fingerprints with the local `.iconia-lock.json` and only regenerates collections that have changes (new icons, updated icons, deleted icons).
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npx @iconia/react sync # all collections
|
|
101
|
+
npx @iconia/react sync --collection my-icons
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Output shows a diff per collection:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
my-icons: +3 added (arrow-right, chevron-down, x)
|
|
108
|
+
team-brand: ~1 updated (logo)
|
|
109
|
+
old-set: up to date
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### `iconia upload <path>`
|
|
115
|
+
|
|
116
|
+
Upload a single SVG file or an entire folder of SVGs to a collection on iconia.io.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx @iconia/react upload ./icons/arrow.svg --collection my-icons
|
|
120
|
+
npx @iconia/react upload ./icons/ --collection my-icons
|
|
121
|
+
npx @iconia/react upload ./icons/ --collection my-icons --tags ui,navigation
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Options:
|
|
125
|
+
|
|
126
|
+
- `-c, --collection <slug>` — target collection **(required)**
|
|
127
|
+
- `--tags <tags>` — comma-separated tags applied to all uploaded icons
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Using icons in your project
|
|
132
|
+
|
|
133
|
+
After `pull` or `sync`, import icons directly from the collection:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { ArrowRight, Home, Settings } from "@iconia/react/my-icons";
|
|
137
|
+
|
|
138
|
+
export function App() {
|
|
139
|
+
return (
|
|
140
|
+
<div>
|
|
141
|
+
<ArrowRight className="size-5" />
|
|
142
|
+
<Home strokeWidth={1.5} />
|
|
143
|
+
<Settings color="gray" />
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
All icons accept standard `SVGProps<SVGSVGElement>` and forward refs.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Lockfile
|
|
154
|
+
|
|
155
|
+
`iconia sync` reads and writes `.iconia-lock.json` in your project root. It stores the fingerprint of every downloaded icon so that `sync` can detect changes without diffing SVG content.
|
|
156
|
+
|
|
157
|
+
Commit this file to version control so your team shares the same sync baseline.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## API key
|
|
162
|
+
|
|
163
|
+
Generate an API key at **iconia.io → Settings → API Keys**. The key authenticates CLI requests and scopes them to your collections.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
export ICONIA_API_KEY=ik_...
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Or add it to `.env` and load it via your preferred env tooling (`dotenv`, Vite, Next.js, etc.).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as ho}from"commander";import{Command as Ro}from"commander";import N from"fs";import jo from"path";import v from"picocolors";var Uo=`import type { IconiaConfig } from '@iconia/react';
|
|
3
|
+
|
|
4
|
+
const config: IconiaConfig = {
|
|
5
|
+
apiKey: process.env.ICONIA_API_KEY ?? '',
|
|
6
|
+
collections: [],
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default config;
|
|
10
|
+
`,X=new Ro("init").description("Create an iconia.config.ts file in the current directory").action(()=>{let o=jo.resolve(process.cwd(),"iconia.config.ts");if(N.existsSync(o)){console.log(v.yellow("iconia.config.ts already exists. Skipping."));return}N.writeFileSync(o,Uo),console.log(v.green("✓ Created iconia.config.ts")),console.log(`
|
|
11
|
+
Next steps:`),console.log(` 1. Set ${v.cyan("ICONIA_API_KEY")} in your environment`),console.log(` 2. Edit ${v.cyan("iconia.config.ts")} to add your collections`),console.log(` 3. Run ${v.cyan("npx iconia pull")} to fetch icons`),console.log(`
|
|
12
|
+
Import icons: ${v.cyan("import { MyIcon } from '@iconia/react/my-collection'")}`)});import{Command as Vo}from"commander";import V from"picocolors";import vo from"ora";import{z as Y}from"zod";import D from"path";var uo=Y.object({apiKey:Y.string().min(1,"apiKey is required"),collections:Y.array(Y.string()).default([])}),Lo="https://api.iconia.io";async function u(){let o=D.resolve(process.cwd(),"iconia.config.ts"),r=D.resolve(process.cwd(),"iconia.config.js"),i;try{let f=await import(o);i=f.default??f}catch{try{let f=await import(r);i=f.default??f}catch{throw Error("Could not find iconia.config.ts or iconia.config.js\nRun `npx @iconia/react init` to create one.")}}let n=uo.safeParse(i);if(!n.success){let f=n.error.issues.map((a)=>` - ${a.path.join(".")}: ${a.message}`).join(`
|
|
13
|
+
`);throw Error(`Invalid iconia config:
|
|
14
|
+
${f}`)}let c=i.__internal?.endpoint??Lo;return{...n.data,apiUrl:c}}function Z(o){return{Authorization:`ApiKey ${o.apiKey}`,"Content-Type":"application/json"}}async function G(o,r=3){for(let i=0;i<=r;i++){let n=await o();if(n.status!==429||i===r)return n;let t=parseInt(n.headers.get("Retry-After")??"60",10);process.stderr.write(`
|
|
15
|
+
⏳ Rate limited — waiting ${t}s before retry ${i+1}/${r}...
|
|
16
|
+
`),await new Promise((c)=>{let f=()=>{clearTimeout(a),process.exit(130)},a=setTimeout(()=>{process.removeListener("SIGINT",f),c()},t*1000);process.once("SIGINT",f)})}return o()}async function l(o){let r=await G(()=>fetch(new URL("/v1/collections",o.apiUrl).toString(),{headers:Z(o)}));if(!r.ok){let n={};try{n=await r.json()}catch{}throw Error(`API error ${r.status}: ${n.error??r.statusText}`)}return(await r.json()).collections}async function P(o,r){let i=new URL("/v1/collections/icons",o.apiUrl);i.searchParams.set("collections",r.join(","));let n=await G(()=>fetch(i.toString(),{headers:Z(o)}));if(!n.ok){let c={};try{c=await n.json()}catch{}throw Error(`API error ${n.status}: ${c.error??n.statusText}`)}return(await n.json()).icons}async function x(o,r){let i=await G(()=>fetch(new URL("/v1/icons",o.apiUrl).toString(),{method:"POST",headers:Z(o),body:JSON.stringify(r)}));if(!i.ok){let n={};try{n=await i.json()}catch{}throw Error(n.error??i.statusText)}return!0}import _ from"fs";import O from"path";import{fileURLToPath as Eo}from"url";import po from"picocolors";import{optimize as _o}from"svgo";function Co(o){return _o(o,{plugins:["removeDoctype","removeXMLProcInst","removeComments","removeMetadata","removeTitle","removeDesc"]}).data.replace(/<svg([^>]*)>/i,(i,n)=>`<svg${n.replace(/\s+(width|height)=['"][^'"]*['"]/gi,"")}>`)}function g(o){let r={},i=/([\w:-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g,n;while((n=i.exec(o))!==null){let t=n[1].replace(/^xlink:/,"").replace(/-([a-z])/g,(c,f)=>f.toUpperCase());r[t]=n[2]??n[3]??""}return r}function oo(o){let r=[],i=0;while(i<o.length){let n=o.indexOf("<",i);if(n===-1)break;let t=o[n+1];if(t==="/"||t==="!"||t==="?"){let I=o.indexOf(">",n);i=I===-1?o.length:I+1;continue}let c=n+1,f=!1,a="";while(c<o.length){let I=o[c];if(f){if(I===a)f=!1}else if(I==='"'||I==="'")f=!0,a=I;else if(I===">")break;c++}if(c>=o.length)break;let $=o.slice(n+1,c),m=$.trimEnd().endsWith("/"),e=m?$.slice(0,$.lastIndexOf("/")).trim():$.trim(),y=e.search(/\s/),w=y===-1?e:e.slice(0,y);if(!w){i=c+1;continue}let j=y===-1?"":e.slice(y+1),p=g(j);if(i=c+1,m)r.push([w,p]);else{let I=`<${w}`,K=`</${w}>`,F=1,R=i;while(F>0&&R<o.length){let U=o.indexOf(I,R),s=o.indexOf(K,R);if(s===-1)break;if(U!==-1&&U<s)F++,R=U+I.length;else if(F--,F===0){let Io=o.slice(i,s),k=oo(Io),Fo=k.length>0?[w,p,k]:[w,p];r.push(Fo),i=s+K.length}else R=s+K.length}if(F>0)r.push([w,p])}}return r}function ro(o){let r=Co(o),i=r.match(/<svg([^>]*)>/i),n=g(i?.[1]??"");delete n.xmlns,delete n.xmlnsXlink;let{viewBox:t}=n,c=t?{viewBox:t}:{},a=r.match(/<svg[^>]*>([\s\S]*)<\/svg>/i)?.[1]?.trim()??"";return{iconNode:oo(a),svgAttrs:c}}function b(o){return o.replace(/[-_\s]+(.)/g,(r,i)=>i.toUpperCase()).replace(/^(.)/,(r,i)=>i.toUpperCase())}function io(o){if(o.length===0)return"";let r=["import { forwardRef, createElement } from 'react';","","const _r = (n) => n.map(([t, a, c], i) => createElement(t, { key: i, ...a }, ...(c ? _r(c) : [])));",""];for(let{name:i,iconNode:n,svgAttrs:t}of o){let c=b(i),f={viewBox:"0 0 24 24",...t},a=JSON.stringify(n),$=JSON.stringify(f);r.push(`export const ${c} = /*#__PURE__*/forwardRef(({ children, ...props }, ref) =>`,` createElement('svg', { ref, xmlns: 'http://www.w3.org/2000/svg', ...${$}, ...props },`,` ..._r(${a}),`," children"," )",");",`${c}.displayName = '${c}';`,"")}return r.join(`
|
|
17
|
+
`)}function no(o){let r=["import type { ForwardRefExoticComponent, SVGProps, RefAttributes } from 'react';","","type IconComponent = ForwardRefExoticComponent<SVGProps<SVGSVGElement> & RefAttributes<SVGSVGElement>>;",""];for(let i of o)r.push(`export declare const ${b(i)}: IconComponent;`);return r.push(""),r.join(`
|
|
18
|
+
`)}function M(){return O.resolve(O.dirname(Eo(import.meta.url)),"..")}function z(o,r){let i=[];for(let t of r)try{let{iconNode:c,svgAttrs:f}=ro(t.svgContent);i.push({name:t.name,iconNode:c,svgAttrs:f})}catch(c){console.warn(po.yellow(` ⚠ Skipped ${t.name}: ${c.message}`))}if(i.length===0)return 0;let n=M();return _.writeFileSync(O.join(n,`${o}.js`),io(i),"utf-8"),_.writeFileSync(O.join(n,`${o}.d.ts`),no(i.map((t)=>t.name)),"utf-8"),i.length}function H(){let o=O.join(M(),"..","package.json");try{let r=JSON.parse(_.readFileSync(o,"utf-8")),i=r.exports??{};if(i["./*"]?.import?.startsWith("./dist/"))return;r.exports={".":i["."]??{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"},"./*":{import:"./dist/*.js",types:"./dist/*.d.ts"}},_.writeFileSync(o,JSON.stringify(r,null,2)+`
|
|
19
|
+
`,"utf-8")}catch{}}function to(o){let r=M(),i=O.join(r,`${o}.js`),n=O.join(r,`${o}.d.ts`);if(_.existsSync(i))_.unlinkSync(i);if(_.existsSync(n))_.unlinkSync(n)}import co from"fs";import Ko from"path";var Oo=".iconia-lock.json";function fo(){return Ko.resolve(process.cwd(),Oo)}function C(){try{let o=co.readFileSync(fo(),"utf-8");return JSON.parse(o)}catch{return{version:1,collections:{}}}}function E(o){co.writeFileSync(fo(),JSON.stringify(o,null,2),"utf-8")}function J(o,r,i){return{...o,collections:{...o.collections,[r]:{icons:Object.fromEntries(i.map((n)=>[n.slug,n.fingerprint]))}}}}function ao(o,r){let{[r]:i,...n}=o.collections;return{...o,collections:n}}var mo=new Vo("pull").description("Fetch icons and regenerate all collection files (replaces existing)").option("-c, --collection <slug>","Pull only this collection").action(async(o)=>{let r=vo("Loading config...").start(),i;try{i=await u()}catch(m){r.fail(V.red(m.message)),process.exit(1)}let n=o.collection?[o.collection]:i.collections;if(n.length===0){r.warn(V.yellow("No collections in config. Add one with `iconia add <slug>`."));return}if(o.collection&&!i.collections.includes(o.collection))r.fail(V.red(`Collection '${o.collection}' is not in your config.`)),process.exit(1);r.text=`Fetching icons for ${n.join(", ")}...`;let t;try{t=await P(i,n)}catch(m){r.fail(V.red(`Failed to fetch icons: ${m.message}`)),process.exit(1)}if(t.length===0){r.warn(V.yellow("No icons found."));return}r.text=`Generating ${t.length} icon${t.length!==1?"s":""}...`;let c=new Map;for(let m of t){let e=c.get(m.collectionSlug)??[];e.push(m),c.set(m.collectionSlug,e)}let f=C(),a=0,$=[];for(let[m,e]of c){let y=z(m,e);if(y===0)continue;a+=y,$.push(m),f=J(f,m,e.map((w)=>({slug:w.slug,fingerprint:w.fingerprint})))}if(E(f),H(),r.succeed(V.green(`Generated ${a} icon${a!==1?"s":""} across ${$.length} collection${$.length!==1?"s":""}`)),$.length>0){let m=$[0]??"",e=b(c.get(m)?.[0]?.name??"MyIcon");console.log(`
|
|
20
|
+
Import icons:
|
|
21
|
+
${V.cyan(`import { ${e} } from '@iconia/react/${m}'`)}`)}});import{Command as Po}from"commander";import L from"picocolors";import bo from"ora";import A from"fs";import yo from"path";function T(){let o=yo.resolve(process.cwd(),"iconia.config.ts"),r=yo.resolve(process.cwd(),"iconia.config.js");if(A.existsSync(o))return o;if(A.existsSync(r))return r;return null}function h(o){let n=A.readFileSync(o,"utf-8").match(/collections:\s*\[([\s\S]*?)\]/)?.[1];if(!n)return null;let t=[],c=/['"]([^'"]+)['"]/g,f;while((f=c.exec(n))!==null){let a=f[1];if(a)t.push(a)}return t}function Q(o,r){let i=A.readFileSync(o,"utf-8"),n=r.length===0?"[]":`[
|
|
22
|
+
${r.map((c)=>` '${c}'`).join(`,
|
|
23
|
+
`)},
|
|
24
|
+
]`,t=i.replace(/collections:\s*\[[\s\S]*?\]/,`collections: ${n}`);if(t===i)return!1;return A.writeFileSync(o,t,"utf-8"),!0}var eo=new Po("add").description("Add a collection to your project (updates config and downloads icons)").argument("<slug>","Collection slug").action(async(o)=>{let r=bo("Loading config...").start(),i;try{i=await u()}catch(a){r.fail(L.red(a.message)),process.exit(1)}if(i.collections.includes(o))r.info(L.yellow(`Collection '${o}' is already in your config. Running pull...`));else{r.text="Verifying collection...";let a;try{a=await l(i)}catch(j){r.fail(L.red(`Failed to fetch collections: ${j.message}`)),process.exit(1)}if(!a.find((j)=>j.slug===o))r.fail(L.red(`Collection '${o}' not found. Available: ${a.map((j)=>j.slug).join(", ")||"none"}`)),process.exit(1);let m=T();if(!m)r.fail(L.red("Config file not found. Run `npx @iconia/react init` first.")),process.exit(1);let y=[...h(m)??i.collections,o];if(!Q(m,y))r.warn(L.yellow(`Could not update config automatically. Add '${o}' to collections in iconia.config.ts manually.`));else r.text=`Added '${o}' to config. Fetching icons...`}r.text=`Fetching icons for '${o}'...`;let n;try{n=await P(i,[o])}catch(a){r.fail(L.red(`Failed to fetch icons: ${a.message}`)),process.exit(1)}if(n.length===0){r.warn(L.yellow(`No icons in collection '${o}'.`));return}r.text=`Generating ${n.length} icon${n.length!==1?"s":""}...`;let t=z(o,n),c=C();c=J(c,o,n.map((a)=>({slug:a.slug,fingerprint:a.fingerprint}))),E(c),r.succeed(L.green(`Added '${o}' — ${t} icon${t!==1?"s":""} generated`));let f=b(n[0]?.name??"Icon");console.log(`
|
|
25
|
+
Import icons:
|
|
26
|
+
${L.cyan(`import { ${f} } from '@iconia/react/${o}'`)}`)});import{Command as zo}from"commander";import W from"picocolors";var wo=new zo("remove").description("Remove a collection from your project (updates config and deletes generated files)").argument("<slug>","Collection slug").action(async(o)=>{let r;try{r=await u()}catch(n){console.error(W.red(n.message)),process.exit(1)}if(!r.collections.includes(o))console.warn(W.yellow(`Collection '${o}' is not in your config.`));else{let n=T();if(n){let c=(h(n)??r.collections).filter((a)=>a!==o);if(!Q(n,c))console.warn(W.yellow(`Could not update config automatically. Remove '${o}' from collections in iconia.config.ts manually.`))}}to(o);let i=C();i=ao(i,o),E(i),console.log(W.green(`✓ Removed collection '${o}'`))});import{Command as Jo}from"commander";import d from"picocolors";import So from"ora";var $o=new Jo("sync").description("Sync icons: add new, update changed, remove deleted — without full re-fetch").option("-c, --collection <slug>","Sync only this collection").action(async(o)=>{let r=So("Loading config...").start(),i;try{i=await u()}catch(y){r.fail(d.red(y.message)),process.exit(1)}let n=o.collection?[o.collection]:i.collections;if(n.length===0){r.warn(d.yellow("No collections in config. Add one with `iconia add <slug>`."));return}if(o.collection&&!i.collections.includes(o.collection))r.fail(d.red(`Collection '${o.collection}' is not in your config.`)),process.exit(1);r.text=`Fetching icons for ${n.join(", ")}...`;let t;try{t=await P(i,n)}catch(y){r.fail(d.red(`Failed to fetch icons: ${y.message}`)),process.exit(1)}let c=new Map;for(let y of t){let w=c.get(y.collectionSlug)??[];w.push(y),c.set(y.collectionSlug,w)}let f=C(),a=0,$=0,m=0,e=0;for(let y of n){let w=c.get(y)??[],j=f.collections[y]?.icons??{},p=new Map(w.map((s)=>[s.slug,s])),I=new Set(Object.keys(j)),K=new Set(p.keys()),F=[...K].filter((s)=>!I.has(s)),R=[...I].filter((s)=>!K.has(s)),U=[...K].filter((s)=>I.has(s)&&j[s]!==p.get(s).fingerprint);if(F.length===0&&R.length===0&&U.length===0){console.log(d.dim(` ${y}: up to date`));continue}if(F.length>0)console.log(d.green(` ${y}: +${F.length} added`)+d.dim(` (${F.slice(0,5).join(", ")}${F.length>5?"…":""})`));if(U.length>0)console.log(d.blue(` ${y}: ~${U.length} updated`)+d.dim(` (${U.slice(0,5).join(", ")}${U.length>5?"…":""})`));if(R.length>0)console.log(d.red(` ${y}: -${R.length} removed`)+d.dim(` (${R.slice(0,5).join(", ")}${R.length>5?"…":""})`));if(w.length>0)z(y,w);f=J(f,y,w.map((s)=>({slug:s.slug,fingerprint:s.fingerprint}))),a+=F.length,$+=U.length,m+=R.length,e++}if(E(f),e>0)H();if(e===0)r.succeed(d.green("Everything is up to date."));else r.succeed(d.green(`Sync complete: ${a} added, ${$} updated, ${m} removed across ${e} collection${e!==1?"s":""}`))});import{Command as qo}from"commander";import B from"fs";import q from"path";import S from"picocolors";import Ao from"ora";function Yo(o){return q.basename(o,q.extname(o)).toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}function Ho(o){return o.split("-").map((r)=>r.charAt(0).toUpperCase()+r.slice(1)).join(" ")}function To(o){let r=B.statSync(o);if(r.isFile()){if(!o.endsWith(".svg"))throw Error(`File '${o}' is not an SVG.`);return[o]}if(r.isDirectory())return B.readdirSync(o).filter((i)=>i.endsWith(".svg")).map((i)=>q.join(o,i));throw Error(`'${o}' is not a file or directory.`)}var so=new qo("upload").description("Upload SVG file(s) to an Iconia collection").argument("<path>","SVG file or directory of SVGs").requiredOption("-c, --collection <slug>","Target collection slug").option("--tags <tags>","Comma-separated tags to apply to all uploaded icons").action(async(o,r)=>{let i=Ao("Loading config...").start(),n;try{n=await u()}catch(m){i.fail(S.red(m.message)),process.exit(1)}let t;try{t=To(q.resolve(process.cwd(),o))}catch(m){i.fail(S.red(m.message)),process.exit(1)}if(t.length===0){i.warn(S.yellow("No SVG files found."));return}let c=r.tags?r.tags.split(",").map((m)=>m.trim()).filter(Boolean):[];i.text=`Uploading ${t.length} file${t.length!==1?"s":""} to '${r.collection}'...`;let f=0,a=0,$=[];for(let m of t){let e=Yo(m);if(!e){$.push(`${q.basename(m)}: could not derive a valid slug`),a++;continue}let y=B.readFileSync(m,"utf-8");try{await x(n,{collectionSlug:r.collection,name:Ho(e),slug:e,svgContent:y,tags:c}),f++,i.text=`Uploading... (${f}/${t.length})`}catch(w){$.push(`${q.basename(m)}: ${w.message}`),a++}}if(a===0)i.succeed(S.green(`Uploaded ${f} icon${f!==1?"s":""} to '${r.collection}'`));else{i.warn(S.yellow(`Uploaded ${f}, failed ${a}`));for(let m of $)console.log(S.dim(` ✗ ${m}`))}});process.on("SIGINT",()=>process.exit(130));var Qo=new ho("iconia").version("0.1.0").description("CLI for fetching and generating React icon components from Iconia").addCommand(X).addCommand(eo).addCommand(wo).addCommand(mo).addCommand($o).addCommand(so);Qo.parse(process.argv);
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var{defineProperty:m,getOwnPropertyNames:a,getOwnPropertyDescriptor:d}=Object,y=Object.prototype.hasOwnProperty;function G(e){return this[e]}var S=(e)=>{var o=(c??=new WeakMap).get(e),t;if(o)return o;if(o=m({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function"){for(var n of a(e))if(!y.call(o,n))m(o,n,{get:G.bind(e,n),enumerable:!(t=d(e,n))||t.enumerable})}return c.set(e,o),o},c;var V=(e)=>e;function I(e,o){this[e]=V.bind(null,o)}var g=(e,o)=>{for(var t in o)m(e,t,{get:o[t],enumerable:!0,configurable:!0,set:I.bind(o,t)})};var E={};g(E,{createIconiaIcon:()=>l});module.exports=S(E);var r=require("react");function i(e){return e.map(([o,t,n],p)=>r.createElement(o,{key:p,...t},...n?i(n):[]))}function l(e,o,t={}){let n=r.forwardRef(({children:p,...s},f)=>r.createElement("svg",{ref:f,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",...t,...s},...i(o),p));return n.displayName=e,n}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
import { ForwardRefExoticComponent, RefAttributes, SVGProps } from 'react';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
export type IconNodeElement = [
|
|
7
|
+
tagName: string,
|
|
8
|
+
attrs: Record<string, string>,
|
|
9
|
+
children?: IconNodeElement[]
|
|
10
|
+
];
|
|
11
|
+
export type IconNode = IconNodeElement[];
|
|
12
|
+
declare const configSchema: z.ZodObject<{
|
|
13
|
+
apiKey: z.ZodString;
|
|
14
|
+
collections: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
export type IconiaConfig = z.infer<typeof configSchema> & {
|
|
17
|
+
apiUrl?: string;
|
|
18
|
+
};
|
|
19
|
+
export type IconComponent = ForwardRefExoticComponent<SVGProps<SVGSVGElement> & RefAttributes<SVGSVGElement>>;
|
|
20
|
+
export declare function createIconiaIcon(displayName: string, iconNode: IconNode, svgAttrs?: Record<string, string>): IconComponent;
|
|
21
|
+
|
|
22
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{forwardRef as s,createElement as p}from"react";function m(o){return o.map(([t,n,e],r)=>p(t,{key:r,...n},...e?m(e):[]))}function a(o,t,n={}){let e=s(({children:r,...c},i)=>p("svg",{ref:i,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",...n,...c},...m(t),r));return e.displayName=o,e}export{a as createIconiaIcon};
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iconia/react",
|
|
3
|
+
"version": "0.1.0-dev.00e4942",
|
|
4
|
+
"description": "Fetch and generate React icon components from your Iconia collections",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./*": {
|
|
16
|
+
"import": "./dist/*.js",
|
|
17
|
+
"types": "./dist/*.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"iconia": "./dist/cli/index.js"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun build.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"commander": "14.0.3",
|
|
32
|
+
"ora": "9.3.0",
|
|
33
|
+
"picocolors": "1.1.1",
|
|
34
|
+
"prettier": "3.8.1",
|
|
35
|
+
"svgo": "4.0.1",
|
|
36
|
+
"zod": "4.3.6"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/bun": "1.3.11",
|
|
40
|
+
"@types/node": "25.5.2",
|
|
41
|
+
"@types/react": "19.2.14",
|
|
42
|
+
"bun-plugin-dts": "0.4.0",
|
|
43
|
+
"typescript": "6.0.2"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist"
|
|
47
|
+
],
|
|
48
|
+
"keywords": [
|
|
49
|
+
"react",
|
|
50
|
+
"nodejs",
|
|
51
|
+
"svg",
|
|
52
|
+
"cli",
|
|
53
|
+
"typescript",
|
|
54
|
+
"icons",
|
|
55
|
+
"bun"
|
|
56
|
+
],
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"author": "Wyesoftware",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/iconia-io/iconia-react.git"
|
|
62
|
+
},
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/iconia-io/iconia-react/issues"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://github.com/iconia-io/iconia-react#readme"
|
|
67
|
+
}
|