@sugarcube-org/cli 0.0.0-alpha.12 → 0.0.0-alpha.13
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 +25 -25
- package/package.json +2 -4
package/dist/index.mjs
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
2
|
+
var xe=Object.defineProperty;var l=(e,n)=>xe(e,"name",{value:n,configurable:!0});import{Command as M}from"commander";import Z,{readFile as ae,writeFile as W,mkdir as P}from"fs/promises";import u from"picocolors";import{select as G,isCancel as S,text as L,confirm as z,multiselect as ee,log as v,intro as J,spinner as te,note as V,cancel as Y,outro as B}from"@clack/prompts";import $e from"node-fetch";import{z as w}from"zod";import{validationPipeline as je,validateConfig as ce,generationPipeline as Ce,getTokenPathsFromConfig as Fe,writeCSSFilesToDisk as Ee,manageCSSIndex as Ie,loadConfig as q}from"@sugarcube-org/core";import m from"path";import le from"fast-glob";import{existsSync as T}from"fs";import{execa as De}from"execa";import{detect as Te}from"@antfu/ni";const ne=w.enum(["react","astro","nunjucks"]),pe=w.object({path:w.string(),type:w.string()}),Ne=pe.extend({framework:ne});w.object({themes:w.record(w.string(),w.array(w.string())).optional()}).strict();const Pe=w.object({name:w.string(),type:w.string(),description:w.string().optional(),frameworks:w.array(w.string()).optional(),files:w.array(w.union([Ne,pe])),tokens:w.record(w.object({type:w.string(),mapping:w.string()})).optional(),dependencies:w.record(ne,w.array(w.string())).optional(),registryDependencies:w.record(ne,w.array(w.string())).optional(),tokenDependencies:w.array(w.string()).optional()}),Oe=w.array(Pe),Re=w.object({content:w.string()}),j={error:u.red,warn:u.yellow,info:u.cyan,success:u.green,bold:u.bold,path:u.cyan};class d extends Error{static{l(this,"CLIError")}constructor(n,t){super(n),this.name="CLIError",this.cause=t}}const ue=process.env.REGISTRY_URL??"https://registry.sugarcube.sh/registry";async function fe(e){try{const n=await $e(e);if(!n.ok){if(n.status===401)throw new d(`Registry access denied: Authentication required
|
|
3
|
+
URL: ${j.info(e)}`);if(n.status===403)throw new d(`Registry access denied: Invalid or missing token
|
|
4
|
+
URL: ${j.info(e)}`);if(n.status===404)throw new d(`Registry resource not found
|
|
5
|
+
URL: ${j.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}
|
|
6
|
+
URL: ${j.info(e)}`)}return n.json()}catch(n){throw n instanceof d?n:new d(`Failed to connect to registry
|
|
7
|
+
URL: ${j.info(e)}
|
|
8
|
+
Check your internet connection`)}}l(fe,"fetchRegistry");async function A(){const e=`${ue}/index.json`,n=await fe(e);try{return Oe.parse(n)}catch{throw new d(`Invalid registry data received
|
|
9
|
+
URL: ${j.info(e)}`)}}l(A,"getRegistryIndex");async function H({type:e,name:n,framework:t}){const o=await A(),s=o.find(a=>a.type===e&&a.name===n);if(!s){const a=o.filter(f=>f.type===e).map(f=>f.name);throw new d(`${e} '${j.info(n)}' not found in registry
|
|
10
|
+
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 f=`${ue}/${a.path}.json`,g=await fe(f);try{const y=Re.parse(g);return{path:a.path,type:a.type,framework:"framework"in a?a.framework:void 0,content:y.content}}catch{throw new d(`Invalid file content received
|
|
11
|
+
File: ${j.info(a.path)}`)}}));return{item:s,files:c.filter(Boolean)}}l(H,"getRegistryFiles");const $={error(...e){console.log(j.error(e.join(" ")))},warn(...e){console.log(j.warn(e.join(" ")))},info(...e){console.log(j.info(e.join(" ")))},success(...e){console.log(j.success(e.join(" ")))},log(...e){console.log(e.join(" "))},break(){console.log("")}};async function U(e,n={type:"files",paths:e}){const{trees:t,resolved:o,errors:s}=await je(e,{loader:n});if(s.load.length>0)throw new d(`Failed to load token files:
|
|
3
12
|
${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
|
-
${u.dim(i)}`)),await ne("Continue?",!0)}try{const i=Ee(t);return await Ie(o,!0,i)}catch(i){throw new d(`Failed to write CSS files: ${i instanceof Error?i.message:"Unknown error"}`)}}l(he,"generateAndWriteCSSVars");function H(e){const t=[...e.config?[e.config]:[],...e.tokens||[],...e.generated].map(s=>m.relative(process.cwd(),s)),o=ee();o.start(`Generating ${t.length} files:`),o.stop(`Generated ${t.length} files:`),t.forEach(s=>{C.log(` - ${s}`)}),C.break()}l(H,"showSummary");const oe=w.enum(["react","astro","nunjucks"]),we=w.object({path:w.string(),type:w.string()}),We=we.extend({framework:oe});w.object({themes:w.record(w.string(),w.array(w.string())).optional()}).strict();const Je=w.object({name:w.string(),type:w.string(),description:w.string().optional(),frameworks:w.array(w.string()).optional(),files:w.array(w.union([We,we])),tokens:w.record(w.object({type:w.string(),mapping:w.string()})).optional(),dependencies:w.record(oe,w.array(w.string())).optional(),registryDependencies:w.record(oe,w.array(w.string())).optional(),tokenDependencies:w.array(w.string()).optional()}),Ve=w.array(Je),_e=w.object({content:w.string()}),ye=process.env.REGISTRY_URL??"https://registry.sugarcube.sh/registry";async function ke(e){try{const n=await Te(e);if(!n.ok){if(n.status===401)throw new d(`Registry access denied: Authentication required
|
|
7
|
-
URL: ${$.info(e)}`);if(n.status===403)throw new d(`Registry access denied: Invalid or missing token
|
|
8
|
-
URL: ${$.info(e)}`);if(n.status===404)throw new d(`Registry resource not found
|
|
9
|
-
URL: ${$.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: ${$.info(e)}`)}return n.json()}catch(n){throw n instanceof d?n:new d(`Failed to connect to registry
|
|
11
|
-
URL: ${$.info(e)}
|
|
12
|
-
Check your internet connection`)}}l(ke,"fetchRegistry");async function A(){const e=`${ye}/index.json`,n=await ke(e);try{return Ve.parse(n)}catch{throw new d(`Invalid registry data received
|
|
13
|
-
URL: ${$.info(e)}`)}}l(A,"getRegistryIndex");async function Q({type:e,name:n,framework:t}){const o=await A(),s=o.find(a=>a.type===e&&a.name===n);if(!s){const a=o.filter(f=>f.type===e).map(f=>f.name);throw new d(`${e} '${$.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 f=`${ye}/${a.path}.json`,g=await ke(f);try{const y=_e.parse(g);return{path:a.path,type:a.type,framework:"framework"in a?a.framework:void 0,content:y.content}}catch{throw new d(`Invalid file content received
|
|
15
|
-
File: ${$.info(a.path)}`)}}));return{item:s,files:c.filter(Boolean)}}l(Q,"getRegistryFiles");async function qe(e,n,t){const o=[],s=new Set;async function i(c){if(s.has(c))return;s.add(c);const a=e.find(g=>g.name===c);if(!a){const g=e.filter(y=>y.type==="component").map(y=>y.name).join(", ");throw new d(`Component '${c}' not found in registry
|
|
16
|
-
Available components: ${g}`)}const f=a.registryDependencies?.[t]||[];for(const g of f)await i(g);o.push(a)}l(i,"resolveComponent");for(const c of n)await i(c);return o}l(qe,"resolveTree");const Ke=l(async(e,n,t)=>{const o=await Q({type:"tokens",name:e});if(!o.files[0])throw new d(`Starter kit '${e}' does not contain any token files`);const s=m.resolve(process.cwd(),t),i=o.files.find(r=>m.basename(r.path)==="config.json");let c={};if(i)try{c=JSON.parse(i.content)}catch{console.warn(`Warning: Could not parse config.json for starter kit '${e}'`)}const a=o.files.filter(r=>m.basename(r.path)!=="config.json").map(r=>({path:m.join(s,m.basename(r.path)),content:r.content})),f=Object.fromEntries(a.map(r=>[m.basename(r.path),m.relative(process.cwd(),r.path)])),g={};if(c.themes)for(const[r,p]of Object.entries(c.themes))g[r]=p.map(h=>{const k=f[h];if(!k)throw new d(`Theme file '${h}' not found in starter kit '${e}'`);return k});const y={tokens:{source:a.map(r=>m.relative(process.cwd(),r.path)),type:"starter-kit",...Object.keys(g).length>0&&{themes:g}},options:{fluid:{min:320,max:1200}},output:{directories:{tokens:m.relative(process.cwd(),s),css:n},css:{separate:!1,manageIndex:!0}}};try{const r=ce(y),{trees:p,resolved:h}=await B(r,{type:"memory",data:Object.fromEntries(a.map(k=>[k.path,{collection:"default",content:k.content}]))});return{config:r,tokens:a.map(k=>m.basename(k.path)),trees:p,resolved:h,tokenFiles:a,tokensDir:s}}catch(r){throw r}},"initializeFromStarterKit"),Ye=l(async e=>{const n=await Le(),t=await pe("**/*.json",{cwd:n,absolute:!0});if(t.length===0)throw new d(`No JSON files found in ${n}`);const o=[];for(const r of t)try{const p=await le(r,"utf-8");JSON.parse(p),o.push(r)}catch{v.warn(u.yellow(`Skipping invalid JSON file: ${r}`));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(r=>m.relative(process.cwd(),r)),i=m.resolve(process.cwd(),n),c=await ue();let a=await ze();const f=new Map;s.forEach(r=>{const p=m.basename(r),h=f.get(p)||[];f.set(p,[...h,r])});const g=Array.from(f.entries()).filter(([r,p])=>p.length>1).map(([r,p])=>({name:r,files:p}));if(a==="simple"&&g.length>0){const r=g.map(({name:p,files:h})=>{const k=h.map(b=>` - ${b}`).join(`
|
|
13
|
+
`)}`);if(s.validation.length>0||s.flatten.length>0||s.resolution.length>0){$.break();const i=[...s.flatten,...s.validation,...s.resolution],c=new Map;throw i.forEach(a=>{if(a.source?.sourcePath){const f=c.get(a.source.sourcePath)||[];f.push(a.message),c.set(a.source.sourcePath,f)}}),c.forEach((a,f)=>{$.error(`Error(s) in ${j.path(f)}:`),a.forEach(g=>{$.error(` - ${g}`)}),$.break()}),new d(`Token validation failed. See ${j.info("https://docs.sugarcube.sh/w3c-token-format")} for help`)}return{trees:t,resolved:o}}l(U,"validateTokens");const Ae=l(async(e,n,t)=>{const o=await H({type:"tokens",name:e});if(!o.files[0])throw new d(`Starter kit '${e}' does not contain any token files`);const s=m.resolve(process.cwd(),t),i=o.files.find(r=>m.basename(r.path)==="config.json");let c={};if(i)try{c=JSON.parse(i.content)}catch{console.warn(`Warning: Could not parse config.json for starter kit '${e}'`)}const a=o.files.filter(r=>m.basename(r.path)!=="config.json").map(r=>({path:m.join(s,m.basename(r.path)),content:r.content})),f=Object.fromEntries(a.map(r=>[m.basename(r.path),m.relative(process.cwd(),r.path)])),g={};if(c.themes)for(const[r,p]of Object.entries(c.themes))g[r]=p.map(h=>{const k=f[h];if(!k)throw new d(`Theme file '${h}' not found in starter kit '${e}'`);return k});const y={tokens:{source:a.map(r=>m.relative(process.cwd(),r.path)),type:"starter-kit",...Object.keys(g).length>0&&{themes:g}},options:{fluid:{min:320,max:1200}},output:{directories:{tokens:m.relative(process.cwd(),s),css:n},css:{separate:!1,manageIndex:!0}}};try{const r=ce(y),{trees:p,resolved:h}=await U(r,{type:"memory",data:Object.fromEntries(a.map(k=>[k.path,{collection:"default",content:k.content}]))});return{config:r,tokens:a.map(k=>m.basename(k.path)),trees:p,resolved:h,tokenFiles:a,tokensDir:s}}catch(r){throw r}},"initializeFromStarterKit");async function me(e="./src/styles"){const n=await L({message:"Save CSS to",placeholder:e,validate:l(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return S(n)&&process.exit(0),n}l(me,"promptCSSOutputDirectory");async function Me(e="./src/design-tokens"){const n=await L({message:"Save tokens to",placeholder:e,validate:l(t=>{if(!t.trim())return"Please provide a path"},"validate")});return S(n)&&process.exit(0),n}l(Me,"promptTokensDirectory");async function Le(e){const t=e.filter(s=>s.type==="tokens"&&s.files[0]?.path.includes("starter-kits")).map(s=>({label:s.name,value:s.name})),o=await G({message:"Which starter kit?",options:t});return S(o)&&process.exit(0),o}l(Le,"promptStarterKit");async function de(e="./src/components"){const n=await L({message:"Save components to",placeholder:e,validate:l(t=>{if(t.trim().replace(/['"]/g,"").length===0)return"Output directory cannot be empty"},"validate")});return S(n)&&process.exit(0),n}l(de,"promptComponentDirectory");async function ge(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 G({message:"Build with",options:n});return S(t)&&process.exit(0),t}l(ge,"promptComponentFramework");async function he(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 S(o)&&process.exit(0),o}l(he,"promptComponentSelectionFiltered");async function ze(){const n=await G({message:"Set up tokens with",options:[{label:"Starter kit",value:"starter"},{label:"Own tokens",value:"existing"}]});return S(n)&&process.exit(0),n}l(ze,"promptTokenSetup");async function se(e,n=!1){const t=await z({message:e,initialValue:n});return(!t||S(t))&&process.exit(0),!0}l(se,"confirmOverwrite");async function Be(){const e=await z({message:"Install CUBE CSS?",active:"Yes",inactive:"No",initialValue:!0});return S(e)&&process.exit(0),e}l(Be,"promptInstallCube");async function Ue(){const e=await L({message:"Path to tokens?",placeholder:"./src/design-tokens",validate:l(n=>{if(n.trim().replace(/['"]/g,"").length===0)return"Path cannot be empty"},"validate")});return S(e)&&process.exit(0),e}l(Ue,"promptExistingTokensPath");async function We(){const e=await z({message:"Generate separate CSS file for each token file?"});return S(e)&&process.exit(0),e}l(We,"promptSeparateCSSFiles");async function Ge(){const e=await G({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 S(e)&&process.exit(0),e}l(Ge,"promptCollectionOrganization");async function we(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 S(t)&&process.exit(0),t.length===0?null:t}l(we,"promptFilesForCollection");async function oe(e){const n=e.map(o=>m.dirname(o).split(m.sep)).reduce((o,s)=>o?o.filter((i,c)=>i===s[c]):s).pop(),t=await L({message:"Name this collection:",placeholder:n||"collection"});return S(t)&&process.exit(0),t}l(oe,"promptCollectionName");async function Je(e){if(e.length<=1)return null;const n=await z({message:"Are any of these theme files?",initialValue:!1});if(S(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(S(s)&&process.exit(0),s.length===0)break;const i=await L({message:"Name this theme:",placeholder:"light"});if(S(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(S(c)&&process.exit(0),!c)break}}return t.length>0?t:null}l(Je,"promptIdentifyThemeFiles");async function Ve(){const e=await z({message:"Allow sugarcube to manage your CSS index file?",initialValue:!1});return S(e)&&process.exit(0),e}l(Ve,"promptManageIndex");const qe=l(async e=>{const n=await Ue(),t=await le("**/*.json",{cwd:n,absolute:!0});if(t.length===0)throw new d(`No JSON files found in ${n}`);const o=[];for(const r of t)try{const p=await ae(r,"utf-8");JSON.parse(p),o.push(r)}catch{v.warn(u.yellow(`Skipping invalid JSON file: ${r}`));continue}if(o.length===0)throw new d(`No valid JSON files found in ${n}
|
|
14
|
+
Ensure files contain valid JSON`);const s=o.map(r=>m.relative(process.cwd(),r)),i=m.resolve(process.cwd(),n),c=await me();let a=await Ge();const f=new Map;s.forEach(r=>{const p=m.basename(r),h=f.get(p)||[];f.set(p,[...h,r])});const g=Array.from(f.entries()).filter(([r,p])=>p.length>1).map(([r,p])=>({name:r,files:p}));if(a==="simple"&&g.length>0){const r=g.map(({name:p,files:h})=>{const k=h.map(b=>` - ${b}`).join(`
|
|
18
15
|
`);return`
|
|
19
16
|
${p} appears in:
|
|
20
17
|
${u.dim(k)}`}).join(`
|
|
21
18
|
`);v.warn(u.yellow(`Simple organization cannot have duplicate filenames:
|
|
22
|
-
${r}`)),v.message(u.cyan("Switching to collections to help organize these files...")),a="collections"}if(a==="simple"){const r={type:"custom",source:s};e.tokens=r,v.message(u.cyan("Token organization complete"))}else{const r={},p=new Set;let h=[...s],k=!0;for(;h.length>0;){let b=await
|
|
19
|
+
${r}`)),v.message(u.cyan("Switching to collections to help organize these files...")),a="collections"}if(a==="simple"){const r={type:"custom",source:s};e.tokens=r,v.message(u.cyan("Token organization complete"))}else{const r={},p=new Set;let h=[...s],k=!0;for(;h.length>0;){let b=await we(h,k);if(!b){v.message(u.cyan("No files selected, skipping collection creation..."));break}let x=await oe(b);if(!x){v.message(u.cyan("No collection name provided, skipping collection creation..."));break}let D=!1;for(;!D;)try{if(p.has(x)){v.warn(u.yellow(`Collection name "${x}" is already in use.`));const F=await oe(b);if(!F){v.message(u.cyan("No collection name provided, skipping collection creation..."));break}x=F;continue}const C={type:"custom",source:b};r[x]=C;const N=await Je(b);if(N&&N.length>0){const F=N.flatMap(I=>I.files),E=new Set;let R=!1;for(const I of N){if(E.has(I.name)){v.warn(u.yellow(`Theme name "${I.name}" is already used in this collection.`)),v.message(u.cyan("Starting theme selection over to avoid conflicts...")),R=!0;break}E.add(I.name)}if(R)continue;C.source=b.filter(I=>!F.includes(I)),C.themes=N.reduce((I,K)=>({...I,[K.name]:K.files}),{})}const O=new Map;C.source.forEach(F=>{const E=m.basename(F),R=O.get(E)||[];O.set(E,[...R,F])});const ie=Array.from(O.entries()).filter(([F,E])=>E.length>1).map(([F,E])=>({name:F,files:E}));if(ie.length>0){const F=ie.map(({name:I,files:K})=>{const ve=K.map(Se=>` - ${Se}`).join(`
|
|
23
20
|
`);return`
|
|
24
21
|
${I} appears in:
|
|
25
|
-
${u.dim(
|
|
22
|
+
${u.dim(ve)}`}).join(`
|
|
26
23
|
`);v.warn(u.yellow(`Found duplicate filenames in collection "${x}":
|
|
27
|
-
${F}`)),v.message(u.cyan("Please either split these files into different collections or mark some as theme files."));const E=await
|
|
24
|
+
${F}`)),v.message(u.cyan("Please either split these files into different collections or mark some as theme files."));const E=await we(h,k);if(!E){v.message(u.cyan("No files selected, skipping collection creation..."));break}b=E;const R=await oe(b);if(!R){v.message(u.cyan("No collection name provided, skipping collection creation..."));break}x=R;continue}D=!0,p.add(x)}catch(C){if(C instanceof d){v.error(`${C.message}`),v.message(u.cyan("Let's try again..."));continue}throw C}if(!D){v.message(u.cyan("Skipping collection creation..."));break}h=h.filter(C=>!b.includes(C)),h.length>0&&v.message(u.cyan(`Remaining files to organize: ${h.length}`)),k=!1}h.length>0?v.warn(u.yellow(`Warning: ${h.length} files were not assigned to any collection`)):v.message(u.cyan("Token organization complete")),e.tokens=r}const y=s.length>1?await We():!1;e.output={directories:{tokens:m.relative(process.cwd(),i),css:c},css:{separate:y,manageIndex:!0}},e.options={fluid:{min:320,max:1200}};try{if(typeof e.tokens=="object"&&!Array.isArray(e.tokens)){const b=e.tokens,x=new Set;for(const[D,C]of Object.entries(b)){if(x.has(D))throw new d(`Configuration Error: Duplicate collection name "${D}". Collection names must be unique.`);if(x.add(D),C.themes){const N=new Set;for(const O of Object.keys(C.themes)){if(N.has(O))throw new d(`Configuration Error: Duplicate theme name "${O}" in collection "${D}". Theme names must be unique within a collection.`);N.add(O)}}}}const r=await Promise.all(s.map(async b=>({path:m.resolve(process.cwd(),b),content:await ae(m.resolve(process.cwd(),b),"utf-8")}))),p=ce(e),{trees:h,resolved:k}=await U(p);return{config:p,tokens:s,trees:h,resolved:k,tokenPath:n,tokensDir:i,tokenFiles:r}}catch(r){throw r}},"initializeFromExistingTokens");async function re(e){const n={$schema:"https://sugarcube.style/schema.json",...e};try{const t=JSON.stringify(n,null,2);await W("sugarcube.config.json",t)}catch(t){const o=t instanceof Error?`: ${t.message}`:"";throw new d(`Failed to write config file${o}`)}}l(re,"writeConfig");function _(e){if($.break(),e instanceof d){const n=e.message.split(`
|
|
25
|
+
`);n.length>1?($.error(n[0]),$.break(),n.slice(1).forEach(t=>{$.error(` ${t.trim()}`)})):$.error(e.message),process.env.DEBUG&&e.cause&&$.info(`
|
|
26
|
+
Caused by:`,e.cause)}else $.error(`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}
|
|
27
|
+
If this issue persists, please report it: ${u.cyanBright("https://github.com/sugarcube-org/sugarcube/issues")}`),process.env.DEBUG&&$.info(`
|
|
28
|
+
Error details:`,e);$.break(),process.exit(0)}l(_,"handleError");const _e=l(()=>{J(u.inverse(" Welcome to sugarcube. The toolkit for seriously sweet front ends "))},"welcome");async function Ke(){if(T("sugarcube.config.json")){const e=await G({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 S(e)&&process.exit(0),{shouldProceed:e==="overwrite"}}return{shouldProceed:!0}}l(Ke,"preflightInit");async function Ye(e,n,t){const o=[],s=new Set;async function i(c){if(s.has(c))return;s.add(c);const a=e.find(g=>g.name===c);if(!a){const g=e.filter(y=>y.type==="component").map(y=>y.name).join(", ");throw new d(`Component '${c}' not found in registry
|
|
29
|
+
Available components: ${g}`)}const f=a.registryDependencies?.[t]||[];for(const g of f)await i(g);o.push(a)}l(i,"resolveComponent");for(const c of n)await i(c);return o}l(Ye,"resolveTree");async function He(e,{withFallback:n=!1}={}){const t=await Te({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"}l(He,"getPackageManager");async function Qe(e,n){const t=await He(n,{withFallback:!0}),o=t==="npm"?"install":"add";try{await De(t,[o,...e],{cwd:n})}catch{const i=`Failed to install dependencies using ${t}.
|
|
28
30
|
|
|
29
31
|
This might be because you're using 'npx' with a pnpm project. Try:
|
|
30
32
|
pnpm dlx @sugarcube-org/cli components
|
|
@@ -33,10 +35,8 @@ Or use the appropriate package runner for your project:
|
|
|
33
35
|
npm: npx @sugarcube-org/cli components
|
|
34
36
|
pnpm: pnpm dlx @sugarcube-org/cli components
|
|
35
37
|
yarn: yarn dlx @sugarcube-org/cli components
|
|
36
|
-
bun: bunx @sugarcube-org/cli components`;throw new Error(i)}}l(
|
|
37
|
-
`);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Please check that the specified files or directories exist`);const t=await pe(n.map(s=>s.endsWith(".json")?s:m.join(s,"**/*.json")));if(t.length===0)throw new d(`No JSON files found in the specified paths
|
|
42
|
-
Please ensure the paths contain .json files`);const o=T("sugarcube.config.json")?await G():{output:{directories:{tokens:".",css:"."},css:{separate:!1}}};await B({...o,tokens:{type:"custom",source:t.map(s=>m.relative(process.cwd(),s))}}),z(u.greenBright("All tokens are valid \u2728"))}catch(n){q(n)}}),rt=new M().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||J(u.inverse(" Add components ")),!T("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");let t=await G(),o,s=[],i;if(e.length>0){if(!n.framework)throw new d("Framework must be specified in non-interactive mode (--framework)");const f=["react","astro","nunjucks"];if(!f.includes(n.framework))throw new d(`Invalid framework. Must be one of: ${f.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=m.resolve(process.cwd(),t.output.directories.components)}else{i=await me(!1);const f=await A();if(f||(Y("Failed to fetch component list"),process.exit(1)),s=await de(f,i),t.output.directories.components)o=m.resolve(process.cwd(),t.output.directories.components);else{const g=await fe();o=m.resolve(process.cwd(),g),t.output.directories.components=m.relative(process.cwd(),o),await re(t)}}const c=await ve({components:s,componentType:i,componentsOutputDirectory:o});if(c.length>0&&!n.silent){const f=c.join(", ");await ne(`The following components already exist and will be overwritten: ${f}. Continue?`,!1)}let a;n.silent||(a=ee(),a.start("Installing components..."));try{const f=await A(),{createdFiles:g,npmDependencies:y}=await be({registryIndex:f,selectedComponents:s,componentType:i,componentsOutputDirectory:o,cssOutputDirectory:t.output.directories.css});t.output.css.manageIndex&&await X({cssOutputDirectory:t.output.directories.css,files:g}),n.silent||(a?.stop("Components installed successfully"),H({generated:g}),z(u.greenBright("Success! Components installed \u2728")))}catch(f){throw n.silent||a?.stop("Installation failed"),f}}catch(t){q(t)}}),it=new M().name("cube").description("Add CUBE CSS to your project").option("-s, --silent","Suppress logs and prompts").action(async e=>{const n=[];try{if(!T("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");e.silent||J(u.inverse(" Add CUBE CSS "));const t=await G(),o=m.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 A()).filter(c=>c.type==="cube").map(c=>c.name);for(const c of i){const a=await Q({type:"cube",name:c});for(const f of a.files){if(!f)continue;const g=f.path.replace(/^styles\//,""),y=m.join(o,g),r=m.dirname(y);try{await P(r,{recursive:!0}),await _(y,f.content),n.push(y)}catch(p){const h=p instanceof Error?`: ${p.message}`:"";throw new d(`Failed to write CUBE CSS file ${y}${h}`)}}}t.output.css.manageIndex&&await X({cssOutputDirectory:o,files:n}),await re(t),!e.silent&&n.length>0&&(H({generated:n}),z(u.greenBright("Success! CUBE CSS added successfully. \u2728")))}catch(t){q(t)}});var at="@sugarcube-org/cli",ct="0.0.0-alpha.12",lt={access:"public"},pt="A CLI for scaffolding sugarcube applications",ut="AGPL-3.0",ft="Mark Tomlinson",mt={type:"git",url:"https://github.com/sugarcube-org/sugarcube"},dt={url:"https://github.com/sugarcube-org/sugarcube/issues"},gt=["cli","design-system","components","CUBE CSS","react","sugarcube"],ht=["dist","README.md","LICENSE.md"],wt="module",yt="./dist/index.mjs",ie={".":"./dist/index.mjs"},kt={sugarcube:"./dist/index.mjs"},bt={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"},vt={"@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"},St={"@types/fs-extra":"^11.0.4","@types/prompts":"^2.4.9",pkgroll:"^2.5.1",tsx:"^4.19.2"},xt={name:at,version:ct,publishConfig:lt,description:pt,license:ut,author:ft,repository:mt,bugs:dt,keywords:gt,files:ht,type:wt,main:yt,exports:ie,bin:kt,scripts:bt,dependencies:vt,devDependencies:St};process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function Ct(){const e=new M().name("sugarcube").description("CLI for scaffolding sugarcube applications").version(xt.version,"-v, --version","display the version number");e.addCommand(nt).addCommand(st).addCommand(ot).addCommand(rt).addCommand(it),e.parse()}l(Ct,"main"),Ct()});export default $t();
|
|
38
|
+
bun: bunx @sugarcube-org/cli components`;throw new Error(i)}}l(Qe,"installDependencies");async function ye({registryIndex:e,selectedComponents:n,componentType:t,componentsOutputDirectory:o,cssOutputDirectory:s}){const i=[],c=new Set;await P(o,{recursive:!0});const a=m.join(s,"global","variables");await P(m.join(s,"global"),{recursive:!0}),await P(a,{recursive:!0});const f=await Ye(e,n,t);for(const g of f){const y=await H({type:"component",name:g.name,framework:t});for(const p of y.files)if(p)try{if(p.path.endsWith(".variables.css")){const h=m.join(a,`${g.name}.variables.css`);await W(h,p.content),i.push(h)}else{const h=m.join(o,g.name);await P(h,{recursive:!0});const k=m.join(h,m.basename(p.path));await W(k,p.content),i.push(k)}}catch(h){const k=h instanceof Error?`: ${h.message}`:"";throw new d(`Failed to write component file for "${g.name}"${k}`)}(g.dependencies?.[t]||[]).forEach(p=>c.add(p))}if(c.size>0)try{await Qe(Array.from(c),process.cwd())}catch(g){const y=g instanceof Error?`: ${g.message}`:"";throw new d(`Failed to install component dependencies${y}`)}return{createdFiles:i,npmDependencies:c}}l(ye,"installComponents");function Xe(e){switch(e){case"react":return"tsx";case"astro":return"astro";case"nunjucks":return"njk";default:return"tsx"}}l(Xe,"getExtension");function ke({components:e,componentType:n,componentsOutputDirectory:t}){return e.filter(o=>{const s=m.join(t,o,`${o}.${Xe(n)}`);return T(s)})}l(ke,"checkComponentExists");async function Ze(e){const n=[],t=m.resolve(process.cwd(),e);await P(t,{recursive:!0});const s=(await A()).filter(i=>i.type==="cube").map(i=>i.name);for(const i of s){const c=await H({type:"cube",name:i});for(const a of c.files){if(!a)continue;const f=a.path.replace(/^styles\//,""),g=m.join(t,f),y=m.dirname(g);try{await P(y,{recursive:!0}),await W(g,a.content),n.push(g)}catch(r){const p=r instanceof Error?`: ${r.message}`:"";throw new d(`Failed to write CUBE module "${i}"${p}`)}}}return n}l(Ze,"installCUBE");async function be(e,n,t){const{output:o}=await Ce(e,n,t),s=o.map(i=>i.path).filter(i=>T(i));if(s.length>0){const i=s.map(c=>` - ${c}`).join(`
|
|
39
|
+
`);v.warn(u.yellow(`The following files will be regenerated:
|
|
40
|
+
${u.dim(i)}`)),await se("Continue?",!0)}try{const i=Fe(t);return await Ee(o,!0,i)}catch(i){throw new d(`Failed to write CSS files: ${i instanceof Error?i.message:"Unknown error"}`)}}l(be,"generateAndWriteCSSVars");function Q(e){const t=[...e.config?[e.config]:[],...e.tokens||[],...e.generated].map(s=>m.relative(process.cwd(),s)),o=te();o.start(`Generating ${t.length} files:`),o.stop(`Generated ${t.length} files:`),t.forEach(s=>{$.log(` - ${s}`)}),$.break()}l(Q,"showSummary");async function X({cssOutputDirectory:e,files:n}){try{await Ie({cssOutputDirectory:e,files:n})}catch(t){const o=t instanceof Error?`: ${t.message}`:"";throw new d(`Failed to manage CSS index file ${o}`)}}l(X,"manageCSSIndexCLI");const et=new M().name("init").description("Initialize a new project").action(async()=>{try{_e(),(await Ke()).shouldProceed||process.exit(0),V("Step 1. Set up tokens");const n=await ze();let t;switch(n){case"starter":const r=await A();r||(Y("Failed to fetch starter kit list"),process.exit(1));const p=await Le(r),h=await me(),k=await Me();try{t=await Ae(p,h,k)}catch(b){Y(`Failed to initialize starter kit: ${b instanceof Error?b.message:"Unknown error"}`),process.exit(1)}break;case"existing":t=await qe({});break}V("Step 2. Add style system");const o=await Be();V("Step 3. Add components");const s=await ge();let i=[];if(s!=="skip"){const r=await A();r||(Y("Failed to fetch component list"),process.exit(1));const p=await he(r,s),h=await de(),k=await ke({components:p,componentType:s,componentsOutputDirectory:h});if(k.length>0){const b=k.join(", ");await se(`The following components already exist and will be overwritten: ${b}. Continue?`,!1)}if(p.length>0){const b=te();b.start("Installing components...");try{const{createdFiles:x,npmDependencies:D}=await ye({registryIndex:r,selectedComponents:p,componentType:s,componentsOutputDirectory:h,cssOutputDirectory:t.config.output.directories.css});i=x,b.stop("Components installed successfully")}catch(x){throw b.stop("Installation failed"),x}}}V("Step 4. Set up index.css");const c=await Ve();t.config.output.css.manageIndex=c,V("Step 5. Generating files");try{if(await Z.mkdir(t.tokensDir,{recursive:!0}),await Z.mkdir(t.config.output.directories.css,{recursive:!0}),n==="starter"){if(!t.tokenFiles||t.tokenFiles.length===0)throw new d("Failed to generate token content for starter kit");for(const r of t.tokenFiles)await Z.writeFile(r.path,r.content)}}catch(r){const p=r instanceof Error?`: ${r.message}`:"";throw new d(`Failed to create project files${p}`)}let a=[];const{trees:f,resolved:g}=await U(t.config);a=await be(f,g,t.config);let y=[];o&&(y=await Ze(t.config.output.directories.css)),t.config.output.css.manageIndex&&await X({cssOutputDirectory:t.config.output.directories.css,files:[...a.map(r=>r.path),...i.filter(r=>r.endsWith(".css")),...y]}),await re(t.config),Q({config:"sugarcube.config.json",tokens:n==="starter"?t.tokens:[],generated:[...a.map(r=>r.path),...y,...i,...t.config.output.css.manageIndex?["index.css"]:[]]}),B(u.greenBright("Success! Project initialized \u2728"))}catch(e){_(e)}}),tt=new M().name("generate").description("Generate CSS from your design tokens").option("--force","Skip confirmation when deleting stale files").action(async e=>{try{if(J(u.inverse(" Generate CSS variables from your design tokens ")),!T("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");const n=await q(),{trees:t,resolved:o}=await U(n),s=await be(t,o,n);n.output.css.manageIndex&&await X({cssOutputDirectory:n.output.directories.css,files:s.map(i=>i.path)}),Q({generated:[...s.map(i=>i.path),...n.output.css.manageIndex?["index.css"]:[]]}),B(u.greenBright("Success! CSS variables generated successfully. \u2728"))}catch(n){_(n)}}),nt=new M().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(J(u.inverse(" Validate design tokens ")),T("sugarcube.config.json")&&!e.length){const s=await q();await U(s),B(u.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=>m.normalize(s));for(const s of n)if(!T(s))throw new d(`Path not found: ${s}
|
|
41
|
+
Please check that the specified files or directories exist`);const t=await le(n.map(s=>s.endsWith(".json")?s:m.join(s,"**/*.json")));if(t.length===0)throw new d(`No JSON files found in the specified paths
|
|
42
|
+
Please ensure the paths contain .json files`);const o=T("sugarcube.config.json")?await q():{output:{directories:{tokens:".",css:"."},css:{separate:!1}}};await U({...o,tokens:{type:"custom",source:t.map(s=>m.relative(process.cwd(),s))}}),B(u.greenBright("All tokens are valid \u2728"))}catch(n){_(n)}}),st=new M().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||J(u.inverse(" Add components ")),!T("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");let t=await q(),o,s=[],i;if(e.length>0){if(!n.framework)throw new d("Framework must be specified in non-interactive mode (--framework)");const f=["react","astro","nunjucks"];if(!f.includes(n.framework))throw new d(`Invalid framework. Must be one of: ${f.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=m.resolve(process.cwd(),t.output.directories.components)}else{i=await ge(!1);const f=await A();if(f||(Y("Failed to fetch component list"),process.exit(1)),s=await he(f,i),t.output.directories.components)o=m.resolve(process.cwd(),t.output.directories.components);else{const g=await de();o=m.resolve(process.cwd(),g),t.output.directories.components=m.relative(process.cwd(),o),await re(t)}}const c=await ke({components:s,componentType:i,componentsOutputDirectory:o});if(c.length>0&&!n.silent){const f=c.join(", ");await se(`The following components already exist and will be overwritten: ${f}. Continue?`,!1)}let a;n.silent||(a=te(),a.start("Installing components..."));try{const f=await A(),{createdFiles:g,npmDependencies:y}=await ye({registryIndex:f,selectedComponents:s,componentType:i,componentsOutputDirectory:o,cssOutputDirectory:t.output.directories.css});t.output.css.manageIndex&&await X({cssOutputDirectory:t.output.directories.css,files:g}),n.silent||(a?.stop("Components installed successfully"),Q({generated:g}),B(u.greenBright("Success! Components installed \u2728")))}catch(f){throw n.silent||a?.stop("Installation failed"),f}}catch(t){_(t)}}),ot=new M().name("cube").description("Add CUBE CSS to your project").option("-s, --silent","Suppress logs and prompts").action(async e=>{const n=[];try{if(!T("sugarcube.config.json"))throw new d("This command requires a sugarcube project. Run 'npx make-sugarcube init' first.");e.silent||J(u.inverse(" Add CUBE CSS "));const t=await q(),o=m.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 A()).filter(c=>c.type==="cube").map(c=>c.name);for(const c of i){const a=await H({type:"cube",name:c});for(const f of a.files){if(!f)continue;const g=f.path.replace(/^styles\//,""),y=m.join(o,g),r=m.dirname(y);try{await P(r,{recursive:!0}),await W(y,f.content),n.push(y)}catch(p){const h=p instanceof Error?`: ${p.message}`:"";throw new d(`Failed to write CUBE CSS file ${y}${h}`)}}}t.output.css.manageIndex&&await X({cssOutputDirectory:o,files:n}),await re(t),!e.silent&&n.length>0&&(Q({generated:n}),B(u.greenBright("Success! CUBE CSS added successfully. \u2728")))}catch(t){_(t)}});var rt="0.0.0-alpha.13",it={version:rt};process.on("SIGINT",()=>process.exit(0)),process.on("SIGTERM",()=>process.exit(0));async function at(){const e=new M().name("sugarcube").description("CLI for scaffolding sugarcube applications").version(it.version,"-v, --version","display the version number");e.addCommand(et).addCommand(tt).addCommand(nt).addCommand(st).addCommand(ot),e.parse()}l(at,"main"),at();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sugarcube-org/cli",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.13",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -38,20 +38,18 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@antfu/ni": "^23.3.0",
|
|
40
40
|
"@clack/prompts": "^0.9.1",
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
42
|
"commander": "^12.1.0",
|
|
42
43
|
"cross-env": "^7.0.3",
|
|
43
44
|
"execa": "^9.5.2",
|
|
44
45
|
"fast-glob": "^3.3.2",
|
|
45
46
|
"fs-extra": "^11.2.0",
|
|
46
|
-
"lucide-react": "^0.468.0",
|
|
47
47
|
"node-fetch": "^3.3.0",
|
|
48
48
|
"picocolors": "^1.1.1",
|
|
49
|
-
"prompts": "^2.4.2",
|
|
50
49
|
"zod": "^3.23.8",
|
|
51
50
|
"@sugarcube-org/core": "0.0.1-alpha.4"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
|
-
"@types/fs-extra": "^11.0.4",
|
|
55
53
|
"@types/prompts": "^2.4.9",
|
|
56
54
|
"pkgroll": "^2.5.1",
|
|
57
55
|
"tsx": "^4.19.2"
|