@seed-design/cli 0.0.2 → 1.0.0

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/bin/index.mjs CHANGED
@@ -1,3 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import*as w from"@clack/prompts";import{cosmiconfig as ce}from"cosmiconfig";import{execa as pe}from"execa";import I from"fs";import D from"path";import{z as x}from"zod";import ie from"picocolors";var y=e=>ie.cyan(e);import{detect as ae}from"@antfu/ni";async function M(e){let t=await ae({programmatic:!0,cwd:e});return t==="yarn@berry"?"yarn":t==="pnpm@6"?"pnpm":t==="bun"?"bun":t==="deno"?"deno":t??"npm"}var F="seed-design",me=ce(F,{searchPlaces:[`${F}.json`]}),_=x.object({$schema:x.string().optional(),rsc:x.coerce.boolean().default(!1),tsx:x.coerce.boolean().default(!0),path:x.string()}).strict(),le=_.extend({resolvedUIPaths:x.string(),resolvedLibPaths:x.string()});async function J(e){let t=await ge(e);return t?await fe(e,t):null}async function fe(e,t){let r=D.resolve(e,t.path);I.existsSync(r)||I.mkdirSync(r,{recursive:!0});let n=D.join(r,"ui"),o=D.join(r,"lib");return I.existsSync(n)||I.mkdirSync(n,{recursive:!0}),I.existsSync(o)||I.mkdirSync(o,{recursive:!0}),le.parse({...t,resolvedUIPaths:n,resolvedLibPaths:o})}async function ge(e){try{let t=await me.search(e);return _.parse(t.config)}catch{if(w.log.error("\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uACBD\uB85C\uC5D0 `seed-design.json` \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694."),await w.confirm({message:"seed-design.json \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC2DC\uACA0\uC5B4\uC694?"})===!0){let r=await M(e);await pe(r,["seed-design","init","--default"],{cwd:e}),w.log.message("seed-design.json \uD30C\uC77C\uC774 \uC0DD\uC131\uB410\uC5B4\uC694.")}else w.outro(y("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}}import*as R from"@clack/prompts";import{z as c}from"zod";var de=c.union([c.literal("ui"),c.literal("lib")]),N=c.object({name:c.string(),description:c.string().optional(),dependencies:c.array(c.string()).optional(),devDependencies:c.array(c.string()).optional(),innerDependencies:c.array(c.string()).optional(),files:c.array(c.string())}),U=c.array(N),ue=N.omit({files:!0}),ye=ue.extend({registries:c.array(c.object({name:c.string(),type:de,content:c.string()}))}),tt=c.array(ye);async function G(e,t,r="ui"){return await Promise.all(e.map(async o=>{try{return await(await fetch(`${t}/__registry__/${r}/${o}.json`)).json()}catch(s){let a=await fetch(`${t}/__registry__/${r}/index.json`).then(g=>g.json()),m=U.parse(a).map(g=>g.name);R.log.error(`${r}:${o} \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC544\uC694.`),R.log.info(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8: ${m.join(", ")}`),R.log.info(JSON.stringify({baseUrl:t,error:s.toString()},null,2)),process.exit(1)}}))}async function B(e,t,r="ui"){let[n]=await G([e],t,r);return n}async function K(e){try{let[t]=await G(["index"],e,"ui");return U.parse(t)}catch(t){R.log.error("\uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC778\uB371\uC2A4\uB97C \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694."),R.log.info(JSON.stringify({baseUrl:e,error:t.toString()},null,2)),process.exit(1)}}async function V(e){let[t]=await G(["index"],e,"lib");return U.parse(t)}import{promises as Re}from"fs";import{tmpdir as be}from"os";import q from"path";import{transformFromAstSync as he}from"@babel/core";import xe from"@babel/plugin-transform-typescript";import*as $ from"recast";import{parse as we}from"@babel/parser";var Se={sourceType:"module",allowImportExportEverywhere:!0,allowReturnOutsideFunction:!0,startLine:1,tokens:!0,plugins:["asyncGenerators","bigInt","classPrivateMethods","classPrivateProperties","classProperties","classStaticBlock","decimal","decorators-legacy","doExpressions","dynamicImport","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","importAssertions","importMeta","nullishCoalescingOperator","numericSeparator","objectRestSpread","optionalCatchBinding","optionalChaining",["pipelineOperator",{proposal:"minimal"}],["recordAndTuple",{syntaxType:"hash"}],"throwExpressions","topLevelAwait","v8intrinsic","typescript","jsx"]},X=async({sourceFile:e,config:t})=>{let r=e.getFullText();if(t.tsx)return r;let n=$.parse(r,{parser:{parse:s=>we(s,Se)}}),o=he(n,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[xe],configFile:!1});if(!o||!o.ast)throw new Error("Failed to transform JSX");return $.print(o.ast).code};import{SyntaxKind as Ie}from"ts-morph";var Y=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(Ie.ExpressionStatement);return r?.getText()==='"use client";'&&r.remove(),e};import{Project as Ce,ScriptKind as ve}from"ts-morph";var Pe=[Y],je=new Ce({compilerOptions:{}});async function ke(e){let t=await Re.mkdtemp(q.join(be(),"seed-deisgn-"));return q.join(t,e)}async function H(e){let t=await ke(e.filename),r=je.createSourceFile(t,e.raw,{scriptKind:ve.TSX});for(let n of Pe)n({sourceFile:r,...e});return await X({sourceFile:r,...e})}import*as l from"@clack/prompts";import z from"fs-extra";import ee from"path";import De from"picocolors";import{z as b}from"zod";var Q="https://seed-design.io";function W({userSelects:e,uiRegistryIndex:t,libRegistryIndex:r}){let n=[];function o({registryName:s,type:a}){if(n.some(m=>m.name===s&&m.type===a))return;n.push({type:a,name:s});let d=a==="ui"?t.find(m=>m.name===s):r.find(m=>m.name===s);if(d&&d.innerDependencies)for(let m of d.innerDependencies){let[g,k]=m.split(":");o({registryName:k,type:g})}}for(let s of e)o({registryName:s,type:"ui"});return Array.from(n)}import*as Z from"@clack/prompts";import{execa as Ae}from"execa";import Te from"picocolors";import Me from"findup-sync";import Ue from"fs-extra";var $e="package.json";function Oe(){let e=Me($e);if(!e)throw new Error("No package.json file found in the project.");return e}function O(){let e=Oe();return Ue.readJSONSync(e)}async function L({cwd:e,deps:t,dev:r=!1}){let{start:n,stop:o}=Z.spinner(),s=await M(e),a=O(),d={...a.dependencies,...a.devDependencies},m=t.filter(h=>!d[h]),g=t.filter(h=>d[h]);if(!m.length)return{installed:new Set,filtered:new Set};n(Te.gray("\uC758\uC874\uC131 \uC124\uCE58\uC911..."));let A=[s==="npm"?"install":"add",r?"-D":null,...m].filter(Boolean);try{await Ae(s,A,{cwd:e})}catch(h){console.error(`\uC758\uC874\uC131 \uC124\uCE58 \uC2E4\uD328: ${h}`),process.exit(1)}return o("\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC."),{installed:m,filtered:g}}var Ge=b.object({components:b.array(b.string()).optional(),cwd:b.string(),all:b.boolean(),baseUrl:b.string().optional()}),te=e=>{e.command("add [...components]","add component").option("-a, --all","Add all components",{default:!1}).option("-c, --cwd <cwd>","the working directory. defaults to the current directory.",{default:process.cwd()}).option("-u, --baseUrl <baseUrl>","the base url of the registry. defaults to the current directory.",{default:Q}).example("seed-design add action-button").example("seed-design add alert-dialog").action(async(t,r)=>{l.intro(De.bgCyan("seed-design add"));let n=Ge.parse({components:t,...r}),o=n.cwd,s=n.baseUrl,a=await J(o),d=await K(s),m=await V(s),g=n.all?d.map(f=>f.name):n.components;if(!n.components?.length&&!n.all){let f=await l.multiselect({message:"\uCD94\uAC00\uD560 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4 \uBC14\uB85C \uC5EC\uB7EC \uAC1C \uC120\uD0DD \uAC00\uB2A5)",options:d.map(i=>({label:i.name,value:i.name,hint:i.description}))});l.isCancel(f)&&(l.log.error("\uCDE8\uC18C\uB418\uC5C8\uC5B4\uC694."),process.exit(0)),g=f}g?.length||(l.log.error("\uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694."),process.exit(0)),l.log.message(`\uC120\uD0DD\uB41C \uCEF4\uD3EC\uB10C\uD2B8: ${y(g.join(", "))}`);let k=W({userSelects:g,uiRegistryIndex:d,libRegistryIndex:m}),C=[],{start:A,stop:h}=l.spinner();A("Registry\uB97C \uAC00\uC838\uC624\uACE0 \uC788\uC5B4\uC694...");for(let f of k){let i=await B(f.name,s,f.type);C.push(i)}if(h(),C.length){let f=C.filter(i=>!g.includes(i.name));l.log.message(`\uCD94\uAC00\uB85C \uC124\uCE58\uB420 \uB808\uC9C0\uC2A4\uD2B8\uB9AC: ${y(f.map(i=>i.name).join(", "))}`)}let T=[],u={installed:new Set,filtered:new Set};for(let f of C){for(let i of f.registries){let v="";switch(i.type){case"ui":v=a.resolvedUIPaths;break;case"lib":v=a.resolvedLibPaths;break;default:break}z.existsSync(v)||await z.mkdir(v,{recursive:!0});let S=ee.resolve(v,i.name),oe=await H({filename:i.name,config:a,raw:i.content});a.tsx||(S=S.replace(/\.tsx$/,".jsx"),S=S.replace(/\.ts$/,".js")),await z.writeFile(S,oe);let se=ee.relative(o,S);T.push({name:i.name,path:se})}if(f.dependencies?.length){let i=await L({cwd:o,deps:f.dependencies});u.installed=new Set([...u.installed,...i.installed]),u.filtered=new Set([...u.filtered,...i.filtered])}if(f.devDependencies?.length){let i=await L({cwd:o,deps:f.devDependencies,dev:!0});u.installed=new Set([...u.installed,...i.installed]),u.filtered=new Set([...u.filtered,...i.filtered])}l.log.success(`${y(f.name)} \uAD00\uB828 \uD30C\uC77C \uCD94\uAC00 \uC644\uB8CC`)}if(u.installed.size&&l.log.message(`\uC124\uCE58\uB41C \uC758\uC874\uC131: ${y(Array.from(u.installed).join(", "))}`),u.filtered.size&&l.log.message(`\uC774\uBBF8 \uC124\uCE58\uB41C \uC758\uC874\uC131: ${y(Array.from(u.filtered).join(", "))}`),T.length)for(let f of T)l.log.message(`\uCD94\uAC00\uB41C \uD30C\uC77C: ${y(f.path)}`);l.outro("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC644\uB8CC.")})};import*as p from"@clack/prompts";import Le from"fs-extra";import re from"path";import P from"picocolors";import{z as E}from"zod";var ze=E.object({cwd:E.string(),yes:E.boolean().optional()}),ne=e=>{e.command("init","seed-design.json \uD30C\uC77C \uC0DD\uC131").option("-c, --cwd <cwd>","\uC791\uC5C5 \uB514\uB809\uD1A0\uB9AC. \uAE30\uBCF8\uAC12\uC740 \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC.",{default:process.cwd()}).option("-y, --yes","\uBAA8\uB4E0 \uC9C8\uBB38\uC5D0 \uB300\uD574 \uAE30\uBCF8\uAC12\uC73C\uB85C \uB2F5\uBCC0\uD569\uB2C8\uB2E4.").action(async t=>{let r=a=>P.cyan(a);p.intro(P.bgCyan("seed-design.json \uD30C\uC77C \uC0DD\uC131"));let n=ze.parse(t),o=n.yes,s={rsc:!1,tsx:!0,path:"./seed-design"};o||(s={...await p.group({tsx:()=>p.confirm({message:`${r("TypeScript")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!0}),rsc:()=>p.confirm({message:`${r("React Server Components")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!1}),path:()=>p.text({message:`${r("seed-design \uD3F4\uB354")} \uACBD\uB85C\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. (\uAE30\uBCF8\uAC12\uC740 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4.)`,initialValue:"./seed-design",defaultValue:"./seed-design",placeholder:"./seed-design"})},{onCancel:()=>{p.cancel("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694."),process.exit(0)}})});try{let{start:a,stop:d}=p.spinner();a("seed-design.json \uD30C\uC77C \uC0DD\uC131\uC911...");let m=re.resolve(n.cwd,"seed-design.json");await Le.writeFile(m,`${JSON.stringify(s,null,2)}
3
- `,"utf-8");let g=re.relative(process.cwd(),m);d(`seed-design.json \uD30C\uC77C\uC774 ${r(g)}\uC5D0 \uC0DD\uC131\uB410\uC5B4\uC694.`),p.log.info(P.gray("seed-design add {component} \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD574\uBCF4\uC138\uC694!")),p.log.info(P.gray("seed-design add \uBA85\uB839\uC5B4\uB85C \uCD94\uAC00\uD560 \uC218 \uC788\uB294 \uBAA8\uB4E0 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD655\uC778\uD574\uBCF4\uC138\uC694.")),p.outro("\uC791\uC5C5\uC774 \uC644\uB8CC\uB410\uC5B4\uC694.")}catch(a){p.log.error(`seed-design.json \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694. ${a}`),p.outro(P.bgRed("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}})};import{cac as Ee}from"cac";var Fe="seed-design",j=Ee(Fe);async function _e(){let e=O();te(j),ne(j),j.version(e.version||"1.0.0","-v, --version"),j.help(),j.parse()}_e();
2
+ import*as A from"@clack/prompts";import{cosmiconfig as ge}from"cosmiconfig";import{execa as ue}from"execa";import{z as O}from"zod";import de from"picocolors";var a=e=>de.cyan(e);import{detect as fe}from"@antfu/ni";async function _(e){let t=await fe({programmatic:!0,cwd:e});return t==="yarn@berry"?"yarn":t==="pnpm@6"?"pnpm":t==="bun"?"bun":t==="deno"?"deno":t??"npm"}var Q="seed-design",ye=ge(Q,{searchPlaces:[`${Q}.json`]}),Z=O.object({$schema:O.string().optional(),rsc:O.coerce.boolean().default(!1),tsx:O.coerce.boolean().default(!0),path:O.string()}).strict();async function U(e){let t=await he(e);return t?Z.parse(t):null}async function he(e){try{let t=await ye.search(e);return Z.parse(t.config)}catch{A.log.error("\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uACBD\uB85C\uC5D0 `seed-design.json` \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694."),await A.confirm({message:"seed-design.json \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC2DC\uACA0\uC5B4\uC694?"})||(A.outro(a("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1));let r=await _(e);await ue(r,["seed-design","init","--default"],{cwd:e}),A.log.message("seed-design.json \uD30C\uC77C\uC774 \uC0DD\uC131\uB410\uC5B4\uC694.")}}function z({selectedItemKeys:e,publicRegistries:t}){let r=[],c=new Set;function i(s,o){let l=r.find(n=>n.registryId===s);if(!l?.items.some(n=>n.id===o.id)){if(l?l.items.push(o):r.push({registryId:s,items:[o]}),o.dependencies?.length)for(let n of o.dependencies)c.add(n);if(o.innerDependencies?.length)for(let n of o.innerDependencies)for(let w of n.itemIds){let x=t.find(j=>j.id===n.registryId)?.items.find(j=>j.id===w);if(!x)throw new Error(`Cannot find dependency item: ${n.registryId}:${w}`);i(n.registryId,x)}}}for(let s of e){let[o,...l]=s.split(":"),n=l.join(":");if(!o||!n)throw new Error(`Invalid snippet format: "${s}"`);let w=t.find(x=>x.id===o)?.items.find(x=>x.id===n);if(!w)throw new Error(`Cannot find snippet: "${s}"`);i(o,w)}return{registryItemsToAdd:r,npmDependenciesToAdd:c}}import*as Y from"@clack/prompts";import{z as d}from"zod";var X=d.object({id:d.string(),description:d.string().optional(),deprecated:d.boolean().optional(),hideFromCLICatalog:d.boolean().optional(),dependencies:d.array(d.string()).optional(),innerDependencies:d.array(d.object({registryId:d.string(),itemIds:d.array(d.string())})).optional(),snippets:d.array(d.object({path:d.string(),content:d.string()}))}),W=d.object({id:d.string(),hideFromCLICatalog:d.boolean().optional(),items:d.array(X.omit({snippets:!0}).extend({snippets:d.array(d.object({path:d.string()}))}))}),ee=d.array(d.object({id:d.string()}));async function M({baseUrl:e}){let t=await fetch(`${e}/__registry__/index.json`);if(!t.ok)throw new Error(`Failed to fetch registries: ${t.status} ${t.statusText}`);let r=await t.json(),{success:c,data:i,error:s}=ee.safeParse(r);if(!c)throw new Error(`Failed to parse registries: ${s?.message}`);return i}async function K({baseUrl:e,registryId:t}){let r=await fetch(`${e}/__registry__/${t}/index.json`);if(!r.ok)throw new Error(`Failed to fetch ${t} registry: ${r.status} ${r.statusText}`);let c=await r.json(),{success:i,data:s,error:o}=W.safeParse(c);if(!i)throw new Error(`Failed to parse ${t} registry: ${o?.message}`);return s}async function we({baseUrl:e,registryId:t,registryItemId:r}){let c=await fetch(`${e}/__registry__/${t}/${r}.json`);if(!c.ok)throw new Error(`Failed to fetch ${r}: ${c.status} ${c.statusText}`);let i=await c.json(),{success:s,data:o,error:l}=X.safeParse(i);if(!s)throw new Error(`Failed to parse ${r}: ${l?.message}`);return o}async function te({baseUrl:e,registryId:t,registryItemIds:r}){return await Promise.all(r.map(async c=>{try{return await we({baseUrl:e,registryId:t,registryItemId:c})}catch(i){let s=await fetch(`${e}/__registry__/${t}/index.json`);if(!s.ok)throw new Error(`${t} \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC5B4\uC694: ${s.status} ${s.statusText}`);let o=await s.json(),{success:l,data:n}=W.safeParse(o);throw l?(Y.log.error(`${c} \uC2A4\uB2C8\uD3AB\uC774 ${t} \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC5C6\uC5B4\uC694.`),Y.log.info(`${t} \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC874\uC7AC\uD558\uB294 \uC2A4\uB2C8\uD3AB:
3
+ ${n.items.map(w=>w.id).join(`
4
+ `)}`),i):new Error(`Failed to parse registry index for ${t}`)}}))}import{promises as Re}from"fs";import{tmpdir as je}from"os";import ie from"path";import{transformFromAstSync as xe}from"@babel/core";import be from"@babel/plugin-transform-typescript";import*as J from"recast";import{parse as Ie}from"@babel/parser";var $e={sourceType:"module",allowImportExportEverywhere:!0,allowReturnOutsideFunction:!0,startLine:1,tokens:!0,plugins:["asyncGenerators","bigInt","classPrivateMethods","classPrivateProperties","classProperties","classStaticBlock","decimal","decorators-legacy","doExpressions","dynamicImport","exportDefaultFrom","exportNamespaceFrom","functionBind","functionSent","importAssertions","importMeta","nullishCoalescingOperator","numericSeparator","objectRestSpread","optionalCatchBinding","optionalChaining",["pipelineOperator",{proposal:"minimal"}],["recordAndTuple",{syntaxType:"hash"}],"throwExpressions","topLevelAwait","v8intrinsic","typescript","jsx"]},re=async({sourceFile:e,config:t})=>{let r=e.getFullText();if(t.tsx)return r;let c=J.parse(r,{parser:{parse:s=>Ie(s,$e)}}),i=xe(c,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[be],configFile:!1});if(!i||!i.ast)throw new Error("Failed to transform JSX");return J.print(i.ast).code};import{SyntaxKind as Pe}from"ts-morph";var se=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(Pe.ExpressionStatement);if(!r)return e;let c=r.getExpression();if(!c)return e;let i=c.getText().trim();if(i!=='"use client"'&&i!=="'use client'")return e;let s=r.getText(),o=r.getFullText();if(s.trim()===o.trim())return e;let n=o.replace(s,"").replace(/^\s*\n/,"").replace(/\n\s*$/,"");return r.replaceWithText(n),e};import{Project as Ce,ScriptKind as Se}from"ts-morph";var Ae=[se],ve=new Ce({compilerOptions:{}});async function Te(e){let t=await Re.mkdtemp(ie.join(je(),"seed-design-"));return ie.join(t,e)}async function oe(e){let t=await Te(e.filename),r=ve.createSourceFile(t,e.raw,{scriptKind:Se.TSX});for(let c of Ae)c({sourceFile:r,...e});return await re({sourceFile:r,...e})}import*as ne from"@clack/prompts";import q from"fs-extra";import N from"path";async function L({registryItemsToAdd:e,rootPath:t,cwd:r,baseUrl:c,config:i}){let s=[];for(let{registryId:o,items:l}of e){let n=N.join(t,o);q.ensureDirSync(n);let w=await te({baseUrl:c,registryId:o,registryItemIds:l.map(x=>x.id)});for(let{id:x,snippets:j}of w){let $=await Promise.all(j.map(async h=>{let P=await oe({filename:h.path,config:i,raw:h.content}),R=N.join(n,h.path);return i.tsx||(R=R.replace(/\.tsx$/,".jsx"),R=R.replace(/\.ts$/,".js")),{filePath:R,content:P,relativePath:N.relative(r,R),name:`${o}:${x}`}}));await Promise.all($.map(async({filePath:h,content:P})=>{await q.ensureDir(N.dirname(h)),await q.writeFile(h,P)}));let I=$.map(({name:h,relativePath:P})=>({name:h,path:P}));s.push(...I),ne.log.success(`${a(`${o}:${x}`)} \uAD00\uB828 \uC2A4\uB2C8\uD3AB \uB2E4\uC6B4\uB85C\uB4DC \uC644\uB8CC: ${a(I.map(h=>h.path).join(", "))}`)}}}import*as f from"@clack/prompts";import _e from"path";import{z as E}from"zod";var B="https://seed-design.io";import*as ae from"@clack/prompts";import{execa as Fe}from"execa";import Ee from"findup-sync";import ke from"fs-extra";var De="package.json";function Oe(){let e=Ee(De);if(!e)throw new Error("No package.json file found in the project.");return e}function V(){let e=Oe();return ke.readJSONSync(e)}async function G({cwd:e,deps:t,dev:r=!1}){let{start:c,stop:i}=ae.spinner(),s=await _(e),l={...V().dependencies},n=new Set(t.filter(I=>!l[I])),w=new Set(t.filter(I=>l[I]));if(!n.size)return{installed:new Set,filtered:n};c("\uC758\uC874\uC131 \uC124\uCE58\uC911...");let $=[s==="npm"?"install":"add",r?"-D":null,...n].filter(Boolean);try{await Fe(s,$,{cwd:e})}catch(I){console.error(`\uC758\uC874\uC131 \uC124\uCE58 \uC2E4\uD328: ${I}`),process.exit(1)}return i("\uC758\uC874\uC131 \uC124\uCE58\uAC00 \uC644\uB8CC\uB410\uC5B4\uC694."),{installed:n,filtered:w}}var Ue=E.object({itemIds:E.array(E.string()).optional(),all:E.boolean(),cwd:E.string(),baseUrl:E.string().optional()}),ce=e=>{e.command("add [...item-ids]","add items").option("-a, --all","[Deprecated] Add all items",{default:!1}).option("-c, --cwd <cwd>","the working directory. defaults to the current directory.",{default:process.cwd()}).option("-u, --baseUrl <baseUrl>","the base url of the registry. defaults to the current directory.",{default:B}).example("seed-design add ui:action-button").example("seed-design add ui:alert-dialog").action(async(t,r)=>{f.intro("seed-design add");let{success:c,data:{all:i,...s},error:o}=Ue.safeParse({itemIds:t,...r});c||(f.log.error(`\uC798\uBABB\uB41C \uC635\uC158\uC774\uC5D0\uC694: ${o?.message}`),process.exit(1)),i&&(f.log.error("`--all` \uC635\uC158\uC740 \uB354 \uC774\uC0C1 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC544\uC694. \uB300\uC2E0 `seed-design add-all` \uBA85\uB839\uC5B4\uB97C \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694."),process.exit(1));let l=s.cwd,n=s.baseUrl,w=await U(l),x=_e.resolve(l,w.path),{start:j,stop:$}=f.spinner();j("Registry\uB97C \uAC00\uC838\uC624\uACE0 \uC788\uC5B4\uC694...");let I=await Promise.all((await M({baseUrl:n})).map(async({id:b})=>K({baseUrl:n,registryId:b})));$("Registry\uB97C \uAC00\uC838\uC654\uC5B4\uC694.");let h=await(async()=>{if(s.itemIds.length>0)return s.itemIds;let b=await f.multiselect({message:"\uCD94\uAC00\uD560 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4 \uBC14\uB85C \uC5EC\uB7EC \uAC1C \uC120\uD0DD \uAC00\uB2A5)",options:I.filter(({hideFromCLICatalog:m})=>!m).flatMap(({id:m,items:p})=>p.filter(({hideFromCLICatalog:y})=>!y).sort((y,S)=>y.id.localeCompare(S.id)).map(({id:y,description:S,deprecated:C})=>({label:`${C?"(deprecated) ":""}${a(m)}:${y}`,value:`${m}:${y}`,hint:S,deprecated:C,registryItemCount:p.length}))).sort((m,p)=>m.deprecated!==p.deprecated?m.deprecated?1:-1:p.registryItemCount-m.registryItemCount)});return f.isCancel(b)&&(f.log.error("\uCDE8\uC18C\uB418\uC5C8\uC5B4\uC694."),process.exit(0)),b})();h?.length||(f.log.error("\uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694."),process.exit(0)),f.log.message(`\uC120\uD0DD\uB41C \uD56D\uBAA9: ${a(h.join(", "))}`);let P=[];for(let b of h){let[m,...p]=b.split(":"),y=p.join(":");(!m||!y)&&(f.log.error(`${a(b)}: \uD56D\uBAA9 \uC774\uB984\uC774 \uC798\uBABB\uB418\uC5C8\uC5B4\uC694. ${a("ui:action-button")}\uACFC \uAC19\uC740 \uD615\uC2DD\uC73C\uB85C \uC785\uB825\uD574\uBCF4\uC138\uC694.`),process.exit(1));let S=I.find(C=>C.id===m)?.items.find(C=>C.id===y);if(S||(f.log.error(`${a(b)}: \uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694.`),process.exit(1)),S.deprecated){let C=await f.confirm({message:`${a(S.id)}: deprecated \uB418\uC5C8\uC5B4\uC694. \uCD94\uAC00\uD560\uAE4C\uC694?`,initialValue:!1});if(C===!1||f.isCancel(C)){f.log.info(`${a(S.id)}: \uCD94\uAC00\uD558\uC9C0 \uC54A\uC744\uAC8C\uC694.`);continue}}P.push(b)}let{registryItemsToAdd:R,npmDependenciesToAdd:F}=z({selectedItemKeys:P,publicRegistries:I});f.log.info(`\uCD94\uAC00\uD560 \uD56D\uBAA9: ${a(R.map(b=>b.items.map(m=>`${b.registryId}:${m.id}`).join(", ")).join(", ")||"\uC5C6\uC74C")}
5
+
6
+ \uC124\uCE58\uD560 \uC758\uC874\uC131: ${a(Array.from(F).join(", ")||"\uC5C6\uC74C")}`),await L({registryItemsToAdd:R,rootPath:x,cwd:l,baseUrl:n,config:w});let{installed:D,filtered:T}=await G({cwd:l,deps:Array.from(F)});D.size===0&&f.log.message("\uBAA8\uB4E0 \uC758\uC874\uC131\uC774 \uC774\uBBF8 \uC124\uCE58\uB418\uC5B4 \uC788\uC5B4\uC694."),D.size&&(f.log.message(`\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC: ${a(Array.from(D).join(", "))}`),T.size&&f.log.message(`\uC124\uCE58\uD558\uC9C0 \uC54A\uC740 \uC758\uC874\uC131 (\uC774\uBBF8 \uC124\uCE58\uB428): ${a(Array.from(T).join(", "))}`)),f.outro("\uC644\uB8CC\uD588\uC5B4\uC694.")})};import*as g from"@clack/prompts";import ze from"path";import{z as v}from"zod";var Me=v.object({registryIds:v.array(v.string()).optional(),all:v.boolean(),includeDeprecated:v.boolean().optional(),cwd:v.string(),baseUrl:v.string().optional()}),pe=e=>{e.command("add-all [...registry-ids]","add all items from registries").option("-a, --all","Add all items from all registries",{default:!1}).option("--include-deprecated","Include deprecated items when used with `--all`",{default:!1}).option("-c, --cwd <cwd>","the working directory. defaults to the current directory.",{default:process.cwd()}).option("-u, --baseUrl <baseUrl>","the base url of the registry. defaults to the current directory.",{default:B}).example("seed-design add-all ui --include-deprecated").example("seed-design add-all ui lib breeze").action(async(t,r)=>{g.intro("seed-design add-all");let{success:c,data:i,error:s}=Me.safeParse({registryIds:t,...r});c||(g.log.error(`\uC798\uBABB\uB41C \uC635\uC158\uC774\uC5D0\uC694: ${s?.message}`),process.exit(1));let o=i.cwd,l=i.baseUrl,n=await U(o),w=ze.resolve(o,n.path),{start:x,stop:j}=g.spinner();x("Registry\uB97C \uAC00\uC838\uC624\uACE0 \uC788\uC5B4\uC694...");let $=await Promise.all((await M({baseUrl:l})).map(async({id:m})=>K({baseUrl:l,registryId:m})));j("Registry\uB97C \uAC00\uC838\uC654\uC5B4\uC694.");let I=await(async()=>{if(i.all){let p=$.map(y=>y.id);return g.log.message(`\uBAA8\uB4E0 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC758 \uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uCD94\uAC00\uD569\uB2C8\uB2E4: ${a(p.join(", "))}`),p}if(i.registryIds?.length){let p=$.map(y=>y.id);for(let y of i.registryIds)p.includes(y)||(g.log.error(`\uB808\uC9C0\uC2A4\uD2B8\uB9AC '${y}'\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694.`),g.log.info(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC9C0\uC2A4\uD2B8\uB9AC: ${p.join(", ")}`),process.exit(1));return g.log.message(`\uC120\uD0DD\uB41C \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC758 \uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uCD94\uAC00\uD569\uB2C8\uB2E4: ${a(i.registryIds.join(", "))}`),i.registryIds}let m=await g.multiselect({message:"\uCD94\uAC00\uD560 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4 \uBC14\uB85C \uC5EC\uB7EC \uAC1C \uC120\uD0DD \uAC00\uB2A5)",options:$.filter(({hideFromCLICatalog:p})=>!p).sort((p,y)=>y.items.length-p.items.length).map(p=>({label:p.id,value:p.id,hint:`${p.items.length}\uAC1C \uD56D\uBAA9 (${p.items[0].id} \uB4F1)`}))});return g.isCancel(m)&&(g.log.error("\uCDE8\uC18C\uB418\uC5C8\uC5B4\uC694."),process.exit(0)),g.log.message(`\uC120\uD0DD\uB41C \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC758 \uD56D\uBAA9\uC744 \uCD94\uAC00\uD569\uB2C8\uB2E4: ${a(m.join(", "))}`),m})(),h=$.filter(m=>I.includes(m.id)),P=h.flatMap(m=>m.items.filter(p=>p.deprecated?i.includeDeprecated:!0).map(p=>`${m.id}:${p.id}`)),R=h.flatMap(m=>m.items.filter(p=>p.deprecated).map(()=>1)).length;!i.includeDeprecated&&R>0&&g.log.info(`${R}\uAC1C\uC758 deprecated \uD56D\uBAA9\uC740 \uC81C\uC678\uB418\uC5C8\uC5B4\uC694. --include-deprecated \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uBA74 \uCD94\uAC00\uD560 \uC218 \uC788\uC5B4\uC694.`),P.length||(g.log.error("\uCD94\uAC00\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC5B4\uC694."),process.exit(0)),g.log.message(`\uCD1D ${a(P.length.toString())}\uAC1C\uC758 \uD56D\uBAA9\uC744 \uCD94\uAC00\uD569\uB2C8\uB2E4.`);let{registryItemsToAdd:F,npmDependenciesToAdd:D}=z({selectedItemKeys:P,publicRegistries:$});await L({registryItemsToAdd:F,rootPath:w,cwd:o,baseUrl:l,config:n});let{installed:T,filtered:b}=await G({cwd:o,deps:Array.from(D)});T.size===0&&g.log.message("\uBAA8\uB4E0 \uC758\uC874\uC131\uC774 \uC774\uBBF8 \uC124\uCE58\uB418\uC5B4 \uC788\uC5B4\uC694."),T.size&&(g.log.message(`\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC: ${a(Array.from(T).join(", "))}`),b.size&&g.log.message(`\uC124\uCE58\uD558\uC9C0 \uC54A\uC740 \uC758\uC874\uC131 (\uC774\uBBF8 \uC124\uCE58\uB428): ${a(Array.from(b).join(", "))}`)),g.outro("\uC644\uB8CC\uD588\uC5B4\uC694.")})};import*as u from"@clack/prompts";import Ke from"fs-extra";import le from"path";import{z as H}from"zod";var Je=H.object({cwd:H.string(),yes:H.boolean().optional()}),me=e=>{e.command("init","seed-design.json \uD30C\uC77C \uC0DD\uC131").option("-c, --cwd <cwd>","\uC791\uC5C5 \uB514\uB809\uD1A0\uB9AC. \uAE30\uBCF8\uAC12\uC740 \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC.",{default:process.cwd()}).option("-y, --yes","\uBAA8\uB4E0 \uC9C8\uBB38\uC5D0 \uB300\uD574 \uAE30\uBCF8\uAC12\uC73C\uB85C \uB2F5\uBCC0\uD569\uB2C8\uB2E4.").action(async t=>{u.intro("seed-design.json \uD30C\uC77C \uC0DD\uC131");let r=Je.parse(t),c=r.yes,i={rsc:!1,tsx:!0,path:"./seed-design"};c||(i={...await u.group({tsx:()=>u.confirm({message:`${a("TypeScript")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!0}),rsc:()=>u.confirm({message:`${a("React Server Components")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!1}),path:()=>u.text({message:`${a("seed-design \uD3F4\uB354")} \uACBD\uB85C\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. (\uAE30\uBCF8\uAC12\uC740 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4.)`,initialValue:"./seed-design",defaultValue:"./seed-design",placeholder:"./seed-design"})},{onCancel:()=>{u.cancel("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694."),process.exit(0)}})});try{let{start:s,stop:o}=u.spinner();s("seed-design.json \uD30C\uC77C \uC0DD\uC131\uC911...");let l=le.resolve(r.cwd,"seed-design.json");await Ke.writeFile(l,`${JSON.stringify(i,null,2)}
7
+ `,"utf-8");let n=le.relative(process.cwd(),l);o(`seed-design.json \uD30C\uC77C\uC774 ${a(n)}\uC5D0 \uC0DD\uC131\uB410\uC5B4\uC694.`),u.log.info(a("seed-design add {component} \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD574\uBCF4\uC138\uC694!")),u.log.info(a("seed-design add \uBA85\uB839\uC5B4\uB85C \uCD94\uAC00\uD560 \uC218 \uC788\uB294 \uBAA8\uB4E0 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD655\uC778\uD574\uBCF4\uC138\uC694.")),u.outro("\uC791\uC5C5\uC774 \uC644\uB8CC\uB410\uC5B4\uC694.")}catch(s){u.log.error(`seed-design.json \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694. ${s}`),u.outro(a("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}})};import{cac as Ne}from"cac";var Le="seed-design",k=Ne(Le);async function Be(){let e=V();ce(k),pe(k),me(k),k.version(e.version||"1.0.0","-v, --version"),k.help(),k.parse()}Be();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-design/cli",
3
- "version": "0.0.2",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,11 +25,11 @@
25
25
  "lint:publish": "bun publint"
26
26
  },
27
27
  "dependencies": {
28
- "@antfu/ni": "^24.3.0",
28
+ "@antfu/ni": "^26.0.0",
29
29
  "@babel/core": "^7.26.10",
30
30
  "@babel/parser": "^7.27.0",
31
31
  "@babel/plugin-transform-typescript": "^7.27.0",
32
- "@clack/prompts": "^0.10.0",
32
+ "@clack/prompts": "^0.11.0",
33
33
  "cac": "^6.7.14",
34
34
  "cosmiconfig": "^9.0.0",
35
35
  "execa": "^9.5.2",
@@ -37,15 +37,15 @@
37
37
  "fs-extra": "^11.3.0",
38
38
  "picocolors": "^1.1.1",
39
39
  "recast": "^0.23.11",
40
- "ts-morph": "^25.0.1",
40
+ "ts-morph": "^27.0.0",
41
41
  "zod": "^3.24.3"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/babel__core": "^7.20.5",
45
45
  "@types/fs-extra": "^11.0.4",
46
46
  "esbuild": "^0.25.3",
47
- "type-fest": "^4.40.0",
48
- "typescript": "^5.8.3"
47
+ "type-fest": "^5.0.0",
48
+ "typescript": "^5.9.2"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
@@ -0,0 +1,180 @@
1
+ import { getConfig } from "@/src/utils/get-config";
2
+ import { resolveDependencies } from "@/src/utils/resolve-dependencies";
3
+ import { fetchAvailableRegistries, fetchRegistry } from "@/src/utils/fetch";
4
+ import { writeRegistryItemSnippets } from "@/src/utils/write";
5
+ import * as p from "@clack/prompts";
6
+ import path from "path";
7
+ import { z } from "zod";
8
+
9
+ import type { CAC } from "cac";
10
+ import { BASE_URL } from "../constants";
11
+ import { highlight } from "../utils/color";
12
+ import { installDependencies } from "../utils/install";
13
+
14
+ const addAllOptionsSchema = z.object({
15
+ registryIds: z.array(z.string()).optional(),
16
+ all: z.boolean(),
17
+ includeDeprecated: z.boolean().optional(),
18
+ cwd: z.string(),
19
+ baseUrl: z.string().optional(),
20
+ });
21
+
22
+ export const addAllCommand = (cli: CAC) => {
23
+ cli
24
+ .command("add-all [...registry-ids]", "add all items from registries")
25
+ .option("-a, --all", "Add all items from all registries", {
26
+ default: false,
27
+ })
28
+ .option("--include-deprecated", "Include deprecated items when used with `--all`", {
29
+ default: false,
30
+ })
31
+ .option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", {
32
+ default: process.cwd(),
33
+ })
34
+ .option(
35
+ "-u, --baseUrl <baseUrl>",
36
+ "the base url of the registry. defaults to the current directory.",
37
+ { default: BASE_URL },
38
+ )
39
+ .example("seed-design add-all ui --include-deprecated")
40
+ .example("seed-design add-all ui lib breeze")
41
+ .action(async (registryIds, opts) => {
42
+ p.intro("seed-design add-all");
43
+
44
+ const {
45
+ success,
46
+ data: options,
47
+ error,
48
+ } = addAllOptionsSchema.safeParse({ registryIds, ...opts });
49
+
50
+ if (!success) {
51
+ p.log.error(`잘못된 옵션이에요: ${error?.message}`);
52
+
53
+ process.exit(1);
54
+ }
55
+
56
+ const cwd = options.cwd;
57
+ const baseUrl = options.baseUrl;
58
+ const config = await getConfig(cwd);
59
+ const rootPath = path.resolve(cwd, config.path);
60
+
61
+ const { start, stop } = p.spinner();
62
+
63
+ start("Registry를 가져오고 있어요...");
64
+
65
+ const publicRegistries = await Promise.all(
66
+ (await fetchAvailableRegistries({ baseUrl })).map(async ({ id }) =>
67
+ fetchRegistry({ baseUrl, registryId: id }),
68
+ ),
69
+ );
70
+
71
+ stop("Registry를 가져왔어요.");
72
+
73
+ const selectedRegistryIds: string[] = await (async () => {
74
+ if (options.all) {
75
+ const ids = publicRegistries.map((r) => r.id);
76
+ p.log.message(`모든 레지스트리의 모든 항목을 추가합니다: ${highlight(ids.join(", "))}`);
77
+
78
+ return ids;
79
+ }
80
+
81
+ if (options.registryIds?.length) {
82
+ const availableIds = publicRegistries.map((r) => r.id);
83
+
84
+ for (const registryId of options.registryIds) {
85
+ if (!availableIds.includes(registryId)) {
86
+ p.log.error(`레지스트리 '${registryId}'를 찾을 수 없어요.`);
87
+ p.log.info(`사용 가능한 레지스트리: ${availableIds.join(", ")}`);
88
+
89
+ process.exit(1);
90
+ }
91
+ }
92
+
93
+ p.log.message(
94
+ `선택된 레지스트리의 모든 항목을 추가합니다: ${highlight(options.registryIds.join(", "))}`,
95
+ );
96
+
97
+ return options.registryIds;
98
+ }
99
+
100
+ const selected = await p.multiselect({
101
+ message: "추가할 레지스트리를 선택해주세요 (스페이스 바로 여러 개 선택 가능)",
102
+ options: publicRegistries
103
+ .filter(({ hideFromCLICatalog }) => !hideFromCLICatalog)
104
+ .sort((a, b) => b.items.length - a.items.length)
105
+ .map((registry) => ({
106
+ label: registry.id,
107
+ value: registry.id,
108
+ hint: `${registry.items.length}개 항목 (${registry.items[0].id} 등)`,
109
+ })),
110
+ });
111
+
112
+ if (p.isCancel(selected)) {
113
+ p.log.error("취소되었어요.");
114
+ process.exit(0);
115
+ }
116
+
117
+ p.log.message(`선택된 레지스트리의 항목을 추가합니다: ${highlight(selected.join(", "))}`);
118
+
119
+ return selected;
120
+ })();
121
+
122
+ const selectedRegistries = publicRegistries.filter((r) => selectedRegistryIds.includes(r.id));
123
+
124
+ const itemKeys = selectedRegistries.flatMap((registry) =>
125
+ registry.items
126
+ .filter((item) => {
127
+ if (item.deprecated) return options.includeDeprecated;
128
+
129
+ return true;
130
+ })
131
+ .map((item) => `${registry.id}:${item.id}`),
132
+ );
133
+
134
+ const deprecatedCount = selectedRegistries.flatMap((r) =>
135
+ r.items.filter((item) => item.deprecated).map(() => 1),
136
+ ).length;
137
+
138
+ if (!options.includeDeprecated && deprecatedCount > 0) {
139
+ p.log.info(
140
+ `${deprecatedCount}개의 deprecated 항목은 제외되었어요. --include-deprecated 옵션을 사용하면 추가할 수 있어요.`,
141
+ );
142
+ }
143
+
144
+ if (!itemKeys.length) {
145
+ p.log.error("추가할 항목이 없어요.");
146
+
147
+ process.exit(0);
148
+ }
149
+
150
+ p.log.message(`총 ${highlight(itemKeys.length.toString())}개의 항목을 추가합니다.`);
151
+
152
+ const { registryItemsToAdd, npmDependenciesToAdd } = resolveDependencies({
153
+ selectedItemKeys: itemKeys,
154
+ publicRegistries,
155
+ });
156
+
157
+ await writeRegistryItemSnippets({ registryItemsToAdd, rootPath, cwd, baseUrl, config });
158
+
159
+ const { installed, filtered } = await installDependencies({
160
+ cwd,
161
+ deps: Array.from(npmDependenciesToAdd),
162
+ });
163
+
164
+ if (installed.size === 0) {
165
+ p.log.message("모든 의존성이 이미 설치되어 있어요.");
166
+ }
167
+
168
+ if (installed.size) {
169
+ p.log.message(`의존성 설치 완료: ${highlight(Array.from(installed).join(", "))}`);
170
+
171
+ if (filtered.size) {
172
+ p.log.message(
173
+ `설치하지 않은 의존성 (이미 설치됨): ${highlight(Array.from(filtered).join(", "))}`,
174
+ );
175
+ }
176
+ }
177
+
178
+ p.outro("완료했어요.");
179
+ });
180
+ };
@@ -1,36 +1,30 @@
1
1
  import { getConfig } from "@/src/utils/get-config";
2
- import {
3
- fetchRegistryItem,
4
- getRegistryLibIndex,
5
- getRegistryUIIndex,
6
- } from "@/src/utils/get-metadata";
7
- import { transform } from "@/src/utils/transformers";
2
+ import { resolveDependencies } from "@/src/utils/resolve-dependencies";
3
+ import { fetchAvailableRegistries, fetchRegistry } from "@/src/utils/fetch";
4
+ import { writeRegistryItemSnippets } from "@/src/utils/write";
8
5
  import * as p from "@clack/prompts";
9
- import fs from "fs-extra";
10
6
  import path from "path";
11
- import color from "picocolors";
12
7
  import { z } from "zod";
13
8
 
14
9
  import type { CAC } from "cac";
15
10
  import { BASE_URL } from "../constants";
16
- import { addRelativeRegistries } from "../utils/add-relative-registries";
17
11
  import { highlight } from "../utils/color";
18
12
  import { installDependencies } from "../utils/install";
19
13
 
20
14
  const addOptionsSchema = z.object({
21
- components: z.array(z.string()).optional(),
22
- cwd: z.string(),
15
+ itemIds: z.array(z.string()).optional(),
16
+ /**
17
+ * @deprecated use `seed-design add-all` instead
18
+ */
23
19
  all: z.boolean(),
20
+ cwd: z.string(),
24
21
  baseUrl: z.string().optional(),
25
- // yes: z.boolean(),
26
- // overwrite: z.boolean(),
27
- // path: z.string().optional(),
28
22
  });
29
23
 
30
24
  export const addCommand = (cli: CAC) => {
31
25
  cli
32
- .command("add [...components]", "add component")
33
- .option("-a, --all", "Add all components", {
26
+ .command("add [...item-ids]", "add items")
27
+ .option("-a, --all", "[Deprecated] Add all items", {
34
28
  default: false,
35
29
  })
36
30
  .option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", {
@@ -39,169 +33,172 @@ export const addCommand = (cli: CAC) => {
39
33
  .option(
40
34
  "-u, --baseUrl <baseUrl>",
41
35
  "the base url of the registry. defaults to the current directory.",
42
- {
43
- default: BASE_URL,
44
- },
36
+ { default: BASE_URL },
45
37
  )
46
- .example("seed-design add action-button")
47
- .example("seed-design add alert-dialog")
48
- .action(async (components, opts) => {
49
- p.intro(color.bgCyan("seed-design add"));
50
- const options = addOptionsSchema.parse({
51
- components,
52
- ...opts,
53
- });
38
+ .example("seed-design add ui:action-button")
39
+ .example("seed-design add ui:alert-dialog")
40
+ .action(async (itemIds, opts) => {
41
+ p.intro("seed-design add");
42
+
43
+ const {
44
+ success,
45
+ data: { all, ...options },
46
+ error,
47
+ } = addOptionsSchema.safeParse({ itemIds, ...opts });
48
+
49
+ if (!success) {
50
+ p.log.error(`잘못된 옵션이에요: ${error?.message}`);
51
+
52
+ process.exit(1);
53
+ }
54
+
55
+ if (all) {
56
+ p.log.error(
57
+ "`--all` 옵션은 더 이상 지원되지 않아요. 대신 `seed-design add-all` 명령어를 사용해주세요.",
58
+ );
59
+
60
+ process.exit(1);
61
+ }
62
+
54
63
  const cwd = options.cwd;
55
64
  const baseUrl = options.baseUrl;
56
65
  const config = await getConfig(cwd);
57
- const registryComponentIndex = await getRegistryUIIndex(baseUrl);
58
- const libRegistryIndex = await getRegistryLibIndex(baseUrl);
59
- let selectedComponents: string[] = options.all
60
- ? registryComponentIndex.map((registry) => registry.name)
61
- : options.components;
62
-
63
- if (!options.components?.length && !options.all) {
64
- const selects = await p.multiselect<
65
- { label: string; value: string; hint: string }[],
66
- string
67
- >({
68
- message: "추가할 컴포넌트를 선택해주세요 (스페이스 바로 여러 개 선택 가능)",
69
- options: registryComponentIndex.map((metadata) => {
70
- return {
71
- label: metadata.name,
72
- value: metadata.name,
73
- hint: metadata.description,
74
- };
75
- }),
66
+ const rootPath = path.resolve(cwd, config.path);
67
+
68
+ const { start, stop } = p.spinner();
69
+
70
+ start("Registry를 가져오고 있어요...");
71
+
72
+ const publicRegistries = await Promise.all(
73
+ (await fetchAvailableRegistries({ baseUrl })).map(async ({ id }) =>
74
+ fetchRegistry({ baseUrl, registryId: id }),
75
+ ),
76
+ );
77
+
78
+ stop("Registry를 가져왔어요.");
79
+
80
+ const selectedItemKeys: string[] = await (async () => {
81
+ if (options.itemIds.length > 0) return options.itemIds;
82
+
83
+ const selected = await p.multiselect({
84
+ message: "추가할 항목을 선택해주세요 (스페이스 바로 여러 개 선택 가능)",
85
+ options: publicRegistries
86
+ .filter(({ hideFromCLICatalog }) => !hideFromCLICatalog)
87
+ .flatMap(({ id: registryId, items }) =>
88
+ items
89
+ .filter(({ hideFromCLICatalog }) => !hideFromCLICatalog)
90
+ .sort((a, b) => a.id.localeCompare(b.id))
91
+ .map(({ id, description, deprecated }) => ({
92
+ label: `${deprecated ? "(deprecated) " : ""}${highlight(registryId)}:${id}`,
93
+ value: `${registryId}:${id}`,
94
+ hint: description,
95
+
96
+ // used for sorting
97
+ deprecated,
98
+ registryItemCount: items.length,
99
+ })),
100
+ )
101
+ .sort((a, b) => {
102
+ if (a.deprecated !== b.deprecated) return a.deprecated ? 1 : -1;
103
+
104
+ return b.registryItemCount - a.registryItemCount;
105
+ }),
76
106
  });
77
107
 
78
- if (p.isCancel(selects)) {
108
+ if (p.isCancel(selected)) {
79
109
  p.log.error("취소되었어요.");
80
110
  process.exit(0);
81
111
  }
82
112
 
83
- selectedComponents = selects as string[];
84
- }
113
+ return selected;
114
+ })();
115
+
116
+ if (!selectedItemKeys?.length) {
117
+ p.log.error("항목을 찾을 수 없어요.");
85
118
 
86
- if (!selectedComponents?.length) {
87
- p.log.error("컴포넌트를 찾을 수 없어요.");
88
119
  process.exit(0);
89
120
  }
90
121
 
91
- p.log.message(`선택된 컴포넌트: ${highlight(selectedComponents.join(", "))}`);
122
+ p.log.message(`선택된 항목: ${highlight(selectedItemKeys.join(", "))}`);
92
123
 
93
- const allRelativeRegistries = addRelativeRegistries({
94
- userSelects: selectedComponents,
95
- uiRegistryIndex: registryComponentIndex,
96
- libRegistryIndex,
97
- });
98
-
99
- const allRegistryItems = [];
100
-
101
- const { start, stop } = p.spinner();
102
- start("Registry를 가져오고 있어요...");
124
+ const filteredItemKeys: string[] = [];
103
125
 
104
- for (const registry of allRelativeRegistries) {
105
- const registryItem = await fetchRegistryItem(registry.name, baseUrl, registry.type);
106
- allRegistryItems.push(registryItem);
107
- }
126
+ for (const itemKey of selectedItemKeys) {
127
+ const [registryId, ...rest] = itemKey.split(":");
128
+ const itemId = rest.join(":");
108
129
 
109
- stop();
130
+ if (!registryId || !itemId) {
131
+ p.log.error(
132
+ `${highlight(itemKey)}: 항목 이름이 잘못되었어요. ${highlight("ui:action-button")}과 같은 형식으로 입력해보세요.`,
133
+ );
110
134
 
111
- if (allRegistryItems.length) {
112
- const filteredRegistryItems = allRegistryItems.filter(
113
- (c) => !selectedComponents.includes(c.name),
114
- );
115
- p.log.message(
116
- `추가로 설치될 레지스트리: ${highlight(
117
- filteredRegistryItems.map((c) => c.name).join(", "),
118
- )}`,
119
- );
120
- }
135
+ process.exit(1);
136
+ }
121
137
 
122
- // 선택된 컴포넌트.json 레지스트리 파일 기반으로 컴포넌트를 추가합니다.
123
- const registryResult = [];
124
- const installResult = {
125
- installed: new Set(),
126
- filtered: new Set(),
127
- };
128
- for (const component of allRegistryItems) {
129
- for (const registry of component.registries) {
130
- let targetPath = "";
131
- switch (registry.type) {
132
- case "ui":
133
- targetPath = config.resolvedUIPaths;
134
- break;
135
- case "lib":
136
- targetPath = config.resolvedLibPaths;
137
- break;
138
- default:
139
- break;
140
- }
138
+ const foundItem = publicRegistries
139
+ .find((r) => r.id === registryId)
140
+ ?.items.find((i) => i.id === itemId);
141
141
 
142
- if (!fs.existsSync(targetPath)) {
143
- await fs.mkdir(targetPath, { recursive: true });
144
- }
142
+ if (!foundItem) {
143
+ p.log.error(`${highlight(itemKey)}: 항목을 찾을 수 없어요.`);
145
144
 
146
- let filePath = path.resolve(targetPath, registry.name);
145
+ process.exit(1);
146
+ }
147
147
 
148
- const content = await transform({
149
- filename: registry.name,
150
- config,
151
- raw: registry.content,
148
+ if (foundItem.deprecated) {
149
+ const confirm = await p.confirm({
150
+ message: `${highlight(foundItem.id)}: deprecated 되었어요. 추가할까요?`,
151
+ initialValue: false,
152
152
  });
153
153
 
154
- if (!config.tsx) {
155
- filePath = filePath.replace(/\.tsx$/, ".jsx");
156
- filePath = filePath.replace(/\.ts$/, ".js");
154
+ if (confirm === false || p.isCancel(confirm)) {
155
+ p.log.info(`${highlight(foundItem.id)}: 추가하지 않을게요.`);
156
+
157
+ continue;
157
158
  }
159
+ }
158
160
 
159
- await fs.writeFile(filePath, content);
160
- const relativePath = path.relative(cwd, filePath);
161
+ filteredItemKeys.push(itemKey);
162
+ }
161
163
 
162
- registryResult.push({
163
- name: registry.name,
164
- path: relativePath,
165
- });
166
- }
164
+ const { registryItemsToAdd, npmDependenciesToAdd } = resolveDependencies({
165
+ selectedItemKeys: filteredItemKeys,
166
+ publicRegistries,
167
+ });
167
168
 
168
- // Install dependencies.
169
- if (component.dependencies?.length) {
170
- const result = await installDependencies({ cwd, deps: component.dependencies });
171
- installResult.installed = new Set([...installResult.installed, ...result.installed]);
172
- installResult.filtered = new Set([...installResult.filtered, ...result.filtered]);
173
- }
169
+ p.log.info(
170
+ `추가할 항목: ${highlight(registryItemsToAdd.map((r) => r.items.map((i) => `${r.registryId}:${i.id}`).join(", ")).join(", ") || "없음")}
174
171
 
175
- // Install devDependencies.
176
- if (component.devDependencies?.length) {
177
- const result = await installDependencies({
178
- cwd,
179
- deps: component.devDependencies,
180
- dev: true,
181
- });
182
- installResult.installed = new Set([...installResult.installed, ...result.installed]);
183
- installResult.filtered = new Set([...installResult.filtered, ...result.filtered]);
184
- }
172
+ 설치할 의존성: ${highlight(Array.from(npmDependenciesToAdd).join(", ") || "없음")}`,
173
+ );
185
174
 
186
- p.log.success(`${highlight(component.name)} 관련 파일 추가 완료`);
187
- }
175
+ await writeRegistryItemSnippets({
176
+ registryItemsToAdd,
177
+ rootPath,
178
+ cwd,
179
+ baseUrl,
180
+ config,
181
+ });
188
182
 
189
- if (installResult.installed.size) {
190
- p.log.message(
191
- `설치된 의존성: ${highlight(Array.from(installResult.installed).join(", "))}`,
192
- );
193
- }
194
- if (installResult.filtered.size) {
195
- p.log.message(
196
- `이미 설치된 의존성: ${highlight(Array.from(installResult.filtered).join(", "))}`,
197
- );
183
+ const { installed, filtered } = await installDependencies({
184
+ cwd,
185
+ deps: Array.from(npmDependenciesToAdd),
186
+ });
187
+
188
+ if (installed.size === 0) {
189
+ p.log.message("모든 의존성이 이미 설치되어 있어요.");
198
190
  }
199
- if (registryResult.length) {
200
- for (const registry of registryResult) {
201
- p.log.message(`추가된 파일: ${highlight(registry.path)}`);
191
+
192
+ if (installed.size) {
193
+ p.log.message(`의존성 설치 완료: ${highlight(Array.from(installed).join(", "))}`);
194
+
195
+ if (filtered.size) {
196
+ p.log.message(
197
+ `설치하지 않은 의존성 (이미 설치됨): ${highlight(Array.from(filtered).join(", "))}`,
198
+ );
202
199
  }
203
200
  }
204
201
 
205
- p.outro("컴포넌트 추가 완료.");
202
+ p.outro("완료했어요.");
206
203
  });
207
204
  };