@sugarcube-org/cli 0.0.0-alpha.5 → 0.0.0-alpha.6
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/dist/index.mjs +31 -22
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var je=Object.defineProperty;var p=(e,n)=>je(e,"name",{value:n,configurable:!0});import{Command as
|
|
2
|
+
var je=Object.defineProperty;var p=(e,n)=>je(e,"name",{value:n,configurable:!0});import{Command as R}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 T}from"fs/promises";import{existsSync as D}from"fs";import{text as M,isCancel as k,select as U,multiselect as ee,confirm as z,log as b,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}),Te=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()}),Pe=l.array(Te),Oe=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},x={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
3
|
${s.load.map(i=>`${i.file}: ${i.message}`).join(`
|
|
4
|
-
`)}`);if(s.validation.length>0||s.flatten.length>0||s.resolution.length>0){
|
|
5
|
-
`);
|
|
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=>{
|
|
4
|
+
`)}`);if(s.validation.length>0||s.flatten.length>0||s.resolution.length>0){x.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)=>{x.error(`Error(s) in ${v.path(r)}:`),a.forEach(u=>{x.error(` - ${u}`)}),x.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 k(n)&&process.exit(0),n}p(fe,"promptCSSOutputDirectory");async function Re(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 k(n)&&process.exit(0),n}p(Re,"promptTokensDirectory");async function Ae(){const e=await U({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 k(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 k(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 U({message:"Build with",options:n});return k(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 k(o)&&process.exit(0),o}p(ge,"promptComponentSelectionFiltered");async function Me(){const n=await U({message:"Set up tokens with",options:[{label:"Starter kit",value:"starter"},{label:"Own tokens",value:"existing"}]});return k(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||k(t))&&process.exit(0),!0}p(K,"confirmOverwrite");async function We(){const e=await z({message:"Install CUBE CSS?",active:"Yes",inactive:"No",initialValue:!0});return k(e)&&process.exit(0),e}p(We,"promptInstallCube");async function Ue(){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 k(e)&&process.exit(0),e}p(Ue,"promptExistingTokensPath");async function ze(){const e=await z({message:"Generate separate CSS file for each token file?"});return k(e)&&process.exit(0),e}p(ze,"promptSeparateCSSFiles");async function Be(){const e=await U({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 k(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 k(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 k(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(k(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(k(s)&&process.exit(0),s.length===0)break;const i=await M({message:"Name this theme:",placeholder:"light"});if(k(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(k(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
|
+
`);b.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=>{x.log(` - ${s}`)}),x.break()}p(Y,"showSummary");const ye=process.env.REGISTRY_URL??"https://registry.sugarcube.sh/registry";async function be(e){try{const n=await Ee(e);if(!n.ok){if(n.status===401)throw new d(`Registry access denied: Authentication required
|
|
7
7
|
URL: ${v.info(e)}`);if(n.status===403)throw new d(`Registry access denied: Invalid or missing token
|
|
8
8
|
URL: ${v.info(e)}`);if(n.status===404)throw new d(`Registry resource not found
|
|
9
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
10
|
URL: ${v.info(e)}`)}return n.json()}catch(n){throw n instanceof d?n:new d(`Failed to connect to registry
|
|
11
11
|
URL: ${v.info(e)}
|
|
12
|
-
Check your internet connection`)}}p(
|
|
12
|
+
Check your internet connection`)}}p(be,"fetchRegistry");async function W(){const e=`${ye}/index.json`,n=await be(e);try{return Pe.parse(n)}catch{throw new d(`Invalid registry data received
|
|
13
13
|
URL: ${v.info(e)}`)}}p(W,"getRegistryIndex");async function Z({type:e,name:n,framework:t}){const o=await W(),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
|
|
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 be(r);try{const f=Oe.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
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 Ue(),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{
|
|
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(
|
|
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 Ue(),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{b.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(S=>` - ${S}`).join(`
|
|
18
18
|
`);return`
|
|
19
19
|
${w} appears in:
|
|
20
20
|
${g.dim($)}`}).join(`
|
|
21
|
-
`);
|
|
22
|
-
${m}`)),
|
|
21
|
+
`);b.warn(g.yellow(`Simple organization cannot have duplicate filenames:
|
|
22
|
+
${m}`)),b.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,b.message(g.cyan("Token organization complete"))}else{const m={},w=new Set;let y=[...s],$=!0;for(;y.length>0;){let S=await he(y,$);if(!S){b.message(g.cyan("No files selected, skipping collection creation..."));break}let j=await ie(S);if(!j){b.message(g.cyan("No collection name provided, skipping collection creation..."));break}let O=!1;for(;!O;)try{if(w.has(j)){b.warn(g.yellow(`Collection name "${j}" is already in use.`));const F=await ie(S);if(!F){b.message(g.cyan("No collection name provided, skipping collection creation..."));break}j=F;continue}const C={type:"custom",source:S};m[j]=C;const N=await Ge(S);if(N&&N.length>0){const F=N.flatMap(I=>I.files),E=new Set;let P=!1;for(const I of N){if(E.has(I.name)){b.warn(g.yellow(`Theme name "${I.name}" is already used in this collection.`)),b.message(g.cyan("Starting theme selection over to avoid conflicts...")),P=!0;break}E.add(I.name)}if(P)continue;C.source=S.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),P=Q.get(E)||[];Q.set(E,[...P,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 Se=q.map($e=>` - ${$e}`).join(`
|
|
23
23
|
`);return`
|
|
24
24
|
${I} appears in:
|
|
25
|
-
${g.dim(
|
|
26
|
-
`);
|
|
27
|
-
${F}`)),
|
|
25
|
+
${g.dim(Se)}`}).join(`
|
|
26
|
+
`);b.warn(g.yellow(`Found duplicate filenames in collection "${j}":
|
|
27
|
+
${F}`)),b.message(g.cyan("Please either split these files into different collections or mark some as theme files."));const E=await he(y,$);if(!E){b.message(g.cyan("No files selected, skipping collection creation..."));break}S=E;const P=await ie(S);if(!P){b.message(g.cyan("No collection name provided, skipping collection creation..."));break}j=P;continue}O=!0,w.add(j)}catch(C){if(C instanceof d){b.error(`${C.message}`),b.message(g.cyan("Let's try again..."));continue}throw C}if(!O){b.message(g.cyan("Skipping collection creation..."));break}y=y.filter(C=>!S.includes(C)),y.length>0&&b.message(g.cyan(`Remaining files to organize: ${y.length}`)),$=!1}y.length>0?b.warn(g.yellow(`Warning: ${y.length} files were not assigned to any collection`)):b.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,S=new Set;for(const[j,O]of Object.entries($)){if(S.has(j))throw new d(`Configuration Error: Duplicate collection name "${j}". Collection names must be unique.`);if(S.add(j),O.themes){const C=new Set;for(const N of Object.keys(O.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 U({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 k(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
28
|
`);throw new d(`Invalid configuration:
|
|
29
29
|
${o}
|
|
30
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(`
|
|
@@ -38,13 +38,22 @@ To fix this:
|
|
|
38
38
|
1. Use unique filenames for source files
|
|
39
39
|
2. Or move some files to a different collection
|
|
40
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
|
|
42
|
-
`).filter(f=>f.trim().startsWith("@import")).map(f=>f.match(/'([^']+)'/)?.[1]).filter(f=>f!==void 0).forEach(f=>
|
|
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?.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(He,"getPackageManager");async function Qe(e,n){const t=await He(n,{withFallback:!0})
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 ke(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(ke,"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=>ke(f,o)),e.filter(r=>r.endsWith(".css")).forEach(r=>{const u=h.relative(n,r).replace(/\\/g,"/");ke(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?.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(He,"getPackageManager");async function Qe(e,n){const t=await He(n,{withFallback:!0}),o=t==="npm"?"install":"add";try{await Ie(t,[o,...e],{cwd:n})}catch{const i=`Failed to install dependencies using ${t}.
|
|
44
|
+
|
|
45
|
+
This might be because you're using 'npx' with a pnpm project. Try:
|
|
46
|
+
pnpm dlx @sugarcube-org/cli components
|
|
47
|
+
|
|
48
|
+
Or use the appropriate package runner for your project:
|
|
49
|
+
npm: npx @sugarcube-org/cli components
|
|
50
|
+
pnpm: pnpm dlx @sugarcube-org/cli components
|
|
51
|
+
yarn: yarn dlx @sugarcube-org/cli components
|
|
52
|
+
bun: bunx @sugarcube-org/cli components`;throw new Error(i)}}p(Qe,"installDependencies");async function ve({registryIndex:e,selectedComponents:n,componentType:t,componentsOutputDirectory:o,cssOutputDirectory:s}){const i=[],c=new Set;await T(o,{recursive:!0});const a=h.join(s,"global","variables");await T(h.join(s,"global"),{recursive:!0}),await T(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 T(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 T(t,{recursive:!0});const s=(await W()).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 T(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 xe({components:e,componentType:n,componentsOutputDirectory:t}){return e.filter(o=>{const s=h.join(t,o,`${o}.${et(n)}`);return D(s)})}p(xe,"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&&(b.warn(g.yellow(`The following file will be overwritten:
|
|
53
|
+
${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(x.break(),e instanceof d){const n=e.message.split(`
|
|
54
|
+
`);n.length>1?(x.error(n[0]),x.break(),n.slice(1).forEach(t=>{x.error(` ${t.trim()}`)})):x.error(e.message),process.env.DEBUG&&e.cause&&x.info(`
|
|
55
|
+
Caused by:`,e.cause)}else x.error(`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}
|
|
56
|
+
If this issue persists, please report it: ${g.cyanBright("https://github.com/sugarcube-org/sugarcube/issues")}`),process.env.DEBUG&&x.info(`
|
|
57
|
+
Error details:`,e);x.break(),process.exit(0)}p(J,"handleError");const tt=new R().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 Re();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 We();V("Step 3. Add components");const s=await de();let i=[];if(s!=="skip"){const r=await W();r||(ne("Failed to fetch component list"),process.exit(1));const u=await ge(r,s),f=await me(),m=await xe({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 R().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 R().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
58
|
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
|
|
59
|
+
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 R().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 W();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 xe({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 W(),{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 R().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 T(o,{recursive:!0})}catch(c){const a=c instanceof Error?`: ${c.message}`:"";throw new d(`Failed to create output directory${a}`)}const i=(await W()).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 T(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.6",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"},bt={"@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"},kt={"@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:bt,devDependencies:kt};process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function xt(){const e=new R().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(xt,"main"),xt();
|