denji 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@
3
3
  "type": "object",
4
4
  "properties": {
5
5
  "$schema": {
6
+ "default": "https://denji-docs.vercel.app/configuration_schema.json",
6
7
  "description": "The URL of the JSON Schema for this configuration file (e.g., './node_modules/denji/configuration_schema.json')",
7
8
  "type": "string"
8
9
  },
@@ -105,6 +106,7 @@
105
106
  }
106
107
  },
107
108
  "required": [
109
+ "$schema",
108
110
  "output",
109
111
  "framework",
110
112
  "typescript"
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{Command as e}from"commander";import t from"node:path";import{cancel as n,confirm as r,intro as i,isCancel as a,outro as o,select as s,text as c}from"@clack/prompts";import{z as l}from"zod";import u from"node:fs/promises";import d from"picocolors";import{spawn as f}from"node:child_process";import{transform as p}from"@svgr/core";import{parseSync as m}from"oxc-parser";var h=`denji`,g=`A CLI tool to manage your SVG icons`,_=`0.1.0`;const v=l.enum([`react`]),y=l.enum([`hidden`,`img`,`title`,`presentation`]).or(l.literal(!1)),b=l.object({$schema:l.string().optional().describe(`The URL of the JSON Schema for this configuration file (e.g., './node_modules/denji/configuration_schema.json')`),output:l.string().describe(`The output file path for generated icon components (e.g., './src/icons.tsx')`),framework:v.describe(`The framework to generate icon components for`),typescript:l.boolean().default(!0).describe(`Whether to generate TypeScript code`),a11y:y.optional().describe(`Accessibility strategy for SVG icons (hidden: aria-hidden, img: role=img with aria-label, title: <title> element, presentation: role=presentation, false: no a11y attrs)`),hooks:l.object({preAdd:l.array(l.string()).describe(`Scripts to run before adding icons`),postAdd:l.array(l.string()).describe(`Scripts to run after adding icons`),preRemove:l.array(l.string()).describe(`Scripts to run before removing icons`),postRemove:l.array(l.string()).describe(`Scripts to run after removing icons`),preClear:l.array(l.string()).describe(`Scripts to run before clearing all icons`),postClear:l.array(l.string()).describe(`Scripts to run after clearing all icons`),preList:l.array(l.string()).describe(`Scripts to run before listing icons`),postList:l.array(l.string()).describe(`Scripts to run after listing icons`)}).partial().optional().describe(`Hooks to run at various stages`)}).meta({title:`Denji Configuration Schema`,description:`Schema for Denji configuration file`}),x=`denji.json`;var S=class{constructor(e){this.value=e}isOk(){return!0}isErr(){return!1}},C=class{constructor(e){this.error=e}isOk(){return!1}isErr(){return!0}};async function w(e,t,n){try{return await u.writeFile(e,t,n),new S(null)}catch{return new C(`Failed to write file.`)}}async function T(e,t){try{return new S(await u.readFile(e,t))}catch{return new C(`Failed to read file.`)}}async function ee(e,t){try{return await u.mkdir(e,t),new S(null)}catch{return new C(`Failed to create directory.`)}}async function E(e,t){try{return await u.access(e,t),!0}catch{return!1}}async function D(e){let n=t.join(e,x);if(!await E(n))return new C(`${x} not found. Run "denji init" first.`);let r=await T(n,`utf-8`);if(r.isErr())return new C(`Failed to read ${x}`);try{let e=b.safeParse(JSON.parse(r.value));return e.success?new S(e.data):new C(`Invalid ${x}: ${e.error.message}`)}catch{return new C(`Invalid JSON in ${x}`)}}const O=console,k={error(e){O.error(d.red(e))},warn(e){O.warn(d.yellow(e))},info(e){O.info(d.cyan(e))},success(e){O.info(d.green(e))},break(){O.info(``)}};function A(e){k.error(`Something went wrong. Please check the error below for more details.`),k.error(`If the problem persists, please open an issue on GitHub.`),k.break(),k.error(e),k.break(),process.exit(1)}async function j(e,t){if(!e||e.length===0)return new S(null);for(let n of e){k.info(`Running: ${n}`);let e=await te(n,t);if(e.isErr())return e}return new S(null)}function te(e,t){return new Promise(n=>{let r=f(e,{cwd:t,shell:!0,stdio:`inherit`});r.on(`error`,e=>{n(new C(`Hook failed: ${e.message}`))}),r.on(`close`,t=>{n(t===0?new S(null):new C(`Hook "${e}" exited with code ${t}`))})})}const M=/^[a-z0-9]+(-[a-z0-9]+)*$/;function ne(e){let t=e.split(`:`);if(t.length!==2)return new C(`Invalid icon format "${e}". Expected "prefix:name" (e.g., mdi:home)`);let[n,r]=t;return M.test(n)?M.test(r)?new S({prefix:n,name:r}):new C(`Invalid name "${r}". Must match: lowercase letters, numbers, hyphens`):new C(`Invalid prefix "${n}". Must match: lowercase letters, numbers, hyphens`)}const re=/[-_]/;function N(e){let t=e.split(`:`)[1];if(!t)throw Error(`Invalid icon format: ${e}`);return t.split(re).filter(Boolean).map(e=>e.at(0)?.toUpperCase()+e.slice(1).toLowerCase()).join(``)}async function P(e){try{let{loadIcon:t,buildIcon:n}=await import(`iconify-icon`),r=await t(e);if(!r)return new C(`Icon "${e}" not found`);let i=n(r,{height:`1em`});return i?new S(`<svg xmlns="http://www.w3.org/2000/svg" ${Object.entries(i.attributes).map(([e,t])=>`${e}="${t}"`).join(` `)}>${i.body}</svg>`):new C(`Failed to build icon "${e}"`)}catch{return new C(`Icon "${e}" not found`)}}const F=/<svg[\s\S]*<\/svg>/,I=/<svg([^>]*)>/;function L(e){return e.replace(/([a-z])([A-Z])/g,`$1 $2`)}function R(e,t){switch(e){case`hidden`:return{"aria-hidden":`true`};case`img`:return{role:`img`,"aria-label":L(t)};case`presentation`:return{role:`presentation`};default:return{}}}async function z(e,t,n={}){let{a11y:r}=n,i=(await p(e,{plugins:[`@svgr/plugin-svgo`,`@svgr/plugin-jsx`],svgoConfig:{plugins:[`preset-default`,`convertStyleToAttrs`,`sortAttrs`,`mergePaths`]},jsxRuntime:`automatic`,typescript:!1,expandProps:`end`,svgProps:R(r,t),titleProp:r===`title`},{componentName:`Icon`})).match(F);if(!i)throw Error(`Failed to extract SVG from SVGR output`);let a=i[0];if(r===`title`){let e=L(t);a=a.replace(I,`<svg$1><title>${e}</title>`)}return`${t}: (props) => (${a})`}function B(e){let t=m(`icons.tsx`,e).program,n=[],r=0,i=0;for(let e of t.body)if(e.type===`ExportNamedDeclaration`&&e.declaration?.type===`VariableDeclaration`){for(let t of e.declaration.declarations)if(t.id?.type===`Identifier`&&t.id.name===`Icons`){let e=t.init;if(e?.type===`TSSatisfiesExpression`&&(e=e.expression),e?.type===`TSAsExpression`&&(e=e.expression),e?.type===`ObjectExpression`){r=e.start+1,i=e.end-1;for(let t of e.properties)t.type!==`SpreadElement`&&t.key?.type===`Identifier`&&n.push({name:t.key.name,start:t.start,end:t.end})}}}return{icons:n,objectStart:r,objectEnd:i}}function V(e){let{icons:t}=B(e);return t.map(e=>e.name)}function H(e,t,n){let{icons:r,objectStart:i,objectEnd:a}=B(e),o=r.findIndex(e=>e.name.localeCompare(t)>0);if(r.length===0)return`${e.slice(0,i)}\n ${n},\n${e.slice(a)}`;if(o===-1){let t=r.at(-1);if(!t)throw Error(`Failed to find last icon`);return`${e.slice(0,t.end)},\n ${n}${e.slice(t.end)}`}if(o===0){let t=r[0];if(!t)throw Error(`Failed to find first icon`);return`${e.slice(0,t.start)}${n},\n ${e.slice(t.start)}`}let s=r[o];if(!s)throw Error(`Failed to find target icon`);let c=s.start;return`${e.slice(0,c)}${n},\n ${e.slice(c)}`}function U(e,t,n){let{icons:r}=B(e),i=r.find(e=>e.name===t);return i?`${e.slice(0,i.start)}${n}${e.slice(i.end)}`:e}function W(e,t){let{icons:n}=B(e),r=n.findIndex(e=>e.name===t);if(r===-1)return e;let i=n[r];if(!i)return e;let a=r===n.length-1;if(n.length===1)return`${e.slice(0,i.start).trimEnd()}${e.slice(i.end).trimStart()}`;if(a){let t=n[r-1];return t?e.slice(t.end,i.start).indexOf(`,`)===-1?`${e.slice(0,t.end)}\n${e.slice(i.end).trimStart()}`:`${e.slice(0,t.end)}${e.slice(i.end)}`:e}let o=n[r+1];return o?`${e.slice(0,i.start)}${e.slice(o.start)}`:e}const G=`Operation cancelled.`;async function K(e,t=G){let i=await r(e);return a(i)&&(n(t),process.exit(0)),i}async function q(e,t=G){let r=await s(e);return a(r)&&(n(t),process.exit(0)),r}async function J(e,t=G){let r=await c({...e,placeholder:e.placeholder??e.defaultValue});return a(r)&&(n(t),process.exit(0)),r}const Y=new e().name(`add`).description(`Add icons to your project`).argument(`<icons...>`,`Icon names (e.g., mdi:home lucide:check)`).option(`--name <name>`,`Custom component name (single icon only)`).option(`--a11y <strategy>`,`Accessibility strategy (overrides config)`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).action(async(e,t)=>{i(`denji add`);let n=await ie(e,t);n.isErr()&&A(n.error),o(`Added ${e.length} icon(s)`)});async function ie(e,n){if(!await E(n.cwd))return new C(`Directory does not exist: ${n.cwd}`);if(n.name&&e.length>1)return new C(`--name can only be used with a single icon`);for(let t of e){let e=ne(t);if(e.isErr())return e}let r;if(n.a11y!==void 0){let e=n.a11y===`false`?!1:n.a11y,t=y.safeParse(e);if(!t.success)return new C(`Invalid a11y strategy: ${n.a11y}. Use: hidden, img, title, presentation, false`);r=t.data}let i=await D(n.cwd);if(i.isErr())return i;let a=i.value,o=await j(a.hooks?.preAdd,n.cwd);if(o.isErr())return o;let s=t.resolve(n.cwd,a.output);if(!await E(s))return new C(`Icons file not found: ${a.output}. Run "denji init" first.`);let c=await T(s,`utf-8`);if(c.isErr())return new C(`Failed to read icons file: ${a.output}`);let l=c.value,u=V(l),d=0;for(let t of e){let e=n.name??N(t);if(u.includes(e)&&!await K({message:`Icon "${e}" already exists. Overwrite?`,initialValue:!1})){k.info(`Skipped ${e}`);continue}let i=await P(t);if(i.isErr()){k.error(`Failed to fetch ${t}: ${i.error}`);continue}let o=await z(i.value,e,{a11y:r??a.a11y});u.includes(e)?(l=U(l,e,o),k.success(`Replaced ${e}`)):(l=H(l,e,o),u.push(e),k.success(`Added ${e}`)),d++}if(d>0){if((await w(s,l)).isErr())return new C(`Failed to write icons file: ${a.output}`);let e=await j(a.hooks?.postAdd,n.cwd);if(e.isErr())return e}return new S(null)}function X(e){return e.framework===`react`?e.typescript?`export type IconProps = React.ComponentProps<"svg">;
2
+ import{Command as e}from"commander";import t from"node:path";import{cancel as n,confirm as r,intro as i,isCancel as a,outro as o,select as s,text as c}from"@clack/prompts";import{z as l}from"zod";import u from"node:fs/promises";import d from"picocolors";import{spawn as f}from"node:child_process";import{transform as p}from"@svgr/core";import{parseSync as m}from"oxc-parser";var h=`denji`,g=`A CLI tool to manage your SVG icons`,_=`0.1.2`;const v=l.enum([`react`]),y=l.enum([`hidden`,`img`,`title`,`presentation`]).or(l.literal(!1)),b=l.object({$schema:l.string().optional().default(`https://denji-docs.vercel.app/configuration_schema.json`).describe(`The URL of the JSON Schema for this configuration file (e.g., './node_modules/denji/configuration_schema.json')`),output:l.string().describe(`The output file path for generated icon components (e.g., './src/icons.tsx')`),framework:v.describe(`The framework to generate icon components for`),typescript:l.boolean().default(!0).describe(`Whether to generate TypeScript code`),a11y:y.optional().describe(`Accessibility strategy for SVG icons (hidden: aria-hidden, img: role=img with aria-label, title: <title> element, presentation: role=presentation, false: no a11y attrs)`),hooks:l.object({preAdd:l.array(l.string()).describe(`Scripts to run before adding icons`),postAdd:l.array(l.string()).describe(`Scripts to run after adding icons`),preRemove:l.array(l.string()).describe(`Scripts to run before removing icons`),postRemove:l.array(l.string()).describe(`Scripts to run after removing icons`),preClear:l.array(l.string()).describe(`Scripts to run before clearing all icons`),postClear:l.array(l.string()).describe(`Scripts to run after clearing all icons`),preList:l.array(l.string()).describe(`Scripts to run before listing icons`),postList:l.array(l.string()).describe(`Scripts to run after listing icons`)}).partial().optional().describe(`Hooks to run at various stages`)}).meta({title:`Denji Configuration Schema`,description:`Schema for Denji configuration file`}),x=`denji.json`;var S=class{constructor(e){this.value=e}isOk(){return!0}isErr(){return!1}},C=class{constructor(e){this.error=e}isOk(){return!1}isErr(){return!0}};async function w(e,t,n){try{return await u.writeFile(e,t,n),new S(null)}catch{return new C(`Failed to write file.`)}}async function T(e,t){try{return new S(await u.readFile(e,t))}catch{return new C(`Failed to read file.`)}}async function ee(e,t){try{return await u.mkdir(e,t),new S(null)}catch{return new C(`Failed to create directory.`)}}async function E(e,t){try{return await u.access(e,t),!0}catch{return!1}}async function D(e){let n=t.join(e,x);if(!await E(n))return new C(`${x} not found. Run "denji init" first.`);let r=await T(n,`utf-8`);if(r.isErr())return new C(`Failed to read ${x}`);try{let e=b.safeParse(JSON.parse(r.value));return e.success?new S(e.data):new C(`Invalid ${x}: ${e.error.message}`)}catch{return new C(`Invalid JSON in ${x}`)}}const O=console,k={error(e){O.error(d.red(e))},warn(e){O.warn(d.yellow(e))},info(e){O.info(d.cyan(e))},success(e){O.info(d.green(e))},break(){O.info(``)}};function A(e){k.error(`Something went wrong. Please check the error below for more details.`),k.error(`If the problem persists, please open an issue on GitHub.`),k.break(),k.error(e),k.break(),process.exit(1)}async function j(e,t){if(!e||e.length===0)return new S(null);for(let n of e){k.info(`Running: ${n}`);let e=await te(n,t);if(e.isErr())return e}return new S(null)}function te(e,t){return new Promise(n=>{let r=f(e,{cwd:t,shell:!0,stdio:`inherit`});r.on(`error`,e=>{n(new C(`Hook failed: ${e.message}`))}),r.on(`close`,t=>{n(t===0?new S(null):new C(`Hook "${e}" exited with code ${t}`))})})}const M=/^[a-z0-9]+(-[a-z0-9]+)*$/;function ne(e){let t=e.split(`:`);if(t.length!==2)return new C(`Invalid icon format "${e}". Expected "prefix:name" (e.g., mdi:home)`);let[n,r]=t;return M.test(n)?M.test(r)?new S({prefix:n,name:r}):new C(`Invalid name "${r}". Must match: lowercase letters, numbers, hyphens`):new C(`Invalid prefix "${n}". Must match: lowercase letters, numbers, hyphens`)}const re=/[-_]/,N=/^\d/;function P(e){let t=e.split(`:`)[1];if(!t)throw Error(`Invalid icon format: ${e}`);let n=t.split(re).filter(Boolean).map(e=>e.at(0)?.toUpperCase()+e.slice(1).toLowerCase()),r=n[0];for(;r&&N.test(r);)n.push(n.shift()??``),r=n[0];return n.join(``)}async function F(e){try{let{loadIcon:t,buildIcon:n}=await import(`iconify-icon`),r=await t(e);if(!r)return new C(`Icon "${e}" not found`);let i=n(r,{height:`1em`});return i?new S(`<svg xmlns="http://www.w3.org/2000/svg" ${Object.entries(i.attributes).map(([e,t])=>`${e}="${t}"`).join(` `)}>${i.body}</svg>`):new C(`Failed to build icon "${e}"`)}catch{return new C(`Icon "${e}" not found`)}}const I=/<svg[\s\S]*<\/svg>/,L=/<svg([^>]*)>/;function R(e){return e.replace(/([a-z])([A-Z])/g,`$1 $2`)}function z(e,t){switch(e){case`hidden`:return{"aria-hidden":`true`};case`img`:return{role:`img`,"aria-label":R(t)};case`presentation`:return{role:`presentation`};default:return{}}}async function B(e,t,n={}){let{a11y:r}=n,i=(await p(e,{plugins:[`@svgr/plugin-svgo`,`@svgr/plugin-jsx`],svgoConfig:{plugins:[`preset-default`,`convertStyleToAttrs`,`sortAttrs`,`mergePaths`]},jsxRuntime:`automatic`,typescript:!1,expandProps:`end`,svgProps:z(r,t),titleProp:r===`title`},{componentName:`Icon`})).match(I);if(!i)throw Error(`Failed to extract SVG from SVGR output`);let a=i[0];if(r===`title`){let e=R(t);a=a.replace(L,`<svg$1><title>${e}</title>`)}return`${t}: (props) => (${a})`}function V(e){let t=m(`icons.tsx`,e).program,n=[],r=0,i=0;for(let e of t.body)if(e.type===`ExportNamedDeclaration`&&e.declaration?.type===`VariableDeclaration`){for(let t of e.declaration.declarations)if(t.id?.type===`Identifier`&&t.id.name===`Icons`){let e=t.init;if(e?.type===`TSSatisfiesExpression`&&(e=e.expression),e?.type===`TSAsExpression`&&(e=e.expression),e?.type===`ObjectExpression`){r=e.start+1,i=e.end-1;for(let t of e.properties)t.type!==`SpreadElement`&&t.key?.type===`Identifier`&&n.push({name:t.key.name,start:t.start,end:t.end})}}}return{icons:n,objectStart:r,objectEnd:i}}function H(e){let{icons:t}=V(e);return t.map(e=>e.name)}function U(e,t,n){let{icons:r,objectStart:i,objectEnd:a}=V(e),o=r.findIndex(e=>e.name.localeCompare(t)>0);if(r.length===0)return`${e.slice(0,i)}\n ${n},\n${e.slice(a)}`;if(o===-1){let t=r.at(-1);if(!t)throw Error(`Failed to find last icon`);return`${e.slice(0,t.end)},\n ${n}${e.slice(t.end)}`}if(o===0){let t=r[0];if(!t)throw Error(`Failed to find first icon`);return`${e.slice(0,t.start)}${n},\n ${e.slice(t.start)}`}let s=r[o];if(!s)throw Error(`Failed to find target icon`);let c=s.start;return`${e.slice(0,c)}${n},\n ${e.slice(c)}`}function W(e,t,n){let{icons:r}=V(e),i=r.find(e=>e.name===t);return i?`${e.slice(0,i.start)}${n}${e.slice(i.end)}`:e}function G(e,t){let{icons:n}=V(e),r=n.findIndex(e=>e.name===t);if(r===-1)return e;let i=n[r];if(!i)return e;let a=r===n.length-1;if(n.length===1)return`${e.slice(0,i.start).trimEnd()}${e.slice(i.end).trimStart()}`;if(a){let t=n[r-1];return t?e.slice(t.end,i.start).indexOf(`,`)===-1?`${e.slice(0,t.end)}\n${e.slice(i.end).trimStart()}`:`${e.slice(0,t.end)}${e.slice(i.end)}`:e}let o=n[r+1];return o?`${e.slice(0,i.start)}${e.slice(o.start)}`:e}const K=`Operation cancelled.`;async function q(e,t=K){let i=await r(e);return a(i)&&(n(t),process.exit(0)),i}async function J(e,t=K){let r=await s(e);return a(r)&&(n(t),process.exit(0)),r}async function Y(e,t=K){let r=await c({...e,placeholder:e.placeholder??e.defaultValue});return a(r)&&(n(t),process.exit(0)),r}const ie=new e().name(`add`).description(`Add icons to your project`).argument(`<icons...>`,`Icon names (e.g., mdi:home lucide:check)`).option(`--name <name>`,`Custom component name (single icon only)`).option(`--a11y <strategy>`,`Accessibility strategy (overrides config)`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).action(async(e,t)=>{i(`denji add`);let n=await ae(e,t);n.isErr()&&A(n.error),o(`Added ${e.length} icon(s)`)});async function ae(e,n){if(!await E(n.cwd))return new C(`Directory does not exist: ${n.cwd}`);if(n.name&&e.length>1)return new C(`--name can only be used with a single icon`);for(let t of e){let e=ne(t);if(e.isErr())return e}let r;if(n.a11y!==void 0){let e=n.a11y===`false`?!1:n.a11y,t=y.safeParse(e);if(!t.success)return new C(`Invalid a11y strategy: ${n.a11y}. Use: hidden, img, title, presentation, false`);r=t.data}let i=await D(n.cwd);if(i.isErr())return i;let a=i.value,o=await j(a.hooks?.preAdd,n.cwd);if(o.isErr())return o;let s=t.resolve(n.cwd,a.output);if(!await E(s))return new C(`Icons file not found: ${a.output}. Run "denji init" first.`);let c=await T(s,`utf-8`);if(c.isErr())return new C(`Failed to read icons file: ${a.output}`);let l=c.value,u=H(l),d=0;for(let t of e){let e=n.name??P(t);if(u.includes(e)&&!await q({message:`Icon "${e}" already exists. Overwrite?`,initialValue:!1})){k.info(`Skipped ${e}`);continue}let i=await F(t);if(i.isErr()){k.error(`Failed to fetch ${t}: ${i.error}`);continue}let o=await B(i.value,e,{a11y:r??a.a11y});u.includes(e)?(l=W(l,e,o),k.success(`Replaced ${e}`)):(l=U(l,e,o),u.push(e),k.success(`Added ${e}`)),d++}if(d>0){if((await w(s,l)).isErr())return new C(`Failed to write icons file: ${a.output}`);let e=await j(a.hooks?.postAdd,n.cwd);if(e.isErr())return e}return new S(null)}function X(e){return e.framework===`react`?e.typescript?`export type IconProps = React.ComponentProps<"svg">;
3
3
  export type Icon = (props: IconProps) => React.JSX.Element;
4
4
 
5
5
  export const Icons = {} as const satisfies Record<string, Icon>;
6
6
  `:`export const Icons = {};
7
- `:``}const ae=new e().name(`clear`).description(`Remove all icons from your project`).aliases([`clr`,`reset`]).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`-y, --yes`,`Skip confirmation prompt`,!1).action(async e=>{i(`denji clear`);let t=await oe(e);t.isErr()&&A(t.error),o(`All icons removed`)});async function oe(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let r=await D(e.cwd);if(r.isErr())return r;let i=r.value,a=t.resolve(e.cwd,i.output);if(!await E(a))return new C(`Icons file not found: ${i.output}. Run "denji init" first.`);let o=await T(a,`utf-8`);if(o.isErr())return new C(`Failed to read icons file: ${i.output}`);let s=o.value,c=V(s);if(c.length===0)return k.info(`No icons to remove`),new S(null);e.yes||await K({message:`Remove all ${c.length} icon(s)?`,initialValue:!1})||n(G);let l=await j(i.hooks?.preClear,e.cwd);if(l.isErr())return l;if((await w(a,X(i))).isErr())return new C(`Failed to write icons file: ${i.output}`);let u=await j(i.hooks?.postClear,e.cwd);return u.isErr()?u:new S(null)}const se=new e().name(`init`).description(`Initialize a new denji project`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`--output <file>`,`Output file path for icons`).option(`--framework <framework>`,`Framework to use`).option(`--typescript`,`Use TypeScript`,!0).option(`--no-typescript`,`Use JavaScript`).option(`--a11y <strategy>`,`Accessibility strategy for SVG icons`).action(async e=>{i(`denji init`);let t=await ce(e);t.isErr()&&A(t.error),o(`Project initialized successfully!`)});async function ce(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let n=t.join(e.cwd,x);if(await E(n))return new C(`${x} already exists in ${e.cwd}`);let r=await Z(e);if(r.isErr())return r;let i=r.value,a=le(i);if(a.isErr())return a;let o=t.resolve(e.cwd,i.output);if(await E(o))return new C(`Output file already exists: ${o}`);let s=t.dirname(o);return!await E(s)&&(await ee(s,{recursive:!0})).isErr()?new C(`Failed to create directory: ${s}`):(await w(n,JSON.stringify(i,null,2))).isErr()?new C(`Failed to write ${x}`):(k.success(`Created ${x}`),(await w(o,X(i))).isErr()?new C(`Failed to write ${i.output}`):(k.success(`Created ${i.output}`),new S(null)))}async function Z(e){let t=e.output??await J({message:`Where should icons be created?`,defaultValue:`./src/icons.tsx`}),n=e.framework??await q({message:`Which framework are you using?`,options:[{value:`react`,label:`React`}],initialValue:`react`}),r=v.safeParse(n);if(!r.success)return new C(`Invalid framework: ${n}. Use: react`);let i=r.data,a=e.typescript??await K({message:`Use TypeScript?`,initialValue:!0}),o=e.a11y??await q({message:`Which accessibility strategy should be used?`,options:[{value:`hidden`,label:`aria-hidden`,hint:`Hide from screen readers (decorative icons)`},{value:`img`,label:`role="img"`,hint:`Meaningful icon with aria-label`},{value:`title`,label:`title`,hint:`Add <title> element inside SVG`},{value:`presentation`,label:`presentation`,hint:`role=presentation (older pattern)`},{value:!1,label:`false`,hint:`No accessibility attributes`}],initialValue:`hidden`}),s=y.safeParse(o);if(!s.success)return new C(`Invalid a11y strategy: ${o}. Use: hidden, img, title, presentation, false`);let c=s.data;return new S(b.parse({output:t,framework:i,typescript:a,a11y:c}))}function le(e){let n=t.extname(e.output);if(e.framework===`react`){if(e.typescript&&n!==`.tsx`)return new C(`Invalid extension "${n}" for React + TypeScript. Use ".tsx"`);if(!e.typescript&&n!==`.jsx`)return new C(`Invalid extension "${n}" for React + JavaScript. Use ".jsx"`)}return new S(null)}const ue=new e().name(`list`).description(`List all icons in your project`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`--json`,`Output icons as JSON`).action(async e=>{e.json||i(`denji list`);let t=await de(e);t.isErr()&&A(t.error),e.json||o(`Done`)});async function de(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let n=await D(e.cwd);if(n.isErr())return n;let r=n.value,i=t.resolve(e.cwd,r.output);if(!await E(i))return new C(`Icons file not found: ${r.output}. Run "denji init" first.`);let a=await T(i,`utf-8`);if(a.isErr())return new C(`Failed to read icons file: ${r.output}`);let o=await j(r.hooks?.preList,e.cwd);if(o.isErr())return o;let s=a.value,{icons:c}=B(s);if(e.json){let t={count:c.length,output:r.output,icons:c.map(e=>e.name)};console.info(JSON.stringify(t,null,2));let n=await j(r.hooks?.postList,e.cwd);return n.isErr()?n:new S(null)}if(c.length===0){k.info(`No icons found in ${r.output}`);let t=await j(r.hooks?.postList,e.cwd);return t.isErr()?t:new S(null)}k.success(`Found ${c.length} icon(s) in ${r.output}`),k.break(),k.info(`Icons:`);for(let e of c)k.info(` • ${e.name}`);let l=await j(r.hooks?.postList,e.cwd);return l.isErr()?l:new S(null)}const fe=new e().name(`remove`).description(`Remove icons from your project`).argument(`<icons...>`,`Icon component names (e.g., Home Check)`).aliases([`rm`,`delete`,`del`]).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).action(async(e,t)=>{i(`denji remove`);let n=await pe(e,t);n.isErr()&&A(n.error),o(`Removed ${e.length} icon(s)`)});async function pe(e,n){if(!await E(n.cwd))return new C(`Directory does not exist: ${n.cwd}`);let r=await D(n.cwd);if(r.isErr())return r;let i=r.value,a=t.resolve(n.cwd,i.output);if(!await E(a))return new C(`Icons file not found: ${i.output}. Run "denji init" first.`);let o=await T(a,`utf-8`);if(o.isErr())return new C(`Failed to read icons file: ${i.output}`);let s=o.value,c=V(s),l=[];for(let t of e)c.includes(t)||l.push(t);if(l.length>0)return new C(`Icon(s) not found: ${l.join(`, `)}`);let u=await j(i.hooks?.preRemove,n.cwd);if(u.isErr())return u;if(c.length-e.length===0){if((await w(a,X(i))).isErr())return new C(`Failed to write icons file: ${i.output}`);for(let t of e)k.success(`Removed ${t}`);let t=await j(i.hooks?.postRemove,n.cwd);return t.isErr()?t:new S(null)}for(let t of e)s=W(s,t),k.success(`Removed ${t}`);if((await w(a,s)).isErr())return new C(`Failed to write icons file: ${i.output}`);let d=await j(i.hooks?.postRemove,n.cwd);return d.isErr()?d:new S(null)}const Q=()=>process.exit(0);process.on(`SIGINT`,Q),process.on(`SIGTERM`,Q);const $=new e().name(h).description(g).version(_,`-v, --version`,`Display the version number.`);$.addCommand(Y),$.addCommand(ae),$.addCommand(se),$.addCommand(ue),$.addCommand(fe),$.parse();export{};
7
+ `:``}const oe=new e().name(`clear`).description(`Remove all icons from your project`).aliases([`clr`,`reset`]).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`-y, --yes`,`Skip confirmation prompt`,!1).action(async e=>{i(`denji clear`);let t=await se(e);t.isErr()&&A(t.error),o(`All icons removed`)});async function se(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let r=await D(e.cwd);if(r.isErr())return r;let i=r.value,a=t.resolve(e.cwd,i.output);if(!await E(a))return new C(`Icons file not found: ${i.output}. Run "denji init" first.`);let o=await T(a,`utf-8`);if(o.isErr())return new C(`Failed to read icons file: ${i.output}`);let s=o.value,c=H(s);if(c.length===0)return k.info(`No icons to remove`),new S(null);e.yes||await q({message:`Remove all ${c.length} icon(s)?`,initialValue:!1})||n(K);let l=await j(i.hooks?.preClear,e.cwd);if(l.isErr())return l;if((await w(a,X(i))).isErr())return new C(`Failed to write icons file: ${i.output}`);let u=await j(i.hooks?.postClear,e.cwd);return u.isErr()?u:new S(null)}const ce=new e().name(`init`).description(`Initialize a new denji project`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`--output <file>`,`Output file path for icons`).option(`--framework <framework>`,`Framework to use`).option(`--typescript`,`Use TypeScript`,!0).option(`--no-typescript`,`Use JavaScript`).option(`--a11y <strategy>`,`Accessibility strategy for SVG icons`).action(async e=>{i(`denji init`);let t=await le(e);t.isErr()&&A(t.error),o(`Project initialized successfully!`)});async function le(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let n=t.join(e.cwd,x);if(await E(n))return new C(`${x} already exists in ${e.cwd}`);let r=await ue(e);if(r.isErr())return r;let i=r.value,a=de(i);if(a.isErr())return a;let o=t.resolve(e.cwd,i.output);if(await E(o))return new C(`Output file already exists: ${o}`);let s=t.dirname(o);return!await E(s)&&(await ee(s,{recursive:!0})).isErr()?new C(`Failed to create directory: ${s}`):(await w(n,JSON.stringify(i,null,2))).isErr()?new C(`Failed to write ${x}`):(k.success(`Created ${x}`),(await w(o,X(i))).isErr()?new C(`Failed to write ${i.output}`):(k.success(`Created ${i.output}`),new S(null)))}async function ue(e){let t=e.output??await Y({message:`Where should icons be created?`,defaultValue:`./src/icons.tsx`}),n=e.framework??await J({message:`Which framework are you using?`,options:[{value:`react`,label:`React`}],initialValue:`react`}),r=v.safeParse(n);if(!r.success)return new C(`Invalid framework: ${n}. Use: react`);let i=r.data,a=e.typescript??await q({message:`Use TypeScript?`,initialValue:!0}),o=e.a11y??await J({message:`Which accessibility strategy should be used?`,options:[{value:`hidden`,label:`aria-hidden`,hint:`Hide from screen readers (decorative icons)`},{value:`img`,label:`role="img"`,hint:`Meaningful icon with aria-label`},{value:`title`,label:`title`,hint:`Add <title> element inside SVG`},{value:`presentation`,label:`presentation`,hint:`role=presentation (older pattern)`},{value:!1,label:`false`,hint:`No accessibility attributes`}],initialValue:`hidden`}),s=y.safeParse(o);if(!s.success)return new C(`Invalid a11y strategy: ${o}. Use: hidden, img, title, presentation, false`);let c=s.data;return new S(b.parse({output:t,framework:i,typescript:a,a11y:c}))}function de(e){let n=t.extname(e.output);if(e.framework===`react`){if(e.typescript&&n!==`.tsx`)return new C(`Invalid extension "${n}" for React + TypeScript. Use ".tsx"`);if(!e.typescript&&n!==`.jsx`)return new C(`Invalid extension "${n}" for React + JavaScript. Use ".jsx"`)}return new S(null)}const Z=new e().name(`list`).description(`List all icons in your project`).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).option(`--json`,`Output icons as JSON`).action(async e=>{e.json||i(`denji list`);let t=await fe(e);t.isErr()&&A(t.error),e.json||o(`Done`)});async function fe(e){if(!await E(e.cwd))return new C(`Directory does not exist: ${e.cwd}`);let n=await D(e.cwd);if(n.isErr())return n;let r=n.value,i=t.resolve(e.cwd,r.output);if(!await E(i))return new C(`Icons file not found: ${r.output}. Run "denji init" first.`);let a=await T(i,`utf-8`);if(a.isErr())return new C(`Failed to read icons file: ${r.output}`);let o=await j(r.hooks?.preList,e.cwd);if(o.isErr())return o;let s=a.value,{icons:c}=V(s);if(e.json){let t={count:c.length,output:r.output,icons:c.map(e=>e.name)};console.info(JSON.stringify(t,null,2));let n=await j(r.hooks?.postList,e.cwd);return n.isErr()?n:new S(null)}if(c.length===0){k.info(`No icons found in ${r.output}`);let t=await j(r.hooks?.postList,e.cwd);return t.isErr()?t:new S(null)}k.success(`Found ${c.length} icon(s) in ${r.output}`),k.break(),k.info(`Icons:`);for(let e of c)k.info(` • ${e.name}`);let l=await j(r.hooks?.postList,e.cwd);return l.isErr()?l:new S(null)}const pe=new e().name(`remove`).description(`Remove icons from your project`).argument(`<icons...>`,`Icon component names (e.g., Home Check)`).aliases([`rm`,`delete`,`del`]).option(`-c, --cwd <cwd>`,`The working directory. Defaults to the current directory.`,process.cwd()).action(async(e,t)=>{i(`denji remove`);let n=await me(e,t);n.isErr()&&A(n.error),o(`Removed ${e.length} icon(s)`)});async function me(e,n){if(!await E(n.cwd))return new C(`Directory does not exist: ${n.cwd}`);let r=await D(n.cwd);if(r.isErr())return r;let i=r.value,a=t.resolve(n.cwd,i.output);if(!await E(a))return new C(`Icons file not found: ${i.output}. Run "denji init" first.`);let o=await T(a,`utf-8`);if(o.isErr())return new C(`Failed to read icons file: ${i.output}`);let s=o.value,c=H(s),l=[];for(let t of e)c.includes(t)||l.push(t);if(l.length>0)return new C(`Icon(s) not found: ${l.join(`, `)}`);let u=await j(i.hooks?.preRemove,n.cwd);if(u.isErr())return u;if(c.length-e.length===0){if((await w(a,X(i))).isErr())return new C(`Failed to write icons file: ${i.output}`);for(let t of e)k.success(`Removed ${t}`);let t=await j(i.hooks?.postRemove,n.cwd);return t.isErr()?t:new S(null)}for(let t of e)s=G(s,t),k.success(`Removed ${t}`);if((await w(a,s)).isErr())return new C(`Failed to write icons file: ${i.output}`);let d=await j(i.hooks?.postRemove,n.cwd);return d.isErr()?d:new S(null)}const Q=()=>process.exit(0);process.on(`SIGINT`,Q),process.on(`SIGTERM`,Q);const $=new e().name(h).description(g).version(_,`-v, --version`,`Display the version number.`);$.addCommand(ie),$.addCommand(oe),$.addCommand(ce),$.addCommand(Z),$.addCommand(pe),$.parse();export{};
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A CLI tool to manage your SVG icons",
4
4
  "type": "module",
5
5
  "private": false,
6
- "version": "0.1.0",
6
+ "version": "0.1.2",
7
7
  "author": {
8
8
  "name": "Fellipe Utaka",
9
9
  "email": "fellipeutaka@gmail.com",