@sugarcube-org/cli 0.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +9 -0
- package/dist/index.mjs +24 -0
- package/package.json +61 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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.
|
package/README.md
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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.
|
|
3
|
+
|
|
4
|
+
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();
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
}
|