@seed-design/cli 0.0.0-alpha-20241204134404 → 0.0.0-alpha-20250210081704

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/README.md CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  ### How to test
4
4
 
5
- - Run `yarn dev` in `@seed-design/component-docs` (snippet download)
6
- - Run `yarn dev` here (watch mode)
7
- - You can test `yarn seed-design <command>` in `@seed-design/example/cli` terminal
5
+ - Run `bun dev` in `@seed-design/component-docs` (snippet download)
6
+ - Run `bun dev` here (watch mode)
7
+ - You can test `bun seed-design <command>` in `@seed-design/example/cli` terminal
package/bin/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import*as S from"@clack/prompts";import{cosmiconfig as ie}from"cosmiconfig";import{execa as ae}from"execa";import u from"fs";import k from"path";import{z as d}from"zod";import ne from"picocolors";var x=e=>ne.cyan(e);import{detect as se}from"@antfu/ni";async function R(e){let t=await se({programmatic:!0,cwd:e});return t==="yarn@berry"?"yarn":t==="pnpm@6"?"pnpm":t==="bun"?"bun":t??"npm"}var E="seed-design",ce=ie(E,{searchPlaces:[`${E}.json`]}),D=d.object({$schema:d.string().optional(),rsc:d.coerce.boolean().default(!1),tsx:d.coerce.boolean().default(!0),css:d.coerce.boolean().default(!0),path:d.string()}).strict(),pe=D.extend({resolvedUIPaths:d.string(),resolbedHookPaths:d.string(),resolvedUtilPaths:d.string()});async function F(e){let t=await fe(e);return t?await me(e,t):null}async function me(e,t){let r=k.resolve(e,t.path);u.existsSync(r)||u.mkdirSync(r,{recursive:!0});let n=k.join(r,"ui"),o=k.join(r,"hook"),c=k.join(r,"util");return u.existsSync(n)||u.mkdirSync(n,{recursive:!0}),u.existsSync(o)||u.mkdirSync(o,{recursive:!0}),u.existsSync(c)||u.mkdirSync(c,{recursive:!0}),pe.parse({...t,resolvedUIPaths:n,resolbedHookPaths:o,resolvedUtilPaths:c})}async function fe(e){try{let t=await ce.search(e);return D.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 R(e);await ae(r,["seed-design","init","--default"],{cwd:e}),S.log.message("seed-design.json \uD30C\uC77C\uC774 \uC0DD\uC131\uB410\uC5B4\uC694.")}else S.outro(x("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}}import*as w from"@clack/prompts";import{z as s}from"zod";var le=s.union([s.literal("ui"),s.literal("hook"),s.literal("util")]),_=s.object({name:s.string(),description:s.string().optional(),dependencies:s.array(s.string()).optional(),devDependencies:s.array(s.string()).optional(),innerDependencies:s.array(s.string()).optional(),files:s.array(s.string())}),b=s.array(_),ge=_.omit({files:!0}),de=ge.extend({registries:s.array(s.object({name:s.string(),type:le,content:s.string()}))}),qe=s.array(de);async function M(e,t){return await Promise.all(e.map(async n=>{try{return await(await fetch(`${t}/__registry__/ui/${n}.json`)).json()}catch(o){let c=await fetch(`${t}/__registry__/ui/index.json`).then(f=>f.json()),h=b.parse(c).map(f=>f.name);w.log.error(`${n} \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC544\uC694.`),w.log.info(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8: ${h.join(", ")}`),w.log.info(JSON.stringify({baseUrl:t,error:o.toString()},null,2)),process.exit(1)}}))}async function J(e){try{let[t]=await M(["index"],e);return b.parse(t)}catch(t){w.log.error("\uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC778\uB371\uC2A4\uB97C \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694."),w.log.info(JSON.stringify({baseUrl:e,error:t.toString()},null,2)),process.exit(1)}}import{promises as Ie}from"fs";import{tmpdir as we}from"os";import V from"path";import{transformFromAstSync as ye}from"@babel/core";import ue from"@babel/plugin-transform-typescript";import*as j from"recast";import{parse as he}from"@babel/parser";var xe={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"]},N=async({sourceFile:e,config:t})=>{let r=e.getFullText();if(t.tsx)return r;let n=j.parse(r,{parser:{parse:c=>he(c,xe)}}),o=ye(n,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[ue],configFile:!1});if(!o||!o.ast)throw new Error("Failed to transform JSX");return j.print(o.ast).code};import{SyntaxKind as Se}from"ts-morph";var H=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(Se.ExpressionStatement);return r?.getText()==='"use client";'&&r.remove(),e};var L=async({sourceFile:e,config:t})=>{if(t.css)return e;let n=e.getImportDeclarations().filter(o=>o.getModuleSpecifierValue().endsWith(".css"));for(let o of n)o.remove();return e};import{Project as Ce,ScriptKind as ve}from"ts-morph";var Pe=[H,L],Ue=new Ce({compilerOptions:{}});async function Re(e){let t=await Ie.mkdtemp(V.join(we(),"seed-deisgn-"));return V.join(t,e)}async function B(e){let t=await Re(e.filename),r=Ue.createSourceFile(t,e.raw,{scriptKind:ve.TSX});for(let n of Pe)n({sourceFile:r,...e});return await N({sourceFile:r,...e})}import*as a from"@clack/prompts";import{execa as W}from"execa";import O from"fs-extra";import q from"path";import $ from"picocolors";import{z as C}from"zod";function K(e,t){let r=new Set;function n(o){if(r.has(o))return;r.add(o);let c=t.find(p=>p.name===o);if(c&&c.innerDependencies)for(let p of c.innerDependencies)n(p)}for(let o of e)n(o);return Array.from(r)}var X="https://v3.seed-design.io";var ke=C.object({components:C.array(C.string()).optional(),cwd:C.string(),all:C.boolean(),baseUrl:C.string().optional()}),Q=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:X}).example("seed-design add box-button").example("seed-design add alert-dialog").action(async(t,r)=>{a.intro($.bgCyan("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00\uD558\uAE30"));let n=ke.parse({components:t,...r}),o=n.cwd,c=n.baseUrl,p=await F(o),h=await J(c),f=n.all?h.map(m=>m.name):n.components;if(!n.components?.length&&!n.all){let m=await a.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:h.map(y=>({label:y.name,value:y.name,hint:y.description}))});a.isCancel(m)&&(a.log.error("\uCDE8\uC18C\uB418\uC5C8\uC5B4\uC694."),process.exit(0)),f=m}f?.length||(a.log.error("\uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4\uC694."),process.exit(0));let U=K(f,h),T=U.filter(m=>!f.includes(m)),te=await M(U,c);a.log.message(`\uC120\uD0DD\uB41C \uCEF4\uD3EC\uB10C\uD2B8: ${x(f.join(", "))}`),T.length&&a.log.message(`\uB0B4\uBD80 \uC758\uC874\uC131: ${x(T.join(", "))} \uCD94\uAC00\uB429\uB2C8\uB2E4.`);for(let m of te){for(let l of m.registries){let g="";switch(l.type){case"ui":g=p.resolvedUIPaths;break;case"hook":g=p.resolbedHookPaths;break;case"util":g=p.resolvedUtilPaths;break;default:break}O.existsSync(g)||await O.mkdir(g,{recursive:!0});let I=q.resolve(g,l.name),re=await B({filename:l.name,config:p,raw:l.content});p.tsx||(I=I.replace(/\.tsx$/,".jsx"),I=I.replace(/\.ts$/,".js")),await O.writeFile(I,re);let oe=q.relative(o,I);a.log.info(`${x(l.name)} \uCD94\uAC00\uB428: ${x(oe)}`)}let y=await R(o),{start:z,stop:A}=a.spinner();if(m.dependencies?.length){z($.gray("\uC758\uC874\uC131 \uC124\uCE58\uC911..."));let l=await W(y,[y==="npm"?"install":"add",...m.dependencies],{cwd:o});if(l.failed)console.error(l.all),process.exit(1);else{for(let g of m.dependencies)a.log.info(`- ${g}`);A("\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC.")}}if(m.devDependencies?.length){z($.gray("\uAC1C\uBC1C \uC758\uC874\uC131 \uC124\uCE58\uC911..."));let l=await W(y,[y==="npm"?"install":"add","-D",...m.devDependencies],{cwd:o});if(l.failed)console.error(l.all),process.exit(1);else{for(let g of m.devDependencies)a.log.info(`- ${g}`);A("\uAC1C\uBC1C \uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC.")}}}a.outro(`${t.join(", ")} \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD588\uC5B4\uC694.`)})};import*as i from"@clack/prompts";import je from"fs-extra";import Y from"path";import v from"picocolors";import{z as G}from"zod";var be=G.object({cwd:G.string(),default:G.boolean().optional()}),Z=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("-d, --default","\uBAA8\uB4E0 \uC9C8\uBB38\uC5D0 \uB300\uD574 \uAE30\uBCF8\uAC12\uC73C\uB85C \uB2F5\uBCC0\uD569\uB2C8\uB2E4.").action(async t=>{let r=p=>v.cyan(p);i.intro(v.bgCyan("seed-design.json \uD30C\uC77C \uC0DD\uC131"));let n=be.parse(t),o=n.default,c={rsc:!1,tsx:!0,css:!0,path:"./seed-design"};o||(c={...await i.group({tsx:()=>i.confirm({message:`${r("TypeScript")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!0}),rsc:()=>i.confirm({message:`${r("React Server Components")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!1}),css:()=>i.confirm({message:`${r("CSS Loader")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694? (true\uC77C \uACBD\uC6B0 \uCEF4\uD3EC\uB10C\uD2B8\uC5D0 CSS import\uAC00 \uCD94\uAC00\uB429\uB2C8\uB2E4.)`,initialValue:!0}),path:()=>i.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:()=>{i.cancel("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694."),process.exit(0)}})});try{let{start:p,stop:h}=i.spinner();p("seed-design.json \uD30C\uC77C \uC0DD\uC131\uC911...");let f=Y.resolve(n.cwd,"seed-design.json");await je.writeFile(f,`${JSON.stringify(c,null,2)}
3
- `,"utf-8");let U=Y.relative(process.cwd(),f);h(`seed-design.json \uD30C\uC77C\uC774 ${r(U)}\uC5D0 \uC0DD\uC131\uB410\uC5B4\uC694.`),i.log.info(v.gray("seed-design add {component} \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD574\uBCF4\uC138\uC694!")),i.log.info(v.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.")),i.outro("\uC791\uC5C5\uC774 \uC644\uB8CC\uB410\uC5B4\uC694.")}catch(p){i.log.error(`seed-design.json \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694. ${p}`),i.outro(v.bgRed("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}})};import Me from"findup-sync";import Oe from"fs-extra";var $e="package.json";function Ge(){let e=Me($e);if(!e)throw new Error("No package.json file found in the project.");return e}function ee(){return Oe.readJSONSync(Ge())}import{cac as Te}from"cac";var ze="seed-design",P=Te(ze);async function Ae(){let e=ee();Q(P),Z(P),P.version(e.version||"1.0.0","-v, --version"),P.help(),P.parse()}Ae();
2
+ import*as S from"@clack/prompts";import{cosmiconfig as pe}from"cosmiconfig";import{execa as me}from"execa";import I from"fs";import D from"path";import{z as h}from"zod";import ae from"picocolors";var y=e=>ae.cyan(e);import{detect as ce}from"@antfu/ni";async function M(e){let t=await ce({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",le=pe(F,{searchPlaces:[`${F}.json`]}),_=h.object({$schema:h.string().optional(),rsc:h.coerce.boolean().default(!1),tsx:h.coerce.boolean().default(!0),css:h.coerce.boolean().default(!0),path:h.string()}).strict(),fe=_.extend({resolvedUIPaths:h.string(),resolvedLibPaths:h.string()});async function J(e){let t=await de(e);return t?await ge(e,t):null}async function ge(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}),fe.parse({...t,resolvedUIPaths:n,resolvedLibPaths:o})}async function de(e){try{let t=await le.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 M(e);await me(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 b from"@clack/prompts";import{z as p}from"zod";var ue=p.union([p.literal("ui"),p.literal("lib")]),N=p.object({name:p.string(),description:p.string().optional(),dependencies:p.array(p.string()).optional(),devDependencies:p.array(p.string()).optional(),innerDependencies:p.array(p.string()).optional(),files:p.array(p.string())}),U=p.array(N),ye=N.omit({files:!0}),he=ye.extend({registries:p.array(p.object({name:p.string(),type:ue,content:p.string()}))}),rt=p.array(he);async function L(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 c=await fetch(`${t}/__registry__/${r}/index.json`).then(g=>g.json()),m=U.parse(c).map(g=>g.name);b.log.error(`${r}:${o} \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC544\uC694.`),b.log.info(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8: ${m.join(", ")}`),b.log.info(JSON.stringify({baseUrl:t,error:s.toString()},null,2)),process.exit(1)}}))}async function B(e,t,r="ui"){let[n]=await L([e],t,r);return n}async function V(e){try{let[t]=await L(["index"],e,"ui");return U.parse(t)}catch(t){b.log.error("\uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC778\uB371\uC2A4\uB97C \uAC00\uC838\uC624\uB294 \uB370 \uC2E4\uD328\uD588\uC5B4\uC694."),b.log.info(JSON.stringify({baseUrl:e,error:t.toString()},null,2)),process.exit(1)}}async function K(e){let[t]=await L(["index"],e,"lib");return U.parse(t)}import{promises as Re}from"fs";import{tmpdir as Ce}from"os";import q from"path";import{transformFromAstSync as xe}from"@babel/core";import Se 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 n=$.parse(r,{parser:{parse:s=>we(s,Ie)}}),o=xe(n,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[Se],configFile:!1});if(!o||!o.ast)throw new Error("Failed to transform JSX");return $.print(o.ast).code};import{SyntaxKind as be}from"ts-morph";var W=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(be.ExpressionStatement);return r?.getText()==='"use client";'&&r.remove(),e};var Y=async({sourceFile:e,config:t})=>{if(t.css)return e;let n=e.getImportDeclarations().filter(o=>o.getModuleSpecifierValue().endsWith(".css"));for(let o of n)o.remove();return e};import{Project as ve,ScriptKind as Pe}from"ts-morph";var je=[W,Y],ke=new ve({compilerOptions:{}});async function Me(e){let t=await Re.mkdtemp(q.join(Ce(),"seed-deisgn-"));return q.join(t,e)}async function H(e){let t=await Me(e.filename),r=ke.createSourceFile(t,e.raw,{scriptKind:Pe.TSX});for(let n of je)n({sourceFile:r,...e});return await X({sourceFile:r,...e})}import*as l from"@clack/prompts";import z from"fs-extra";import te from"path";import Le from"picocolors";import{z as R}from"zod";var Q="https://v3.seed-design.io";function Z({userSelects:e,uiRegistryIndex:t,libRegistryIndex:r}){let n=[];function o({registryName:s,type:c}){if(n.some(m=>m.name===s&&m.type===c))return;n.push({type:c,name:s});let d=c==="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 ee from"@clack/prompts";import{execa as Ae}from"execa";import De from"picocolors";import Ue from"findup-sync";import $e from"fs-extra";var Te="package.json";function Oe(){let e=Ue(Te);if(!e)throw new Error("No package.json file found in the project.");return e}function T(){let e=Oe();return $e.readJSONSync(e)}async function G({cwd:e,deps:t,dev:r=!1}){let{start:n,stop:o}=ee.spinner(),s=await M(e),c=T(),d={...c.dependencies,...c.devDependencies},m=t.filter(x=>!d[x]),g=t.filter(x=>d[x]);if(!m.length)return{installed:new Set,filtered:new Set};n(De.gray("\uC758\uC874\uC131 \uC124\uCE58\uC911..."));let O=[s==="npm"?"install":"add",r?"-D":null,...m].filter(Boolean);try{await Ae(s,O,{cwd:e})}catch(x){console.error(`\uC758\uC874\uC131 \uC124\uCE58 \uC2E4\uD328: ${x}`),process.exit(1)}return o("\uC758\uC874\uC131 \uC124\uCE58 \uC644\uB8CC."),{installed:m,filtered:g}}var Ge=R.object({components:R.array(R.string()).optional(),cwd:R.string(),all:R.boolean(),baseUrl:R.string().optional()}),re=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 box-button").example("seed-design add alert-dialog").action(async(t,r)=>{l.intro(Le.bgCyan("seed-design add"));let n=Ge.parse({components:t,...r}),o=n.cwd,s=n.baseUrl,c=await J(o),d=await V(s),m=await K(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=Z({userSelects:g,uiRegistryIndex:d,libRegistryIndex:m}),C=[],{start:O,stop:x}=l.spinner();O("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(x(),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 A=[],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=c.resolvedUIPaths;break;case"lib":v=c.resolvedLibPaths;break;default:break}z.existsSync(v)||await z.mkdir(v,{recursive:!0});let w=te.resolve(v,i.name),se=await H({filename:i.name,config:c,raw:i.content});c.tsx||(w=w.replace(/\.tsx$/,".jsx"),w=w.replace(/\.ts$/,".js")),await z.writeFile(w,se);let ie=te.relative(o,w);A.push({name:i.name,path:ie})}if(f.dependencies?.length){let i=await G({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 G({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(", "))}`),A.length)for(let f of A)l.log.message(`\uCD94\uAC00\uB41C \uD30C\uC77C: ${y(f.path)}`);l.outro("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC644\uB8CC.")})};import*as a from"@clack/prompts";import ze from"fs-extra";import ne from"path";import P from"picocolors";import{z as E}from"zod";var Ee=E.object({cwd:E.string(),yes:E.boolean().optional()}),oe=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=c=>P.cyan(c);a.intro(P.bgCyan("seed-design.json \uD30C\uC77C \uC0DD\uC131"));let n=Ee.parse(t),o=n.yes,s={rsc:!1,tsx:!0,css:!0,path:"./seed-design"};o||(s={...await a.group({tsx:()=>a.confirm({message:`${r("TypeScript")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!0}),rsc:()=>a.confirm({message:`${r("React Server Components")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694?`,initialValue:!1}),css:()=>a.confirm({message:`${r("CSS Loader")}\uB97C \uC0AC\uC6A9\uC911\uC774\uC2E0\uAC00\uC694? (true\uC77C \uACBD\uC6B0 \uCEF4\uD3EC\uB10C\uD2B8\uC5D0 CSS import\uAC00 \uCD94\uAC00\uB429\uB2C8\uB2E4.)`,initialValue:!0}),path:()=>a.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:()=>{a.cancel("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694."),process.exit(0)}})});try{let{start:c,stop:d}=a.spinner();c("seed-design.json \uD30C\uC77C \uC0DD\uC131\uC911...");let m=ne.resolve(n.cwd,"seed-design.json");await ze.writeFile(m,`${JSON.stringify(s,null,2)}
3
+ `,"utf-8");let g=ne.relative(process.cwd(),m);d(`seed-design.json \uD30C\uC77C\uC774 ${r(g)}\uC5D0 \uC0DD\uC131\uB410\uC5B4\uC694.`),a.log.info(P.gray("seed-design add {component} \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD574\uBCF4\uC138\uC694!")),a.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.")),a.outro("\uC791\uC5C5\uC774 \uC644\uB8CC\uB410\uC5B4\uC694.")}catch(c){a.log.error(`seed-design.json \uD30C\uC77C \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694. ${c}`),a.outro(P.bgRed("\uC791\uC5C5\uC774 \uCDE8\uC18C\uB410\uC5B4\uC694.")),process.exit(1)}})};import{cac as Fe}from"cac";var _e="seed-design",j=Fe(_e);async function Je(){let e=T();re(j),oe(j),j.version(e.version||"1.0.0","-v, --version"),j.help(),j.parse()}Je();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-design/cli",
3
- "version": "0.0.0-alpha-20241204134404",
3
+ "version": "0.0.0-alpha-20250210081704",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,13 +19,13 @@
19
19
  "node": ">=18"
20
20
  },
21
21
  "scripts": {
22
- "prepack": "yarn build",
23
- "build": "ENV=prod node ./build.mjs",
24
- "dev": "ENV=dev node ./dev.mjs",
25
- "test": "yarn vitest"
22
+ "prepack": "bun run build",
23
+ "build": "ENV=prod bun ./build.mjs",
24
+ "dev": "ENV=dev bun ./dev.mjs",
25
+ "test": "bun vitest"
26
26
  },
27
27
  "dependencies": {
28
- "@antfu/ni": "^0.22.0",
28
+ "@antfu/ni": "^23.3.1",
29
29
  "@babel/core": "^7.24.9",
30
30
  "@babel/parser": "^7.24.8",
31
31
  "@babel/plugin-transform-typescript": "^7.24.8",
@@ -44,11 +44,11 @@
44
44
  "devDependencies": {
45
45
  "@types/babel__core": "^7.20.5",
46
46
  "@types/fs-extra": "^11.0.4",
47
- "esbuild": "^0.19.3",
48
47
  "type-fest": "^4.23.0",
49
48
  "typescript": "^5.4.5",
50
49
  "ultra-runner": "^3.10.5",
51
- "vitest": "^2.0.5"
50
+ "vitest": "^2.0.5",
51
+ "esbuild": "^0.25.0"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"
@@ -1,18 +1,21 @@
1
1
  import { getConfig } from "@/src/utils/get-config";
2
- import { fetchRegistryUIItem, getRegistryUIIndex } from "@/src/utils/get-metadata";
3
- import { getPackageManager } from "@/src/utils/get-package-manager";
2
+ import {
3
+ fetchRegistryItem,
4
+ getRegistryLibIndex,
5
+ getRegistryUIIndex,
6
+ } from "@/src/utils/get-metadata";
4
7
  import { transform } from "@/src/utils/transformers";
5
8
  import * as p from "@clack/prompts";
6
- import { execa } from "execa";
7
9
  import fs from "fs-extra";
8
10
  import path from "path";
9
11
  import color from "picocolors";
10
12
  import { z } from "zod";
11
13
 
12
14
  import type { CAC } from "cac";
13
- import { addRelativeComponents } from "../utils/add-relative-components";
14
15
  import { BASE_URL } from "../constants";
16
+ import { addRelativeRegistries } from "../utils/add-relative-registries";
15
17
  import { highlight } from "../utils/color";
18
+ import { installDependencies } from "../utils/install";
16
19
 
17
20
  const addOptionsSchema = z.object({
18
21
  components: z.array(z.string()).optional(),
@@ -43,7 +46,7 @@ export const addCommand = (cli: CAC) => {
43
46
  .example("seed-design add box-button")
44
47
  .example("seed-design add alert-dialog")
45
48
  .action(async (components, opts) => {
46
- p.intro(color.bgCyan("컴포넌트 추가하기"));
49
+ p.intro(color.bgCyan("seed-design add"));
47
50
  const options = addOptionsSchema.parse({
48
51
  components,
49
52
  ...opts,
@@ -52,7 +55,7 @@ export const addCommand = (cli: CAC) => {
52
55
  const baseUrl = options.baseUrl;
53
56
  const config = await getConfig(cwd);
54
57
  const registryComponentIndex = await getRegistryUIIndex(baseUrl);
55
-
58
+ const libRegistryIndex = await getRegistryLibIndex(baseUrl);
56
59
  let selectedComponents: string[] = options.all
57
60
  ? registryComponentIndex.map((registry) => registry.name)
58
61
  : options.components;
@@ -62,7 +65,7 @@ export const addCommand = (cli: CAC) => {
62
65
  { label: string; value: string; hint: string }[],
63
66
  string
64
67
  >({
65
- message: "추가할 컴포넌트를 선택해주세요 (스페이스바로 여러 개 선택 가능)",
68
+ message: "추가할 컴포넌트를 선택해주세요 (스페이스 바로 여러 개 선택 가능)",
66
69
  options: registryComponentIndex.map((metadata) => {
67
70
  return {
68
71
  label: metadata.name,
@@ -85,28 +88,52 @@ export const addCommand = (cli: CAC) => {
85
88
  process.exit(0);
86
89
  }
87
90
 
88
- const allComponents = addRelativeComponents(selectedComponents, registryComponentIndex);
89
- const addedComponents = allComponents.filter((c) => !selectedComponents.includes(c));
90
- const registryComponentItems = await fetchRegistryUIItem(allComponents, baseUrl);
91
-
92
91
  p.log.message(`선택된 컴포넌트: ${highlight(selectedComponents.join(", "))}`);
93
- if (addedComponents.length) {
94
- p.log.message(`내부 의존성: ${highlight(addedComponents.join(", "))} 추가됩니다.`);
92
+
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를 가져오고 있어요...");
103
+
104
+ for (const registry of allRelativeRegistries) {
105
+ const registryItem = await fetchRegistryItem(registry.name, baseUrl, registry.type);
106
+ allRegistryItems.push(registryItem);
107
+ }
108
+
109
+ stop();
110
+
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
+ );
95
120
  }
96
121
 
97
122
  // 선택된 컴포넌트.json 레지스트리 파일 기반으로 컴포넌트를 추가합니다.
98
- for (const component of registryComponentItems) {
123
+ const registryResult = [];
124
+ const installResult = {
125
+ installed: new Set(),
126
+ filtered: new Set(),
127
+ };
128
+ for (const component of allRegistryItems) {
99
129
  for (const registry of component.registries) {
100
130
  let targetPath = "";
101
131
  switch (registry.type) {
102
132
  case "ui":
103
133
  targetPath = config.resolvedUIPaths;
104
134
  break;
105
- case "hook":
106
- targetPath = config.resolbedHookPaths;
107
- break;
108
- case "util":
109
- targetPath = config.resolvedUtilPaths;
135
+ case "lib":
136
+ targetPath = config.resolvedLibPaths;
110
137
  break;
111
138
  default:
112
139
  break;
@@ -131,60 +158,50 @@ export const addCommand = (cli: CAC) => {
131
158
 
132
159
  await fs.writeFile(filePath, content);
133
160
  const relativePath = path.relative(cwd, filePath);
134
- p.log.info(`${highlight(registry.name)} 추가됨: ${highlight(relativePath)}`);
135
- }
136
-
137
- const packageManager = await getPackageManager(cwd);
138
161
 
139
- const { start, stop } = p.spinner();
162
+ registryResult.push({
163
+ name: registry.name,
164
+ path: relativePath,
165
+ });
166
+ }
140
167
 
141
168
  // Install dependencies.
142
169
  if (component.dependencies?.length) {
143
- start(color.gray("의존성 설치중..."));
144
-
145
- const result = await execa(
146
- packageManager,
147
- [packageManager === "npm" ? "install" : "add", ...component.dependencies],
148
- {
149
- cwd,
150
- },
151
- );
152
-
153
- if (result.failed) {
154
- console.error(result.all);
155
- process.exit(1);
156
- } else {
157
- for (const deps of component.dependencies) {
158
- p.log.info(`- ${deps}`);
159
- }
160
- stop("의존성 설치 완료.");
161
- }
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]);
162
173
  }
163
174
 
164
175
  // Install devDependencies.
165
176
  if (component.devDependencies?.length) {
166
- start(color.gray("개발 의존성 설치중..."));
167
-
168
- const result = await execa(
169
- packageManager,
170
- [packageManager === "npm" ? "install" : "add", "-D", ...component.devDependencies],
171
- {
172
- cwd,
173
- },
174
- );
175
-
176
- if (result.failed) {
177
- console.error(result.all);
178
- process.exit(1);
179
- } else {
180
- for (const deps of component.devDependencies) {
181
- p.log.info(`- ${deps}`);
182
- }
183
- stop("개발 의존성 설치 완료.");
184
- }
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
+ }
185
+
186
+ p.log.success(`${highlight(component.name)} 관련 파일 추가 완료`);
187
+ }
188
+
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
+ );
198
+ }
199
+ if (registryResult.length) {
200
+ for (const registry of registryResult) {
201
+ p.log.message(`추가된 파일: ${highlight(registry.path)}`);
185
202
  }
186
203
  }
187
204
 
188
- p.outro(`${components.join(", ")} 컴포넌트를 추가했어요.`);
205
+ p.outro("컴포넌트 추가 완료.");
189
206
  });
190
207
  };
@@ -10,7 +10,7 @@ import type { CAC } from "cac";
10
10
 
11
11
  const initOptionsSchema = z.object({
12
12
  cwd: z.string(),
13
- default: z.boolean().optional(),
13
+ yes: z.boolean().optional(),
14
14
  });
15
15
 
16
16
  export const initCommand = (cli: CAC) => {
@@ -19,14 +19,13 @@ export const initCommand = (cli: CAC) => {
19
19
  .option("-c, --cwd <cwd>", "작업 디렉토리. 기본값은 현재 디렉토리.", {
20
20
  default: process.cwd(),
21
21
  })
22
- .option("-d, --default", "모든 질문에 대해 기본값으로 답변합니다.")
22
+ .option("-y, --yes", "모든 질문에 대해 기본값으로 답변합니다.")
23
23
  .action(async (opts) => {
24
24
  const highlight = (text: string) => color.cyan(text);
25
25
  p.intro(color.bgCyan("seed-design.json 파일 생성"));
26
26
 
27
27
  const options = initOptionsSchema.parse(opts);
28
- const isDefaultOption = options.default;
29
-
28
+ const isYesOption = options.yes;
30
29
  let config: RawConfig = {
31
30
  rsc: false,
32
31
  tsx: true,
@@ -34,7 +33,7 @@ export const initCommand = (cli: CAC) => {
34
33
  path: "./seed-design",
35
34
  };
36
35
 
37
- if (!isDefaultOption) {
36
+ if (!isYesOption) {
38
37
  const group = await p.group(
39
38
  {
40
39
  tsx: () =>
package/src/schema.ts CHANGED
@@ -1,7 +1,6 @@
1
- // TODO: Extract to shared package
2
1
  import { z } from "zod";
3
2
 
4
- export const registryType = z.union([z.literal("ui"), z.literal("hook"), z.literal("util")]);
3
+ export const registryType = z.union([z.literal("ui"), z.literal("lib")]);
5
4
 
6
5
  export const registryUIItemSchema = z.object({
7
6
  /**
@@ -25,7 +24,9 @@ export const registryUIItemSchema = z.object({
25
24
 
26
25
  /**
27
26
  * @description 레지스트리 내부의 Seed Design 컴포넌트 의존성
28
- * @example action-button
27
+ * * `:`를 기준으로 왼쪽은 {registryType}, 오른쪽은 파일 이름
28
+ * @example ui:action-button
29
+ * @example lib:manner-temp-level
29
30
  */
30
31
  innerDependencies: z.array(z.string()).optional(),
31
32
 
@@ -34,7 +35,8 @@ export const registryUIItemSchema = z.object({
34
35
  * 컴포넌트 코드 스니펫 경로, 여러 파일이 될 수 있어서 배열로 정의
35
36
  * `:`를 기준으로 왼쪽은 {registryType}, 오른쪽은 파일 이름
36
37
  * @example ui:alert-dialog.tsx
37
- * @example hook:use-dismissible.ts
38
+ * @example ui:use-dismissible.ts
39
+ * @example lib:manner-temp-level.ts
38
40
  */
39
41
  files: z.array(z.string()),
40
42
  });
@@ -59,21 +61,16 @@ export const registryComponentMachineGeneratedSchema = z.array(
59
61
  registryUIItemMachineGeneratedSchema,
60
62
  );
61
63
 
62
- // NOTE: 현재는 hook, util이 ui와 타입이 동일하지만, 따로 가져가야한다면 타입을 변경해야해요.
63
- export const registryHookItemMachineGeneratedSchema = registryUIItemMachineGeneratedSchema;
64
- export const registryUtilItemMachineGeneratedSchema = registryUIItemMachineGeneratedSchema;
64
+ // NOTE: 현재는 lib이 ui와 타입이 동일하지만, 따로 가져가야한다면 타입을 변경해야해요.
65
+ export const registryLibItemMachineGeneratedSchema = registryUIItemMachineGeneratedSchema;
65
66
 
66
- // NOTE: 현재는 hook, util이 ui와 타입이 동일하지만, 따로 가져가야한다면 타입을 변경해야해요.
67
- export type RegistryUtilItem = z.infer<typeof registryUIItemSchema>;
68
- export type RegistryUtil = z.infer<typeof registryUISchema>;
69
- export type RegistryHookItem = z.infer<typeof registryUIItemSchema>;
70
- export type RegistryHook = z.infer<typeof registryUISchema>;
67
+ // NOTE: 현재는 lib이 ui와 타입이 동일하지만, 따로 가져가야한다면 타입을 변경해야해요.
68
+ export type RegistryLibItem = z.infer<typeof registryUIItemSchema>;
69
+ export type RegistryLib = z.infer<typeof registryUISchema>;
71
70
  export type RegistryUIItem = z.infer<typeof registryUIItemSchema>;
72
71
  export type RegistryUI = z.infer<typeof registryUISchema>;
73
72
 
74
73
  export type RegistryUIItemMachineGenerated = z.infer<typeof registryUIItemMachineGeneratedSchema>;
75
74
  export type RegistryUIMachineGenerated = z.infer<typeof registryComponentMachineGeneratedSchema>;
76
- export type RegistryHookItemMachineGenerated = z.infer<typeof registryUIItemMachineGeneratedSchema>;
77
- export type RegistryHookMachineGenerated = z.infer<typeof registryComponentMachineGeneratedSchema>;
78
- export type RegistryUtilItemMachineGenerated = z.infer<typeof registryUIItemMachineGeneratedSchema>;
79
- export type RegistryUtilMachineGenerated = z.infer<typeof registryComponentMachineGeneratedSchema>;
75
+ export type RegistryLibItemMachineGenerated = z.infer<typeof registryLibItemMachineGeneratedSchema>;
76
+ export type RegistryLibMachineGenerated = z.infer<typeof registryComponentMachineGeneratedSchema>;
@@ -0,0 +1,182 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { addRelativeRegistries } from "../utils/add-relative-registries";
3
+ import type { RegistryLib, RegistryUI } from "@/src/schema";
4
+
5
+ const libConfig: RegistryLib = [
6
+ {
7
+ name: "a",
8
+ files: ["a.tsx"],
9
+ },
10
+ ];
11
+
12
+ const uiConfig: RegistryUI = [
13
+ {
14
+ name: "a",
15
+ files: ["a.tsx"],
16
+ },
17
+ {
18
+ name: "b",
19
+ innerDependencies: ["ui:a"],
20
+ files: ["b.tsx"],
21
+ },
22
+ {
23
+ name: "c",
24
+ innerDependencies: ["ui:b"],
25
+ files: ["c.tsx"],
26
+ },
27
+ {
28
+ name: "d",
29
+ innerDependencies: ["ui:a", "ui:b"],
30
+ files: ["d.tsx"],
31
+ },
32
+ {
33
+ name: "e",
34
+ innerDependencies: ["ui:d"],
35
+ files: ["e.tsx"],
36
+ },
37
+ {
38
+ name: "f",
39
+ innerDependencies: ["lib:a"],
40
+ files: ["f.tsx"],
41
+ },
42
+ ];
43
+
44
+ describe("addRelativeRegistries", () => {
45
+ test("4 deps test", () => {
46
+ const userSelects = ["e"];
47
+ const result = addRelativeRegistries({
48
+ userSelects,
49
+ uiRegistryIndex: uiConfig,
50
+ libRegistryIndex: [],
51
+ });
52
+ expect(result).toEqual(
53
+ expect.arrayContaining([
54
+ {
55
+ type: "ui",
56
+ name: "e",
57
+ },
58
+ {
59
+ type: "ui",
60
+ name: "d",
61
+ },
62
+ {
63
+ type: "ui",
64
+ name: "a",
65
+ },
66
+ {
67
+ type: "ui",
68
+ name: "b",
69
+ },
70
+ ]),
71
+ );
72
+ });
73
+
74
+ test("3 deps test", () => {
75
+ const userSelects = ["d"];
76
+ const result = addRelativeRegistries({
77
+ userSelects,
78
+ uiRegistryIndex: uiConfig,
79
+ libRegistryIndex: [],
80
+ });
81
+ expect(result).toEqual(
82
+ expect.arrayContaining([
83
+ {
84
+ type: "ui",
85
+ name: "d",
86
+ },
87
+ {
88
+ type: "ui",
89
+ name: "a",
90
+ },
91
+ {
92
+ type: "ui",
93
+ name: "b",
94
+ },
95
+ ]),
96
+ );
97
+ });
98
+
99
+ test("3 deps test", () => {
100
+ const userSelects = ["c"];
101
+ const result = addRelativeRegistries({
102
+ userSelects,
103
+ uiRegistryIndex: uiConfig,
104
+ libRegistryIndex: [],
105
+ });
106
+ expect(result).toEqual(
107
+ expect.arrayContaining([
108
+ {
109
+ type: "ui",
110
+ name: "c",
111
+ },
112
+ {
113
+ type: "ui",
114
+ name: "b",
115
+ },
116
+ {
117
+ type: "ui",
118
+ name: "a",
119
+ },
120
+ ]),
121
+ );
122
+ });
123
+
124
+ test("2 deps test", () => {
125
+ const userSelects = ["b"];
126
+ const result = addRelativeRegistries({
127
+ userSelects,
128
+ uiRegistryIndex: uiConfig,
129
+ libRegistryIndex: [],
130
+ });
131
+ expect(result).toEqual(
132
+ expect.arrayContaining([
133
+ {
134
+ type: "ui",
135
+ name: "b",
136
+ },
137
+ {
138
+ type: "ui",
139
+ name: "a",
140
+ },
141
+ ]),
142
+ );
143
+ });
144
+
145
+ test("1 deps test", () => {
146
+ const userSelects = ["a"];
147
+ const result = addRelativeRegistries({
148
+ userSelects,
149
+ uiRegistryIndex: uiConfig,
150
+ libRegistryIndex: [],
151
+ });
152
+ expect(result).toEqual(
153
+ expect.arrayContaining([
154
+ {
155
+ type: "ui",
156
+ name: "a",
157
+ },
158
+ ]),
159
+ );
160
+ });
161
+
162
+ test("lib deps test", () => {
163
+ const userSelects = ["f"];
164
+ const result = addRelativeRegistries({
165
+ userSelects,
166
+ uiRegistryIndex: uiConfig,
167
+ libRegistryIndex: libConfig,
168
+ });
169
+ expect(result).toEqual(
170
+ expect.arrayContaining([
171
+ {
172
+ type: "ui",
173
+ name: "f",
174
+ },
175
+ {
176
+ type: "lib",
177
+ name: "a",
178
+ },
179
+ ]),
180
+ );
181
+ });
182
+ });
@@ -0,0 +1,43 @@
1
+ import type { RegistryLibMachineGenerated, RegistryUIMachineGenerated } from "@/src/schema";
2
+
3
+ interface AddRelativeComponentsProps {
4
+ userSelects: string[];
5
+ uiRegistryIndex: RegistryUIMachineGenerated;
6
+ libRegistryIndex: RegistryLibMachineGenerated;
7
+ }
8
+
9
+ export function addRelativeRegistries({
10
+ userSelects,
11
+ uiRegistryIndex,
12
+ libRegistryIndex,
13
+ }: AddRelativeComponentsProps) {
14
+ const selectedComponents: { type: "ui" | "lib"; name: string }[] = [];
15
+
16
+ function addSeedDependencies({
17
+ registryName,
18
+ type,
19
+ }: { registryName: string; type: "ui" | "lib" }) {
20
+ if (selectedComponents.some((c) => c.name === registryName && c.type === type)) return;
21
+
22
+ selectedComponents.push({ type, name: registryName });
23
+
24
+ const registry =
25
+ type === "ui"
26
+ ? uiRegistryIndex.find((c) => c.name === registryName)
27
+ : libRegistryIndex.find((c) => c.name === registryName);
28
+ if (!registry) return;
29
+
30
+ if (registry.innerDependencies) {
31
+ for (const dep of registry.innerDependencies) {
32
+ const [depType, depName] = dep.split(":");
33
+ addSeedDependencies({ registryName: depName, type: depType as "ui" | "lib" });
34
+ }
35
+ }
36
+ }
37
+
38
+ for (const registryName of userSelects) {
39
+ addSeedDependencies({ registryName, type: "ui" });
40
+ }
41
+
42
+ return Array.from(selectedComponents);
43
+ }
@@ -27,8 +27,7 @@ export type RawConfig = z.infer<typeof rawConfigSchema>;
27
27
 
28
28
  export const configSchema = rawConfigSchema.extend({
29
29
  resolvedUIPaths: z.string(),
30
- resolbedHookPaths: z.string(),
31
- resolvedUtilPaths: z.string(),
30
+ resolvedLibPaths: z.string(),
32
31
  });
33
32
 
34
33
  export async function getConfig(cwd: string) {
@@ -51,26 +50,20 @@ export async function resolveConfigPaths(cwd: string, config: RawConfig) {
51
50
  }
52
51
 
53
52
  const resolvedUIPaths = path.join(seedComponentRootPath, "ui");
54
- const resolbedHookPaths = path.join(seedComponentRootPath, "hook");
55
- const resolvedUtilPaths = path.join(seedComponentRootPath, "util");
53
+ const resolvedLibPaths = path.join(seedComponentRootPath, "lib");
56
54
 
57
55
  if (!fs.existsSync(resolvedUIPaths)) {
58
56
  fs.mkdirSync(resolvedUIPaths, { recursive: true });
59
57
  }
60
58
 
61
- if (!fs.existsSync(resolbedHookPaths)) {
62
- fs.mkdirSync(resolbedHookPaths, { recursive: true });
63
- }
64
-
65
- if (!fs.existsSync(resolvedUtilPaths)) {
66
- fs.mkdirSync(resolvedUtilPaths, { recursive: true });
59
+ if (!fs.existsSync(resolvedLibPaths)) {
60
+ fs.mkdirSync(resolvedLibPaths, { recursive: true });
67
61
  }
68
62
 
69
63
  return configSchema.parse({
70
64
  ...config,
71
65
  resolvedUIPaths,
72
- resolbedHookPaths,
73
- resolvedUtilPaths,
66
+ resolvedLibPaths,
74
67
  });
75
68
  }
76
69
 
@@ -1,23 +1,24 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import { registryUISchema, type RegistryUIMachineGenerated } from "@/src/schema";
3
3
 
4
- export async function fetchRegistryUIItem(
4
+ export async function fetchRegistryItems(
5
5
  fileNames?: string[],
6
6
  baseUrl?: string,
7
+ type: "ui" | "lib" = "ui",
7
8
  ): Promise<RegistryUIMachineGenerated> {
8
9
  const results = await Promise.all(
9
10
  fileNames.map(async (fileName) => {
10
11
  try {
11
- const response = await fetch(`${baseUrl}/__registry__/ui/${fileName}.json`);
12
+ const response = await fetch(`${baseUrl}/__registry__/${type}/${fileName}.json`);
12
13
  return await response.json();
13
14
  } catch (error) {
14
- const index = await fetch(`${baseUrl}/__registry__/ui/index.json`).then((res) =>
15
+ const index = await fetch(`${baseUrl}/__registry__/${type}/index.json`).then((res) =>
15
16
  res.json(),
16
17
  );
17
18
  const parsedIndex = registryUISchema.parse(index);
18
19
  const availableComponents = parsedIndex.map((component) => component.name);
19
20
 
20
- p.log.error(`${fileName} 컴포넌트는 레지스트리에 존재하지 않아요.`);
21
+ p.log.error(`${type}:${fileName} 컴포넌트는 레지스트리에 존재하지 않아요.`);
21
22
  p.log.info(`사용 가능한 컴포넌트: ${availableComponents.join(", ")}`);
22
23
  p.log.info(
23
24
  JSON.stringify(
@@ -37,9 +38,18 @@ export async function fetchRegistryUIItem(
37
38
  return results;
38
39
  }
39
40
 
41
+ export async function fetchRegistryItem(
42
+ fileName: string,
43
+ baseUrl: string,
44
+ type: "ui" | "lib" = "ui",
45
+ ) {
46
+ const [result] = await fetchRegistryItems([fileName], baseUrl, type);
47
+ return result;
48
+ }
49
+
40
50
  export async function getRegistryUIIndex(baseUrl?: string) {
41
51
  try {
42
- const [result] = await fetchRegistryUIItem(["index"], baseUrl);
52
+ const [result] = await fetchRegistryItems(["index"], baseUrl, "ui");
43
53
 
44
54
  return registryUISchema.parse(result);
45
55
  } catch (error) {
@@ -57,3 +67,9 @@ export async function getRegistryUIIndex(baseUrl?: string) {
57
67
  process.exit(1);
58
68
  }
59
69
  }
70
+
71
+ export async function getRegistryLibIndex(baseUrl?: string) {
72
+ const [result] = await fetchRegistryItems(["index"], baseUrl, "lib");
73
+
74
+ return registryUISchema.parse(result);
75
+ }
@@ -13,5 +13,6 @@ function getPackagePath() {
13
13
  }
14
14
 
15
15
  export function getPackageInfo() {
16
- return fs.readJSONSync(getPackagePath()) as PackageJson;
16
+ const packageJsonPath = getPackagePath();
17
+ return fs.readJSONSync(packageJsonPath) as PackageJson;
17
18
  }
@@ -2,12 +2,12 @@ import { detect } from "@antfu/ni";
2
2
 
3
3
  export async function getPackageManager(
4
4
  targetDir: string,
5
- ): Promise<"yarn" | "pnpm" | "bun" | "npm"> {
5
+ ): Promise<"yarn" | "pnpm" | "bun" | "npm" | "deno"> {
6
6
  const packageManager = await detect({ programmatic: true, cwd: targetDir });
7
7
 
8
8
  if (packageManager === "yarn@berry") return "yarn";
9
9
  if (packageManager === "pnpm@6") return "pnpm";
10
10
  if (packageManager === "bun") return "bun";
11
-
11
+ if (packageManager === "deno") return "deno";
12
12
  return packageManager ?? "npm";
13
13
  }
@@ -0,0 +1,53 @@
1
+ import * as p from "@clack/prompts";
2
+ import { execa } from "execa";
3
+ import color from "picocolors";
4
+ import { getPackageManager } from "./get-package-manager";
5
+ import { getPackageInfo } from "./get-package-info";
6
+
7
+ interface InstallDependenciesProps {
8
+ cwd: string;
9
+ deps: string[];
10
+ dev?: boolean;
11
+ }
12
+
13
+ export async function installDependencies({ cwd, deps, dev = false }: InstallDependenciesProps) {
14
+ const { start, stop } = p.spinner();
15
+ const packageManager = await getPackageManager(cwd);
16
+ const packageInfo = getPackageInfo();
17
+
18
+ // 이미 설치된 의존성 필터링
19
+ const existingDeps = {
20
+ ...packageInfo.dependencies,
21
+ ...packageInfo.devDependencies,
22
+ };
23
+
24
+ const depsToInstall = deps.filter((dep) => !existingDeps[dep]);
25
+ const filteredDeps = deps.filter((dep) => existingDeps[dep]);
26
+
27
+ if (!depsToInstall.length) {
28
+ return {
29
+ installed: new Set(),
30
+ filtered: new Set(),
31
+ };
32
+ }
33
+
34
+ start(color.gray("의존성 설치중..."));
35
+
36
+ const isDev = dev ? "-D" : null;
37
+ const addCommand = packageManager === "npm" ? "install" : "add";
38
+ const command = [addCommand, isDev, ...depsToInstall].filter(Boolean);
39
+
40
+ try {
41
+ await execa(packageManager, command, { cwd });
42
+ } catch (error) {
43
+ console.error(`의존성 설치 실패: ${error}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ stop("의존성 설치 완료.");
48
+
49
+ return {
50
+ installed: depsToInstall,
51
+ filtered: filteredDeps,
52
+ };
53
+ }
@@ -1,62 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { addRelativeComponents } from "../utils/add-relative-components";
3
- import type { RegistryComponent } from "@/src/schema";
4
-
5
- const config: RegistryComponent = [
6
- {
7
- name: "a",
8
- files: ["a.tsx"],
9
- },
10
- {
11
- name: "b",
12
- innerDependencies: ["a"],
13
- files: ["b.tsx"],
14
- },
15
- {
16
- name: "c",
17
- innerDependencies: ["b"],
18
- files: ["c.tsx"],
19
- },
20
- {
21
- name: "d",
22
- innerDependencies: ["a", "b"],
23
- files: ["d.tsx"],
24
- },
25
- {
26
- name: "e",
27
- innerDependencies: ["d"],
28
- files: ["d.tsx"],
29
- },
30
- ];
31
-
32
- describe("addRelativeComponents", () => {
33
- test("4 deps test", () => {
34
- const userSelects = ["e"];
35
- const result = addRelativeComponents(userSelects, config);
36
- expect(result).toEqual(expect.arrayContaining(["a", "b", "d", "e"]));
37
- });
38
-
39
- test("3 deps test", () => {
40
- const userSelects = ["d"];
41
- const result = addRelativeComponents(userSelects, config);
42
- expect(result).toEqual(expect.arrayContaining(["a", "b", "d"]));
43
- });
44
-
45
- test("3 deps test", () => {
46
- const userSelects = ["c"];
47
- const result = addRelativeComponents(userSelects, config);
48
- expect(result).toEqual(expect.arrayContaining(["a", "b", "c"]));
49
- });
50
-
51
- test("2 deps test", () => {
52
- const userSelects = ["b"];
53
- const result = addRelativeComponents(userSelects, config);
54
- expect(result).toEqual(expect.arrayContaining(["a", "b"]));
55
- });
56
-
57
- test("1 deps test", () => {
58
- const userSelects = ["a"];
59
- const result = addRelativeComponents(userSelects, config);
60
- expect(result).toEqual(expect.arrayContaining(["a"]));
61
- });
62
- });
@@ -1,29 +0,0 @@
1
- import type { RegistryUIMachineGenerated } from "@/src/schema";
2
-
3
- export function addRelativeComponents(
4
- userSelects: string[],
5
- registryIndex: RegistryUIMachineGenerated,
6
- ) {
7
- const selectedComponents = new Set<string>();
8
-
9
- function addSeedDependencies(componentName: string) {
10
- if (selectedComponents.has(componentName)) return;
11
-
12
- selectedComponents.add(componentName);
13
-
14
- const component = registryIndex.find((c) => c.name === componentName);
15
- if (!component) return;
16
-
17
- if (component.innerDependencies) {
18
- for (const dep of component.innerDependencies) {
19
- addSeedDependencies(dep);
20
- }
21
- }
22
- }
23
-
24
- for (const componentName of userSelects) {
25
- addSeedDependencies(componentName);
26
- }
27
-
28
- return Array.from(selectedComponents);
29
- }