@sugarcube-org/cli 0.0.0-alpha.1 → 0.0.0-alpha.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.
Files changed (3) hide show
  1. package/dist/index.mjs +47 -21
  2. package/package.json +61 -60
  3. package/LICENSE.md +0 -21
package/dist/index.mjs CHANGED
@@ -1,24 +1,50 @@
1
1
  #!/usr/bin/env node
2
- var oe=Object.defineProperty;var f=(e,t)=>oe(e,"name",{value:t,configurable:!0});import{Command as F}from"commander";import T,{writeFile as C,readdir as se,readFile as B,mkdir as $}from"fs/promises";import{z as u}from"zod";import ne from"ora";import{validationPipeline as ie,generationPipeline as re}from"@sugarcube-org/core";import{red as ae,yellow as ce,cyan as le,green as ue,bold as de}from"kleur/colors";import g,{join as _,dirname as J}from"path";import w from"prompts";import fe,{existsSync as j}from"fs";import pe from"node-fetch";import me from"fast-glob";import ge from"fs-extra";const U=u.enum(["react","astro","nunjucks"]),q=u.object({path:u.string(),type:u.string()}),he=q.extend({framework:U}),we=u.object({name:u.string(),type:u.string(),description:u.string().optional(),files:u.array(u.union([he,q])),tokens:u.record(u.object({type:u.string(),mapping:u.string()})).optional(),dependencies:u.record(U,u.array(u.string())).optional(),registryDependencies:u.record(U,u.array(u.string())).optional(),tokenDependencies:u.array(u.string()).optional()}),ye=u.array(we),ve=u.object({content:u.string()}),K=u.object({files:u.record(u.string(),u.string()).refine(e=>Object.keys(e).length>0,"No token files found in sugarcube.config.json").refine(e=>Object.values(e).every(t=>t.endsWith(".json")),`Token files in the config must be specified individually (e.g., 'tokens': './tokens/tokens.json'). Directory paths like './tokens' are not allowed.
2
+ var je=Object.defineProperty;var p=(e,n)=>je(e,"name",{value:n,configurable:!0});import{Command as O}from"commander";import{validationPipeline as Ce,generationPipeline as Fe}from"@sugarcube-org/core";import{z as l}from"zod";import g from"picocolors";import h from"path";import X,{readFile as le,writeFile as A,mkdir as P}from"fs/promises";import{existsSync as D}from"fs";import{text as M,isCancel as b,select as W,multiselect as ee,confirm as z,log as k,spinner as te,intro as B,note as V,cancel as ne,outro as L}from"@clack/prompts";import Ee from"node-fetch";import ue from"fast-glob";import{execa as Ie}from"execa";import{detect as De}from"@antfu/ni";import se from"node:fs/promises";const oe=l.enum(["react","astro","nunjucks"]),pe=l.object({path:l.string(),type:l.string()}),Ne=pe.extend({framework:oe}),Pe=l.object({name:l.string(),type:l.string(),description:l.string().optional(),frameworks:l.array(l.string()).optional(),files:l.array(l.union([Ne,pe])),tokens:l.record(l.object({type:l.string(),mapping:l.string()})).optional(),dependencies:l.record(oe,l.array(l.string())).optional(),registryDependencies:l.record(oe,l.array(l.string())).optional(),tokenDependencies:l.array(l.string()).optional()}),Te=l.array(Pe),Re=l.object({content:l.string()}),re=l.object({tokens:l.union([l.object({source:l.array(l.string()),type:l.enum(["starter-kit","custom"]),themes:l.record(l.array(l.string())).optional()}),l.record(l.string(),l.object({source:l.array(l.string()),type:l.enum(["starter-kit","custom"]),themes:l.record(l.array(l.string())).optional()}))]),options:l.object({fluid:l.object({min:l.number(),max:l.number()}).optional(),prefix:l.string().optional(),color:l.enum(["hex","rgb","rgba","hsl","hsla","oklch","p3"]).optional()}).optional(),output:l.object({directories:l.object({tokens:l.string(),components:l.string().optional(),css:l.string()}),css:l.object({separate:l.boolean(),manageIndex:l.boolean().optional(),format:l.enum(["css","scss","less"]).optional()})})}),v={error:g.red,warn:g.yellow,info:g.cyan,success:g.green,bold:g.bold,path:g.cyan},S={error(...e){console.log(v.error(e.join(" ")))},warn(...e){console.log(v.warn(e.join(" ")))},info(...e){console.log(v.info(e.join(" ")))},success(...e){console.log(v.success(e.join(" ")))},log(...e){console.log(e.join(" "))},break(){console.log("")}};class d extends Error{static{p(this,"CLIError")}constructor(n,t){super(n),this.name="CLIError",this.cause=t}}async function G(e,n={type:"files",paths:e}){const{trees:t,resolved:o,errors:s}=await Ce(e,{loader:n});if(s.load.length>0)throw new d(`Failed to load token files:
3
+ ${s.load.map(i=>`${i.file}: ${i.message}`).join(`
4
+ `)}`);if(s.validation.length>0||s.flatten.length>0||s.resolution.length>0){S.break();const i=[...s.flatten,...s.validation,...s.resolution],c=new Map;throw i.forEach(a=>{if(a.source?.sourcePath){const r=c.get(a.source.sourcePath)||[];r.push(a.message),c.set(a.source.sourcePath,r)}}),c.forEach((a,r)=>{S.error(`Error(s) in ${v.path(r)}:`),a.forEach(u=>{S.error(` - ${u}`)}),S.break()}),new d(`Token validation failed. See ${v.info("https://docs.sugarcube.sh/w3c-token-format")} for help`)}return{trees:t,resolved:o}}p(G,"validateTokens");async function fe(e="./src/styles"){const n=await M({message:"Save CSS to",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return b(n)&&process.exit(0),n}p(fe,"promptCSSOutputDirectory");async function Oe(e="./src/design-tokens"){const n=await M({message:"Save tokens to",placeholder:e,validate:p(t=>{if(!t.trim())return"Please provide a path"},"validate")});return b(n)&&process.exit(0),n}p(Oe,"promptTokensDirectory");async function Ae(){const e=await W({message:"Which kit?",options:[{label:"Responsive",value:"starter-fluid",hint:"Tokens with fluid typography and spacing (recommended)"},{label:"Static",value:"starter-static",hint:"Tokens with fixed typography and spacing"}]});return b(e)&&process.exit(0),e}p(Ae,"promptStarterKit");async function me(e="./src/components"){const n=await M({message:"Save components to",placeholder:e,validate:p(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return b(n)&&process.exit(0),n}p(me,"promptComponentDirectory");async function de(e=!0){const n=[{label:"React",value:"react",hint:".tsx"},{label:"Astro",value:"astro",hint:".astro"},{label:"Nunjucks",value:"nunjucks",hint:".njk"}];e&&n.push({label:"Skip",value:"skip",hint:"continue without components"});const t=await W({message:"Build with",options:n});return b(t)&&process.exit(0),t}p(de,"promptComponentFramework");async function ge(e,n){const t=e.filter(s=>s.type==="component"&&s.frameworks?.includes(n)),o=await ee({message:"Select components to add",options:t.map(s=>({label:s.name,value:s.name,hint:s.description})),required:!0});return b(o)&&process.exit(0),o}p(ge,"promptComponentSelectionFiltered");async function Me(){const n=await W({message:"Set up tokens with",options:[{label:"Starter kit",value:"starter"},{label:"Own tokens",value:"existing"}]});return b(n)&&process.exit(0),n}p(Me,"promptTokenSetup");function Le(e,n,t,o){return e.output=e.output||{},e.output.directories={tokens:h.relative(process.cwd(),t),css:h.relative(process.cwd(),h.resolve(process.cwd(),n))},e.tokens||(e.tokens={type:"starter-kit",source:[]}),e}p(Le,"updateConfigWithTokenDirs");async function K(e,n=!1){const t=await z({message:e,initialValue:n});return(!t||b(t))&&process.exit(0),!0}p(K,"confirmOverwrite");async function Ue(){const e=await z({message:"Install CUBE CSS?",active:"Yes",inactive:"No",initialValue:!0});return b(e)&&process.exit(0),e}p(Ue,"promptInstallCube");async function We(){const e=await M({message:"Path to tokens?",placeholder:"./src/design-tokens",validate:p(n=>{if(n.trim().replace(/['"]/g,"").length===0)return"Path cannot be empty"},"validate")});return b(e)&&process.exit(0),e}p(We,"promptExistingTokensPath");async function ze(){const e=await z({message:"Generate separate CSS file for each token file?"});return b(e)&&process.exit(0),e}p(ze,"promptSeparateCSSFiles");async function Be(){const e=await W({message:"How would you like to organize your tokens?",options:[{value:"simple",label:"Simple (all tokens in one place)"},{value:"collections",label:"Collections (group related tokens together)"}]});return b(e)&&process.exit(0),e}p(Be,"promptCollectionOrganization");async function he(e,n=!1){if(e.length===0)return null;const t=await ee({message:`Select files for your ${n?"first":"next"} collection (including any theme files)`,options:e.map(o=>({label:o,value:o}))});return b(t)&&process.exit(0),t.length===0?null:t}p(he,"promptFilesForCollection");async function ie(e){const n=e.map(o=>h.dirname(o).split(h.sep)).reduce((o,s)=>o?o.filter((i,c)=>i===s[c]):s).pop(),t=await M({message:"Name this collection:",placeholder:n||"collection"});return b(t)&&process.exit(0),t}p(ie,"promptCollectionName");async function Ge(e){if(e.length<=1)return null;const n=await z({message:"Are any of these theme files?",initialValue:!1});if(b(n)&&process.exit(0),!n)return null;const t=[];let o=[...e];for(;o.length>0;){const s=await ee({message:t.length===0?"Select files for first theme:":"Select files for another theme (or Enter to finish):",options:o.map(c=>({label:c,value:c}))});if(b(s)&&process.exit(0),s.length===0)break;const i=await M({message:"Name this theme:",placeholder:"light"});if(b(i)&&process.exit(0),t.push({name:i,files:s}),o=o.filter(c=>!s.includes(c)),o.length>0){const c=await z({message:"Create another theme?",initialValue:!0});if(b(c)&&process.exit(0),!c)break}}return t.length>0?t:null}p(Ge,"promptIdentifyThemeFiles");async function we(e,n,t){const{output:o}=await Fe(e,n,t),s=o.map(i=>i.name).filter(i=>D(i));if(s.length>0){const i=s.map(c=>` - ${c}`).join(`
5
+ `);k.warn(g.yellow(`The following files will be regenerated:
6
+ ${g.dim(i)}`)),await K("Continue?",!0)}for(const i of o)try{await X.mkdir(h.dirname(i.name),{recursive:!0}),await X.writeFile(i.name,i.content,"utf-8")}catch(c){const a=c instanceof Error?`: ${c.message}`:"";throw new d(`Failed to write CSS file ${i.name}${a}`)}return o}p(we,"generateAndWriteCSSVars");function Y(e){const t=[...e.config?[e.config]:[],...e.tokens||[],...e.generated].map(s=>h.relative(process.cwd(),s)),o=te();o.start(`Generating ${t.length} files:`),o.stop(`Generated ${t.length} files:`),t.forEach(s=>{S.log(` - ${s}`)}),S.break()}p(Y,"showSummary");const ye=process.env.REGISTRY_URL??"https://registry.sugarcube.sh/registry";async function ke(e){try{const n=await Ee(e);if(!n.ok){if(n.status===401)throw new d(`Registry access denied: Authentication required
7
+ URL: ${v.info(e)}`);if(n.status===403)throw new d(`Registry access denied: Invalid or missing token
8
+ URL: ${v.info(e)}`);if(n.status===404)throw new d(`Registry resource not found
9
+ URL: ${v.info(e)}`);const t=await n.json().catch(()=>null),o=t&&typeof t=="object"&&"error"in t?t.error:n.statusText;throw new d(`Registry request failed: ${o}
10
+ URL: ${v.info(e)}`)}return n.json()}catch(n){throw n instanceof d?n:new d(`Failed to connect to registry
11
+ URL: ${v.info(e)}
12
+ Check your internet connection`)}}p(ke,"fetchRegistry");async function U(){const e=`${ye}/index.json`,n=await ke(e);try{return Te.parse(n)}catch{throw new d(`Invalid registry data received
13
+ URL: ${v.info(e)}`)}}p(U,"getRegistryIndex");async function Z({type:e,name:n,framework:t}){const o=await U(),s=o.find(a=>a.type===e&&a.name===n);if(!s){const a=o.filter(r=>r.type===e).map(r=>r.name);throw new d(`${e} '${v.info(n)}' not found in registry
14
+ Available ${e}s: ${a.join(", ")}`)}let i=s.files;e==="component"&&t&&(i=s.files.filter(a=>"framework"in a&&a.framework===t));const c=await Promise.all(i.map(async a=>{const r=`${ye}/${a.path}.json`,u=await ke(r);try{const f=Re.parse(u);return{path:a.path,type:a.type,framework:"framework"in a?a.framework:void 0,content:f.content}}catch{throw new d(`Invalid file content received
15
+ File: ${v.info(a.path)}`)}}));return{item:s,files:c.filter(Boolean)}}p(Z,"getRegistryFiles");async function _e(e,n,t){const o=[],s=new Set;async function i(c){if(s.has(c))return;s.add(c);const a=e.find(u=>u.name===c);if(!a){const u=e.filter(f=>f.type==="component").map(f=>f.name).join(", ");throw new d(`Component '${c}' not found in registry
16
+ Available components: ${u}`)}const r=a.registryDependencies?.[t]||[];for(const u of r)await i(u);o.push(a)}p(i,"resolveComponent");for(const c of n)await i(c);return o}p(_e,"resolveTree");const Je=p(async(e,n,t)=>{const o=await Z({type:"tokens",name:e});if(!o.files[0])throw new d(`Starter kit '${e}' does not contain any token files`);const s=h.resolve(process.cwd(),t),i=`${e}.json`,c=h.join(s,i),a={tokens:{source:[h.relative(process.cwd(),c)],type:"starter-kit"},options:e==="starter-fluid"?{fluid:{min:320,max:1200}}:void 0,output:{directories:{tokens:h.relative(process.cwd(),s),css:n},css:{separate:!1,manageIndex:!0}}};try{const r=re.parse(a),{trees:u,resolved:f}=await G(r,{type:"memory",data:{[c]:{collection:"default",content:o.files[0].content}}});return{config:r,tokens:[],trees:u,resolved:f,tokenContent:o.files[0].content,tokenPath:c,tokensDir:s}}catch(r){throw r instanceof l.ZodError?new d(`Invalid starter kit configuration: ${r.message}`):r}},"initializeFromStarterKit"),qe=p(async e=>{const n=await We(),t=await ue("**/*.json",{cwd:n,absolute:!0});if(t.length===0)throw new d(`No JSON files found in ${n}`);const o=[];for(const m of t)try{const w=await le(m,"utf-8");JSON.parse(w),o.push(m)}catch{k.warn(g.yellow(`Skipping invalid JSON file: ${m}`));continue}if(o.length===0)throw new d(`No valid JSON files found in ${n}
17
+ Ensure files contain valid JSON`);const s=o.map(m=>h.relative(process.cwd(),m)),i=h.resolve(process.cwd(),n),c=await fe();let a=await Be();const r=new Map;s.forEach(m=>{const w=h.basename(m),y=r.get(w)||[];r.set(w,[...y,m])});const u=Array.from(r.entries()).filter(([m,w])=>w.length>1).map(([m,w])=>({name:m,files:w}));if(a==="simple"&&u.length>0){const m=u.map(({name:w,files:y})=>{const $=y.map(x=>` - ${x}`).join(`
18
+ `);return`
19
+ ${w} appears in:
20
+ ${g.dim($)}`}).join(`
21
+ `);k.warn(g.yellow(`Simple organization cannot have duplicate filenames:
22
+ ${m}`)),k.message(g.cyan("Switching to collections to help organize these files...")),a="collections"}if(a==="simple"){const m={type:"custom",source:s};e.tokens=m,k.message(g.cyan("Token organization complete"))}else{const m={},w=new Set;let y=[...s],$=!0;for(;y.length>0;){let x=await he(y,$);if(!x){k.message(g.cyan("No files selected, skipping collection creation..."));break}let j=await ie(x);if(!j){k.message(g.cyan("No collection name provided, skipping collection creation..."));break}let R=!1;for(;!R;)try{if(w.has(j)){k.warn(g.yellow(`Collection name "${j}" is already in use.`));const F=await ie(x);if(!F){k.message(g.cyan("No collection name provided, skipping collection creation..."));break}j=F;continue}const C={type:"custom",source:x};m[j]=C;const N=await Ge(x);if(N&&N.length>0){const F=N.flatMap(I=>I.files),E=new Set;let T=!1;for(const I of N){if(E.has(I.name)){k.warn(g.yellow(`Theme name "${I.name}" is already used in this collection.`)),k.message(g.cyan("Starting theme selection over to avoid conflicts...")),T=!0;break}E.add(I.name)}if(T)continue;C.source=x.filter(I=>!F.includes(I)),C.themes=N.reduce((I,q)=>({...I,[q.name]:q.files}),{})}const Q=new Map;C.source.forEach(F=>{const E=h.basename(F),T=Q.get(E)||[];Q.set(E,[...T,F])});const ce=Array.from(Q.entries()).filter(([F,E])=>E.length>1).map(([F,E])=>({name:F,files:E}));if(ce.length>0){const F=ce.map(({name:I,files:q})=>{const xe=q.map($e=>` - ${$e}`).join(`
23
+ `);return`
24
+ ${I} appears in:
25
+ ${g.dim(xe)}`}).join(`
26
+ `);k.warn(g.yellow(`Found duplicate filenames in collection "${j}":
27
+ ${F}`)),k.message(g.cyan("Please either split these files into different collections or mark some as theme files."));const E=await he(y,$);if(!E){k.message(g.cyan("No files selected, skipping collection creation..."));break}x=E;const T=await ie(x);if(!T){k.message(g.cyan("No collection name provided, skipping collection creation..."));break}j=T;continue}R=!0,w.add(j)}catch(C){if(C instanceof d){k.error(`${C.message}`),k.message(g.cyan("Let's try again..."));continue}throw C}if(!R){k.message(g.cyan("Skipping collection creation..."));break}y=y.filter(C=>!x.includes(C)),y.length>0&&k.message(g.cyan(`Remaining files to organize: ${y.length}`)),$=!1}y.length>0?k.warn(g.yellow(`Warning: ${y.length} files were not assigned to any collection`)):k.message(g.cyan("Token organization complete")),e.tokens=m}const f=s.length>1?await ze():!1;e.output={directories:{tokens:h.relative(process.cwd(),i),css:c},css:{separate:f,manageIndex:!0}},e.options={fluid:{min:320,max:1200}};try{if(typeof e.tokens=="object"&&!Array.isArray(e.tokens)){const $=e.tokens,x=new Set;for(const[j,R]of Object.entries($)){if(x.has(j))throw new d(`Configuration Error: Duplicate collection name "${j}". Collection names must be unique.`);if(x.add(j),R.themes){const C=new Set;for(const N of Object.keys(R.themes)){if(C.has(N))throw new d(`Configuration Error: Duplicate theme name "${N}" in collection "${j}". Theme names must be unique within a collection.`);C.add(N)}}}}const m=re.parse(e),{trees:w,resolved:y}=await G(m);return{config:m,tokens:s,trees:w,resolved:y,tokenPath:n,tokensDir:i,tokenContent:void 0}}catch(m){throw m instanceof l.ZodError?new d(`Invalid configuration: ${m.message}`):m}},"initializeFromExistingTokens");async function Ve(){if(D("sugarcube.config.json")){const e=await W({message:"A sugarcube.config.json already exists in this project",options:[{label:"Overwrite existing config (start from scratch)",value:"overwrite"},{label:"Cancel initialization",value:"cancel"}]});return b(e)&&process.exit(0),{shouldProceed:e==="overwrite"}}return{shouldProceed:!0}}p(Ve,"preflightInit");async function _(){let e;try{e=await X.readFile("sugarcube.config.json","utf-8")}catch{throw new d("Cannot read config file - check file permissions")}let n;try{n=JSON.parse(e)}catch{throw new d("Invalid JSON in config file - check for syntax errors")}const t=re.safeParse(n);if(!t.success){const o=t.error.errors.map(s=>{const i=s.path.join(".");return i?`${i}: ${s.message}`:s.message}).join(`
28
+ `);throw new d(`Invalid configuration:
29
+ ${o}
30
+ See ${v.info("https://docs.sugarcube.sh/configuration")} for help.`)}if(typeof t.data.tokens=="object")for(const[o,s]of Object.entries(t.data.tokens)){if(!s.source?.length)continue;const i=new Map;s.source.forEach(a=>{const r=h.basename(a),u=i.get(r)||[];i.set(r,[...u,a])});const c=Array.from(i.entries()).filter(([a,r])=>r.length>1).map(([a,r])=>({name:a,files:r}));if(c.length>0){const a=c.map(({name:r,files:u})=>{const f=u.map(m=>` - ${m}`).join(`
31
+ `);return`
32
+ ${r} appears in:
33
+ ${f}`}).join(`
34
+ `);throw new d(`Duplicate filenames found in collection "${o}":
35
+ ${a}
3
36
 
4
37
  To fix this:
5
- 1. Run 'make-sugarcube init' to generate a valid config, or
6
- 2. Update your config to specify individual files and run 'make-sugarcube generate' again.`),fluid:u.object({viewports:u.object({min:u.object({value:u.number().min(1),unit:u.enum(["px","rem"])}),max:u.object({value:u.number().min(2),unit:u.enum(["px","rem"])})}).refine(({min:e,max:t})=>t.value>e.value,"Max viewport must be greater than min viewport")}).optional(),collections:u.array(u.object({name:u.string().min(1),modes:u.array(u.object({name:u.string().min(1),files:u.array(u.string().min(1))})).min(1)})).optional(),output:u.object({directory:u.string().min(1,"Output directory cannot be empty"),separate:u.boolean().default(!1)})});async function ke(e){const t={$schema:"https://sugarcube.style/schema.json",...e},i=JSON.stringify(t,null,2);await C("sugarcube.config.json",i)}f(ke,"writeConfig");function S(e,t){return ne({text:e,isSilent:t?.silent})}f(S,"spinner");async function V(e){const t=K.parse(e),i=S("Writing sugarcube.config.json").start();return await ke(t),i.succeed(),t}f(V,"validateAndWriteConfig");const b={error:ae,warn:ce,info:le,success:ue,bold:de},s={error(...e){console.log(b.error(e.join(" ")))},warn(...e){console.log(b.warn(e.join(" ")))},info(...e){console.log(b.info(e.join(" ")))},success(...e){console.log(b.success(e.join(" ")))},log(...e){console.log(e.join(" "))},break(){console.log("")}};class E extends Error{static{f(this,"ValidationError")}constructor(t){super(t),this.name="ValidationError"}}class H extends Error{static{f(this,"NetworkError")}constructor(t){super(t),this.name="NetworkError"}}class Z extends Error{static{f(this,"FileSystemError")}constructor(t){super(t),this.name="FileSystemError"}}class be extends Error{static{f(this,"ConfigurationError")}constructor(t){super(t),this.name="ConfigurationError"}}function D(e){if(e instanceof E&&(s.break(),s.error(e.message),s.break(),process.exit(0)),s.break(),s.error("Something went wrong. Please check the details below:"),s.error("If the problem persists, please open an issue on GitHub."),s.break(),e instanceof H)s.error("Network Error:"),s.error(` ${e.message}`),s.error(`
7
- Please check your internet connection and try again.`);else if(e instanceof Z)s.error("File System Error:"),s.error(` ${e.message}`),s.error(`
8
- Please check file permissions and try again.`);else if(e instanceof be)s.error("Configuration Error:"),s.error(` ${e.message}`),s.error(`
9
- Please check your configuration and try again.`);else if(e instanceof u.ZodError){s.error("Validation failed:");for(const[t,i]of Object.entries(e.flatten().fieldErrors))s.error(` - ${b.info(t)}: ${i}`)}else s.error("An unexpected error occurred:"),s.error(` ${e instanceof Error?e.message:String(e)}`);s.break(),process.exit(0)}f(D,"handleError");async function P(e){const t=S("Validating tokens").start(),{trees:i,resolved:o,errors:n}=await ie(e);if(n.load.length>0)throw t.fail(),s.break(),new E(n.load[0]?.message??"Failed to load tokens");if(n.validation.length>0)throw t.fail(),s.break(),Object.keys(e.files).forEach(c=>{const d=g.basename(c,".json"),l={flatten:n.flatten.filter(a=>a.source?.file===d),validation:n.validation.filter(a=>a.source?.file===d),resolution:n.resolution.filter(a=>a.source?.file===d)};Object.values(l).some(a=>a.length>0)&&(s.error(`Error(s) found in ${b.info(`${d}.json`)}:`),l.flatten.length>0&&(s.error(" Structure errors:"),l.flatten.forEach(a=>{s.error(` - ${a.message}`)})),l.validation.length>0&&(s.error(" Validation errors:"),l.validation.forEach(a=>{s.error(` - ${a.message}`)})),l.resolution.length>0&&(s.error(" Reference errors:"),l.resolution.forEach(a=>{s.error(` - ${a.message}`)})))}),s.break(),new E("Please fix all validation errors before continuing. For information on valid W3C token formats, visit "+b.info("https://tr.designtokens.org/format"));return t.succeed(),{trees:i,resolved:o}}f(P,"validateTokens");async function L(e,t,i){const{output:o,errors:n}=await re(e,t,i);if(n.generation.length>0)throw n.generation.forEach(d=>{s.error(` ${d.message}`)}),new E("Failed to generate CSS files");const c=o.map(d=>d.name).filter(d=>j(d));if(c.length>0){const d=c.map(a=>` - ${a}`).join(`
10
- `);s.warn(`The following CSS files will be overwritten:
11
- ${d}`),(await w({type:"confirm",name:"overwrite",message:"Do you want to continue?",initial:!1})).overwrite||(s.log("CSS generation cancelled"),process.exit(0))}for(const d of o)try{await T.mkdir(g.dirname(d.name),{recursive:!0}),await T.writeFile(d.name,d.content,"utf-8")}catch{throw new Z(`Failed to write file: ${d.name}`)}return o}f(L,"generateAndWriteCSS");function I(e){const i=[...e.config?[e.config]:[],...e.tokens||[],...e.generated].map(n=>g.relative(process.cwd(),n));S(`Created ${i.length} files:`).start().succeed(),i.forEach(n=>{s.log(` - ${n}`)}),s.break()}f(I,"showSummary");const Se=["design-tokens","tokens"];function Q(e){return typeof e!="object"||e===null?!1:"$type"in e?!0:Object.values(e).some(t=>typeof t=="object"&&t!==null&&Q(t))}f(Q,"hasTypeProperty");function X(e){return typeof e!="object"||e===null?!1:e.$type==="fluidDimension"?!0:Object.values(e).some(t=>typeof t=="object"&&t!==null&&X(t))}f(X,"hasFluidDimensionTokens");async function M(e){const t=[],i=await se(e,{withFileTypes:!0});for(const o of i){const n=_(e,o.name);if(o.isDirectory())t.push(...await M(n));else if(o.isFile()&&o.name.endsWith(".json"))try{const c=await B(n,"utf-8"),d=JSON.parse(c);Q(d)&&t.push({path:n,hasFluidDimensionTokens:X(d)})}catch{continue}}return t}f(M,"findTokenFiles");async function xe(){const e=process.cwd();for(const i of Se){const o=_(e,i);if(j(o)){const n=await M(o);if(n.length>0)return{rootDir:i,files:n.map(c=>"./"+c.path.replace(e,"").replace(/^\//,"")),hasFluidDimensionTokens:n.some(c=>c.hasFluidDimensionTokens)}}}const t=await M(e);if(t.length>0){const i=t.map(c=>J(c.path)),o=i[0];return o?{rootDir:i.reduce((c,d)=>{for(;!d.startsWith(c);)c=J(c);return c},o).replace(e,".").replace(/^\.\//,""),files:t.map(c=>"./"+c.path.replace(e,"").replace(/^\//,"")),hasFluidDimensionTokens:t.some(c=>c.hasFluidDimensionTokens)}:null}return null}f(xe,"detectTokens");const ee=process.env.REGISTRY_URL??"https://sugarcube-registry.mark-tomlinson3.workers.dev/registry";async function te(e){const t=await pe(e);if(!t.ok){const i={400:"Bad request",401:"Unauthorized",403:"Forbidden",404:"Not found",500:"Internal server error"};if(t.status===401)throw new Error(`You are not authorized to access the registry at ${b.info(e)}.
12
- You may need to authenticate.`);if(t.status===404)throw new Error(`The requested resource at ${b.info(e)} was not found.
13
- Please check the path and try again.`);if(t.status===403)throw new Error(`You do not have access to the registry at ${b.info(e)}.
14
- You may need to authenticate or provide a token.`);const o=await t.json().catch(()=>null),n=o&&typeof o=="object"&&"error"in o?o.error:t.statusText||i[t.status];throw new Error(`Failed to fetch from ${b.info(e)}.
15
- ${n}`)}return t.json()}f(te,"fetchRegistry");async function R(){const e=`${ee}/index.json`,t=await te(e);return ye.parse(t)}f(R,"getRegistryIndex");async function O({type:e,name:t,framework:i}){const o=await R(),n=o.find(l=>l.type===e&&l.name===t);if(!n){const l=o.filter(a=>a.type===e).map(a=>a.name);throw new Error(`${e} '${b.info(t)}' not found in registry.
16
- Available ${e}s: ${l.join(", ")}`)}let c=n.files;e==="component"&&i&&(c=n.files.filter(l=>"framework"in l&&l.framework===i));const d=await Promise.all(c.map(async l=>{const a=`${ee}/${l.path}.json`,p=await te(a),r=ve.parse(p);return{path:l.path,type:l.type,framework:"framework"in l?l.framework:void 0,content:r.content}}));return{item:n,files:d.filter(Boolean)}}f(O,"getRegistryFiles");async function je(e,t,i){const o=[],n=new Set;async function c(d){if(n.has(d))return;n.add(d);const l=e.find(p=>p.name===d);if(!l)throw new Error(`Component "${d}" not found in registry`);const a=l.registryDependencies?.[i]||[];for(const p of a)await c(p);o.push(l)}f(c,"resolveComponent");for(const d of t)await c(d);return o}f(je,"resolveTree");const Ee=f(async()=>{(await w({type:"toggle",name:"useStarter",message:"No tokens found. Use a sugarcube starter kit?",initial:!0,active:"Yes",inactive:"No"})).useStarter||process.exit(0);const t=await w({type:"select",name:"kit",message:"Which starter kit would you like to use?",choices:[{title:"Fluid",description:"Design tokens with fluid scaling (recommended)",value:"starter-fluid"},{title:"Static",description:"Static tokens",value:"starter-static"}]});t.kit===void 0&&process.exit(0);const i=S("Setting up starter kit").start();try{const o=await O({type:"tokens",name:t.kit});if(!o?.files[0])throw new H(`Failed to load starter kit: ${t.kit}`);const n="design-tokens",c=`${t.kit}.json`,d=g.join(n,c);j(d)&&((await w({type:"confirm",name:"overwrite",message:`Token file ${c} already exists. Overwrite?`,initial:!1})).overwrite||(s.log("Setup cancelled"),process.exit(0))),await T.mkdir(n,{recursive:!0}),await T.writeFile(d,o.files[0].content),i.succeed();const l=await w({type:"text",name:"directory",message:"Where should your CSS variables be written?",initial:"./src/styles",validate:f(m=>m.trim().replace(/['"]/g,"").length>0||"Output directory cannot be empty","validate")});l.directory===void 0&&(s.break(),s.log("Operation cancelled. The following files were created:"),s.log(` - ${c}`),s.break(),process.exit(0));const a={files:{[t.kit]:d},fluid:t.kit==="starter-fluid"?{viewports:{min:{value:320,unit:"px"},max:{value:1200,unit:"px"}}}:void 0,output:{directory:l.directory,separate:!1}},p=await V(a),{trees:r,resolved:h}=await P(p),y=await L(r,h,p);I({config:"sugarcube.config.json",tokens:[c],generated:y.map(m=>m.name)}),s.log("Setup complete! \u{1F389}\u{1F36D}")}catch(o){throw i.fail(),o}},"initializeFromStarterKit"),Fe=f(async(e,t)=>{t.files.forEach(r=>{e.files[g.basename(r,".json")]=r});const i=t.files.length;if(S(`Found ${i} token ${i===1?"file":"files"}:`).start().succeed(),t.files.forEach(r=>{s.log(` - ${r}`)}),t.hasFluidDimensionTokens){s.log("Detected fluid scaling. Please set your viewport range:");const r=await w([{type:"number",name:"minValue",message:"Min viewport width (px)",initial:320,validate:f(h=>h>0||"Viewport width must be greater than 0","validate")},{type:"number",name:"maxValue",message:"Max viewport width (px)",initial:1200,validate:f(h=>h>0||"Viewport width must be greater than 0","validate")}]);(r.minValue===void 0||r.maxValue===void 0)&&process.exit(0),e.fluid={viewports:{min:{value:r.minValue,unit:"px"},max:{value:r.maxValue,unit:"px"}}}}const n=await w({name:"useThemes",message:"Would you like to add themes?",initial:!1,type:"toggle",active:"Yes",inactive:"No"});if(n.useThemes===void 0&&process.exit(0),n.useThemes){const r=await w({type:"text",name:"names",message:"What are your themes called?",initial:"dark, jazzy, compact",validate:f(m=>{const v=m.trim().replace(/['"]/g,"");return v?v.split(",").map(x=>x.trim().replace(/['"]/g,"")).every(x=>x.length>0)||"Please provide valid theme names (e.g. dark, light)":"Theme names cannot be empty"},"validate")});n.useThemes===void 0&&process.exit(0);const h=r.names.split(",").map(m=>m.trim()),y=[];for(const m of h){const v=await w({type:"multiselect",name:"files",message:`Select files for "${m}" theme`,hint:"Space to select. A to toggle all. Enter to submit.",instructions:!1,choices:t.files.map(k=>({title:k,value:k,selected:k.toLowerCase().includes(m.toLowerCase())}))});v.files===void 0&&process.exit(0),y.push({name:m,modes:[{name:m,files:v.files.map(k=>g.basename(k,".json"))}]})}e.collections=y}const c=await w({type:"text",name:"directory",message:"Where should we save the CSS files?",initial:"./src/styles",validate:f(r=>r.trim().replace(/['"]/g,"").length>0||"Output directory cannot be empty","validate")});c.directory===void 0&&process.exit(0),e.output={directory:c.directory,separate:Object.keys(e.files).length>1?(await w({type:"toggle",name:"separate",message:"Generate a separate CSS file for each token file?",initial:!1,active:"Yes",inactive:"No"})).separate??!1:!1};const d=await V(e),{trees:l,resolved:a}=await P(d),p=await L(l,a,d);I({config:"sugarcube.config.json",generated:p.map(r=>r.name)}),s.log("Setup complete! \u{1F389}\u{1F36D}")},"initializeFromExistingTokens");async function Ce(){return j("sugarcube.config.json")?{shouldProceed:(await w({type:"select",name:"shouldProceed",message:"A sugarcube.config.json already exists. What would you like to do?",choices:[{title:"Overwrite existing config (start from scratch)",value:"overwrite"},{title:"Cancel initialization",value:"cancel"}]})).shouldProceed==="overwrite"}:{shouldProceed:!0}}f(Ce,"preflightInit");async function z(){try{const e=await T.readFile("sugarcube.config.json","utf-8"),t=JSON.parse(e),i=K.safeParse(t);if(!i.success){const o=i.error.errors.map(n=>` - ${n.path.join(".")}: ${n.message}`).join(`
17
- `);throw new E(`Invalid configuration:
18
- ${o}`)}return i.data}catch(e){throw e instanceof E?e:new E(`Failed to load configuration: ${e instanceof Error?e.message:String(e)}`)}}f(z,"validateAndLoadConfig");const $e=new F().name("init").description("Initialize a new project").action(async()=>{try{(await Ce()).shouldProceed||process.exit(0);const t={files:{}},i=S("Looking for design tokens").start(),o=await xe();i.succeed(),o?await Fe(t,o):await Ee()}catch(e){D(e)}}),De=new F().name("generate").description("Generate CSS from your design tokens").action(async()=>{const e=S("Loading configuration");try{fe.existsSync("sugarcube.config.json")||(s.error("No sugarcube.config.json found. Run 'make-sugarcube init' first."),process.exit(0)),e.start();const t=await z();e.succeed();const{trees:i,resolved:o}=await P(t),n=await L(i,o,t);I({generated:n.map(c=>c.name)})}catch(t){e.fail(),D(t)}}),Pe=new F().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(!e.length){ge.existsSync("sugarcube.config.json")||(s.error("No paths specified and no sugarcube.config.json found."),s.error("Either specify files/directories to validate or run this command in a sugarcube project."),process.exit(0));const o=await z();await P(o);return}const t=await me(e.map(o=>o.endsWith(".json")?o:`${o}/**/*.json`));t.length===0&&(s.error("No JSON files found. Please check that the specified files or directories exist."),s.break(),process.exit(0));const i=t.reduce((o,n)=>({...o,[g.basename(n,".json")]:g.join(".",n)}),{});await P({files:i,output:{directory:"",separate:!1}})}catch(t){D(t)}}),Te=new F().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, nunjucks)").option("-o, --output <path>","Output directory","./components").option("-s, --silent","Suppress logs and prompts").action(async(e,t)=>{const i=[];let o,n,c;try{j("sugarcube.config.json")||(s.error("No sugarcube.config.json found. Please run 'make-sugarcube init' first to set up your project."),process.exit(0));let d,l,a;if(a=await z(),e.length>0){t.framework||(s.error("Framework must be specified in non-interactive mode (--framework)"),process.exit(0));const r=["react","astro","nunjucks"];r.includes(t.framework)||(s.error(`Invalid framework. Must be one of: ${r.join(", ")}`),process.exit(0)),n=await R(),n||(s.error("Failed to fetch component list"),process.exit(0)),c=t.framework,d=e,l=g.resolve(process.cwd(),t.output)}else{const r=await w({type:"select",name:"type",message:"Which framework are you using?",choices:[{title:"React",value:"react",description:"React components with TypeScript"},{title:"Astro",value:"astro",description:"Astro components with TypeScript"},{title:"Nunjucks",value:"nunjucks",description:"Nunjucks templates"}]});r.type===void 0&&(s.break(),process.exit(0)),n=await R(),n||(s.error("Failed to fetch component list"),process.exit(0));const h=await w({type:"multiselect",name:"list",message:"Select components to add:",hint:"Space to select. A to toggle all. Enter to submit.",instructions:!1,min:1,choices:n.filter(m=>m.type==="component").map(m=>({title:m.name,value:m.name,description:m.description}))});h.list===void 0&&(s.break(),process.exit(0));const y=await w({type:"text",name:"path",message:"Where should we add the components?",initial:"./components",validate:f(m=>m?!0:"Please provide a path","validate")});y.path===void 0&&(s.break(),process.exit(0)),c=r.type,d=h.list,l=g.resolve(process.cwd(),y.path)}await $(l,{recursive:!0}),o=S("Resolving dependencies.",{silent:t.silent})?.start();const p=await je(n,d,c);o?.succeed("Dependencies resolved"),o=S("Adding components.",{silent:t.silent})?.start();for(const r of p){const h=await O({type:"component",name:r.name,framework:c});if(!h)return o?.fail(),D(new Error(`Failed to fetch component: ${r}`));const y=g.join(l,r.name);if(j(y)){if(o?.stop(),!(await w({type:"confirm",name:"value",message:`Component '${r.name}' already exists. Overwrite?`,initial:!1})).value){s.log(`Skipping ${r.name}`),o?.start();continue}o?.start()}await $(y,{recursive:!0});for(const v of h.files){if(!v)continue;const k=g.join(y,g.basename(v.path));await C(k,v.content),i.push(k)}const m=await O({type:"global-styles",name:r.name});if(m?.files.length){const v=g.resolve(process.cwd(),"styles/global");await $(v,{recursive:!0});const k=g.join(v,"global-styles.css");let x="";try{x=await B(k,"utf8")}catch{}if(!x.includes(`Added by sugarcube ${r.name} component`)){const N=m.files.map(W=>`
19
- /* Added by sugarcube ${r.name} component */
20
- ${W.content}
21
- `).join(`
22
- `);await C(k,x+N),i.push(k)}}if(r.tokenDependencies){const v=Object.values(a.files)[0],k=g.dirname(v||"design-tokens");for(const W of r.tokenDependencies){const Y=await O({type:"tokens",name:W.replace(".json","")});if(Y)for(const G of Y.files){const A=g.join(k,g.basename(G.path));await $(g.dirname(A),{recursive:!0}),await C(A,G.content),i.push(A),a.files[W.replace(".json","")]=A}}await V(a);const{trees:x,resolved:N}=await P(a);await L(x,N,a)}}if(o?.succeed(),!t.silent&&i.length>0){s.break(),I({generated:i}),s.log("Success! Components added successfully.");const r=new Set;p.forEach(h=>{(h.dependencies?.[c]||[]).forEach(m=>r.add(m))}),r.size>0&&(s.break(),s.log("Don't forget to install required dependencies:"),s.log(` ${b.info(Array.from(r).join(" "))}`)),s.break()}}catch(d){D(d),process.exit(0)}});async function Ie(e,t){const i=e.filter(a=>a.endsWith(".css")),o={global:[],compositions:[],utilities:[]};i.forEach(a=>{const p=g.relative(t,a).replace(/\\/g,"/");p.startsWith("global/")?o.global.push(p):p.startsWith("compositions/")?o.compositions.push(p):p.startsWith("utilities/")&&o.utilities.push(p)});const n=["reset.css","fonts.css","global-styles.css"];o.global.sort((a,p)=>{const r=n.findIndex(y=>a.endsWith(y)),h=n.findIndex(y=>p.endsWith(y));return r-h});const d=[...o.global,...o.compositions,...o.utilities].map(a=>`@import '${a}';`).join(`
23
- `),l=g.join(t,"index.css");return await C(l,d),l}f(Ie,"generateIndexFile");const Re=new F().name("cube").description("Add CUBE CSS modules to your project").argument("[modules...]","Modules to add (e.g., compositions utilities)").option("-o, --output <path>","Output directory","./styles").option("-s, --silent","Suppress logs and prompts").option("-a, --all","Add all CUBE modules").action(async(e,t)=>{const i=[];let o;try{let n,c;if(e.length>0||t.all){const l=await R();if(!l)throw new Error("Failed to fetch module list");if(t.all)n=l.filter(a=>a.type==="cube").map(a=>a.name);else{n=e;const a=n.filter(p=>!l.some(r=>r.name===p&&r.type==="cube"));if(a.length>0)throw new Error(`Invalid modules: ${a.join(", ")}
24
- Available modules: compositions, utilities`)}c=g.resolve(process.cwd(),t.output)}else{o=S("Fetching available modules.").start();const l=await R();if(!l)throw o.fail(),new Error("Failed to fetch module list");o.succeed();const a=await w({type:"multiselect",name:"list",message:"Select CUBE CSS modules to add:",hint:"Space to select. A to toggle all. Enter to submit.",instructions:!1,min:1,choices:l.filter(r=>r.type==="cube").map(r=>({title:r.name,value:r.name,description:r.description}))});a.list===void 0&&(s.break(),process.exit(0));const p=await w({type:"text",name:"path",message:"Where should we add the modules?",initial:"./styles",validate:f(r=>r?!0:"Please provide a path","validate")});p.path===void 0&&(s.break(),process.exit(0)),n=a.list,c=g.resolve(process.cwd(),p.path)}await $(c,{recursive:!0}),o=S("Adding CUBE CSS modules.",{silent:t.silent})?.start();for(const l of n){const a=await O({type:"cube",name:l});if(!a)throw o?.fail(),new Error(`Failed to fetch module: ${l}`);for(const p of a.files){if(!p)continue;const r=p.path.replace(/^styles\//,""),h=g.join(c,r),y=g.dirname(h);if(await $(y,{recursive:!0}),j(h)){if(o?.stop(),!(await w({type:"confirm",name:"value",message:`File '${r}' already exists. Overwrite?`,initial:!1})).value){s.log(`Skipping ${r}`),o?.start();continue}o?.start()}await C(h,p.content),i.push(h)}}o?.succeed();const d=await Ie(i,c);i.push(d),!t.silent&&i.length>0&&(I({generated:i}),s.break(),s.log("Success! CUBE CSS modules added successfully."),s.break())}catch(n){o&&o.fail(),D(n)}});var Oe="@sugarcube-org/cli",Ne="0.0.0-alpha.1",We={access:"restricted"},Ae="A CLI for scaffolding sugarcube applications",Le="UNLICENSED",Ue="Mark Tomlinson",Ve={type:"git",url:"https://github.com/sugarcube-org/sugarcube"},Me={url:"https://github.com/sugarcube-org/sugarcube/issues"},ze=["cli","design-system","components","CUBE CSS","react","sugarcube"],Ye=["dist","README.md","LICENSE.md"],Ge="module",Be={sugarcube:"./dist/index.mjs"},_e={build:"pkgroll --minify",dev:"cross-env REGISTRY_URL=http://localhost:8787/registry tsx src/index.ts",test:"vitest","type-check":"tsc --noEmit",start:"cross-env REGISTRY_URL=https://sugarcube-registry.mark-tomlinson3.workers.dev/registry tsx src/index.ts",prepublishOnly:"pnpm up @sugarcube-org/core --filter @sugarcube-org/cli && pnpm build"},Je={"@clack/prompts":"^0.7.0","@sugarcube-org/core":"workspace:*",commander:"^12.1.0","cross-env":"^7.0.3","fast-glob":"^3.3.2","fs-extra":"^11.2.0",kleur:"^4.1.5","node-fetch":"^3.3.0",ora:"^8.1.1",prompts:"^2.4.2",zod:"^3.21.4"},qe={"@types/fs-extra":"^11.0.4","@types/prompts":"^2.4.9",pkgroll:"^2.5.1",tsx:"^4.19.2"},Ke={name:Oe,version:Ne,publishConfig:We,description:Ae,license:Le,author:Ue,repository:Ve,bugs:Me,keywords:ze,files:Ye,type:Ge,bin:Be,scripts:_e,dependencies:Je,devDependencies:qe};process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function He(){const e=new F().name("sugarcube").description("CLI for scaffolding sugarcube applications").version(Ke.version,"-v, --version","display the version number");e.addCommand($e).addCommand(De).addCommand(Pe).addCommand(Te).addCommand(Re),e.parse()}f(He,"main"),He();
38
+ 1. Use unique filenames for source files
39
+ 2. Or move some files to a different collection
40
+ 3. Or mark some files as theme files
41
+ See ${v.info("https://docs.sugarcube.sh")} to learn more.`)}}return t.data}p(_,"validateAndLoadConfig");async function ae(e){const n={$schema:"https://sugarcube.style/schema.json",...e};try{const t=JSON.stringify(n,null,2);await A("sugarcube.config.json",t)}catch(t){const o=t instanceof Error?`: ${t.message}`:"";throw new d(`Failed to write config file${o}`)}}p(ae,"writeConfig");function Ke(e){const n=e.split("/"),t=n.findIndex(s=>s==="variables");if(t===-1||t===n.length-1)return"default";const o=n[t+1];return!o||o.endsWith(".css")?"default":o}p(Ke,"getCollectionFromPath");function be(e,n){if(e.includes("/variables/")){const t=Ke(e),o=n.variables.get(t)??[];o.push(e),n.variables.set(t,o)}else e.startsWith("global/")?n.global.push(e):e.startsWith("compositions/")?n.compositions.push(e):e.startsWith("utilities/")&&n.utilities.push(e)}p(be,"groupFile");async function Ye(e,n,t="merge"){const o={variables:new Map,global:[],compositions:[],utilities:[]},s=h.join(n,"index.css");t==="merge"&&D(s)&&(await le(s,"utf-8")).split(`
42
+ `).filter(f=>f.trim().startsWith("@import")).map(f=>f.match(/'([^']+)'/)?.[1]).filter(f=>f!==void 0).forEach(f=>be(f,o)),e.filter(r=>r.endsWith(".css")).forEach(r=>{const u=h.relative(n,r).replace(/\\/g,"/");be(u,o)});const i=["reset.css","fonts.css","global-styles.css"];o.global.sort((r,u)=>{const f=i.findIndex(w=>r.endsWith(w)),m=i.findIndex(w=>u.endsWith(w));return f-m});const c=Array.from(o.variables.entries()).sort(([r,u],[f,m])=>r==="default"?-1:f==="default"?1:r.localeCompare(f)).flatMap(([r,u])=>u.sort((f,m)=>f.endsWith("tokens.variables.css")?-1:m.endsWith("tokens.variables.css")?1:f.localeCompare(m))),a=[...new Set([...c,...o.global,...o.compositions.sort(),...o.utilities.sort()])].map(r=>`@import '${r}';`);return await A(s,a.join(`
43
+ `)),s}p(Ye,"generateIndexFile");const Ze=p(()=>{B(g.inverse(" Welcome to sugarcube. The toolkit for seriously sweet front ends "))},"welcome");async function He(e,{withFallback:n=!1}={}){const t=await De({programmatic:!0,cwd:e});if(t==="yarn@berry")return"yarn";if(t==="pnpm@6")return"pnpm";if(t==="bun")return"bun";if(!n)return t??"npm";const o=process.env.npm_config_user_agent||"";return o.startsWith("yarn")?"yarn":o.startsWith("pnpm")?"pnpm":o.startsWith("bun")?"bun":"npm"}p(He,"getPackageManager");async function Qe(e,n){const t=await He(n,{withFallback:!0}),o=t==="npm"?["install",...e]:["add",...e];await Ie(t,o,{cwd:n})}p(Qe,"installDependencies");async function ve({registryIndex:e,selectedComponents:n,componentType:t,componentsOutputDirectory:o,cssOutputDirectory:s}){const i=[],c=new Set;await P(o,{recursive:!0});const a=h.join(s,"global","variables");await P(h.join(s,"global"),{recursive:!0}),await P(a,{recursive:!0});const r=await _e(e,n,t);for(const u of r){const f=await Z({type:"component",name:u.name,framework:t});for(const w of f.files)if(w)try{if(w.path.endsWith(".variables.css")){const y=h.join(a,`${u.name}.variables.css`);await A(y,w.content),i.push(y)}else{const y=h.join(o,u.name);await P(y,{recursive:!0});const $=h.join(y,h.basename(w.path));await A($,w.content),i.push($)}}catch(y){const $=y instanceof Error?`: ${y.message}`:"";throw new d(`Failed to write component file for "${u.name}"${$}`)}(u.dependencies?.[t]||[]).forEach(w=>c.add(w))}if(c.size>0)try{await Qe(Array.from(c),process.cwd())}catch(u){const f=u instanceof Error?`: ${u.message}`:"";throw new d(`Failed to install component dependencies${f}`)}return{createdFiles:i,npmDependencies:c}}p(ve,"installComponents");async function Xe(e){const n=[],t=h.resolve(process.cwd(),e);await P(t,{recursive:!0});const s=(await U()).filter(i=>i.type==="cube").map(i=>i.name);for(const i of s){const c=await Z({type:"cube",name:i});for(const a of c.files){if(!a)continue;const r=a.path.replace(/^styles\//,""),u=h.join(t,r),f=h.dirname(u);try{await P(f,{recursive:!0}),await A(u,a.content),n.push(u)}catch(m){const w=m instanceof Error?`: ${m.message}`:"";throw new d(`Failed to write CUBE module "${i}"${w}`)}}}return n}p(Xe,"installCUBE");function et(e){switch(e){case"react":return"tsx";case"astro":return"astro";case"nunjucks":return"njk";default:return"tsx"}}p(et,"getExtension");function Se({components:e,componentType:n,componentsOutputDirectory:t}){return e.filter(o=>{const s=h.join(t,o,`${o}.${et(n)}`);return D(s)})}p(Se,"checkComponentExists");async function H({mode:e,cssOutputDirectory:n,files:t,config:o}){if(!o.output.css.manageIndex)return;const s=t.filter(a=>a.endsWith(".css"));if(s.length===0)return;const i=h.join(n,"index.css"),c=D(i);if(!(e==="create"&&c&&(k.warn(g.yellow(`The following file will be overwritten:
44
+ ${g.dim(" - index.css")}`)),!await K("Continue?",!1))))try{return await Ye(s,n,e)}catch(a){const r=a instanceof Error?`: ${a.message}`:"";throw new d(`Failed to ${e} CSS index file${r}`)}}p(H,"manageCSSIndex");function J(e){if(S.break(),e instanceof d){const n=e.message.split(`
45
+ `);n.length>1?(S.error(n[0]),S.break(),n.slice(1).forEach(t=>{S.error(` ${t.trim()}`)})):S.error(e.message),process.env.DEBUG&&e.cause&&S.info(`
46
+ Caused by:`,e.cause)}else S.error(`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}
47
+ If this issue persists, please report it: ${g.cyanBright("https://github.com/sugarcube-org/sugarcube/issues")}`),process.env.DEBUG&&S.info(`
48
+ Error details:`,e);S.break(),process.exit(0)}p(J,"handleError");const tt=new O().name("init").description("Initialize a new project").action(async()=>{try{Ze(),(await Ve()).shouldProceed||process.exit(0),V("Step 1. Set up tokens");const n=await Me();let t;switch(n){case"starter":const r=await Ae(),u=await fe(),f=await Oe();try{t=await Je(r,u,f),t.config=Le(t.config||{},u,f,r)}catch(m){ne(`Failed to initialize starter kit: ${m instanceof Error?m.message:"Unknown error"}`),process.exit(1)}break;case"existing":t=await qe({});break}V("Step 2. Add style system");const o=await Ue();V("Step 3. Add components");const s=await de();let i=[];if(s!=="skip"){const r=await U();r||(ne("Failed to fetch component list"),process.exit(1));const u=await ge(r,s),f=await me(),m=await Se({components:u,componentType:s,componentsOutputDirectory:f});if(m.length>0){const w=m.join(", ");await K(`The following components already exist and will be overwritten: ${w}. Continue?`,!1)}if(u.length>0){const w=te();w.start("Installing components...");try{const{createdFiles:y,npmDependencies:$}=await ve({registryIndex:r,selectedComponents:u,componentType:s,componentsOutputDirectory:f,cssOutputDirectory:t.config.output.directories.css});i=y,w.stop("Components installed successfully")}catch(y){throw w.stop("Installation failed"),y}}}V("Step 4. Generating files");try{if(await se.mkdir(t.tokensDir,{recursive:!0}),await se.mkdir(t.config.output.directories.css,{recursive:!0}),n==="starter"){if(!t.tokenContent)throw new d("Failed to generate token content for starter kit");await se.writeFile(t.tokenPath,t.tokenContent)}}catch(r){const u=r instanceof Error?`: ${r.message}`:"";throw new d(`Failed to create project files${u}`)}let c=[];c=await we(t.trees,t.resolved,t.config);let a=[];o&&(a=await Xe(t.config.output.directories.css)),await H({mode:"create",cssOutputDirectory:t.config.output.directories.css,files:[...c.map(r=>r.name),...i.filter(r=>r.endsWith(".css")),...a],config:t.config}),await ae(t.config),Y({config:"sugarcube.config.json",tokens:n==="starter"?t.tokens:[],generated:[...c.map(r=>r.name),...a,...i,...t.config.output.css.manageIndex?["index.css"]:[]]}),L(g.greenBright("Success! Project initialized \u2728"))}catch(e){J(e)}}),nt=new O().name("generate").description("Generate CSS from your design tokens").option("--force","Skip confirmation when deleting stale files").action(async e=>{try{if(B(g.inverse(" Generate CSS variables from your design tokens ")),!D("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");const n=await _(),{trees:t,resolved:o}=await G(n),s=await we(t,o,n);n.output.css.manageIndex&&await H({mode:"merge",cssOutputDirectory:n.output.directories.css,files:s.map(i=>i.name),config:n}),Y({generated:[...s.map(i=>i.name),...n.output.css.manageIndex?["index.css"]:[]]}),L(g.greenBright("Success! CSS variables generated successfully. \u2728"))}catch(n){J(n)}}),st=new O().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(B(g.inverse(" Validate design tokens ")),D("sugarcube.config.json")&&!e.length){const s=await _();await G(s),L(g.greenBright("All tokens are valid \u2728"));return}if(!e.length)throw new d("No paths specified. Please provide files/directories to validate, or run this in a Sugarcube project directory.");const n=e.map(s=>h.normalize(s));for(const s of n)if(!D(s))throw new d(`Path not found: ${s}
49
+ Please check that the specified files or directories exist`);const t=await ue(n.map(s=>s.endsWith(".json")?s:h.join(s,"**/*.json")));if(t.length===0)throw new d(`No JSON files found in the specified paths
50
+ Please ensure the paths contain .json files`);const o=D("sugarcube.config.json")?await _():{output:{directories:{tokens:".",css:"."},css:{separate:!1}}};await G({...o,tokens:{type:"custom",source:t.map(s=>h.relative(process.cwd(),s))}}),L(g.greenBright("All tokens are valid \u2728"))}catch(n){J(n)}}),ot=new O().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, nunjucks)").option("-s, --silent","Suppress logs and prompts").action(async(e,n)=>{try{if(n.silent||B(g.inverse(" Add components ")),!D("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");let t=await _(),o,s=[],i;if(e.length>0){if(!n.framework)throw new d("Framework must be specified in non-interactive mode (--framework)");const r=["react","astro","nunjucks"];if(!r.includes(n.framework))throw new d(`Invalid framework. Must be one of: ${r.join(", ")}`);if(i=n.framework,s=e,!t.output.directories.components)throw new d("Components directory must be configured in non-interactive mode. Please run without arguments first.");o=h.resolve(process.cwd(),t.output.directories.components)}else{i=await de(!1);const r=await U();if(r||(ne("Failed to fetch component list"),process.exit(1)),s=await ge(r,i),t.output.directories.components)o=h.resolve(process.cwd(),t.output.directories.components);else{const u=await me();o=h.resolve(process.cwd(),u),t.output.directories.components=h.relative(process.cwd(),o),await ae(t)}}const c=await Se({components:s,componentType:i,componentsOutputDirectory:o});if(c.length>0&&!n.silent){const r=c.join(", ");await K(`The following components already exist and will be overwritten: ${r}. Continue?`,!1)}let a;n.silent||(a=te(),a.start("Installing components..."));try{const r=await U(),{createdFiles:u,npmDependencies:f}=await ve({registryIndex:r,selectedComponents:s,componentType:i,componentsOutputDirectory:o,cssOutputDirectory:t.output.directories.css});await H({mode:"merge",cssOutputDirectory:t.output.directories.css,files:u,config:t}),n.silent||(a?.stop("Components installed successfully"),Y({generated:u}),L(g.greenBright("Success! Components installed \u2728")))}catch(r){throw n.silent||a?.stop("Installation failed"),r}}catch(t){J(t)}}),rt=new O().name("cube").description("Add CUBE CSS to your project").option("-s, --silent","Suppress logs and prompts").action(async e=>{const n=[];try{if(!D("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");e.silent||B(g.inverse(" Add CUBE CSS "));const t=await _(),o=h.resolve(process.cwd(),t.output.directories.css);try{await P(o,{recursive:!0})}catch(c){const a=c instanceof Error?`: ${c.message}`:"";throw new d(`Failed to create output directory${a}`)}const i=(await U()).filter(c=>c.type==="cube").map(c=>c.name);for(const c of i){const a=await Z({type:"cube",name:c});for(const r of a.files){if(!r)continue;const u=r.path.replace(/^styles\//,""),f=h.join(o,u),m=h.dirname(f);try{await P(m,{recursive:!0}),await A(f,r.content),n.push(f)}catch(w){const y=w instanceof Error?`: ${w.message}`:"";throw new d(`Failed to write CUBE CSS file ${f}${y}`)}}}await H({mode:"merge",cssOutputDirectory:o,files:n,config:t}),await ae(t),!e.silent&&n.length>0&&(Y({generated:n}),L(g.greenBright("Success! CUBE CSS added successfully. \u2728")))}catch(t){J(t)}});var it="@sugarcube-org/cli",at="0.0.0-alpha.2",ct={access:"restricted"},lt="A CLI for scaffolding sugarcube applications",ut="UNLICENSED",pt="Mark Tomlinson",ft={type:"git",url:"https://github.com/sugarcube-org/sugarcube"},mt={url:"https://github.com/sugarcube-org/sugarcube/issues"},dt=["cli","design-system","components","CUBE CSS","react","sugarcube"],gt=["dist","README.md","LICENSE.md"],ht="module",wt={sugarcube:"./dist/index.mjs"},yt={build:"pkgroll --minify",dev:"cross-env REGISTRY_URL=http://localhost:8787/registry tsx src/index.ts",test:"vitest","type-check":"tsc --noEmit",start:"cross-env REGISTRY_URL=https://sugarcube-registry.mark-tomlinson3.workers.dev/registry tsx src/index.ts",prepublishOnly:"pnpm up @sugarcube-org/core --filter @sugarcube-org/cli && pnpm build"},kt={"@antfu/ni":"^23.3.0","@clack/prompts":"^0.9.1","@sugarcube-org/core":"workspace:*",commander:"^12.1.0","cross-env":"^7.0.3",execa:"^9.5.2","fast-glob":"^3.3.2","fs-extra":"^11.2.0","lucide-react":"^0.468.0","node-fetch":"^3.3.0",picocolors:"^1.1.1",prompts:"^2.4.2",zod:"^3.23.8"},bt={"@types/fs-extra":"^11.0.4","@types/prompts":"^2.4.9",pkgroll:"^2.5.1",tsx:"^4.19.2"},vt={name:it,version:at,publishConfig:ct,description:lt,license:ut,author:pt,repository:ft,bugs:mt,keywords:dt,files:gt,type:ht,bin:wt,scripts:yt,dependencies:kt,devDependencies:bt};process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function St(){const e=new O().name("sugarcube").description("CLI for scaffolding sugarcube applications").version(vt.version,"-v, --version","display the version number");e.addCommand(tt).addCommand(nt).addCommand(st).addCommand(ot).addCommand(rt),e.parse()}p(St,"main"),St();
package/package.json CHANGED
@@ -1,61 +1,62 @@
1
1
  {
2
- "name": "@sugarcube-org/cli",
3
- "version": "0.0.0-alpha.1",
4
- "publishConfig": {
5
- "access": "restricted"
6
- },
7
- "description": "A CLI for scaffolding sugarcube applications",
8
- "license": "UNLICENSED",
9
- "author": "Mark Tomlinson",
10
- "repository": {
11
- "type": "git",
12
- "url": "https://github.com/sugarcube-org/sugarcube"
13
- },
14
- "bugs": {
15
- "url": "https://github.com/sugarcube-org/sugarcube/issues"
16
- },
17
- "keywords": [
18
- "cli",
19
- "design-system",
20
- "components",
21
- "CUBE CSS",
22
- "react",
23
- "sugarcube"
24
- ],
25
- "files": [
26
- "dist",
27
- "README.md",
28
- "LICENSE.md"
29
- ],
30
- "type": "module",
31
- "bin": {
32
- "sugarcube": "./dist/index.mjs"
33
- },
34
- "scripts": {
35
- "build": "pkgroll --minify",
36
- "dev": "cross-env REGISTRY_URL=http://localhost:8787/registry tsx src/index.ts",
37
- "test": "vitest",
38
- "type-check": "tsc --noEmit",
39
- "start": "cross-env REGISTRY_URL=https://sugarcube-registry.mark-tomlinson3.workers.dev/registry tsx src/index.ts",
40
- "prepublishOnly": "pnpm up @sugarcube-org/core --filter @sugarcube-org/cli && pnpm build"
41
- },
42
- "dependencies": {
43
- "@clack/prompts": "^0.7.0",
44
- "@sugarcube-org/core": "workspace:*",
45
- "commander": "^12.1.0",
46
- "cross-env": "^7.0.3",
47
- "fast-glob": "^3.3.2",
48
- "fs-extra": "^11.2.0",
49
- "kleur": "^4.1.5",
50
- "node-fetch": "^3.3.0",
51
- "ora": "^8.1.1",
52
- "prompts": "^2.4.2",
53
- "zod": "^3.21.4"
54
- },
55
- "devDependencies": {
56
- "@types/fs-extra": "^11.0.4",
57
- "@types/prompts": "^2.4.9",
58
- "pkgroll": "^2.5.1",
59
- "tsx": "^4.19.2"
60
- }
61
- }
2
+ "name": "@sugarcube-org/cli",
3
+ "version": "0.0.0-alpha.2",
4
+ "publishConfig": {
5
+ "access": "restricted"
6
+ },
7
+ "description": "A CLI for scaffolding sugarcube applications",
8
+ "license": "UNLICENSED",
9
+ "author": "Mark Tomlinson",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/sugarcube-org/sugarcube"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/sugarcube-org/sugarcube/issues"
16
+ },
17
+ "keywords": [
18
+ "cli",
19
+ "design-system",
20
+ "components",
21
+ "CUBE CSS",
22
+ "react",
23
+ "sugarcube"
24
+ ],
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE.md"
29
+ ],
30
+ "type": "module",
31
+ "bin": {
32
+ "sugarcube": "./dist/index.mjs"
33
+ },
34
+ "dependencies": {
35
+ "@antfu/ni": "^23.3.0",
36
+ "@clack/prompts": "^0.9.1",
37
+ "commander": "^12.1.0",
38
+ "cross-env": "^7.0.3",
39
+ "execa": "^9.5.2",
40
+ "fast-glob": "^3.3.2",
41
+ "fs-extra": "^11.2.0",
42
+ "lucide-react": "^0.468.0",
43
+ "node-fetch": "^3.3.0",
44
+ "picocolors": "^1.1.1",
45
+ "prompts": "^2.4.2",
46
+ "zod": "^3.23.8",
47
+ "@sugarcube-org/core": "0.0.1-alpha.3"
48
+ },
49
+ "devDependencies": {
50
+ "@types/fs-extra": "^11.0.4",
51
+ "@types/prompts": "^2.4.9",
52
+ "pkgroll": "^2.5.1",
53
+ "tsx": "^4.19.2"
54
+ },
55
+ "scripts": {
56
+ "build": "pkgroll --minify",
57
+ "dev": "cross-env REGISTRY_URL=http://localhost:8787/registry tsx src/index.ts",
58
+ "test": "vitest",
59
+ "type-check": "tsc --noEmit",
60
+ "start": "cross-env REGISTRY_URL=https://sugarcube-registry.mark-tomlinson3.workers.dev/registry tsx src/index.ts"
61
+ }
62
+ }
package/LICENSE.md DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Mark Tomlinson
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.