@seed-design/cli 0.0.3 → 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 S from"@clack/prompts";import{cosmiconfig as ce}from"cosmiconfig";import{execa as pe}from"execa";import b from"fs";import T from"path";import{z as I}from"zod";import se from"picocolors";var y=e=>se.cyan(e);import{detect as ae}from"@antfu/ni";async function U(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`]}),_=I.object({$schema:I.string().optional(),rsc:I.coerce.boolean().default(!1),tsx:I.coerce.boolean().default(!0),path:I.string()}).strict(),le=_.extend({resolvedUIPaths:I.string(),resolvedLibPaths:I.string()});async function J(e){let t=await fe(e);return t?await de(e,t):null}async function de(e,t){let r=T.resolve(e,t.path);b.existsSync(r)||b.mkdirSync(r,{recursive:!0});let o=T.join(r,"ui"),s=T.join(r,"lib");return b.existsSync(o)||b.mkdirSync(o,{recursive:!0}),b.existsSync(s)||b.mkdirSync(s,{recursive:!0}),le.parse({...t,resolvedUIPaths:o,resolvedLibPaths:s})}async function fe(e){try{let t=await me.search(e);return _.parse(t.config)}catch{if(S.log.error("\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uACBD\uB85C\uC5D0 `seed-design.json` \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694."),await S.confirm({message:"seed-design.json \uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uC2DC\uACA0\uC5B4\uC694?"})===!0){let r=await U(e);await pe(r,["seed-design","init","--default"],{cwd:e}),S.log.message("seed-design.json \uD30C\uC77C\uC774 \uC0DD\uC131\uB410\uC5B4\uC694.")}else S.outro(y("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}}import*as C from"@clack/prompts";import{z as c}from"zod";var ge=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()),deprecated:c.literal(!0).optional()}),k=c.array(N),ue=N.omit({files:!0}),ye=ue.extend({registries:c.array(c.object({name:c.string(),type:ge,content:c.string()}))}),tt=c.array(ye);async function G(e,t,r="ui"){return await Promise.all(e.map(async s=>{try{return await(await fetch(`${t}/__registry__/${r}/${s}.json`)).json()}catch(a){let p=await fetch(`${t}/__registry__/${r}/index.json`).then(g=>g.json()),l=k.parse(p).map(g=>g.name);C.log.error(`${r}:${s} \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC544\uC694.`),C.log.info(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8: ${l.join(", ")}`),C.log.info(JSON.stringify({baseUrl:t,error:a.toString()},null,2)),process.exit(1)}}))}async function B(e,t,r="ui"){let[o]=await G([e],t,r);return o}async function V(e){try{let[t]=await G(["index"],e,"ui");return k.parse(t)}catch(t){C.log.error("\uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC778\uB371\uC2A4\uB97C \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694."),C.log.info(JSON.stringify({baseUrl:e,error:t.toString()},null,2)),process.exit(1)}}async function K(e){let[t]=await G(["index"],e,"lib");return k.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 Ie={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 o=$.parse(r,{parser:{parse:a=>we(a,Ie)}}),s=he(o,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[xe],configFile:!1});if(!s||!s.ast)throw new Error("Failed to transform JSX");return $.print(s.ast).code};import{SyntaxKind as Se}from"ts-morph";var Y=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(Se.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 Me(e){let t=await Re.mkdtemp(q.join(be(),"seed-deisgn-"));return q.join(t,e)}async function H(e){let t=await Me(e.filename),r=je.createSourceFile(t,e.raw,{scriptKind:ve.TSX});for(let o of Pe)o({sourceFile:r,...e});return await X({sourceFile:r,...e})}import*as i from"@clack/prompts";import z from"fs-extra";import ee from"path";import Te from"picocolors";import{z as R}from"zod";var Q="https://seed-design.io";function W({userSelects:e,uiRegistryIndex:t,libRegistryIndex:r}){let o=[];function s({registryName:a,type:p}){if(o.some(l=>l.name===a&&l.type===p))return;o.push({type:p,name:a});let f=p==="ui"?t.find(l=>l.name===a):r.find(l=>l.name===a);if(f&&f.innerDependencies)for(let l of f.innerDependencies){let[g,M]=l.split(":");s({registryName:M,type:g})}}for(let a of e)s({registryName:a,type:"ui"});return Array.from(o)}import*as Z from"@clack/prompts";import{execa as Oe}from"execa";import Ae from"picocolors";import Ue from"findup-sync";import ke from"fs-extra";var $e="package.json";function De(){let e=Ue($e);if(!e)throw new Error("No package.json file found in the project.");return e}function D(){let e=De();return ke.readJSONSync(e)}async function L({cwd:e,deps:t,dev:r=!1}){let{start:o,stop:s}=Z.spinner(),a=await U(e),p=D(),f={...p.dependencies,...p.devDependencies},l=t.filter(w=>!f[w]),g=t.filter(w=>f[w]);if(!l.length)return{installed:new Set,filtered:new Set};o(Ae.gray("\uC758\uC874\uC131 \uC124\uCE58\uC911..."));let O=[a==="npm"?"install":"add",r?"-D":null,...l].filter(Boolean);try{await Oe(a,O,{cwd:e})}catch(w){console.error(`\uC758\uC874\uC131 \uC124\uCE58 \uC2E4\uD328: ${w}`),process.exit(1)}return s("\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC."),{installed:l,filtered:g}}var Ge=R.object({components:R.array(R.string()).optional(),all:R.boolean(),includeDeprecated:R.boolean().optional(),cwd:R.string(),baseUrl:R.string().optional()}),te=e=>{e.command("add [...components]","add component").option("-a, --all","Add all components",{default:!1}).option("--include-deprecated","Include deprecated components 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:Q}).example("seed-design add action-button").example("seed-design add alert-dialog").action(async(t,r)=>{i.intro(Te.bgCyan("seed-design add"));let o=Ge.parse({components:t,...r}),s=o.cwd,a=o.baseUrl,p=await J(s),f=await V(a),l=await K(a),g=await(async()=>{if(o.all)return o.includeDeprecated?f.map(n=>n.name):f.filter(({deprecated:n})=>!n).map(n=>n.name);if(o.components.length>0)return o.components;let d=await i.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:f.map(({name:n,description:h,deprecated:x})=>({label:`${x?"(deprecated) ":""}${n}`,value:n,hint:h,deprecated:x})).sort((n,h)=>n.deprecated===h.deprecated?n.label.localeCompare(h.label):n.deprecated?1:-1)});return i.isCancel(d)&&(i.log.error("\uCDE8\uC18C\uB418\uC5C8\uC5B4\uC694."),process.exit(0)),d})();g?.length||(i.log.error("\uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694."),process.exit(0)),i.log.message(`\uC120\uD0DD\uB41C \uCEF4\uD3EC\uB10C\uD2B8: ${y(g.join(", "))}`);let M=W({userSelects:g,uiRegistryIndex:f,libRegistryIndex:l}),v=[],{start:O,stop:w}=i.spinner();O("Registry\uB97C \uAC00\uC838\uC624\uACE0 \uC788\uC5B4\uC694...");for(let d of M){let n=await B(d.name,a,d.type);v.push(n)}if(w(),v.length){let d=v.filter(n=>!g.includes(n.name));i.log.message(`\uCD94\uAC00\uB85C \uC124\uCE58\uB420 \uB808\uC9C0\uC2A4\uD2B8\uB9AC: ${y(d.map(n=>n.name).join(", "))}`)}let A=[],u={installed:new Set,filtered:new Set};for(let d of v){if(d.deprecated&&!o.includeDeprecated){let n=await i.confirm({message:`${y(d.name)}\uB294 deprecated \uB418\uC5C8\uC5B4\uC694. \uCD94\uAC00\uD560\uAE4C\uC694?`,initialValue:!1});if(n===!1||i.isCancel(n)){i.log.info(`${y(d.name)} \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uCD94\uAC00\uD558\uC9C0 \uC54A\uC744\uAC8C\uC694.`);continue}}for(let n of d.registries){let h="";switch(n.type){case"ui":h=p.resolvedUIPaths;break;case"lib":h=p.resolvedLibPaths;break;default:break}z.existsSync(h)||await z.mkdir(h,{recursive:!0});let x=ee.resolve(h,n.name),oe=await H({filename:n.name,config:p,raw:n.content});p.tsx||(x=x.replace(/\.tsx$/,".jsx"),x=x.replace(/\.ts$/,".js")),await z.writeFile(x,oe);let ie=ee.relative(s,x);A.push({name:n.name,path:ie})}if(d.dependencies?.length){let n=await L({cwd:s,deps:d.dependencies});u.installed=new Set([...u.installed,...n.installed]),u.filtered=new Set([...u.filtered,...n.filtered])}if(d.devDependencies?.length){let n=await L({cwd:s,deps:d.devDependencies,dev:!0});u.installed=new Set([...u.installed,...n.installed]),u.filtered=new Set([...u.filtered,...n.filtered])}i.log.success(`${y(d.name)} \uAD00\uB828 \uD30C\uC77C \uCD94\uAC00 \uC644\uB8CC`)}if(u.installed.size&&i.log.message(`\uC124\uCE58\uB41C \uC758\uC874\uC131: ${y(Array.from(u.installed).join(", "))}`),u.filtered.size&&i.log.message(`\uC774\uBBF8 \uC124\uCE58\uB41C \uC758\uC874\uC131: ${y(Array.from(u.filtered).join(", "))}`),A.length)for(let d of A)i.log.message(`\uCD94\uAC00\uB41C \uD30C\uC77C: ${y(d.path)}`);i.outro("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC644\uB8CC.")})};import*as m 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=p=>P.cyan(p);m.intro(P.bgCyan("seed-design.json \uD30C\uC77C \uC0DD\uC131"));let o=ze.parse(t),s=o.yes,a={rsc:!1,tsx:!0,path:"./seed-design"};s||(a={...await m.group({tsx:()=>m.confirm({message:`${r("TypeScript")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!0}),rsc:()=>m.confirm({message:`${r("React Server Components")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!1}),path:()=>m.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:()=>{m.cancel("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694."),process.exit(0)}})});try{let{start:p,stop:f}=m.spinner();p("seed-design.json \uD30C\uC77C \uC0DD\uC131\uC911...");let l=re.resolve(o.cwd,"seed-design.json");await Le.writeFile(l,`${JSON.stringify(a,null,2)}
3
- `,"utf-8");let g=re.relative(process.cwd(),l);f(`seed-design.json \uD30C\uC77C\uC774 ${r(g)}\uC5D0 \uC0DD\uC131\uB410\uC5B4\uC694.`),m.log.info(P.gray("seed-design add {component} \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD574\uBCF4\uC138\uC694!")),m.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.")),m.outro("\uC791\uC5C5\uC774 \uC644\uB8CC\uB410\uC5B4\uC694.")}catch(p){m.log.error(`seed-design.json \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694. ${p}`),m.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=D();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.3",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "lint:publish": "bun publint"
26
26
  },
27
27
  "dependencies": {
28
- "@antfu/ni": "^25.0.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",
@@ -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": "^26.0.0",
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,41 +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
- import type { RegistryUIMachineGenerated } from "../schema";
20
13
 
21
14
  const addOptionsSchema = z.object({
22
- components: z.array(z.string()).optional(),
15
+ itemIds: z.array(z.string()).optional(),
16
+ /**
17
+ * @deprecated use `seed-design add-all` instead
18
+ */
23
19
  all: z.boolean(),
24
- includeDeprecated: z.boolean().optional(),
25
20
  cwd: z.string(),
26
21
  baseUrl: z.string().optional(),
27
- // yes: z.boolean(),
28
- // overwrite: z.boolean(),
29
- // path: z.string().optional(),
30
22
  });
31
23
 
32
24
  export const addCommand = (cli: CAC) => {
33
25
  cli
34
- .command("add [...components]", "add component")
35
- .option("-a, --all", "Add all components", {
36
- default: false,
37
- })
38
- .option("--include-deprecated", "Include deprecated components when used with `--all`", {
26
+ .command("add [...item-ids]", "add items")
27
+ .option("-a, --all", "[Deprecated] Add all items", {
39
28
  default: false,
40
29
  })
41
30
  .option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", {
@@ -44,46 +33,75 @@ export const addCommand = (cli: CAC) => {
44
33
  .option(
45
34
  "-u, --baseUrl <baseUrl>",
46
35
  "the base url of the registry. defaults to the current directory.",
47
- {
48
- default: BASE_URL,
49
- },
36
+ { default: BASE_URL },
50
37
  )
51
- .example("seed-design add action-button")
52
- .example("seed-design add alert-dialog")
53
- .action(async (components, opts) => {
54
- p.intro(color.bgCyan("seed-design add"));
55
- const options = addOptionsSchema.parse({
56
- components,
57
- ...opts,
58
- });
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
+
59
63
  const cwd = options.cwd;
60
64
  const baseUrl = options.baseUrl;
61
65
  const config = await getConfig(cwd);
62
- const registryComponentIndex = await getRegistryUIIndex(baseUrl);
63
- const libRegistryIndex = await getRegistryLibIndex(baseUrl);
66
+ const rootPath = path.resolve(cwd, config.path);
64
67
 
65
- const selectedComponents: string[] = await (async () => {
66
- if (options.all) {
67
- if (options.includeDeprecated) return registryComponentIndex.map((c) => c.name);
68
+ const { start, stop } = p.spinner();
68
69
 
69
- return registryComponentIndex.filter(({ deprecated }) => !deprecated).map((c) => c.name);
70
- }
70
+ start("Registry를 가져오고 있어요...");
71
+
72
+ const publicRegistries = await Promise.all(
73
+ (await fetchAvailableRegistries({ baseUrl })).map(async ({ id }) =>
74
+ fetchRegistry({ baseUrl, registryId: id }),
75
+ ),
76
+ );
71
77
 
72
- if (options.components.length > 0) return options.components;
78
+ stop("Registry를 가져왔어요.");
79
+
80
+ const selectedItemKeys: string[] = await (async () => {
81
+ if (options.itemIds.length > 0) return options.itemIds;
73
82
 
74
83
  const selected = await p.multiselect({
75
- message: "추가할 컴포넌트를 선택해주세요 (스페이스 바로 여러 개 선택 가능)",
76
- options: registryComponentIndex
77
- .map(({ name, description, deprecated }) => ({
78
- label: `${deprecated ? "(deprecated) " : ""}${name}`,
79
- value: name,
80
- hint: description,
81
- deprecated,
82
- }))
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
+ )
83
101
  .sort((a, b) => {
84
- if (a.deprecated === b.deprecated) return a.label.localeCompare(b.label);
102
+ if (a.deprecated !== b.deprecated) return a.deprecated ? 1 : -1;
85
103
 
86
- return a.deprecated ? 1 : -1;
104
+ return b.registryItemCount - a.registryItemCount;
87
105
  }),
88
106
  });
89
107
 
@@ -95,138 +113,92 @@ export const addCommand = (cli: CAC) => {
95
113
  return selected;
96
114
  })();
97
115
 
98
- if (!selectedComponents?.length) {
99
- p.log.error("컴포넌트를 찾을 수 없어요.");
116
+ if (!selectedItemKeys?.length) {
117
+ p.log.error("항목을 찾을 수 없어요.");
118
+
100
119
  process.exit(0);
101
120
  }
102
121
 
103
- p.log.message(`선택된 컴포넌트: ${highlight(selectedComponents.join(", "))}`);
122
+ p.log.message(`선택된 항목: ${highlight(selectedItemKeys.join(", "))}`);
104
123
 
105
- const allRelativeRegistries = addRelativeRegistries({
106
- userSelects: selectedComponents,
107
- uiRegistryIndex: registryComponentIndex,
108
- libRegistryIndex,
109
- });
124
+ const filteredItemKeys: string[] = [];
110
125
 
111
- const allRegistryItems: RegistryUIMachineGenerated = [];
126
+ for (const itemKey of selectedItemKeys) {
127
+ const [registryId, ...rest] = itemKey.split(":");
128
+ const itemId = rest.join(":");
112
129
 
113
- const { start, stop } = p.spinner();
114
- start("Registry를 가져오고 있어요...");
130
+ if (!registryId || !itemId) {
131
+ p.log.error(
132
+ `${highlight(itemKey)}: 항목 이름이 잘못되었어요. ${highlight("ui:action-button")}과 같은 형식으로 입력해보세요.`,
133
+ );
115
134
 
116
- for (const registry of allRelativeRegistries) {
117
- const registryItem = await fetchRegistryItem(registry.name, baseUrl, registry.type);
118
- allRegistryItems.push(registryItem);
119
- }
135
+ process.exit(1);
136
+ }
120
137
 
121
- stop();
138
+ const foundItem = publicRegistries
139
+ .find((r) => r.id === registryId)
140
+ ?.items.find((i) => i.id === itemId);
122
141
 
123
- if (allRegistryItems.length) {
124
- const filteredRegistryItems = allRegistryItems.filter(
125
- (c) => !selectedComponents.includes(c.name),
126
- );
127
- p.log.message(
128
- `추가로 설치될 레지스트리: ${highlight(
129
- filteredRegistryItems.map((c) => c.name).join(", "),
130
- )}`,
131
- );
132
- }
142
+ if (!foundItem) {
143
+ p.log.error(`${highlight(itemKey)}: 항목을 찾을 수 없어요.`);
144
+
145
+ process.exit(1);
146
+ }
133
147
 
134
- // 선택된 컴포넌트.json 레지스트리 파일 기반으로 컴포넌트를 추가합니다.
135
- const registryResult = [];
136
- const installResult = {
137
- installed: new Set(),
138
- filtered: new Set(),
139
- };
140
- for (const component of allRegistryItems) {
141
- if (component.deprecated && !options.includeDeprecated) {
148
+ if (foundItem.deprecated) {
142
149
  const confirm = await p.confirm({
143
- message: `${highlight(component.name)} deprecated 되었어요. 추가할까요?`,
150
+ message: `${highlight(foundItem.id)}: deprecated 되었어요. 추가할까요?`,
144
151
  initialValue: false,
145
152
  });
146
153
 
147
154
  if (confirm === false || p.isCancel(confirm)) {
148
- p.log.info(`${highlight(component.name)} 컴포넌트는 추가하지 않을게요.`);
155
+ p.log.info(`${highlight(foundItem.id)}: 추가하지 않을게요.`);
149
156
 
150
157
  continue;
151
158
  }
152
159
  }
153
160
 
154
- for (const registry of component.registries) {
155
- let targetPath = "";
156
- switch (registry.type) {
157
- case "ui":
158
- targetPath = config.resolvedUIPaths;
159
- break;
160
- case "lib":
161
- targetPath = config.resolvedLibPaths;
162
- break;
163
- default:
164
- break;
165
- }
166
-
167
- if (!fs.existsSync(targetPath)) {
168
- await fs.mkdir(targetPath, { recursive: true });
169
- }
170
-
171
- let filePath = path.resolve(targetPath, registry.name);
172
-
173
- const content = await transform({
174
- filename: registry.name,
175
- config,
176
- raw: registry.content,
177
- });
161
+ filteredItemKeys.push(itemKey);
162
+ }
178
163
 
179
- if (!config.tsx) {
180
- filePath = filePath.replace(/\.tsx$/, ".jsx");
181
- filePath = filePath.replace(/\.ts$/, ".js");
182
- }
164
+ const { registryItemsToAdd, npmDependenciesToAdd } = resolveDependencies({
165
+ selectedItemKeys: filteredItemKeys,
166
+ publicRegistries,
167
+ });
183
168
 
184
- await fs.writeFile(filePath, content);
185
- const relativePath = path.relative(cwd, filePath);
169
+ p.log.info(
170
+ `추가할 항목: ${highlight(registryItemsToAdd.map((r) => r.items.map((i) => `${r.registryId}:${i.id}`).join(", ")).join(", ") || "없음")}
186
171
 
187
- registryResult.push({
188
- name: registry.name,
189
- path: relativePath,
190
- });
191
- }
172
+ 설치할 의존성: ${highlight(Array.from(npmDependenciesToAdd).join(", ") || "없음")}`,
173
+ );
192
174
 
193
- // Install dependencies.
194
- if (component.dependencies?.length) {
195
- const result = await installDependencies({ cwd, deps: component.dependencies });
196
- installResult.installed = new Set([...installResult.installed, ...result.installed]);
197
- installResult.filtered = new Set([...installResult.filtered, ...result.filtered]);
198
- }
175
+ await writeRegistryItemSnippets({
176
+ registryItemsToAdd,
177
+ rootPath,
178
+ cwd,
179
+ baseUrl,
180
+ config,
181
+ });
199
182
 
200
- // Install devDependencies.
201
- if (component.devDependencies?.length) {
202
- const result = await installDependencies({
203
- cwd,
204
- deps: component.devDependencies,
205
- dev: true,
206
- });
207
- installResult.installed = new Set([...installResult.installed, ...result.installed]);
208
- installResult.filtered = new Set([...installResult.filtered, ...result.filtered]);
209
- }
183
+ const { installed, filtered } = await installDependencies({
184
+ cwd,
185
+ deps: Array.from(npmDependenciesToAdd),
186
+ });
210
187
 
211
- p.log.success(`${highlight(component.name)} 관련 파일 추가 완료`);
188
+ if (installed.size === 0) {
189
+ p.log.message("모든 의존성이 이미 설치되어 있어요.");
212
190
  }
213
191
 
214
- if (installResult.installed.size) {
215
- p.log.message(
216
- `설치된 의존성: ${highlight(Array.from(installResult.installed).join(", "))}`,
217
- );
218
- }
219
- if (installResult.filtered.size) {
220
- p.log.message(
221
- `이미 설치된 의존성: ${highlight(Array.from(installResult.filtered).join(", "))}`,
222
- );
223
- }
224
- if (registryResult.length) {
225
- for (const registry of registryResult) {
226
- p.log.message(`추가된 파일: ${highlight(registry.path)}`);
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
+ );
227
199
  }
228
200
  }
229
201
 
230
- p.outro("컴포넌트 추가 완료.");
202
+ p.outro("완료했어요.");
231
203
  });
232
204
  };