@jongh/cli 1.6.0 → 1.6.2
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 +38 -0
- package/dist/index.js +21 -7
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## ⚡ 주요 기능
|
|
2
|
+
|
|
3
|
+
### init
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @jongh/cli init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
- primary / secondary / grey color를 입력받아, 컴포넌트 스타일의 기반이 될 preset을 생성합니다
|
|
10
|
+
- 사용자의 경로 설정 파일을 분석하여 이후 코드가 저장될 장소를 components.json이라는 파일에 저장합니다
|
|
11
|
+
|
|
12
|
+
### add
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx @jongh/cli add [component-name]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- 원격 Registry에서 컴포넌트 코드를 가져와 components.json에 명시된 경로에 저장합니다
|
|
19
|
+
- ui 코드 뿐 아니라 hook,util 등의 코드도 같이 저장될 수 있습니다
|
|
20
|
+
|
|
21
|
+
### codemod: radix-ui-import
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @jongh/cli radix-ui-import
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- 최근 변경된 Radix UI의 설치 방식에 맞게 기존 코드를 자동으로 변환합니다
|
|
28
|
+
- 기존에 사용하던 import statement 방식에 따라 변경사항이 다를 수 있습니다
|
|
29
|
+
|
|
30
|
+
**변환 예시:**
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
// before
|
|
34
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
|
35
|
+
|
|
36
|
+
// after
|
|
37
|
+
import { Accordion as AccordionPrimitive } from "radix-ui"
|
|
38
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {Command}from'commander';import {intro,confirm,outro,spinner,select,log}from'@clack/prompts';import
|
|
2
|
+
import {Command}from'commander';import {intro,confirm,outro,spinner,select,log}from'@clack/prompts';import se from'chalk';import M from'fs-extra';import k from'path';import {z as z$1}from'zod';import we from'fast-glob';import {loadConfig,createMatchPath}from'tsconfig-paths';import {Project,SyntaxKind}from'ts-morph';import {detect,resolveCommand}from'package-manager-detector';import {packageDirectory}from'pkg-dir';import Fe from'handlebars';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {ListToolsRequestSchema,CallToolRequestSchema,ListPromptsRequestSchema,GetPromptRequestSchema}from'@modelcontextprotocol/sdk/types.js';import {zodToJsonSchema}from'zod-to-json-schema';var V=o=>{let e={};for(let t of o)e[t]=t;return e},N=V(["config_not_found","resolve_path_fail","failed_to_fetch"]);V(["components.json","tsconfig.json","panda.config.*","package.json"]);var b=class extends Error{issue;constructor(e){super(),this.name="NewError",this.issue=e;}get format(){return this.issue.code+`
|
|
3
3
|
`+this.issue.message?.join(`
|
|
4
|
-
`)}},
|
|
5
|
-
${
|
|
4
|
+
`)}},u=o=>{let e;switch(o.code){case N.config_not_found:e=`error occured while config ${o.configFile}`;break;case N.resolve_path_fail:e=`failed to resolve your path, current ${o.cwd}`;break;case N.failed_to_fetch:e=`failed to fetch ${o.target} [${o.statusCode}]`;break;default:e="error occured";}return new b({...o,message:[...o.message||"",e]})};var w={fileName:"components.json",schema:z$1.object({utils:z$1.string(),components:z$1.string(),hooks:z$1.string(),styledsystem:z$1.string()}).strict()},xe=["ui","hooks","utils"],be=z$1.object({name:z$1.string(),content:z$1.string(),type:z$1.enum(xe)}),G=z$1.object({name:z$1.string(),dependencies:z$1.array(z$1.string()).optional(),files:z$1.array(be).optional()});z$1.object({name:z$1.string(),dependencies:z$1.array(z$1.string()).optional(),file:z$1.string()});function Y(o){try{return M.readJsonSync(k.resolve(o,w.fileName))}catch(e){return u({code:"config_not_found",configFile:w.fileName,message:[e instanceof Error?e.message:""]})}}function $(o){let e=loadConfig(o);if(e.resultType==="failed")throw u({code:"config_not_found",configFile:"tsconfig.json",message:["cannot found tsconfig.json"]});return e}async function Z(o){return await M.pathExists(k.join(o,"components.json"))}function Q(o,e){let t=["./","./src/","./app/","./src/app"].map(r=>k.resolve(o,r));for(let[r,s]of Object.entries(e.paths)){let a=k.join(o,e.baseUrl||"",s[0].replace(/\/\*$/,""));if(t.some(l=>l===a||l.includes(a)))return r.replace(/\/\*$/,"")}return null}function X(o,e,t){let r=k.join(o,e);for(let[s,a]of Object.entries(t.paths)){let l=a[0].replace(/\/\*$/,"");if(r===k.join(o,t.baseUrl||"",l))return s.replace(/\/\*$/,"")}return null}async function F(o){try{let e=await we.glob(["panda.config.*"],{cwd:o,deep:3});if(!e.length)throw u({code:"config_not_found",configFile:"panda.config.*",message:["failed to find panda.config file"]});return e[0]}catch(e){throw u({code:"config_not_found",configFile:"panda.config.*",message:[e instanceof Error?e.message:"unknown error occured finding panda.config"]})}}async function A(o,e){let t=createMatchPath(e.absoluteBaseUrl,e.paths)(o,void 0,()=>true);if(!t)throw u({code:"resolve_path_fail",target:o,cwd:process.cwd(),message:["resolve error"]});return t}async function D(o){let e=o.match(/outdir:\s*["']([^"']+)["']/),t=o.match(/importMap:\s*({[^}]+}|["'][^"']+["'])/),r=e?e[1]:null,s=null;if(t){let a=t[1];if(a.startsWith("{")){let l=a.match(/css:\s*["']([^"']+)["']/);s=l?l[1].replace(/\/css$/,""):null;}else s=a.replace(/["']/g,"");}return {outdir:r,importMap:s}}function ee(o,e){let t=o;return [{from:/@utils\//g,to:`${e.utils}/`},{from:/@hooks\//g,to:`${e.hooks}/`},{from:/@styled-system\//g,to:`${e.styledsystem}/`},{from:/@components\//g,to:`${e.components}/`}].forEach(({from:s,to:a})=>{t=t.replace(s,a);}),t}function oe(o){let t=new Project().addSourceFileAtPath(o),r=[{namedImports:[{name:"preset"}],moduleSpecifier:"panda-animation"},{namedImports:[{name:"defaultPreset"}],moduleSpecifier:"./preset"}];t.addImportDeclarations(r);let a=t.getFirstDescendantByKind(SyntaxKind.CallExpression)?.getArguments()[0];a?(a.getProperty("presets")||a.addPropertyAssignment({name:"presets",initializer:'[preset(), "@pandacss/preset-panda", defaultPreset]'}),t.saveSync()):console.warn("Could not modify panda.config.ts. add presets : [preset(), @pandacss/preset-panda, defaultPreset] in your panda.config");}async function te(o,e){let t=await detect({cwd:o}),r=resolveCommand(t?.agent||"npm","add",e);if(r)return r}var Pe=z$1.object({components:z$1.array(z$1.string()).optional(),cwd:z$1.string()}),$e="https://whdgur.shop",S=se.bold.red,O=se.bold.blue,ae=new Command().name("add").argument("[components...]").option("-c, --cwd <cwd>","current working directory, default to process.cwd()",process.cwd()).action(async(o,e)=>{try{let t=Pe.parse({components:o,...e});intro(O("install components..."));let r=w.schema.parse(Y(t.cwd)),s=$(t.cwd),a=await F(t.cwd),l=await M.readFile(k.resolve(t.cwd,a),"utf-8"),{outdir:g}=await D(l),y=w.schema.parse({utils:await A(r.utils,s),components:await A(r.components,s),hooks:await A(r.hooks,s),styledsystem:k.join(t.cwd,g||"styled-system")},{errorMap:()=>({message:"validation failed at components.json"})}),p=t.components?.map(d=>d.toLowerCase());if(!p?.length)return;let h=await Promise.allSettled(p.map(async d=>{let n=await fetch(`${$e}/${d}.json`);if(!n.ok)throw u({code:"failed_to_fetch",target:d,statusCode:n.status,message:[n.statusText]});return await n.json()}));for(let[d,n]of h.entries()){if(n.status==="rejected"){n.reason instanceof b?console.log(S(n.reason.format)):console.log(S(n.reason));continue}try{let i=G.parse(n.value,{errorMap:()=>({message:`registry for ${p[d]} is invaliad`})}),f=k.join(y.components,p[d]);if(M.pathExistsSync(f)&&!await confirm({message:`Component ${p[d]} already exists. Do you want to overwrite it?`}))return;i.files?.forEach(x=>{let H=ee(x.content,r);x.type==="ui"?M.outputFileSync(k.join(f,x.name),H):M.outputFileSync(k.join(y[x.type],x.name),H,"utf-8");});let m=await te(t.cwd,i.dependencies||[]),P=m?`Run this command in the terminal : ${m.command} ${m.args.join(" ")}`:`Cannot find your package manager, install this dependencies: ${i.dependencies?.join(" ")}`;outro(O(`${p[d]} completed successfully
|
|
5
|
+
${P}`));}catch(i){i instanceof Error&&console.log(S(i.message));}}}catch(t){outro(S(O("error occured"))),t instanceof z$1.ZodError&&console.log(t.message),t instanceof b&&console.log(S(t.format)),t instanceof Error&&console.log(S(t.message)),process.exit(1);}});var ne=`
|
|
6
6
|
import { defineSemanticTokens, definePreset } from "@pandacss/dev"
|
|
7
7
|
|
|
8
8
|
const colors = defineSemanticTokens.colors({
|
|
@@ -105,7 +105,21 @@ export const defaultPreset = definePreset({
|
|
|
105
105
|
})
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
`;var _=["rose","pink","fuchsia","purple","violet","indigo","blue","sky","cyan","teal","emerald","green","lime","yellow","amber","orange","red","neutral","stone","zinc","gray","slate"],
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
`;var _=["rose","pink","fuchsia","purple","violet","indigo","blue","sky","cyan","teal","emerald","green","lime","yellow","amber","orange","red","neutral","stone","zinc","gray","slate"],U=["neutral","stone","zinc","gray","slate"],ie=z$1.object({primary:z$1.enum(_),secondary:z$1.enum(_),gray:z$1.enum(U)}),ce=o=>Fe.compile(ne)(o);var ze=z$1.object({primaryColor:z$1.string().optional(),secondaryColor:z$1.string().optional(),grayColor:z$1.string().optional()}),z=z$1.object({cwd:z$1.string(),default:z$1.boolean().optional(),theme:ze.optional()}),W=se.bold.red,Re=se.bold.blue,de=new Command().name("init").description("Initialize the project").option("-c, --cwd <cwd>","current working directory, default to process.cwd()",process.cwd()).option("-d, --default","use default color",false).action(async o=>{let e=spinner();e.start("Initializing");try{let t=z.parse({cwd:k.resolve(o.cwd),default:o.default});await K(t),e.stop("successfully Initialized!");}catch(t){e.stop(W(Re("error occured"))),t instanceof z$1.ZodError&&console.log(t.message),t instanceof b&&console.log(W(t.format)),t instanceof Error&&console.log(W(t.message)),process.exit(1);}});async function K(o){let e=o.cwd||await packageDirectory();if(!e)throw u({code:"config_not_found",configFile:"package.json",message:[`cannot find package.json in ${e}`]});let t=$(e);await Z(e)&&(await confirm({message:"you already initialize,are you want to overwrite it?"})||process.exit(0));let s=await F(e),a=await M.readFile(k.join(e,s),"utf-8"),l=Q(e,t);if(l===null)throw u({code:"resolve_path_fail",target:"tsconfig.json",cwd:e,message:[`cannot find paths alias in your ${k.join(e,"tsconfig.json")}`]});let g="styled-system",{outdir:y,importMap:p}=await D(a);p?g=p:g=X(e,y||"styled-system",t)||".",y&&(g=y);let h=w.schema.parse({utils:`${l}/utils`,components:`${l}/components`,hooks:`${l}/hooks`,styledsystem:g},{errorMap:()=>({message:"components.json is invalid"})});M.writeFile(k.resolve(e,"components.json"),JSON.stringify(h),"utf-8");let d=o.theme?.primaryColor||"neutral",n=o.theme?.secondaryColor||"slate",i=o.theme?.grayColor||"gray";o.default||(d=await select({message:"Pick primary color",initialValue:"neutral",options:_.map(m=>({value:m,label:m}))}),n=await select({message:"Pick secondary color",initialValue:"slate",options:_.map(m=>({value:m,label:m}))}),i=await select({message:"Pick gray color",initialValue:"gray",options:U.map(m=>({value:m,label:m}))}));let f=ce(ie.parse({primary:d,secondary:n,gray:i}));return M.writeFile(k.join(e,"preset.ts"),f),oe(k.resolve(e,s)),h}var T=new Server({name:"DesignSystemCLI",version:"1.0.0"},{capabilities:{resources:{},tools:{},prompts:{}}});T.setRequestHandler(ListToolsRequestSchema,async()=>({tools:[{name:"init",description:"Initialize the design system and generate theme files.",inputSchema:zodToJsonSchema(z)}]}));T.setRequestHandler(CallToolRequestSchema,async o=>{try{if(!o.params.arguments)throw new Error("Arguments are required for the init tool.");switch(o.params.name){case "init":try{let{cwd:e,...t}=o.params.arguments,r=z.parse({cwd:e,default:!0,theme:t});return await K(r),{content:[{type:"text",text:`Design system initialized for directory: ${r.cwd}. Theme files generated.`}]}}catch(e){return console.error("Error during init tool execution:",e),{content:[{type:"text",text:`Error initializing design system: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}default:return {content:[{type:"text",text:`Tool ${o.params.name} not found`}],isError:!0}}}catch(e){return console.error("Error parsing MCP request arguments:",e),e instanceof z$1.ZodError?{content:[{type:"text",text:`Invalid arguments for tool ${o.params?.name}: ${JSON.stringify(e.errors)}`}],isError:true}:{content:[{type:"text",text:`An unexpected error occurred: ${e instanceof Error?e.message:String(e)}`}],isError:true}}});T.setRequestHandler(ListPromptsRequestSchema,async()=>({prompts:[{name:"selectThemeColors",description:"Theme color selection guide for AI",arguments:[{name:"primaryColor",description:"The primary color using design system"},{name:"secondaryColor",description:"The secondary color using design system."},{name:"grayColor",description:"The gray color using design system."}]}]}));T.setRequestHandler(GetPromptRequestSchema,async o=>o.params.name!=="selectThemeColors"?{jsonrpc:"2.0",error:{code:-32601,message:`Prompt ${o.params.name} not found.`}}:{description:"Help the user select appropriate primary, secondary, and gray colors for the design system theme.",messages:[{role:"assistant",content:{type:"text",text:`You are an AI assistant helping the user configure their design system theme colors.
|
|
109
|
+
|
|
110
|
+
**Your goal is to determine the primary, secondary, and gray colors based on user preference.**
|
|
111
|
+
|
|
112
|
+
**Follow these steps:**
|
|
113
|
+
1. **Start by asking the user** (e.g., "What is your preferred color for the primary theme color?").
|
|
114
|
+
2. **Based on the user's preferred color, determine the secondary and gray colors.** You should use color theory and harmony principles to select colors that complement the primary color.
|
|
115
|
+
3. **When selecting secondary and gray colors, prioritize color harmony and contrast.**
|
|
116
|
+
4. **Ensure all selected colors comply with WCAG (Web Content Accessibility Guidelines) Contrast Ratio of at least 4.5** against standard background and foreground text colors where applicable.
|
|
117
|
+
5. **Strictly avoid color overlap.** The primary, secondary, and gray colors must be distinct from each other (e.g., the chosen primary color should not be the same as the chosen secondary or gray, secondary should not be the same as gray, etc.).
|
|
118
|
+
6. Once you have determined a suitable primary, secondary, and gray color (either directly provided by the user or chosen based on their preference and the criteria above), **confirm the selected colors with the user** if necessary.
|
|
119
|
+
7. Inform the user of the reason for the selection of the colors.
|
|
120
|
+
8. Finally, with the determined primary, secondary, and gray color values, **execute the 'init' tool** provided by the DesignSystemCLI server, passing the colors as arguments.
|
|
121
|
+
|
|
122
|
+
**Tool to use after color determination:** 'DesignSystemCLI init'`}},{role:"user",content:{type:"text",text:"I need to set up the theme colors for my design system. Can you guide me through the process?"}},{role:"assistant",content:{type:"text",text:"Okay, I can help you with that. Let's start by selecting the theme colors. To begin, what is your preferred color for the primary theme color?"}}]});var me=new Command().name("mcp").description("Manage your design system").action(async()=>{try{let o=new StdioServerTransport;await T.connect(o);}catch(o){console.error(o);}});var pe={"@radix-ui/react-accessible-icon":"AccessibleIcon","@radix-ui/react-accordion":"Accordion","@radix-ui/react-alert-dialog":"AlertDialog","@radix-ui/react-aspect-ratio":"AspectRatio","@radix-ui/react-avatar":"Avatar","@radix-ui/react-checkbox":"Checkbox","@radix-ui/react-collapsible":"Collapsible","@radix-ui/react-context-menu":"ContextMenu","@radix-ui/react-dialog":"Dialog","@radix-ui/react-direction":"Direction","@radix-ui/react-dropdown-menu":"DropdownMenu","@radix-ui/react-form":"Form","@radix-ui/react-hover-card":"HoverCard","@radix-ui/react-label":"Label","@radix-ui/react-menubar":"Menubar","@radix-ui/react-navigation-menu":"NavigationMenu","@radix-ui/react-popover":"Popover","@radix-ui/react-portal":"Portal","@radix-ui/react-progress":"Progress","@radix-ui/react-radio-group":"RadioGroup","@radix-ui/react-scroll-area":"ScrollArea","@radix-ui/react-select":"Select","@radix-ui/react-separator":"Separator","@radix-ui/react-slider":"Slider","@radix-ui/react-slot":"Slot","@radix-ui/react-switch":"Switch","@radix-ui/react-tabs":"Tabs","@radix-ui/react-toast":"Toast","@radix-ui/react-toggle":"Toggle","@radix-ui/react-toggle-group":"ToggleGroup","@radix-ui/react-toolbar":"Toolbar","@radix-ui/react-tooltip":"Tooltip","@radix-ui/react-visually-hidden":"VisuallyHidden"};function R(o,e,t){return t?o===t:o===e}function fe(o){return `import {${o}} from "radix-ui"`}function ge(o){let e=[];return o.getImportDeclarations().forEach(t=>{let r=t.getModuleSpecifierValue();if(!(r in pe))return;let s=pe[r];e.push(r);let a=t.getNamespaceImport();if(a){let g=a.getText();s!==g&&(s+=` as ${g}`),t.replaceWithText(fe(s));return}let l=t.getNamedImports();if(l.length){let g=s;l.forEach(y=>{let p=y.getName(),h=y.getAliasNode()?.getText(),d=`${g}.${p}`;for(let n of [SyntaxKind.JsxOpeningElement,SyntaxKind.JsxClosingElement,SyntaxKind.JsxSelfClosingElement])o.getDescendantsOfKind(n).forEach(i=>{let f=i.getTagNameNode().getText();R(f,p,h)&&i.getTagNameNode().replaceWithText(d);});o.getDescendantsOfKind(SyntaxKind.TypeQuery).forEach(n=>{n.getChildren().forEach(i=>{let f=i.getText();R(f,p,h)&&i.replaceWithText(d);});}),o.getDescendantsOfKind(SyntaxKind.ExpressionStatement).forEach(n=>{n.getChildrenOfKind(SyntaxKind.BinaryExpression).forEach(f=>{f.getRight().getChildren().forEach(P=>{let x=P.getText();R(x,p,h)&&P.replaceWithText(d);});});}),o.getDescendantsOfKind(SyntaxKind.VariableDeclaration).forEach(n=>{let i=n.getInitializer();i&&i.getChildren().forEach(f=>{let m=f.getText();R(m,p,h)&&f.replaceWithText(d);});});}),t.replaceWithText(fe(g));}}),{source:o.getFullText(),packages:e}}var ye=new Command().name("radix-ui-import").action(()=>{intro("find .tsx files...");let o=we.sync("**/*.tsx",{ignore:["**/node_modules/**"],dot:false}),e=new Project,t="";o.forEach(r=>{let s=e.addSourceFileAtPathIfExists(k.join(process.cwd(),r));if(s)try{let{packages:a}=ge(s);t+=a.join(" "),s.save();}catch(a){log.error(`failed to transform ${r} ${a}`);}}),t.length?`copy this to delete and install new package
|
|
123
|
+
<your package manager's delete command> ${t}
|
|
124
|
+
<your package manager's install command> ${se.bgGreen("radix-ui")}`.split(`
|
|
125
|
+
`).forEach(log.step):outro("no transform occured");});var he={version:"1.6.02"};async function ro(){new Command().name("design-system").version(he.version).addCommand(de).addCommand(ae).addCommand(ye).addCommand(me).parse();}ro();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jongh/cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.02",
|
|
4
4
|
"description": "",
|
|
5
5
|
"bin": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@clack/prompts": "^0.7.0",
|
|
25
25
|
"@effect/platform": "^0.69.13",
|
|
26
26
|
"@effect/schema": "^0.75.5",
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
27
28
|
"@types/babel__traverse": "^7.20.6",
|
|
28
29
|
"chalk": "^5.4.1",
|
|
29
30
|
"commander": "^13.0.0",
|
|
@@ -37,7 +38,8 @@
|
|
|
37
38
|
"ts-morph": "^25.0.0",
|
|
38
39
|
"tsconfig-paths": "^4.2.0",
|
|
39
40
|
"yargs": "^17.7.2",
|
|
40
|
-
"zod": "^3.24.1"
|
|
41
|
+
"zod": "^3.24.1",
|
|
42
|
+
"zod-to-json-schema": "^3.24.5"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@types/babel__generator": "^7.6.8",
|