@sugarcube-org/cli 0.0.0-alpha.15 → 0.0.0-alpha.16

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.md ADDED
@@ -0,0 +1,9 @@
1
+ # Proprietary Software
2
+
3
+ This software is proprietary and confidential. All rights reserved.
4
+
5
+ Copyright (c) 2025 Mark Tomlinson
6
+
7
+ This software is provided "as is" without warranty of any kind, either express or implied.
8
+
9
+ Unauthorized copying, distribution, or use of this software is strictly prohibited.
package/README.md CHANGED
@@ -1,9 +1,52 @@
1
- # make-sugarcube
1
+ # @sugarcube-org/cli
2
2
 
3
- 🚧 Coming soon! 🚧
3
+ CLI for scaffolding sugarcube projects and managing design tokens.
4
4
 
5
- A CLI for scaffolding sugarcube applications. This package is currently in development.
5
+ ## What it does
6
6
 
7
- ## Stay Tuned
7
+ - Initializes new sugarcube projects with design tokens and CSS architecture
8
+ - Generates CSS variables and utility classes from design tokens
9
+ - Validates design token files for W3C compliance
10
+ - Installs copy-to-use components for multiple frameworks
11
+ - Adds CUBE CSS architecture to projects
8
12
 
9
- Follow our progress on [GitHub](https://github.com/sugarcube-org)
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install -g @sugarcube-org/cli
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```bash
22
+ # Initialize a new project
23
+ make-sugarcube init
24
+
25
+ # Generate CSS from tokens
26
+ make-sugarcube generate
27
+
28
+ # Validate token files
29
+ make-sugarcube validate
30
+
31
+ # Add components
32
+ make-sugarcube components
33
+
34
+ # Add CUBE CSS
35
+ make-sugarcube cube
36
+ ```
37
+
38
+ ## Commands
39
+
40
+ - `init` - Set up a new sugarcube project
41
+ - `generate` - Generate CSS variables and utilities
42
+ - `validate` - Validate design token files
43
+ - `components` - Install copy-to-use components
44
+ - `cube` - Add CUBE CSS architecture
45
+
46
+ ## Development
47
+
48
+ ```bash
49
+ pnpm install
50
+ pnpm build
51
+ pnpm dev
52
+ ```
package/dist/index.mjs CHANGED
@@ -1,58 +1,50 @@
1
1
  #!/usr/bin/env node
2
- var Ne=Object.defineProperty;var p=(e,n)=>Ne(e,"name",{value:n,configurable:!0});import{Command as z}from"commander";import{existsSync as j}from"node:fs";import i from"node:path";import{select as H,isCancel as C,log as b,multiselect as X,text as U,confirm as W,spinner as oe,intro as V,cancel as Z,outro as L,note as ie}from"@clack/prompts";import{manageCSSIndex as Re,loadConfig as _,generationPipeline as Pe,getTokenPathsFromConfig as De,writeCSSFilesToDisk as Oe,validationPipeline as Ae,validateConfig as me}from"@sugarcube-org/core";import f from"picocolors";import Me from"node-fetch";import{z as y}from"zod";import re,{mkdir as A,writeFile as q,readFile as de}from"node:fs/promises";import{execa as Ue}from"execa";import{detect as We}from"@antfu/ni";import ge from"fast-glob";var Be="0.0.0-alpha.15",ze={version:Be};class g extends Error{static{p(this,"CLIError")}constructor(n,t){super(n),this.name="CLIError",this.cause=t}}const ae=y.enum(["react","astro-native","astro-enhanced","web-components"]),we=y.object({path:y.string(),type:y.string()}),Le=we.extend({framework:ae});y.object({themes:y.record(y.string(),y.array(y.string())).optional()}).strict();const Ge=y.object({name:y.string(),type:y.string(),description:y.string().optional(),frameworks:y.array(y.string()).optional(),files:y.array(y.union([Le,we])),tokens:y.record(y.object({type:y.string(),mapping:y.string()})).optional(),dependencies:y.record(ae,y.array(y.string())).optional(),registryDependencies:y.record(ae,y.array(y.string())).optional(),tokenDependencies:y.array(y.string()).optional()}),Je=y.array(Ge),Ve=y.object({content:y.string()}),E={error:f.red,warn:f.yellow,info:f.cyan,success:f.green,bold:f.bold,path:f.cyan},he=process.env.REGISTRY_URL??"https://sugarcube.sh/r";async function ye(e){try{const n=await Me(e);if(!n.ok){if(n.status===401)throw new g(`Registry access denied: Authentication required
3
- URL: ${E.info(e)}`);if(n.status===403)throw new g(`Registry access denied: Invalid or missing token
4
- URL: ${E.info(e)}`);if(n.status===404)throw new g(`Registry resource not found
5
- URL: ${E.info(e)}`);const t=await n.json().catch(()=>null),o=t&&typeof t=="object"&&"error"in t?t.error:n.statusText;throw new g(`Registry request failed: ${o}
6
- URL: ${E.info(e)}`)}return n.json()}catch(n){throw n instanceof g?n:new g(`Failed to connect to registry
7
- URL: ${E.info(e)}
8
- Check your internet connection`)}}p(ye,"fetchRegistry");async function O(){const e=`${he}/index.json`,n=await ye(e);try{return Je.parse(n)}catch{throw new g(`Invalid registry data received
9
- URL: ${E.info(e)}`)}}p(O,"getRegistryIndex");async function ee({type:e,name:n,framework:t}){const o=await O(),s=o.find(c=>c.type===e&&c.name===n);if(!s){const c=o.filter(u=>u.type===e).map(u=>u.name);throw new g(`${e} '${E.info(n)}' not found in registry
10
- Available ${e}s: ${c.join(", ")}`)}let a=s.files;e==="component"&&t&&(a=s.files.filter(c=>"framework"in c&&c.framework===t));const r=await Promise.all(a.map(async c=>{const u=`${he}/${c.path}.json`,m=await ye(u);try{const S=Ve.parse(m);return{path:c.path,type:c.type,framework:"framework"in c?c.framework:void 0,content:S.content}}catch{throw new g(`Invalid file content received
11
- File: ${E.info(c.path)}`)}}));return{item:s,files:r.filter(Boolean)}}p(ee,"getRegistryFiles");async function ke(e,n,t){const o=[],s=new Set;async function a(r){if(s.has(r))return;s.add(r);const c=e.find(m=>m.name===r);if(!c){const m=e.filter(S=>S.type==="component").map(S=>S.name).join(", ");throw new g(`Component '${r}' not found in registry
12
- Available components: ${m}`)}const u=c.registryDependencies?.[t]||[];for(const m of u)await a(m);o.push(c)}p(a,"resolveComponent");for(const r of n)await a(r);return o}p(ke,"resolveTree");async function Se({config:e,cssOutputDirectory:n}){const t={designTokenCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]};if(e.tokens.type==="starter-kit"&&Array.isArray(e.tokens.source))if(e.output.css.separate){const o=e.tokens.source.map(s=>{const a=i.basename(s,".json");return i.relative(process.cwd(),i.join(n,"global/variables",`${a}.variables.css`))});t.designTokenCSS=o.filter(s=>j(i.join(process.cwd(),s)))}else{const o=i.relative(process.cwd(),i.join(n,"global/variables/tokens.variables.css"));j(i.join(process.cwd(),o))&&(t.designTokenCSS=[o])}else{const o="source"in e.tokens?[["default",e.tokens]]:Object.entries(e.tokens);if(e.output.css.separate){const s=o.flatMap(([a,r])=>r.source.map(c=>{const u=i.basename(c,".json");return i.relative(process.cwd(),i.join(n,"global/variables",`${u}.variables.css`))}));t.designTokenCSS=s.filter(a=>j(i.join(process.cwd(),a)))}else{const s=o.map(([a])=>i.relative(process.cwd(),i.join(n,"global/variables",a,"tokens.variables.css")));t.designTokenCSS=s.filter(a=>j(i.join(process.cwd(),a)))}}if(!e.output.css.separate&&e.tokens.type!=="starter-kit"){const o=i.relative(process.cwd(),i.join(n,"global/variables/tokens.variables.css"));j(i.join(process.cwd(),o))&&t.designTokenCSS.push(o)}if(e.output.css.manageIndex){const o=[i.relative(process.cwd(),i.join(n,"index.css")),i.relative(process.cwd(),i.join(n,"index.dark.css"))];t.indexFiles=o.filter(s=>j(i.join(process.cwd(),s)))}return t}p(Se,"collectTokenCSSOverwriteWarnings");async function be({cssOutputDirectory:e,selectedComponents:n,componentType:t,componentsOutputDirectory:o}){const s={designTokenCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]},a=await O();if(!a)throw new Error("Failed to fetch registry index");const c=(await ke(a,n,t)).map(w=>w.name),u=a.filter(w=>w.type==="component").filter(w=>c.includes(w.name)),m=u.flatMap(w=>w.files.filter(k=>(k.type==="tsx"||k.type==="astro"||k.type==="njk")&&"framework"in k&&k.framework===t).map(k=>i.relative(process.cwd(),i.join(o,w.name,`${w.name}.${k.type}`))));s.componentFiles=m.filter(w=>j(i.join(process.cwd(),w)));const S=u.flatMap(w=>{const k=w.name,x=i.relative(process.cwd(),i.join(o,w.name,`${k}.css`)),l=i.relative(process.cwd(),i.join(e,"global/variables",`${k}.variables.css`));return[x,l]});return s.componentCSS=S.filter(w=>j(i.join(process.cwd(),w))),s}p(be,"collectComponentOverwriteWarnings");async function ve({cssOutputDirectory:e}){const n={designTokenCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]},t=await O();if(!t)throw new Error("Failed to fetch registry index");const o=t.filter(s=>s.type==="cube").flatMap(s=>s.files).map(s=>{const a=s.path.replace(/^styles\//,"");return i.relative(process.cwd(),i.join(e,a))});return n.cubeCSS=o.filter(s=>j(i.join(process.cwd(),s))),n}p(ve,"collectCubeOverwriteWarnings");function _e(...e){return e.reduce((n,t)=>({designTokenCSS:[...n.designTokenCSS,...t.designTokenCSS],componentFiles:[...n.componentFiles,...t.componentFiles],componentCSS:[...n.componentCSS,...t.componentCSS],cubeCSS:[...n.cubeCSS,...t.cubeCSS],indexFiles:[...n.indexFiles,...t.indexFiles]}))}p(_e,"mergeWarnings");const ce={PROJECT_REQUIRED:`This command requires you to initialize a sugarcube project. Run ${f.cyan("make-sugarcube init")} first.
2
+ var Te=Object.defineProperty;var a=(e,t)=>Te(e,"name",{value:t,configurable:!0});import{Command as j}from"commander";import{existsSync as k,readFileSync as re}from"node:fs";import{relative as E,join as h,basename as U,resolve as F,dirname as Ie,normalize as Fe}from"pathe";import{select as $e,isCancel as M,log as v,multiselect as Re,text as xe,confirm as Pe,spinner as K,intro as ie,cancel as je,outro as _}from"@clack/prompts";import{GLOBAL_DIR as _e,VARIABLES_FILE_SUFFIX as Ne,TOKENS_FILE_SUFFIX as ae,tokenProcessingPipeline as Ae,SUGARCUBE_CONFIG_FILE as b,loadConfig as L,DEFAULT_STYLES_PATH as V,DEFAULT_DESIGN_TOKENS_PATH as Oe,generateCSSVariables as H,writeCSSVariablesToDisk as q,generateIndex as B,generateCSSUtilityClasses as ce,writeCSSUtilitiesToDisk as le,SUGARCUBE_FILE as Ue,validateConfig as Le,DEFAULT_CONFIG as ue,normalizeConfig as Ge}from"@sugarcube-org/core";import w from"picocolors";import We from"node-fetch";import{z as d}from"zod";import J,{mkdir as $,writeFile as N}from"node:fs/promises";import{execa as Me}from"execa";import{detect as Be}from"@antfu/ni";import I,{resolve as Je}from"node:path";import Q from"fast-glob";var ze="0.0.0-alpha.16",Ke={version:ze};class m extends Error{static{a(this,"CLIError")}constructor(t,n){super(t),this.name="CLIError",this.cause=n}}class z extends m{static{a(this,"ValidationError")}constructor(t,n,o,s){super(t),this.field=n,this.value=o,this.suggestion=s,this.name="ValidationError"}}class X extends m{static{a(this,"FileOperationError")}constructor(t,n,o,s){super(t),this.filePath=n,this.operation=o,this.cause=s,this.name="FileOperationError"}}const Y=d.enum(["react","astro-native","astro-enhanced","web-components"]),pe=d.object({path:d.string(),type:d.string()}),Ve=pe.extend({framework:Y});d.object({themes:d.record(d.string(),d.array(d.string())).optional()}).strict();const He=d.object({name:d.string(),type:d.string(),description:d.string().optional(),frameworks:d.array(d.string()).optional(),files:d.array(d.union([Ve,pe])),tokens:d.record(d.object({type:d.string(),mapping:d.string()})).optional(),dependencies:d.record(Y,d.array(d.string())).optional(),registryDependencies:d.record(Y,d.array(d.string())).optional(),tokenDependencies:d.array(d.string()).optional()}),qe=d.array(He),Qe=d.object({content:d.string()}),C={error:w.red,warn:w.yellow,info:w.cyan,success:w.green,bold:w.bold,path:w.cyan},fe=process.env.REGISTRY_URL??"https://sugarcube.sh/r";async function me(e){try{const t=await We(e);if(!t.ok){if(t.status===401)throw new m(`Registry access denied: Authentication required
3
+ URL: ${C.info(e)}`);if(t.status===403)throw new m(`Registry access denied: Invalid or missing token
4
+ URL: ${C.info(e)}`);if(t.status===404)throw new m(`Registry resource not found
5
+ URL: ${C.info(e)}`);const n=await t.json().catch(()=>null),o=n&&typeof n=="object"&&"error"in n?n.error:t.statusText;throw new m(`Registry request failed: ${o}
6
+ URL: ${C.info(e)}`)}return t.json()}catch(t){throw t instanceof m?t:new m(`Failed to connect to registry
7
+ URL: ${C.info(e)}
8
+ Check your internet connection`)}}a(me,"fetchRegistry");async function A(){const e=`${fe}/index.json`,t=await me(e);try{return qe.parse(t)}catch{throw new m(`Invalid registry data received
9
+ URL: ${C.info(e)}`)}}a(A,"getRegistryIndex");async function Z({type:e,name:t,framework:n}){const o=await A(),s=o.find(c=>c.type===e&&c.name===t);if(!s){const c=o.filter(i=>i.type===e).map(i=>i.name);throw new m(`${e} '${C.info(t)}' not found in registry
10
+ Available ${e}s: ${c.join(", ")}`)}let r=s.files;e==="component"&&n&&(r=s.files.filter(c=>"framework"in c&&c.framework===n));const l=await Promise.all(r.map(async c=>{const i=`${fe}/${c.path}.json`,p=await me(i);try{const g=Qe.parse(p);return{path:c.path,type:c.type,framework:"framework"in c?c.framework:void 0,content:g.content}}catch{throw new m(`Invalid file content received
11
+ File: ${C.info(c.path)}`)}}));return{item:s,files:l}}a(Z,"getRegistryFiles");async function de(e,t,n){const o=[],s=new Set;async function r(l){if(s.has(l))return;s.add(l);const c=e.find(p=>p.name===l);if(!c){const p=e.filter(g=>g.type==="component").map(g=>g.name).join(", ");throw new m(`Component '${l}' not found in registry
12
+ Available components: ${p}`)}const i=c.registryDependencies?.[n]||[];for(const p of i)await r(p);o.push(c)}a(r,"resolveComponent");for(const l of t)await r(l);return o}a(de,"resolveTree");async function Xe({cssOutputDirectory:e,selectedComponents:t,componentType:n,componentsOutputDirectory:o,tokensOutputDirectory:s}){const r={variableCSS:[],utilityCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]},l=await A(),i=(await de(l,t,n)).map(f=>f.name),p=l.filter(f=>f.type==="component").filter(f=>i.includes(f.name)),g=p.flatMap(f=>f.files.filter(u=>(u.type==="tsx"||u.type==="astro"||u.type==="njk")&&"framework"in u&&u.framework===n).map(u=>E(process.cwd(),h(o,f.name,`${f.name}.${u.type}`))));r.componentFiles=g.filter(f=>k(h(process.cwd(),f)));const y=p.flatMap(f=>{const u=f.name;return[E(process.cwd(),h(o,f.name,`${u}.css`)),E(process.cwd(),h(e,_e,`${u}${Ne}`))]});r.componentCSS=y.filter(f=>k(h(process.cwd(),f)));const S=p.flatMap(f=>{const u=f.name;return[E(process.cwd(),h(s,`${u}${ae}`)),E(process.cwd(),h(s,"components",`${u}${ae}`))]});return r.componentCSS.push(...S.filter(f=>k(h(process.cwd(),f)))),r}a(Xe,"collectComponentOverwriteWarnings");async function ge({cubeDirectory:e}){const t={variableCSS:[],utilityCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]},n=await A();if(!n)throw new m("Failed to fetch registry index");const o=n.filter(s=>s.type==="cube").flatMap(s=>s.files).map(s=>{const r=s.path.replace(/^styles\//,"");return E(process.cwd(),h(e,r))});return t.cubeCSS=o.filter(s=>k(h(process.cwd(),s))),t}a(ge,"collectCubeOverwriteWarnings");const ee={PROJECT_REQUIRED:`This command requires you to initialize a sugarcube project. Run ${w.cyan("make-sugarcube init")} first.
13
13
 
14
- For more information, visit: ${f.cyan("https://sugarcube.sh/docs/initialize")}`};function te(e){const n=[];if(e.designTokenCSS.length>0&&n.push(`Design token CSS files:
15
- ${e.designTokenCSS.map(t=>` - ${t}`).join(`
16
- `)}`),e.cubeCSS.length>0&&n.push(`CUBE CSS files:
17
- ${e.cubeCSS.map(t=>` - ${t}`).join(`
18
- `)}`),e.componentFiles.length>0||e.componentCSS.length>0){const t=[...e.componentFiles,...e.componentCSS];n.push(`Component files:
19
- ${t.map(o=>` - ${o}`).join(`
20
- `)}`)}if(e.indexFiles.length>0&&n.push(`Index files:
21
- ${e.indexFiles.map(t=>` - ${t}`).join(`
22
- `)}`),n.length!==0)return f.yellow(`WARNING: The following file(s) already exist and will be OVERWRITTEN:
14
+ For more information, visit: ${w.cyan("https://sugarcube.sh/docs/initialize")}`};function te(e){const t=[];if(e.variableCSS.length>0&&t.push(`CSS variables files:
15
+ ${e.variableCSS.map(n=>` - ${n}`).join(`
16
+ `)}`),e.utilityCSS.length>0&&t.push(`CSS utility files:
17
+ ${e.utilityCSS.map(n=>` - ${n}`).join(`
18
+ `)}`),e.cubeCSS.length>0&&t.push(`CUBE CSS files:
19
+ ${e.cubeCSS.map(n=>` - ${n}`).join(`
20
+ `)}`),e.componentFiles.length>0||e.componentCSS.length>0){const n=[...e.componentFiles,...e.componentCSS];t.push(`Component files:
21
+ ${n.map(o=>` - ${o}`).join(`
22
+ `)}`)}if(e.indexFiles.length>0&&t.push(`Index files:
23
+ ${e.indexFiles.map(n=>` - ${n}`).join(`
24
+ `)}`),t.length!==0)return w.yellow(`WARNING: The following file(s) already exist and will be OVERWRITTEN:
23
25
 
24
- ${f.dim(n.join(`
26
+ ${w.dim(t.join(`
25
27
 
26
- `))}`)}p(te,"formatOverwriteWarnings");const $={error(...e){console.log(E.error(e.join(" ")))},warn(...e){console.log(E.warn(e.join(" ")))},info(...e){console.log(E.info(e.join(" ")))},success(...e){console.log(E.success(e.join(" ")))},log(...e){console.log(e.join(" "))},break(){console.log("")}};function K(e){if($.break(),e instanceof g){const n=e.message.split(`
27
- `);if(n.length>1){$.error(n[0]),$.break();for(const t of n.slice(1))$.error(` ${t.trim()}`)}else $.error(e.message);process.env.DEBUG&&e.cause&&$.info(`
28
- Caused by:`,e.cause)}else $.error(`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}
29
- If this issue persists, please report it: ${f.cyanBright("https://github.com/sugarcube-org/sugarcube/issues")}`),process.env.DEBUG&&$.info(`
30
- Error details:`,e);$.break(),process.exit(0)}p(K,"handleError");async function qe(e,{withFallback:n=!1}={}){const t=await We({programmatic:!0,cwd:e});if(t?.startsWith("yarn@"))return"yarn";if(t?.startsWith("pnpm@"))return"pnpm";if(t==="bun")return"bun";if(!n)return t?.split("@")[0]??"npm";const o=process.env.npm_config_user_agent||"";return o.startsWith("yarn")?"yarn":o.startsWith("pnpm")?"pnpm":o.startsWith("bun")?"bun":"npm"}p(qe,"getPackageManager");async function Ke(e,n){const t=await qe(n,{withFallback:!0}),o=t==="npm"?"install":"add";try{await Ue(t,[o,...e],{cwd:n})}catch{const a=`Failed to install dependencies using ${t}.
28
+ `))}`)}a(te,"formatOverwriteWarnings");function Ye(e){return typeof e.tokens=="object"&&!("source"in e.tokens)}a(Ye,"hasCollections$1");async function Ze(e){if(Ye(e)){const t=e.tokens;return"components"in t?!1:(t.components={source:["components/*.tokens.json"]},!0)}return!1}a(Ze,"handleComponentTokenIntegration");const D={error(...e){console.log(C.error(e.join(" ")))},warn(...e){console.log(C.warn(e.join(" ")))},info(...e){console.log(C.info(e.join(" ")))},success(...e){console.log(C.success(e.join(" ")))},log(...e){console.log(e.join(" "))},break(){console.log("")}};function et(...e){process.env.DEBUG==="true"&&process.stderr.write(`[sugarcube:debug] ${e.map(String).join(" ")}
29
+ `)}a(et,"debugLog");function G(e){if(D.break(),e instanceof m){const t=e.message.split(`
30
+ `);if(t.length>1){D.error(t[0]),D.break();for(const n of t.slice(1))D.error(` ${n.trim()}`)}else D.error(e.message);process.env.DEBUG&&e.cause&&D.info(`
31
+ Caused by:`,e.cause)}else D.error(`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}
32
+ If this issue persists, please report it: ${w.cyanBright("https://github.com/sugarcube-org/sugarcube/issues")}`),process.env.DEBUG&&D.info(`
33
+ Error details:`,e);D.break(),process.exit(0)}a(G,"handleError");async function tt(e,{withFallback:t=!1}={}){const n=await Be({programmatic:!0,cwd:e});if(n?.startsWith("yarn@"))return"yarn";if(n?.startsWith("pnpm@"))return"pnpm";if(n==="bun")return"bun";if(!t)return n?.split("@")[0]??"npm";const o=process.env.npm_config_user_agent||"";return o.startsWith("yarn")?"yarn":o.startsWith("pnpm")?"pnpm":o.startsWith("bun")?"bun":"npm"}a(tt,"getPackageManager");async function we(e,t){const n=await tt(t,{withFallback:!0}),o=n==="npm"?"install":"add";try{await Me(n,[o,...e],{cwd:t})}catch{const r=`Failed to install dependencies using ${n}.
31
34
 
32
35
  This might be because you're using 'npx' with a pnpm project. Try:
33
- pnpm dlx @sugarcube-org/cli components
36
+ pnpm dlx make-sugarcube components
34
37
 
35
38
  Or use the appropriate package runner for your project:
36
- npm: npx @sugarcube-org/cli components
37
- pnpm: pnpm dlx @sugarcube-org/cli components
38
- yarn: yarn dlx @sugarcube-org/cli components
39
- bun: bunx @sugarcube-org/cli components`;throw new Error(a)}}p(Ke,"installDependencies");async function Ce({registryIndex:e,selectedComponents:n,componentType:t,componentsOutputDirectory:o,cssOutputDirectory:s}){const a=[],r=new Set;await A(o,{recursive:!0});const c=i.join(s,"global","variables");await A(i.join(s,"global"),{recursive:!0}),await A(c,{recursive:!0});const u=await ke(e,n,t);for(const m of u){const S=await ee({type:"component",name:m.name,framework:t});for(const k of S.files)if(k)try{if(k.path.endsWith(".variables.css")){const x=i.join(c,`${m.name}.variables.css`);await q(x,k.content),a.push(x)}else{const x=i.join(o,m.name);await A(x,{recursive:!0});const l=i.join(x,i.basename(k.path));await q(l,k.content),a.push(l)}}catch(x){const l=x instanceof Error?`: ${x.message}`:"";throw new g(`Failed to write component file for "${m.name}"${l}`)}const w=m.dependencies?.[t]||[];for(const k of w)r.add(k)}if(r.size>0)try{await Ke(Array.from(r),process.cwd())}catch(m){const S=m instanceof Error?`: ${m.message}`:"";throw new g(`Failed to install component dependencies${S}`)}return{createdFiles:a,npmDependencies:r}}p(Ce,"installComponents");async function ne({cssOutputDirectory:e,files:n}){try{await Re({cssOutputDirectory:e,files:n})}catch(t){const o=t instanceof Error?`: ${t.message}`:"";throw new g(`Failed to manage CSS index file ${o}`)}}p(ne,"manageCSSIndexCLI");async function xe(e="src/styles"){const n=await U({message:"Save CSS to",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return C(n)&&process.exit(0),i.relative(process.cwd(),i.resolve(process.cwd(),n))}p(xe,"promptCSSOutputDirectory");async function Qe(e="src/design-tokens"){const n=await U({message:"Save tokens to",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return C(n)&&process.exit(0),i.relative(process.cwd(),i.resolve(process.cwd(),n))}p(Qe,"promptTokensDirectory");async function Ye(e){const n=e.filter(r=>r.type==="tokens"&&r.files[0]?.path.includes("starter-kits")),t=n.map(r=>({label:r.name,value:r.name})),o=await H({message:"Which starter kit?",options:t});C(o)&&process.exit(0);const s=n.find(r=>r.name===o);if(!s)throw new Error(`Starter kit '${o}' not found`);const a=s.files.filter(r=>i.basename(r.path)!=="config.json").map(r=>i.basename(r.path));return{name:o,tokenFilePaths:a}}p(Ye,"promptStarterKit");async function Fe(e="src/components"){const n=await U({message:"Save components to",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return C(n)&&process.exit(0),i.relative(process.cwd(),i.resolve(process.cwd(),n))}p(Fe,"promptComponentDirectory");async function le(e=!0){const n=[{label:"React",value:"react",hint:".tsx"},{label:"Astro (Native)",value:"astro-native",hint:"Minimal, standards-based .astro; uses native HTML features"},{label:"Astro (Enhanced)",value:"astro-enhanced",hint:"Advanced features, richer client-side logic - coming soon"},{label:"Web Components",value:"web-components",hint:"Native web components using TypeScript - coming soon"}];e&&n.push({label:"Skip",value:"skip",hint:"continue without components"});const t=await H({message:"Build with",options:n});return C(t)&&process.exit(0),t==="astro-enhanced"||t==="web-components"?(b.info(f.blue("Sorry, this framework is coming soon! Please choose 'Astro (Native)' or 'React' for now.")),le(e)):t}p(le,"promptComponentFramework");async function je(e,n){const t=e.filter(s=>s.type==="component"&&s.frameworks?.includes(n)),o=await X({message:"Select components to add",options:t.map(s=>({label:s.name,value:s.name,hint:s.description})),required:!0});return C(o)&&process.exit(0),o}p(je,"promptComponentSelectionFiltered");async function He(){const n=await H({message:"Set up tokens with",options:[{label:"Starter kit",value:"starter"},{label:"Own tokens",value:"existing"}]});return C(n)&&process.exit(0),n}p(He,"promptTokenSetup");async function Q(e,n=!1){const t=await W({message:e,initialValue:n});return(!t||C(t))&&process.exit(0),!0}p(Q,"confirmOverwrite");async function Xe(){const e=await W({message:"Install CUBE CSS?",active:"Yes",inactive:"No",initialValue:!0});return C(e)&&process.exit(0),e}p(Xe,"promptInstallCube");async function Ze(e="src/design-tokens"){const n=await U({message:"Path to existing tokens",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Path cannot be empty"},"validate")});return C(n)&&process.exit(0),i.relative(process.cwd(),i.resolve(process.cwd(),n))}p(Ze,"promptExistingTokensPath");async function et(){const e=await W({message:"Do you need to create collections from your token files? (e.g., core, marketing)",initialValue:!1});return C(e)&&process.exit(0),e?"collections":"simple"}p(et,"promptCollectionOrganization");async function $e(e,n=!1){if(e.length===0)return null;const t=await X({message:`Select files for your ${n?"first":"next"} collection (including any theme files)`,options:e.map(o=>({label:o,value:o}))});return C(t)&&process.exit(0),t.length===0?null:t}p($e,"promptFilesForCollection");async function pe(e){const n=e.map(o=>i.dirname(o).split(i.sep)).reduce((o,s)=>o?o.filter((a,r)=>a===s[r]):s).pop(),t=await U({message:"Name this collection:",placeholder:n||"collection"});return C(t)&&process.exit(0),t}p(pe,"promptCollectionName");async function tt(e){if(e.length<=1)return null;const n=await W({message:"Are any of these theme files?",initialValue:!1});if(C(n)&&process.exit(0),!n)return null;const t=[];let o=[...e];for(;o.length>0;){const s=await X({message:t.length===0?"Select files for first theme:":"Select files for another theme (or Enter to finish):",options:o.map(r=>({label:r,value:r}))});if(C(s)&&process.exit(0),s.length===0)break;const a=await U({message:"Name this theme:",placeholder:"light"});if(C(a)&&process.exit(0),t.push({name:a,files:s}),o=o.filter(r=>!s.includes(r)),o.length>0){const r=await W({message:"Create another theme?",initialValue:!0});if(C(r)&&process.exit(0),!r)break}}return t.length>0?t:null}p(tt,"promptIdentifyThemeFiles");async function nt(e){if(e.length<=1)return null;const n=await W({message:"Do you need to create themes from your token files? (e.g. dark, light, high contrast)",initialValue:!1});if(C(n)&&process.exit(0),!n)return null;const t=[];let o=[...e];for(;o.length>0;){const s=await X({message:t.length===0?"Select files for first theme:":"Select files for your next theme:",options:o.map(r=>({label:r,value:r}))});if(C(s)&&process.exit(0),s.length===0)break;const a=await U({message:"Name this theme:",placeholder:"dark"});if(C(a)&&process.exit(0),t.push({name:a,files:s}),o=o.filter(r=>!s.includes(r)),o.length>0){const r=await W({message:"Do you need to create another theme?",initialValue:!1});if(C(r)&&process.exit(0),!r)break}}return t.length>0?t:null}p(nt,"promptIdentifyAllThemeFiles");function se(e){const t=[...e.config?[e.config]:[],...e.tokens||[],...e.generated].map(s=>i.relative(process.cwd(),s)),o=oe();o.start(`Generating ${t.length} files:`),o.stop(`Generated ${t.length} files:`);for(const s of t)$.log(` - ${s}`);$.break()}p(se,"showSummary");async function fe(e){const n={$schema:"https://sugarcube.sh/r/schema.json",...e};try{const t=JSON.stringify(n,null,2);await q("sugarcube.config.json",t)}catch(t){const o=t instanceof Error?`: ${t.message}`:"";throw new g(`Failed to write config file${o}`)}}p(fe,"writeConfig");const st=new z().name("components").description("Add components to your project").argument("[components...]","Components to add (e.g., button card)").option("-f, --framework <type>","Framework to use (react, astro-native, astro-enhanced)").action(async(e,n)=>{try{if(V(f.inverse(" Add components ")),!j("sugarcube.config.json"))throw new g(ce.PROJECT_REQUIRED);const t=await _();let o,s=[],a;if(e.length>0||n.framework){if(!n.framework)throw new g(`Framework is required when specifying components. Use --framework to specify a framework (react, astro-native, astro-enhanced) or run without arguments for interactive mode.
39
+ npm: npx make-sugarcube components
40
+ pnpm: pnpm dlx make-sugarcube components
41
+ yarn: yarn dlx make-sugarcube components
42
+ bun: bunx make-sugarcube components`;throw new Error(r)}}a(we,"installDependencies");function nt(e){const t=h(e,"blocks");return k(t)}a(nt,"hasBlocksDirectory");function st(e){return typeof e.tokens=="object"&&!("source"in e.tokens)}a(st,"hasCollections");async function ot(e,t,n){const{hasCollectionsMode:o,useBlocksDir:s,componentsOutputDirectory:r,cssOutputDirectory:l,tokensOutputDirectory:c}=n;if(e.path.endsWith(".tokens.json")){const g=o?h(c,"components",`${t.name}.tokens.json`):h(c,`${t.name}.tokens.json`);return await N(g,e.content),g}if(e.path.endsWith(".css")){if(s){const S=h(l,"blocks",`${t.name}.css`);return await N(S,e.content),S}const g=h(r,t.name);await $(g,{recursive:!0});const y=h(g,`${t.name}.css`);return await N(y,e.content),y}const i=h(r,t.name);await $(i,{recursive:!0});const p=h(i,U(e.path));return await N(p,e.content),p}a(ot,"writeComponentFile");async function rt({registryIndex:e,selectedComponents:t,componentType:n,componentsOutputDirectory:o,cssOutputDirectory:s,tokensOutputDirectory:r,config:l,overwrite:c}){const i=[],p=new Set,g=nt(s),y=st(l);if(await $(o,{recursive:!0}),await $(r,{recursive:!0}),y){const f=h(r,"components");await $(f,{recursive:!0})}const S=await de(e,t,n);for(const f of S){const u=await Z({type:"component",name:f.name,framework:n});for(const x of u.files)if(x)try{const P=await ot(x,f,{hasCollectionsMode:y,useBlocksDir:g,componentsOutputDirectory:o,cssOutputDirectory:s,tokensOutputDirectory:r});i.push(P)}catch(P){const be=P instanceof Error?`: ${P.message}`:"";throw new m(`Failed to write component file for "${f.name}"${be}`)}const W=f.dependencies?.[n]||[];for(const x of W)p.add(x)}if(p.size>0)try{await we(Array.from(p),process.cwd())}catch(f){const u=f instanceof Error?`: ${f.message}`:"";throw new m(`Failed to install component dependencies${u}`)}return{createdFiles:i,npmDependencies:p}}a(rt,"installComponents");async function it(e="src/ui/components"){const t=await xe({message:"Save components to",placeholder:e,validate:a(n=>{if(n.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return M(t)&&process.exit(0),I.relative(process.cwd(),I.resolve(process.cwd(),t))}a(it,"promptComponentDirectory");async function he(e=!0){const t=[{label:"React",value:"react",hint:".tsx"},{label:"Astro (Native)",value:"astro-native",hint:"Minimal, standards-based .astro; uses native HTML features"},{label:"Astro (Enhanced)",value:"astro-enhanced",hint:"Advanced features, richer client-side logic - coming soon"},{label:"Web Components",value:"web-components",hint:"Native web components using TypeScript - coming soon"}];e&&t.push({label:"Skip",value:"skip",hint:"continue without components"});const n=await $e({message:"Build with",options:t});return M(n)&&process.exit(0),n==="astro-enhanced"||n==="web-components"?(v.info(w.blue("Sorry, this framework is coming soon! Please choose 'Astro (Native)' or 'React' for now.")),he(e)):n}a(he,"promptComponentFramework");async function at(e,t){const n=e.filter(s=>s.type==="component"&&s.frameworks?.includes(t)),o=await Re({message:"Select components to add",options:n.map(s=>({label:s.name,value:s.name,hint:s.description})),required:!0});return M(o)&&process.exit(0),o}a(at,"promptComponentSelectionFiltered");async function ne(e,t=!1){const n=await Pe({message:e,initialValue:t});return(!n||M(n))&&process.exit(0),!0}a(ne,"confirmOverwrite");function se(e){const t=e.generated,o=[...new Set(t)].map(r=>I.relative(process.cwd(),r)),s=K();s.start(`Writing ${o.length} files:`),s.stop(`Wrote ${o.length} files:`);for(const r of o)D.log(` - ${r}`);D.break()}a(se,"showSummary");async function O(e,t){const n=t?{type:"memory",data:t,config:e}:{type:"files",config:e},{trees:o,resolved:s,errors:r}=await Ae(n);if(r.load.length>0)throw new m(`Failed to load token files:
43
+ ${r.load.map(l=>`${l.file}: ${l.message}`).join(`
44
+ `)}`);if(r.validation.length>0||r.flatten.length>0||r.resolution.length>0){D.break();const l=[...r.flatten,...r.validation,...r.resolution],c=new Map;for(const i of l)if(i.source?.sourcePath){const p=c.get(i.source.sourcePath)||[];p.push(i.message),c.set(i.source.sourcePath,p)}for(const[i,p]of c){D.error(`Error(s) in ${C.path(i)}:`);for(const g of p)D.error(` - ${g}`);D.break()}throw new m(`Token validation failed. See ${C.info("https://sugarcube.sh/docs/design-tokens")} for valid token formats`)}return{trees:o,resolved:s}}a(O,"processTokensForCLI");async function ye(e){const t={$schema:"https://sugarcube.sh/r/schema.json",...e};try{const n=JSON.stringify(t,null,2);await N(b,n)}catch(n){const o=n instanceof Error?`: ${n.message}`:"";throw new m(`Failed to write config file${o}`)}}a(ye,"writeConfig");async function ct(e,t,n,o,s,r,l){const c=await A(),{createdFiles:i}=await rt({registryIndex:c,selectedComponents:e,componentType:t,componentsOutputDirectory:n,cssOutputDirectory:o,tokensOutputDirectory:s,config:r,overwrite:l});return i}a(ct,"installComponentFiles");const lt=new j().name("components").description("Add components to your project").argument("[components...]","Components to add (e.g., button card)").option("-f, --framework <type>","Framework to use (react, astro-native)").option("-s, --silent","Suppress logs and prompts").option("-o, --overwrite","Overwrite existing files").action(async(e,t)=>{try{if(t.silent||ie(w.inverse(" Add components ")),!k(b))throw new m(ee.PROJECT_REQUIRED);const{config:n}=await L();let o,s=[],r;if(e.length>0||t.framework){if(!t.framework)throw new m(`Framework is required when specifying components. Use --framework to specify a framework (react, astro-native) or run without arguments for interactive mode.
40
45
 
41
- See ${f.cyan("https://docs.sugarcube.sh/cli/components")} for more information.`);if(!["react","astro-native","astro-enhanced","web-components"].includes(n.framework))throw new g("Invalid framework. Must be one of: React, Astro (Native) or Astro (Enhanced).");if(n.framework==="astro-enhanced"||n.framework==="web-components")throw new g("Sorry, this framework is coming soon! Please choose Astro (Native) or React for now.");if(a=n.framework,s=e,!t.output.directories.components)throw new g("Components directory must be configured in non-interactive mode. Please run the 'components' commandwithout arguments first.");o=i.resolve(process.cwd(),t.output.directories.components)}else{a=await le(!1);const u=await O();if(u||(Z("Failed to fetch component list"),process.exit(1)),s=await je(u,a),t.output.directories.components)o=i.resolve(process.cwd(),t.output.directories.components);else{const m=await Fe();o=i.resolve(process.cwd(),m),t.output.directories.components=i.relative(process.cwd(),o),await fe(t)}}const r=await be({cssOutputDirectory:i.resolve(process.cwd(),t.output.directories.css),selectedComponents:s,componentType:a,componentsOutputDirectory:o});if(r.componentFiles.length>0){const u=te(r);u&&(b.warn(u),await Q("Continue?",!1))}const c=oe();c.start("Installing components...");try{const u=await O(),{createdFiles:m,npmDependencies:S}=await Ce({registryIndex:u,selectedComponents:s,componentType:a,componentsOutputDirectory:o,cssOutputDirectory:t.output.directories.css});t.output.css.manageIndex&&await ne({cssOutputDirectory:t.output.directories.css,files:m}),c.stop("Components installed successfully"),se({generated:m}),L(f.greenBright("Success! Components installed \u2728"))}catch(u){throw c.stop("Installation failed"),u}}catch(t){K(t)}}),ot=new z().name("cube").description("Add CUBE CSS to your project").option("-s, --silent","Suppress logs and prompts").option("-f, --force","Skip overwrite confirmation").action(async e=>{const n=[];try{if(!j("sugarcube.config.json"))throw new g(ce.PROJECT_REQUIRED);e.silent||V(f.inverse(" Add CUBE CSS "));const t=await _(),o=i.resolve(process.cwd(),t.output.directories.css);try{await A(o,{recursive:!0})}catch(c){const u=c instanceof Error?`: ${c.message}`:"";throw new g(`Failed to create output directory${u}`)}const s=await ve({cssOutputDirectory:o});if(s.cubeCSS.length>0&&!e.silent&&!e.force){const c=te(s);c&&(b.warn(c),await Q("Continue?",!1))}const r=(await O()).filter(c=>c.type==="cube").map(c=>c.name);for(const c of r){const u=await ee({type:"cube",name:c});for(const m of u.files){if(!m)continue;const S=m.path.replace(/^styles\//,""),w=i.join(o,S),k=i.dirname(w);try{await A(k,{recursive:!0}),await q(w,m.content),n.push(w)}catch(x){const l=x instanceof Error?`: ${x.message}`:"";throw new g(`Failed to write CUBE CSS file ${w}${l}`)}}}t.output.css.manageIndex&&await ne({cssOutputDirectory:o,files:n}),await fe(t),!e.silent&&n.length>0&&(se({generated:n}),L(f.greenBright("Success! CUBE CSS added successfully. \u2728")))}catch(t){K(t)}});async function Te(e,n,t){const{output:o}=await Pe(e,n,t);try{const s=De(t);return await Oe(o,!0,s)}catch(s){throw new g(`Failed to write CSS files: ${s instanceof Error?s.message:"Unknown error"}`)}}p(Te,"generateAndWriteCSSVars");async function G(e,n={type:"files",paths:e}){const{trees:t,resolved:o,errors:s}=await Ae(e,{loader:n});if(s.load.length>0)throw new g(`Failed to load token files:
42
- ${s.load.map(a=>`${a.file}: ${a.message}`).join(`
43
- `)}`);if(s.validation.length>0||s.flatten.length>0||s.resolution.length>0){$.break();const a=[...s.flatten,...s.validation,...s.resolution],r=new Map;for(const c of a)if(c.source?.sourcePath){const u=r.get(c.source.sourcePath)||[];u.push(c.message),r.set(c.source.sourcePath,u)}for(const[c,u]of r){$.error(`Error(s) in ${E.path(c)}:`);for(const m of u)$.error(` - ${m}`);$.break()}throw new g(`Token validation failed. See ${E.info("https://docs.sugarcube.sh/w3c-token-format")} for help`)}return{trees:t,resolved:o}}p(G,"validateTokens");const it=new z().name("generate").description("Generate CSS from your design tokens").option("-s, --silent","Suppress logs and prompts").option("-f, --force","Skip overwrite confirmation").action(async e=>{try{if(e.silent||V(f.inverse(" Generate CSS variables from your design tokens ")),!j("sugarcube.config.json"))throw new g(ce.PROJECT_REQUIRED);const n=await _(),{trees:t,resolved:o}=await G(n),s=await Se({config:n,cssOutputDirectory:n.output.directories.css}),a=te(s);a&&!e.force&&!e.silent&&(b.warn(a),await Q("Continue?",!1));const r=await Te(t,o,n);n.output.css.manageIndex&&await ne({cssOutputDirectory:n.output.directories.css,files:r.map(c=>c.path)}),e.silent||(se({generated:[...r.map(c=>c.path),...n.output.css.manageIndex?["index.css"]:[]]}),L(f.greenBright("Success! CSS variables generated successfully. \u2728")))}catch(n){K(n)}}),rt=p(async({tokenFiles:e,tokensOutputDirectory:n})=>{const t=[];for(const o of e){const s=i.join(n,i.basename(o.path));j(s)&&t.push(s)}return t},"checkTokenFileExists"),at=p(async e=>{const n=await Ze(),t=await ge("**/*.json",{cwd:n,absolute:!0});if(t.length===0)throw new g(`No JSON files found in ${n}`);const o=[];for(const l of t)try{const d=await de(l,"utf-8");JSON.parse(d),o.push(l)}catch{b.warn(f.yellow(`Skipping invalid JSON file: ${l}`))}if(o.length===0)throw new g(`No valid JSON files found in ${n}
44
- Ensure files contain valid JSON`);const s=o.map(l=>i.relative(process.cwd(),l)),a=i.resolve(process.cwd(),n),r=await xe();let c=await et();const u=new Map;for(const l of s){const d=i.basename(l),h=u.get(d)||[];u.set(d,[...h,l])}const m=Array.from(u.entries()).filter(([l,d])=>d.length>1).map(([l,d])=>({name:l,files:d}));if(c==="simple"&&m.length>0){const l=m.map(({name:d,files:h})=>{const F=h.map(v=>` - ${v}`).join(`
45
- `);return`
46
- ${d} appears in:
47
- ${f.dim(F)}`}).join(`
48
- `);b.warn(f.yellow(`Simple organization cannot have duplicate filenames:
49
- ${l}`)),b.message(f.cyan("Switching to collections to help organize these files...")),c="collections"}if(c==="simple"){const l={type:"custom",source:s},d=await nt(s);if(d&&d.length>0){const h=d.flatMap(F=>F.files);l.source=s.filter(F=>!h.includes(F)),l.themes=d.reduce((F,v)=>Object.assign(F,{[v.name]:v.files}),{})}e.tokens=l}else{const l={},d=new Set;let h=[...s],F=!0;for(;h.length>0;){let v=await $e(h,F);if(!v){b.message(f.cyan("No files selected, skipping collection creation..."));break}let T=await pe(v);if(!T){b.message(f.cyan("No collection name provided, skipping collection creation..."));break}let B=!1;for(;!B;)try{if(d.has(T)){b.warn(f.yellow(`Collection name "${T}" is already in use.`));const R=await pe(v);if(!R){b.message(f.cyan("No collection name provided, skipping collection creation..."));break}T=R;continue}const I={type:"custom",source:v};l[T]=I;const N=await tt(v);if(N&&N.length>0){const R=N.flatMap(D=>D.files),P=new Set;let M=!1;for(const D of N){if(P.has(D.name)){b.warn(f.yellow(`Theme name "${D.name}" is already used in this collection.`)),b.message(f.cyan("Starting theme selection over to avoid conflicts...")),M=!0;break}P.add(D.name)}if(M)continue;I.source=v.filter(D=>!R.includes(D)),I.themes=N.reduce((D,Y)=>Object.assign(D,{[Y.name]:Y.files}),{})}const J=new Map;for(const R of I.source){const P=i.basename(R),M=J.get(P)||[];J.set(P,[...M,R])}const ue=Array.from(J.entries()).filter(([R,P])=>P.length>1).map(([R,P])=>({name:R,files:P}));if(ue.length>0){const R=ue.map(({name:D,files:Y})=>{const Ee=Y.map(Ie=>` - ${Ie}`).join(`
50
- `);return`
51
- ${D} appears in:
52
- ${f.dim(Ee)}`}).join(`
53
- `);b.warn(f.yellow(`Found duplicate filenames in collection "${T}":
54
- ${R}`)),b.message(f.cyan("Please either split these files into different collections or mark some as theme files."));const P=await $e(h,F);if(!P){b.message(f.cyan("No files selected, skipping collection creation..."));break}v=P;const M=await pe(v);if(!M){b.message(f.cyan("No collection name provided, skipping collection creation..."));break}T=M;continue}B=!0,d.add(T)}catch(I){if(I instanceof g){b.error(`${I.message}`),b.message(f.cyan("Let's try again..."));continue}throw I}if(!B){b.message(f.cyan("Skipping collection creation..."));break}h=h.filter(I=>!v.includes(I)),h.length>0&&b.message(f.cyan(`Remaining files to organize: ${h.length}`)),F=!1}h.length>0?b.warn(f.yellow(`Warning: ${h.length} files were not assigned to any collection`)):b.message(f.cyan("Token organization complete")),e.tokens=l}if(e.output={directories:{tokens:i.relative(process.cwd(),a),css:r},css:{separate:!1,manageIndex:!0}},e.options={fluid:{min:320,max:1200}},typeof e.tokens=="object"&&!Array.isArray(e.tokens)){const l=e.tokens,d=new Set;for(const[h,F]of Object.entries(l)){if(d.has(h))throw new g(`Configuration Error: Duplicate collection name "${h}". Collection names must be unique.`);if(d.add(h),F.themes){const v=new Set;for(const T of Object.keys(F.themes)){if(v.has(T))throw new g(`Configuration Error: Duplicate theme name "${T}" in collection "${h}". Theme names must be unique within a collection.`);v.add(T)}}}}const S=await Promise.all(s.map(async l=>({path:i.resolve(process.cwd(),l),content:await de(i.resolve(process.cwd(),l),"utf-8")}))),w=me(e),{trees:k,resolved:x}=await G(w);return{config:w,tokens:s,trees:k,resolved:x,tokenPath:n,tokensDir:a,tokenFiles:S}},"initializeFromExistingTokens"),ct=p(async(e,n,t)=>{const o=await ee({type:"tokens",name:e});if(!o.files[0])throw new g(`Starter kit '${e}' does not contain any token files`);const s=i.resolve(process.cwd(),t),a=o.files.find(l=>i.basename(l.path)==="config.json");let r={};if(a)try{r=JSON.parse(a.content)}catch{console.warn(`Warning: Could not parse config.json for starter kit '${e}'`)}const c=o.files.filter(l=>i.basename(l.path)!=="config.json").map(l=>({path:i.join(s,i.basename(l.path)),content:l.content})),u=Object.fromEntries(c.map(l=>[i.basename(l.path),i.relative(process.cwd(),l.path)])),m={};if(r.themes)for(const[l,d]of Object.entries(r.themes))m[l]=d.map(h=>{const F=u[h];if(!F)throw new g(`Theme file '${h}' not found in starter kit '${e}'`);return F});const S={tokens:{source:c.map(l=>i.relative(process.cwd(),l.path)),type:"starter-kit",...Object.keys(m).length>0&&{themes:m}},options:{fluid:{min:320,max:1200}},output:{directories:{tokens:i.relative(process.cwd(),s),css:n},css:{separate:!1,manageIndex:!0}}},w=me(S),{trees:k,resolved:x}=await G(w,{type:"memory",data:Object.fromEntries(c.map(l=>[l.path,{collection:"default",content:l.content}]))});return{config:w,tokens:c.map(l=>i.relative(process.cwd(),l.path)),trees:k,resolved:x,tokenFiles:c,tokensDir:s}},"initializeFromStarterKit");async function lt(e){const n=[],t=i.resolve(process.cwd(),e);await A(t,{recursive:!0});const s=(await O()).filter(a=>a.type==="cube").map(a=>a.name);for(const a of s){const r=await ee({type:"cube",name:a});for(const c of r.files){if(!c)continue;const u=c.path.replace(/^styles\//,""),m=i.join(t,u),S=i.dirname(m);try{await A(S,{recursive:!0}),await q(m,c.content),n.push(m)}catch(w){const k=w instanceof Error?`: ${w.message}`:"";throw new g(`Failed to write CUBE module "${a}"${k}`)}}}return n}p(lt,"installCUBE");async function pt(){if(j("sugarcube.config.json")){const e=await H({message:f.yellow("WARNING: You are about to re-initialize an existing sugarcube project! This will overwrite your existing configuration file. "),options:[{label:"Overwrite existing file (start from scratch)",value:"overwrite"},{label:"Cancel initialization",value:"cancel"}]});return C(e)&&process.exit(0),{shouldProceed:e==="overwrite"}}return{shouldProceed:!0}}p(pt,"preflightInit");const ft=p(()=>{$.break(),V(" Welcome to sugarcube. The toolkit for seriously sweet front ends ")},"welcome"),ut=new z().name("init").description("Initialize a new sugarcube project").action(async()=>{try{ft(),(await pt()).shouldProceed||process.exit(0),ie("Step 1. Set up tokens");const n=await He();let t;switch(n){case"starter":{const d=await O();d||(Z("Failed to fetch starter kit list"),process.exit(1));const{name:h,tokenFilePaths:F}=await Ye(d),v=await Qe(),T=F.map(N=>({path:i.join(v,N)})),B=await rt({tokenFiles:T,tokensOutputDirectory:v});if(B.length>0){const N=B.map(J=>` - ${J}`).join(`
55
- `);b.warn(f.yellow(`WARNING: The following token file(s) already exist in ${v} and will be OVERWRITTEN:
56
- ${f.dim(N)}`)),await Q("Continue?",!1)}const I=await xe();try{t=await ct(h,I,v)}catch(N){Z(`Failed to initialize starter kit: ${N instanceof Error?N.message:"Unknown error"}`),process.exit(1)}break}case"existing":t=await at({});break}ie("Step 2. Add style system");const o=await Xe();ie("Step 3. Add components");const s=await le();t.config.output.css.manageIndex=!1;let a,r,c;s!=="skip"&&(a=await Fe(),t.config.output.directories.components=i.relative(process.cwd(),a),c=await O(),c||(Z("Failed to fetch component list"),process.exit(1)),r=await je(c,s));const u=_e(await Se({config:t.config,cssOutputDirectory:t.config.output.directories.css}),r&&s!=="skip"&&a?await be({cssOutputDirectory:t.config.output.directories.css,selectedComponents:r,componentType:s,componentsOutputDirectory:a}):{designTokenCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]},o?await ve({cssOutputDirectory:t.config.output.directories.css}):{designTokenCSS:[],componentFiles:[],componentCSS:[],cubeCSS:[],indexFiles:[]}),m=te(u);m&&(b.warn(m),await Q("Continue?",!1));try{if(!t.tokensDir)throw new g("Tokens directory is not defined");if(await re.mkdir(t.tokensDir,{recursive:!0}),await re.mkdir(t.config.output.directories.css,{recursive:!0}),n==="starter"){if(!t.tokenFiles||t.tokenFiles.length===0)throw new g("Failed to generate token content for starter kit");for(const d of t.tokenFiles)await re.writeFile(d.path,d.content)}}catch(d){const h=d instanceof Error?`: ${d.message}`:"";throw new g(`Failed to create project files${h}`)}let S=[];const{trees:w,resolved:k}=await G(t.config);S=await Te(w,k,t.config);let x=[];o&&(x=await lt(t.config.output.directories.css));let l=[];if(r&&s!=="skip"&&a){const d=oe();d.start("Installing components...");try{const h=await O();if(!h)throw new g("Failed to fetch component list");const{createdFiles:F,npmDependencies:v}=await Ce({registryIndex:h,selectedComponents:r,componentType:s,componentsOutputDirectory:a,cssOutputDirectory:t.config.output.directories.css});l=F,d.stop("Components installed successfully")}catch(h){throw d.stop("Installation failed"),h}}t.config.output.css.manageIndex&&await ne({cssOutputDirectory:t.config.output.directories.css,files:[...S.map(d=>d.path),...l.filter(d=>d.endsWith(".css")),...x]}),await fe(t.config),se({config:"sugarcube.config.json",tokens:n==="starter"?t.tokens:[],generated:[...S.map(d=>d.path),...x,...l,...t.config.output.css.manageIndex?["index.css"]:[]]}),L(f.greenBright("Success! Project initialized \u2728"))}catch(e){K(e)}}),mt=new z().name("validate").description("Validate design token files").argument("[paths...]","Token files or directories to validate (e.g., tokens.json or ./tokens)").action(async e=>{try{if(V(f.inverse(" Validate design tokens ")),j("sugarcube.config.json")&&!e.length){const s=await _();await G(s),L(f.greenBright("All tokens are valid \u2728"));return}if(!e.length)throw new g("No paths specified. Please provide files/directories to validate, or run this in a sugarcube project directory.");const n=e.map(s=>i.normalize(s));for(const s of n)if(!j(s))throw new g(`Path not found: ${s}
57
- Please check that the specified files or directories exist`);const t=await ge(n.map(s=>s.endsWith(".json")?s:i.join(s,"**/*.json")));if(t.length===0)throw new g(`No JSON files found in the specified paths
58
- Please ensure the paths contain .json files`);const o=j("sugarcube.config.json")?await _():{output:{directories:{tokens:".",css:"."},css:{separate:!1}}};await G({...o,tokens:{type:"custom",source:t.map(s=>i.relative(process.cwd(),s))}}),L(f.greenBright("All tokens are valid \u2728"))}catch(n){K(n)}});process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function dt(){const e=new z().name("sugarcube").description("CLI for scaffolding sugarcube projects").version(ze.version,"-v, --version","display the version number");e.addCommand(ut).addCommand(it).addCommand(mt).addCommand(st).addCommand(ot),e.parse()}p(dt,"main"),dt();
46
+ See ${w.cyan("https://docs.sugarcube.sh/cli/components")} for more information.`);if(!["react","astro-native"].includes(t.framework))throw new m("Invalid framework. Must be one of: react, astro-native.");if(r=t.framework,s=e,!n.output?.components)throw new m("Components directory must be configured in non-interactive mode. Please run the 'components' command without arguments first.");o=F(process.cwd(),n.output.components)}else{r=await he(!1);const y=await A();if(y||(je("Failed to fetch component list"),process.exit(1)),s=await at(y,r),n.output?.components)o=F(process.cwd(),n.output.components);else{const S=await it();o=F(process.cwd(),S),n.output.components=E(process.cwd(),o),await ye(n)}}const l=F(process.cwd(),n.output?.css||V),c=F(process.cwd(),n.output?.tokens||Oe),i=await Xe({cssOutputDirectory:l,selectedComponents:s,componentType:r,componentsOutputDirectory:o,tokensOutputDirectory:c}),p=te(i);p&&!t.force&&!t.silent&&(v.warn(p),await ne("Continue?",!1));const g=K();g.start("Installing components...");try{const y=await ct(s,r,o,l,c,n,t.overwrite||!1);g.stop("Components installed successfully"),await Ze(n);const{trees:S,resolved:f}=await O(n),{output:u}=await H(S,f,n);await q(u),await B(l,n),t.silent||(se({generated:y}),_(w.greenBright("Success! Components installed \u2728")))}catch(y){throw g.stop("Installation failed"),y}}catch(n){G(n)}});async function ke(e){const t=[],n=F(process.cwd(),e);await $(n,{recursive:!0});const s=(await A()).filter(r=>r.type==="cube").map(r=>r.name);for(const r of s){const l=await Z({type:"cube",name:r});for(const c of l.files){if(!c)continue;const i=c.path.replace(/^styles\//,""),p=h(n,i),g=Ie(p);try{await $(g,{recursive:!0}),await N(p,c.content),t.push(p)}catch(y){const S=y instanceof Error?`: ${y.message}`:"";throw new m(`Failed to write CUBE module "${r}"${S}`)}}}return t}a(ke,"installCUBE");const ut=new j().name("cube").description("Add CUBE CSS to your project").option("-s, --silent","Suppress logs and prompts").option("-f, --force","Skip overwrite confirmation").action(async e=>{try{if(!k(b))throw new m(ee.PROJECT_REQUIRED);const{config:t}=await L(),n=F(process.cwd(),t.output?.css||V);try{await $(n,{recursive:!0})}catch(l){const c=l instanceof Error?`: ${l.message}`:"";throw new m(`Failed to create output directory${c}`)}const o=await ge({cubeDirectory:n}),s=te(o);s&&!e.force&&!e.silent&&(v.warn(s),await ne("Continue?",!1));const r=await ke(n);await B(n,t),e.silent||(se({generated:r}),_(w.greenBright("Success! CUBE CSS added successfully. \u2728")))}catch(t){G(t)}});async function pt(e,t,n){const o=[],{output:s}=await H(e,t,n);await q(s),o.push(...s);const{output:r}=ce(t,n);await le(r),o.push(...r);const l=await B(n.output?.css||V,n);return o.push({path:l,css:Ue}),o}a(pt,"generateAllCSS");const ft=new j().name("generate").description("Generate CSS from your design tokens").option("--force","Skip overwrite confirmation").option("-s, --silent","Suppress logs and prompts").action(async e=>{try{if(!k(b))throw new m(ee.PROJECT_REQUIRED);const{config:t}=await L(),{trees:n,resolved:o}=await O(t),s=await pt(n,o,t);e.silent||(se({generated:s.map(r=>r.path)}),_(w.greenBright("Success! CSS generated successfully. \u2728")))}catch(t){G(t)}}),mt=["**/node_modules/**",".next","public","dist","build",".astro",".nuxt",".output",".svelte-kit"],R={"next-app":{tokensDir:"app/design-tokens",stylesDir:"app/styles",componentDir:"app/components"},"next-pages":{tokensDir:"src/design-tokens",stylesDir:"src/styles",componentDir:"src/components"},astro:{tokensDir:"src/design-tokens",stylesDir:"src/styles",componentDir:"src/components"},vite:{tokensDir:"src/design-tokens",stylesDir:"src/styles",componentDir:"src/components"},nuxt:{tokensDir:"assets/design-tokens",stylesDir:"assets/styles",componentDir:"components"},sveltekit:{tokensDir:"src/lib/design-tokens",stylesDir:"src/lib/styles",componentDir:"src/lib/components"},remix:{tokensDir:"app/design-tokens",stylesDir:"app/styles",componentDir:"app/components"},eleventy:{tokensDir:"src/_data/design-tokens",stylesDir:"src/styles",componentDir:"src/components"},none:{tokensDir:"src/design-tokens",stylesDir:"src/styles",componentDir:"src/components"}};function dt(e){try{const t=I.resolve(e,"package.json");if(!k(t))return null;const n=re(t,"utf-8");return JSON.parse(n)}catch{return null}}a(dt,"getPackageJson");function gt(e){const t=["design-tokens","tokens","src/design-tokens","src/tokens","app/design-tokens","assets/design-tokens","src/_data/design-tokens","src/lib/design-tokens"],n=["styles","css","src/styles","src/css","app/styles","assets/styles","assets/css","src/lib/styles"],o=["components","src/components","app/components","src/lib/components"],s={};for(const r of t)if(k(I.resolve(e,r))){s.existingTokensDir=r;break}for(const r of n)if(k(I.resolve(e,r))){s.existingStylesDir=r;break}for(const r of o)if(k(I.resolve(e,r))){s.existingComponentsDir=r;break}return s}a(gt,"findExistingPaths");async function wt(e){const[t,n,o]=await Promise.all([Q.glob("**/{next,vite,astro,nuxt,svelte,remix,eleventy}.config.*|.eleventy.js",{cwd:e,deep:3,ignore:mt}),k(I.resolve(e,"src")),dt(e)]),s=gt(e),r=k(I.resolve(e,`${n?"src/":""}app`)),l={framework:"none",isSrcDir:n,tokensDir:"src/design-tokens",stylesDir:"src/styles",componentDir:"src/components"};if(t.find(i=>i.startsWith("next.config."))?.length){const i=r?"next-app":"next-pages",p=R[i];return{...l,framework:i,tokensDir:s.existingTokensDir||p.tokensDir,stylesDir:s.existingStylesDir||p.stylesDir,componentDir:s.existingComponentsDir||p.componentDir}}if(t.find(i=>i.startsWith("astro.config."))?.length){const i=R.astro;return{...l,framework:"astro",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}if(t.find(i=>i.startsWith("nuxt.config."))?.length){const i=R.nuxt;return{...l,framework:"nuxt",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}if(t.find(i=>i.startsWith("svelte.config."))?.length){const i=R.sveltekit;return{...l,framework:"sveltekit",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}if(Object.keys(o?.dependencies??{}).find(i=>i.startsWith("@remix-run/"))){const i=R.remix;return{...l,framework:"remix",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}if(t.find(i=>i.startsWith("eleventy.config.")||i.startsWith(".eleventy."))?.length){const i=R.eleventy;return{...l,framework:"eleventy",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}if(t.find(i=>i.startsWith("vite.config."))?.length){const i=R.vite;return{...l,framework:"vite",tokensDir:s.existingTokensDir||i.tokensDir,stylesDir:s.existingStylesDir||i.stylesDir,componentDir:s.existingComponentsDir||i.componentDir}}const c=R.none;return{...l,framework:"none",tokensDir:s.existingTokensDir||(n?c.tokensDir:"design-tokens"),stylesDir:s.existingStylesDir||(n?c.stylesDir:"styles"),componentDir:s.existingComponentsDir||(n?c.componentDir:"components")}}a(wt,"getProjectInfo");function ht(e){return{"next-app":"Next.js (App Router)","next-pages":"Next.js (Pages Router)",astro:"Astro",vite:"Vite",nuxt:"Nuxt",sveltekit:"SvelteKit",remix:"Remix",eleventy:"11ty (Eleventy)",none:"No framework"}[e]}a(ht,"getFrameworkDisplayName");async function yt(){k(b)&&(v.error(`A ${w.cyan(b)} file already exists at ${w.cyan(process.cwd())}.
47
+ To start over, remove the ${w.cyan(b)} file and run ${w.cyan("init")} again.
48
+ `),process.exit(1))}a(yt,"preflightInit");function kt(e,t=process.cwd()){try{const n=Je(t,"package.json");if(!k(n))return!1;const o=re(n,"utf-8"),s=JSON.parse(o),r={...s.dependencies,...s.devDependencies,...s.peerDependencies};return e in r}catch{return!1}}a(kt,"isPackageInstalled");const Dt=a(async(e,t,n)=>{const o=await Z({type:"tokens",name:e});if(!o.files[0])throw new m(`Starter kit '${e}' does not contain any token files`);const s=F(process.cwd(),t),r=o.files.find(u=>U(u.path)==="config.json");let l={};if(r)try{l=JSON.parse(r.content)}catch(u){et(`Could not parse config.json for starter kit '${e}':`,u)}const c=o.files.filter(u=>U(u.path)!=="config.json").map(u=>({path:h(s,U(u.path)),content:u.content})),i=Object.fromEntries(c.map(u=>[U(u.path),E(process.cwd(),u.path)])),p={};if(l.themes)for(const[u,W]of Object.entries(l.themes))p[u]=W.map(x=>{const P=i[x];if(!P)throw new m(`Theme file '${x}' not found in starter kit '${e}'`);return P});const g={...n};Object.keys(p).length>0&&typeof g.tokens=="object"&&!Array.isArray(g.tokens)&&"source"in g.tokens&&(g.tokens.themes=p);const y=Le(g),{trees:S,resolved:f}=await O(y,Object.fromEntries(c.map(u=>[u.path,{collection:"default",content:u.content}])));return{config:y,trees:S,resolved:f,tokensDir:s,tokenFiles:c,createdTokenPaths:c.map(u=>E(process.cwd(),u.path))}},"initializeFromStarterKit"),St="https://sugarcube.sh/r/schema.json",oe="tailwind-fluid",De="**/*.json",Se="@sugarcube-org/vite",ve="@sugarcube-org/postcss",vt=["node_modules",".git",".next","dist","build"],Ct=/^[a-zA-Z][a-zA-Z0-9_-]*$/,Et=5,T={LAUNCH_INITIATED:"Launch sequence initiated.",PREFLIGHT_COMPLETE:"\u2714 Preflight checks",PROJECT_DETECTED:a(e=>`\u2714 Project detected (${e})`,"PROJECT_DETECTED"),TOKENS_INSTALLED:"\u2714 Design tokens installed",TOKENS_CONFIGURED:"\u2714 Design tokens configured",CUBE_INSTALLED:"\u2714 CUBE CSS installed",CSS_VARS_GENERATED:"\u2714 CSS variables generated",UTILITIES_GENERATED:"\u2714 Utility classes generated",DEPS_INSTALLED:"\u2714 Dependencies installed",CONFIG_WRITTEN:a(e=>`\u2714 Config file written (${e})`,"CONFIG_WRITTEN"),SUGARCUBE_ENTRY_CREATED:a(e=>`\u2714 Sugarcube entry point created (${e})`,"SUGARCUBE_ENTRY_CREATED"),SUCCESS:"Success! Project initialized \u2728",CONTINUING_WITHOUT_PLUGIN:"\u2714 Continuing without plugin..."};async function bt(e){if(e.setupResult=await Dt(e.starterKit??"",e.tokensDir,e.config),e.setupResult.tokenFiles)for(const t of e.setupResult.tokenFiles)try{await J.writeFile(t.path,t.content),e.createdFiles.push(t.path)}catch(n){throw new X("Failed to write token file",t.path,"writeFile",n instanceof Error?n:void 0)}}a(bt,"downloadAndWriteTokenFiles");async function Tt(e){await bt(e),v.success(T.TOKENS_INSTALLED)}a(Tt,"installFromStarterKit");async function It(e){try{await J.mkdir(e.tokensDir,{recursive:!0}),e.createdDirectories.push(e.tokensDir),await J.mkdir(e.stylesDir,{recursive:!0}),e.createdDirectories.push(e.stylesDir)}catch(t){throw new X("Failed to create directories",e.tokensDir,"mkdir",t instanceof Error?t:void 0)}}a(It,"createProjectDirectories");async function Ft(e,t,n,o,s){return{options:e,...t,config:n,minimalUserConfig:o,...s,createdFiles:[],createdDirectories:[],installedDependencies:[]}}a(Ft,"buildInitContext");function Ce(e,t){if(!e||e.trim()==="")throw new z(`${t} cannot be empty`,t,e,'Provide a valid directory path like "src/styles"');const n=Fe(e);if(n.includes("../")||n.startsWith("/"))throw new z(`${t} should be a relative path within your project`,t,e,`Use a relative path like "src/styles" instead of "${e}"`);const o=n.split("/")[0];if(o&&vt.includes(o))throw new z(`${t} cannot be in a reserved directory`,t,e,`Avoid using ${o}. Try "src/styles" or "assets/styles" instead`)}a(Ce,"validateDirectoryPath");function $t(e){if(e.tokensDir&&Ce(e.tokensDir,"tokens-dir"),e.stylesDir&&Ce(e.stylesDir,"styles-dir"),e.kit&&!Ct.test(e.kit))throw new z("Kit name contains invalid characters","kit",e.kit,'Use only letters, numbers, hyphens, and underscores. Try "tailwind-fluid" or "tailwind-static"')}a($t,"validateOptions");function Ee(e,t=0){if(typeof e!="object"||e===null||t>Et)return!1;for(const[n,o]of Object.entries(e))if(!n.startsWith("$")&&typeof o=="object"&&o!==null&&("$value"in o||Ee(o,t+1)))return!0;return!1}a(Ee,"hasAnyToken");async function Rt(e){try{const t=await Q(De,{cwd:e,absolute:!0});for(const n of t)try{const o=await J.readFile(n,"utf-8"),s=JSON.parse(o);if(Ee(s))return!0}catch{}return!1}catch{return!1}}a(Rt,"detectExistingTokens");function xt(e){return{source:[h(E(process.cwd(),e.actualTokensDir),De)]}}a(xt,"createSingleTokenCollection");async function Pt(e){return xt(e)}a(Pt,"buildTokensConfig");async function jt(e){const t=await Pt(e),n=E(process.cwd(),e.actualStylesDir),o=E(process.cwd(),e.actualTokensDir),s={$schema:St,tokens:t},r={};return n!==ue.output.css&&(r.css=n),o!==ue.output.tokens&&(r.tokens=o),Object.keys(r).length>0&&(s.output=r),s}a(jt,"generateMinimalConfig");function _t(e){return e==="next-app"||e==="next-pages"?ve:Se}a(_t,"selectPluginForFramework");async function Nt(e){await yt();const t=await wt(process.cwd());v.success(T.PREFLIGHT_COMPLETE),v.success(T.PROJECT_DETECTED(ht(t.framework)));const n=e.tokensDir||t.tokensDir,o=e.stylesDir||t.stylesDir,s=await Rt(n);return{projectInfo:t,tokensDir:n,stylesDir:o,hasExistingTokens:s}}a(Nt,"initializeProjectContext");function At(e,t,n){let o=null;n||(o=e.kit||oe);let s=null;e.plugin==="auto"||!e.plugin?s=_t(t.framework):e.plugin==="vite"?s=Se:e.plugin==="postcss"&&(s=ve);const r=e.cube!==!1;return{starterKit:o,pluginToInstall:s,installCube:r}}a(At,"determineInstallationTargets");async function Ot(e){if(!e.installCube)return;const t=await ge({cubeDirectory:e.stylesDir}),n=te(t);n&&(v.warn(n),await ne("Continue and overwrite existing files?",!1))}a(Ot,"checkConflicts");async function Ut(e){const{trees:t,resolved:n}=await O(e.config);e.setupResult={config:e.config,createdTokenPaths:[],trees:t,resolved:n,tokenFiles:[],tokensDir:e.tokensDir},v.success(T.TOKENS_CONFIGURED)}a(Ut,"processExistingTokens");async function Lt(e){!e.hasExistingTokens&&e.starterKit?await Tt(e):await Ut(e)}a(Lt,"setupDesignTokens");async function Gt(e){e.installCube&&(await ke(e.stylesDir),v.success(T.CUBE_INSTALLED))}a(Gt,"installCubeFiles");async function Wt(e){if(!e.setupResult)throw new m("Initialization incomplete. Please re-run the `init` command.");const{trees:t,resolved:n}=e.setupResult,{output:o}=await H(t,n,e.config);await q(o);const{output:s}=ce(n,e.config);await le(s),await B(e.stylesDir,e.config)}a(Wt,"writeStyles");async function Mt(e){if(!e.pluginToInstall)return;if(kt(e.pluginToInstall)){v.success(T.DEPS_INSTALLED);return}const t=K();t.start(`Installing ${e.pluginToInstall.split("/")[1]} plugin...`);try{await we([e.pluginToInstall],process.cwd()),e.installedDependencies.push(e.pluginToInstall),t.stop(T.DEPS_INSTALLED)}catch{t.stop(""),v.warn(w.yellow(`Failed to install ${e.pluginToInstall}. Install manually: npm install ${e.pluginToInstall}`)),v.info(T.CONTINUING_WITHOUT_PLUGIN)}}a(Mt,"installPlugins");async function Bt(e){try{await ye(e.minimalUserConfig),e.createdFiles.push(b),v.success(T.CONFIG_WRITTEN(b))}catch(t){throw new X("Failed to write configuration file",b,"writeFile",t instanceof Error?t:void 0)}_(w.greenBright(T.SUCCESS))}a(Bt,"finalize");const Jt=new j().name("init").description("Initialize a new sugarcube project").option("--kit <kit>",`Starter kit to use (default: ${oe})`,oe).option("--tokens-dir <dir>","Design tokens directory (e.g., 'src/design-tokens')").option("--styles-dir <dir>","Styles output directory (e.g., 'src/styles')").option("--plugin <type>","Plugin to install: vite, postcss, none, or auto (default: auto)","auto").option("--no-cube","Don't install CUBE CSS").action(async e=>{let t;try{$t(e);const n=await Nt(e),o=await jt({actualTokensDir:n.tokensDir,actualStylesDir:n.stylesDir}),r={...Ge(o),$schema:o.$schema},l=o,c=At(e,n.projectInfo,n.hasExistingTokens);t=await Ft(e,n,r,l,c),await Ot(t),await It(t),await Lt(t),await Gt(t),await Mt(t),await Wt(t),await Bt(t)}catch(n){G(n)}}),zt=new j().name("validate").description("Validate design token files").argument("[paths...]","Token files or directories to validate (e.g., src/design-tokens)").action(async e=>{try{if(ie(w.inverse(" Validate design tokens ")),k(b)&&!e.length){const{config:o}=await L();await O(o),_(w.greenBright("All tokens are valid \u2728"));return}if(!e.length)throw new m("No paths specified. Please provide files/directories to validate, or run this in a sugarcube project directory.");for(const o of e)if(!k(o))throw new m(`Path not found: ${o}
49
+ Please check that the specified files or directories exist`);const t=await Q(e.map(o=>o.endsWith(".json")?o:h(o,"**/*.json")));if(t.length===0)throw new m(`No JSON files found in the specified paths
50
+ Please ensure the paths contain .json files`);const n=k(b)?(await L()).config:{tokens:{source:[]},output:{tokens:".",css:".",separate:!1}};await O({...n,tokens:{source:t.map(o=>E(process.cwd(),o))}}),_(w.greenBright("All tokens are valid \u2728"))}catch(t){G(t)}});process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function Kt(){const e=new j().name("sugarcube").description("CLI for scaffolding sugarcube projects"),t=process.argv[2];(!t||!["init","generate","validate","components","cube"].includes(t))&&e.version(Ke.version,"-v, --version","display the version number"),e.addCommand(Jt).addCommand(ft).addCommand(zt).addCommand(lt).addCommand(ut),e.parse()}a(Kt,"main"),Kt();
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@sugarcube-org/cli",
3
- "version": "0.0.0-alpha.15",
3
+ "version": "0.0.0-alpha.16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "description": "A CLI for scaffolding sugarcube applications",
8
- "license": "AGPL-3.0",
8
+ "license": "UNLICENSED",
9
9
  "author": "Mark Tomlinson",
10
10
  "repository": {
11
11
  "type": "git",
@@ -43,9 +43,10 @@
43
43
  "execa": "^9.5.2",
44
44
  "fast-glob": "^3.3.2",
45
45
  "node-fetch": "^3.3.0",
46
+ "pathe": "^2.0.3",
46
47
  "picocolors": "^1.1.1",
47
48
  "zod": "^3.24.2",
48
- "@sugarcube-org/core": "0.0.1-alpha.5"
49
+ "@sugarcube-org/core": "0.0.1-alpha.6"
49
50
  },
50
51
  "devDependencies": {
51
52
  "pkgroll": "^2.5.1",
@@ -55,7 +56,7 @@
55
56
  "node": ">=18.0.0"
56
57
  },
57
58
  "scripts": {
58
- "build": "pkgroll --minify",
59
+ "build": "tsc --noEmit && pkgroll --minify",
59
60
  "dev": "cross-env REGISTRY_URL=http://localhost:4321/r tsx src/index.ts",
60
61
  "test": "vitest run",
61
62
  "test:watch": "vitest",