@seed-design/cli 0.0.0-alpha-20241016030836 → 0.0.0-alpha-20241031063855
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 +2 -8
- package/package.json +2 -8
- package/src/index.ts +0 -2
- package/src/schema.ts +1 -1
- package/src/utils/get-metadata.ts +1 -1
- package/src/commands/icon-shift.ts +0 -147
- package/src/test/icon-shift/cases/migrateIdentifiers/expected.tsx +0 -11
- package/src/test/icon-shift/cases/migrateIdentifiers/input.tsx +0 -11
- package/src/test/icon-shift/cases/migrateImportDeclarations/expected.tsx +0 -3
- package/src/test/icon-shift/cases/migrateImportDeclarations/input.tsx +0 -3
- package/src/test/icon-shift/cases/migrateImportDeclarationsVariousTypes/expected.tsx +0 -6
- package/src/test/icon-shift/cases/migrateImportDeclarationsVariousTypes/input.tsx +0 -6
- package/src/test/icon-shift/cases/migrateImportDeclarationsVariousTypesWithMigrateIdentifiers/expected.tsx +0 -19
- package/src/test/icon-shift/cases/migrateImportDeclarationsVariousTypesWithMigrateIdentifiers/input.tsx +0 -19
- package/src/test/icon-shift/cases/migrateImportDeclarationsWithMigrateIdentifiers/expected.tsx +0 -13
- package/src/test/icon-shift/cases/migrateImportDeclarationsWithMigrateIdentifiers/input.tsx +0 -13
- package/src/test/icon-shift/index.test.ts +0 -90
- package/src/utils/files.ts +0 -63
- package/src/utils/migrate.ts +0 -156
package/bin/index.mjs
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{cosmiconfig as
|
|
3
|
-
`,"utf-8");let
|
|
4
|
-
(old) import { IconHeart } from "old-package"; <IconHeart />;
|
|
5
|
-
\u2192 (new) import { NewIconHeart } from "new-package"; <NewIconHeart />;
|
|
6
|
-
`},{label:"import\uB9CC \uBCC0\uACBD",value:"importOnly",hint:`
|
|
7
|
-
(old) import { IconHeart } from "old-package"; <IconHeart />;
|
|
8
|
-
\u2192 (new) import { NewIconHeart as IconHeart } from "new-package"; <IconHeart />;
|
|
9
|
-
`}],initialValue:"all"})}),c=(()=>{switch(s.target){case"tsconfig":return re({dirToFindTsconfig:process.cwd(),excludeDTs:!0});case"cwd":return k({dir:process.cwd(),extensionsToFind:ae,extensionsToExclude:ce});case"path":return k({dir:r.path,extensionsToFind:ae,extensionsToExclude:ce})}})(),{start:p,message:u,stop:m}=x.spinner();p("\uBA87 \uAC1C\uC758 \uD30C\uC77C\uC744 \uD655\uC778\uD560\uC9C0 \uACB0\uC815\uD558\uACE0 \uC788\uC5B4\uC694.");let a=r.includeIgnored?c:await oe({git:Ue(),filePaths:c});m(`${a.length}\uAC1C \uD30C\uC77C\uC5D0\uC11C \uC608\uC804 \uC544\uC774\uCF58\uC744 \uCC3E\uC544\uBCFC\uAC8C\uC694.`),p(`${a.length}\uAC1C \uD30C\uC77C\uC5D0\uC11C \uC608\uC804 \uC544\uC774\uCF58\uC744 \uCC3E\uC544\uBCFC\uAC8C\uC694.`);let w=Ke.withParser("tsx");for(let i=0;i<a.length;i++){let d=a[i],h=((i+1)/a.length*100).toFixed(1);u(`\uD30C\uC77C ${i+1}/${a.length} \uBCC0\uACBD \uC2DC\uC791 (${h}%): ${d}`),se({filePath:d,jscodeshift:w,importTransformers:Le})}m("\uCF54\uB4DC \uBCC0\uACBD\uC774 \uB05D\uB0AC\uC5B4\uC694.")})};var qe="seed-design",T=Xe(qe);async function Qe(){let e=Y();Q(T),te(T),pe(T),T.version(e.version||"1.0.0","-v, --version"),T.help(),T.parse()}Qe();
|
|
2
|
+
import{cosmiconfig as Z}from"cosmiconfig";import $ from"path";import{z as u}from"zod";var k="seed-design",ee=Z(k,{searchPlaces:[`${k}.json`]}),M=u.object({$schema:u.string().optional(),rsc:u.coerce.boolean().default(!1),tsx:u.coerce.boolean().default(!0),css:u.coerce.boolean().default(!0),path:u.string()}).strict(),te=M.extend({resolvedUIPaths:u.string()});async function O(e){let t=await re(e);return t?await oe(e,t):null}async function oe(e,t){let r=$.resolve(e,t.path);return te.parse({...t,resolvedUIPaths:$.join(r,"ui")})}async function re(e){try{let t=await ee.search(e);return t?M.parse(t.config):null}catch(t){throw console.log(t),new Error(`Invalid configuration found in ${e}/seed-design.json.`)}}import{z as s}from"zod";var A=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())}),E=s.array(A),ne=A.omit({files:!0}),se=ne.extend({registries:s.array(s.object({name:s.string(),content:s.string()}))}),De=s.array(se);var I="https://v3.seed-design.io";async function R(e){try{return await Promise.all(e.map(async r=>await(await fetch(`${I}/__registry__/component/${r}.json`)).json()))}catch(t){throw console.log(t),new Error(`Failed to fetch registry from ${I}.`)}}async function D(){try{let[e]=await R(["index"]);return E.parse(e)}catch(e){throw console.log(e),new Error(`Failed to fetch components from ${I}.`)}}import{detect as ie}from"@antfu/ni";async function F(e){let t=await ie({programmatic:!0,cwd:e});return t==="yarn@berry"?"yarn":t==="pnpm@6"?"pnpm":t==="bun"?"bun":t??"npm"}import{promises as le}from"fs";import{tmpdir as de}from"os";import J from"path";import{transformFromAstSync as ae}from"@babel/core";import ce from"@babel/plugin-transform-typescript";import*as v from"recast";import{parse as pe}from"@babel/parser";var me={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"]},G=async({sourceFile:e,config:t})=>{let r=e.getFullText();if(t.tsx)return r;let n=v.parse(r,{parser:{parse:c=>pe(c,me)}}),o=ae(n,r,{cloneInputAst:!1,code:!1,ast:!0,plugins:[ce],configFile:!1});if(!o||!o.ast)throw new Error("Failed to transform JSX");return v.print(o.ast).code};import{SyntaxKind as fe}from"ts-morph";var z=async({sourceFile:e,config:t})=>{if(t.rsc)return e;let r=e.getFirstChildByKind(fe.ExpressionStatement);return r?.getText()==='"use client";'&&r.remove(),e};var N=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 ge,ScriptKind as ue}from"ts-morph";var ye=[z,N],he=new ge({compilerOptions:{}});async function Ce(e){let t=await le.mkdtemp(J.join(de(),"seed-deisgn-"));return J.join(t,e)}async function _(e){let t=await Ce(e.filename),r=he.createSourceFile(t,e.raw,{scriptKind:ue.TSX});for(let n of ye)n({sourceFile:r,...e});return await G({sourceFile:r,...e})}import*as i from"@clack/prompts";import{execa as U}from"execa";import P from"fs-extra";import B from"path";import j from"picocolors";import{z as x}from"zod";function V(e,t){let r=new Set;function n(o){if(r.has(o))return;r.add(o);let c=t.find(f=>f.name===o);if(c&&c.innerDependencies)for(let f of c.innerDependencies)n(f)}for(let o of e)n(o);return Array.from(r)}var xe=x.object({components:x.array(x.string()).optional(),cwd:x.string(),all:x.boolean()}),K=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()}).example("seed-design add box-button").example("seed-design add alert-dialog").action(async(t,r)=>{let n=xe.parse({components:t,...r}),o=a=>j.cyan(a),c=n.cwd;P.existsSync(c)||(i.log.error(`The path ${c} does not exist. Please try again.`),process.exit(1));let f=await D(),d=n.all?f.map(a=>a.name):n.components;if(!n.components?.length&&!n.all){let a=await i.multiselect({message:"Select all components to add",options:f.map(l=>({label:l.name,value:l.name,hint:l.description}))});i.isCancel(a)&&(i.log.error("Aborted."),process.exit(0)),d=a}d?.length||(i.log.error("No components found."),process.exit(0));let h=V(d,f),S=h.filter(a=>!d.includes(a)),C=await O(c),H=await R(h);i.log.message(`Selection: ${o(d.join(", "))}`),S.length&&i.log.message(`Inner Dependencies: ${o(S.join(", "))} will be also added.`);for(let a of H){for(let m of a.registries){let g=C.resolvedUIPaths;P.existsSync(g)||await P.mkdir(g,{recursive:!0});let y=B.resolve(g,m.name),Q=await _({filename:m.name,config:C,raw:m.content});C.tsx||(y=y.replace(/\.tsx$/,".jsx"),y=y.replace(/\.ts$/,".js")),await P.writeFile(y,Q);let Y=B.relative(c,y);i.log.info(`Added ${o(m.name)} to ${o(Y)}`)}let l=await F(c),{start:b,stop:T}=i.spinner();if(a.dependencies?.length){b(j.gray("Installing dependencies"));let m=await U(l,[l==="npm"?"install":"add",...a.dependencies],{cwd:c});if(m.failed)console.error(m.all),process.exit(1);else{for(let g of a.dependencies)i.log.info(`- ${g}`);T("Dependencies installed.")}}if(a.devDependencies?.length){b(j.gray("Installing devDependencies"));let m=await U(l,[l==="npm"?"install":"add","-D",...a.devDependencies],{cwd:c});if(m.failed)console.error(m.all),process.exit(1);else{for(let g of a.devDependencies)i.log.info(`- ${g}`);T("Dependencies installed.")}}}i.outro("Components added.")})};import we from"findup-sync";import Se from"fs-extra";var ve="package.json";function Pe(){let e=we(ve);if(!e)throw new Error("No package.json file found in the project.");return e}function L(){return Se.readJSONSync(Pe())}import{cac as be}from"cac";import*as p from"@clack/prompts";import Ie from"fs-extra";import W from"path";import Re from"picocolors";import{z as X}from"zod";var je=X.object({cwd:X.string()}),q=e=>{e.command("init","initialize seed-design.json").option("-c, --cwd <cwd>","the working directory. defaults to the current directory.",{default:process.cwd()}).action(async t=>{let r=je.parse({...t}),n=C=>Re.cyan(C),o=await p.group({tsx:()=>p.confirm({message:`Would you like to use ${n("TypeScript")} (recommended)?`,initialValue:!0}),rsc:()=>p.confirm({message:`Are you using ${n("React Server Components")}?`,initialValue:!1}),css:()=>p.confirm({message:`Would you like to use ${n("CSS Modules")}? (If true, CSS import will be added in components)`,initialValue:!0}),path:()=>p.text({message:`Enter the path to your ${n("seed-design directory")}`,initialValue:"./seed-design",defaultValue:"./seed-design",placeholder:"./seed-design"})},{onCancel:()=>{p.cancel("Operation cancelled."),process.exit(0)}}),c={rsc:o.rsc,tsx:o.tsx,css:o.css,path:o.path},{start:f,stop:d}=p.spinner();f("Writing seed-design.json...");let h=W.resolve(r.cwd,"seed-design.json");await Ie.writeFile(h,`${JSON.stringify(c,null,2)}
|
|
3
|
+
`,"utf-8");let S=W.relative(process.cwd(),h);d(`seed-design.json written to ${n(S)}`)})};var Te="seed-design",w=be(Te);async function $e(){let e=L();K(w),q(w),w.version(e.version||"1.0.0","-v, --version"),w.help(),w.parse()}$e();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-design/cli",
|
|
3
|
-
"version": "0.0.0-alpha-
|
|
3
|
+
"version": "0.0.0-alpha-20241031063855",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,30 +29,24 @@
|
|
|
29
29
|
"@babel/core": "^7.24.9",
|
|
30
30
|
"@babel/parser": "^7.24.8",
|
|
31
31
|
"@babel/plugin-transform-typescript": "^7.24.8",
|
|
32
|
-
"@biomejs/js-api": "^0.7.1",
|
|
33
|
-
"@biomejs/wasm-nodejs": "^1.9.3",
|
|
34
32
|
"@clack/prompts": "^0.7.0",
|
|
35
33
|
"cac": "^6.7.14",
|
|
36
34
|
"cosmiconfig": "^9.0.0",
|
|
37
35
|
"execa": "^9.3.0",
|
|
38
36
|
"findup-sync": "^5.0.0",
|
|
39
37
|
"fs-extra": "^11.2.0",
|
|
40
|
-
"jscodeshift": "^17.0.0",
|
|
41
38
|
"mktemp": "^1.0.1",
|
|
42
39
|
"picocolors": "^1.0.1",
|
|
43
40
|
"recast": "^0.23.9",
|
|
44
|
-
"simple-git": "^3.27.0",
|
|
45
41
|
"ts-morph": "^23.0.0",
|
|
46
|
-
"typescript": "^5.4.5",
|
|
47
42
|
"zod": "^3.23.8"
|
|
48
43
|
},
|
|
49
44
|
"devDependencies": {
|
|
50
45
|
"@types/babel__core": "^7.20.5",
|
|
51
46
|
"@types/fs-extra": "^11.0.4",
|
|
52
|
-
"@types/jscodeshift": "^0.12.0",
|
|
53
|
-
"@types/node": "^20",
|
|
54
47
|
"esbuild": "^0.19.3",
|
|
55
48
|
"type-fest": "^4.23.0",
|
|
49
|
+
"typescript": "^5.4.5",
|
|
56
50
|
"ultra-runner": "^3.10.5",
|
|
57
51
|
"vitest": "^2.0.5"
|
|
58
52
|
},
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { addCommand } from "@/src/commands/add";
|
|
|
4
4
|
import { getPackageInfo } from "@/src/utils/get-package-info";
|
|
5
5
|
import { cac } from "cac";
|
|
6
6
|
import { initCommand } from "./commands/init";
|
|
7
|
-
import { iconShiftCommand } from "./commands/icon-shift";
|
|
8
7
|
|
|
9
8
|
const NAME = "seed-design";
|
|
10
9
|
const CLI = cac(NAME);
|
|
@@ -15,7 +14,6 @@ async function main() {
|
|
|
15
14
|
/* Commands */
|
|
16
15
|
addCommand(CLI);
|
|
17
16
|
initCommand(CLI);
|
|
18
|
-
iconShiftCommand(CLI);
|
|
19
17
|
|
|
20
18
|
CLI.version(packageInfo.version || "1.0.0", "-v, --version");
|
|
21
19
|
CLI.help();
|
package/src/schema.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { registryComponentSchema, type RegistryComponentMachineGenerated } from "@/src/schema";
|
|
2
2
|
|
|
3
3
|
const BASE_URL =
|
|
4
|
-
process.env.NODE_ENV === "prod" ? "https://
|
|
4
|
+
process.env.NODE_ENV === "prod" ? "https://v3.seed-design.io" : "http://localhost:3000";
|
|
5
5
|
|
|
6
6
|
export async function fetchRegistryComponentItem(
|
|
7
7
|
fileNames?: string[],
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import * as prompt from "@clack/prompts";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type { CAC } from "cac";
|
|
4
|
-
import {
|
|
5
|
-
filterGitIgnoredFiles,
|
|
6
|
-
getAllFileNamesWithMatchingExtension,
|
|
7
|
-
getAllTypeScriptCompiledFileNames,
|
|
8
|
-
} from "@/src/utils/files";
|
|
9
|
-
import { simpleGit } from "simple-git";
|
|
10
|
-
import jscodeshift from "jscodeshift";
|
|
11
|
-
import { migrateFile, type ImportTransformers } from "@/src/utils/migrate";
|
|
12
|
-
|
|
13
|
-
const importTransformersReact: ImportTransformers = {
|
|
14
|
-
source: [
|
|
15
|
-
{ startsWith: "@seed-design/icons", replaceWith: "@seed-design/react-icon" },
|
|
16
|
-
{ startsWith: "@seed-design/react-icon" },
|
|
17
|
-
],
|
|
18
|
-
identifier: {
|
|
19
|
-
Icon: "NewIcon",
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const iconShiftOptionsSchema = z.object({
|
|
24
|
-
path: z.string().optional(),
|
|
25
|
-
includeIgnored: z.boolean().optional(),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// 3. 로그 파일
|
|
29
|
-
// 3-1. 변경된 파일, (라인)
|
|
30
|
-
// 3-2. 에러났을 때 에러 메세지도 같이 들어있고, 어디서 문제 생겼는지도 나와야 한다.
|
|
31
|
-
// 3-2. 그 안에서의 아이콘 AS-IS, TO-BE
|
|
32
|
-
// 4. 마이그레이션 끝났습니다. 로그 파일은 어디 생성됐고, 총 몇개의 아이콘 변경됐습니다.
|
|
33
|
-
|
|
34
|
-
const EXTENSIONS_TO_FIND = [".js", ".jsx", ".ts", ".tsx"];
|
|
35
|
-
const EXTENSIONS_TO_EXCLUDE = [".d.ts"];
|
|
36
|
-
|
|
37
|
-
export const iconShiftCommand = (cli: CAC) => {
|
|
38
|
-
cli
|
|
39
|
-
.command("icon-shift", "V2 아이콘을 V3 아이콘으로 변환하는 명령어")
|
|
40
|
-
.option("--path <path>", "마이그레이션할 소스 코드가 있는 경로 (선택)")
|
|
41
|
-
.option("--include-ignored", ".gitignore를 통해 트래킹되지 않는 파일도 포함할지 여부 (선택)")
|
|
42
|
-
.example("seed-design icon-shift")
|
|
43
|
-
.action(async (opts) => {
|
|
44
|
-
const options = iconShiftOptionsSchema.parse({ ...opts });
|
|
45
|
-
|
|
46
|
-
const pathAvailableTargetPrompt = {
|
|
47
|
-
target: () =>
|
|
48
|
-
prompt.select({
|
|
49
|
-
message: `입력한 경로: ${options.path} 맞나요?`,
|
|
50
|
-
options: [{ label: "네", value: "path" }],
|
|
51
|
-
}),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const pathUnavailableTargetPrompt = {
|
|
55
|
-
target: () =>
|
|
56
|
-
prompt.select({
|
|
57
|
-
message: "어떤 파일을 대상으로 마이그레이션을 진행할까요?",
|
|
58
|
-
options: [
|
|
59
|
-
{
|
|
60
|
-
label: "현재 디렉토리에서 사용되는 tsconfig가 컴파일하는 파일",
|
|
61
|
-
value: "tsconfig",
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
// FIXME: 확장자
|
|
65
|
-
label: "현재 디렉토리 안의 .js, .jsx, .ts, .tsx (excluding d.ts)",
|
|
66
|
-
value: "cwd",
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
initialValue: "tsconfig.json",
|
|
70
|
-
}),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const group = await prompt.group({
|
|
74
|
-
...(options.path ? pathAvailableTargetPrompt : pathUnavailableTargetPrompt),
|
|
75
|
-
...(options.includeIgnored && {
|
|
76
|
-
includeIgnored: () =>
|
|
77
|
-
prompt.confirm({
|
|
78
|
-
message: "git에 트래킹되지 않는 파일도 포함할까요?",
|
|
79
|
-
initialValue: true,
|
|
80
|
-
}),
|
|
81
|
-
}),
|
|
82
|
-
migrateIdentifiers: () =>
|
|
83
|
-
prompt.select({
|
|
84
|
-
message: "어디까지 변경할까요?",
|
|
85
|
-
options: [
|
|
86
|
-
{
|
|
87
|
-
label: "모두 변경",
|
|
88
|
-
value: "all",
|
|
89
|
-
hint: `\n(old) import { IconHeart } from "old-package"; <IconHeart />;\n→ (new) import { NewIconHeart } from "new-package"; <NewIconHeart />;\n`,
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
label: "import만 변경",
|
|
93
|
-
value: "importOnly",
|
|
94
|
-
hint: `\n(old) import { IconHeart } from "old-package"; <IconHeart />;\n→ (new) import { NewIconHeart as IconHeart } from "new-package"; <IconHeart />;\n`,
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
initialValue: "all",
|
|
98
|
-
}),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const filesFound = (() => {
|
|
102
|
-
switch (group.target) {
|
|
103
|
-
case "tsconfig":
|
|
104
|
-
return getAllTypeScriptCompiledFileNames({
|
|
105
|
-
dirToFindTsconfig: process.cwd(),
|
|
106
|
-
excludeDTs: true,
|
|
107
|
-
});
|
|
108
|
-
case "cwd":
|
|
109
|
-
return getAllFileNamesWithMatchingExtension({
|
|
110
|
-
dir: process.cwd(),
|
|
111
|
-
extensionsToFind: EXTENSIONS_TO_FIND,
|
|
112
|
-
extensionsToExclude: EXTENSIONS_TO_EXCLUDE,
|
|
113
|
-
});
|
|
114
|
-
case "path":
|
|
115
|
-
return getAllFileNamesWithMatchingExtension({
|
|
116
|
-
dir: options.path,
|
|
117
|
-
extensionsToFind: EXTENSIONS_TO_FIND,
|
|
118
|
-
extensionsToExclude: EXTENSIONS_TO_EXCLUDE,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
})();
|
|
122
|
-
|
|
123
|
-
const { start, message, stop } = prompt.spinner();
|
|
124
|
-
|
|
125
|
-
start("몇 개의 파일을 확인할지 결정하고 있어요.");
|
|
126
|
-
|
|
127
|
-
const filesTracked = options.includeIgnored
|
|
128
|
-
? filesFound
|
|
129
|
-
: await filterGitIgnoredFiles({ git: simpleGit(), filePaths: filesFound });
|
|
130
|
-
|
|
131
|
-
stop(`${filesTracked.length}개 파일에서 예전 아이콘을 찾아볼게요.`);
|
|
132
|
-
|
|
133
|
-
start(`${filesTracked.length}개 파일에서 예전 아이콘을 찾아볼게요.`);
|
|
134
|
-
const j = jscodeshift.withParser("tsx");
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < filesTracked.length; i++) {
|
|
137
|
-
const filePath = filesTracked[i];
|
|
138
|
-
const percent = (((i + 1) / filesTracked.length) * 100).toFixed(1);
|
|
139
|
-
|
|
140
|
-
message(`파일 ${i + 1}/${filesTracked.length} 변경 시작 (${percent}%): ${filePath}`);
|
|
141
|
-
|
|
142
|
-
migrateFile({ filePath, jscodeshift: j, importTransformers: importTransformersReact });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
stop("코드 변경이 끝났어요.");
|
|
146
|
-
});
|
|
147
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
|
|
3
|
-
import { IconHeart } from "some-new-package";
|
|
4
|
-
import { IconStar } from "some-new-package";
|
|
5
|
-
import IconMoon from "some-new-package/IconMoon";
|
|
6
|
-
import * as IconFlame from "some-new-package/IconFlame";
|
|
7
|
-
|
|
8
|
-
export function IconDiv() {
|
|
9
|
-
console.log(IconHeart);
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<div>
|
|
13
|
-
<IconHeart />
|
|
14
|
-
<IconStar />
|
|
15
|
-
<IconMoon />
|
|
16
|
-
<IconFlame />
|
|
17
|
-
</div>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
|
|
3
|
-
import { IconLike } from "some-package";
|
|
4
|
-
import { IconFavorite } from "some-package";
|
|
5
|
-
import IconNight from "some-package/IconNight";
|
|
6
|
-
import * as IconHot from "some-package/IconHot";
|
|
7
|
-
|
|
8
|
-
export function IconDiv() {
|
|
9
|
-
console.log(IconLike);
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<div>
|
|
13
|
-
<IconLike />
|
|
14
|
-
<IconFavorite />
|
|
15
|
-
<IconNight />
|
|
16
|
-
<IconHot />
|
|
17
|
-
</div>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getFirstNode,
|
|
3
|
-
migrateIdentifiers,
|
|
4
|
-
migrateImportDeclarations,
|
|
5
|
-
type ImportTransformers,
|
|
6
|
-
} from "../../utils/migrate";
|
|
7
|
-
import jscodeshift from "jscodeshift";
|
|
8
|
-
import { describe, expect, test } from "vitest";
|
|
9
|
-
import { Biome, Distribution } from "@biomejs/js-api";
|
|
10
|
-
import fs from "fs";
|
|
11
|
-
import path from "path";
|
|
12
|
-
|
|
13
|
-
const biome = await Biome.create({ distribution: Distribution.NODE });
|
|
14
|
-
biome.applyConfiguration({ formatter: { indentStyle: "space", lineWidth: 100 } });
|
|
15
|
-
|
|
16
|
-
describe("shiftingIcons", () => {
|
|
17
|
-
const j = jscodeshift.withParser("tsx");
|
|
18
|
-
|
|
19
|
-
const importTransformers: ImportTransformers = {
|
|
20
|
-
source: [{ startsWith: "some-package", replaceWith: "some-new-package" }],
|
|
21
|
-
identifier: {
|
|
22
|
-
IconLike: "IconHeart",
|
|
23
|
-
IconFavorite: "IconStar",
|
|
24
|
-
IconHot: "IconFlame",
|
|
25
|
-
IconNight: "IconMoon",
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const cases: Record<string, ("migrateImportDeclarations" | "migrateIdentifiers")[]> = {
|
|
30
|
-
migrateImportDeclarations: ["migrateImportDeclarations"],
|
|
31
|
-
migrateImportDeclarationsVariousTypes: ["migrateImportDeclarations"],
|
|
32
|
-
migrateIdentifiers: ["migrateIdentifiers"],
|
|
33
|
-
migrateImportDeclarationsWithMigrateIdentifiers: [
|
|
34
|
-
"migrateImportDeclarations",
|
|
35
|
-
"migrateIdentifiers",
|
|
36
|
-
],
|
|
37
|
-
migrateImportDeclarationsVariousTypesWithMigrateIdentifiers: [
|
|
38
|
-
"migrateImportDeclarations",
|
|
39
|
-
"migrateIdentifiers",
|
|
40
|
-
],
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// migrateImportDeclarations
|
|
44
|
-
for (const name in cases) {
|
|
45
|
-
test(name, () => {
|
|
46
|
-
const input = fs.readFileSync(path.resolve(__dirname, `./cases/${name}/input.tsx`), "utf-8");
|
|
47
|
-
const expected = fs.readFileSync(
|
|
48
|
-
path.resolve(__dirname, `./cases/${name}/expected.tsx`),
|
|
49
|
-
"utf-8",
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const tree = j(input);
|
|
53
|
-
const firstNode = getFirstNode({ tree, jscodeshift: j });
|
|
54
|
-
|
|
55
|
-
if (cases[name].includes("migrateImportDeclarations")) {
|
|
56
|
-
const importDeclarations = tree.find(j.ImportDeclaration, {
|
|
57
|
-
source: {
|
|
58
|
-
value: (value: unknown) => {
|
|
59
|
-
if (typeof value !== "string") return false;
|
|
60
|
-
|
|
61
|
-
return importTransformers.source.some(({ startsWith }) =>
|
|
62
|
-
value.startsWith(startsWith),
|
|
63
|
-
);
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
migrateImportDeclarations({ importDeclarations, importTransformers });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (cases[name].includes("migrateIdentifiers")) {
|
|
71
|
-
const identifiers = tree.find(j.Identifier, {
|
|
72
|
-
name: (value) => Object.keys(importTransformers.identifier).includes(value),
|
|
73
|
-
});
|
|
74
|
-
migrateIdentifiers({ identifiers, identifierTransformers: importTransformers.identifier });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const firstNodeAfterModification = getFirstNode({ tree, jscodeshift: j });
|
|
78
|
-
|
|
79
|
-
if (firstNode !== firstNodeAfterModification) {
|
|
80
|
-
firstNodeAfterModification.comments = firstNode.comments;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const { content } = biome.formatContent(tree.toSource(), {
|
|
84
|
-
filePath: `${name}.tsx`,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(content).toBe(expected);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
});
|
package/src/utils/files.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import type { SimpleGit } from "simple-git";
|
|
5
|
-
|
|
6
|
-
export function getAllFileNamesWithMatchingExtension({
|
|
7
|
-
dir,
|
|
8
|
-
extensionsToFind,
|
|
9
|
-
extensionsToExclude,
|
|
10
|
-
}: {
|
|
11
|
-
dir: string;
|
|
12
|
-
extensionsToFind: string[];
|
|
13
|
-
extensionsToExclude?: string[];
|
|
14
|
-
}) {
|
|
15
|
-
// XXX: requires Node.js 20+
|
|
16
|
-
return fs
|
|
17
|
-
.readdirSync(dir, { withFileTypes: true, recursive: true })
|
|
18
|
-
.filter(
|
|
19
|
-
(item) =>
|
|
20
|
-
item.isFile() &&
|
|
21
|
-
extensionsToFind.some((ext) => item.name.endsWith(ext)) &&
|
|
22
|
-
(extensionsToExclude ? !extensionsToExclude.some((ext) => item.name.endsWith(ext)) : true),
|
|
23
|
-
)
|
|
24
|
-
.map((item) => `${item.parentPath}/${item.name}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function getAllTypeScriptCompiledFileNames({
|
|
28
|
-
dirToFindTsconfig,
|
|
29
|
-
excludeDTs,
|
|
30
|
-
}: {
|
|
31
|
-
dirToFindTsconfig: string;
|
|
32
|
-
excludeDTs?: boolean;
|
|
33
|
-
}) {
|
|
34
|
-
const tsconfigPath = ts.findConfigFile(dirToFindTsconfig, ts.sys.fileExists);
|
|
35
|
-
const tsconfigFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
36
|
-
|
|
37
|
-
// FIXME: throw할 수 있을 것 같음
|
|
38
|
-
const { fileNames } = ts.parseJsonConfigFileContent(
|
|
39
|
-
tsconfigFile.config,
|
|
40
|
-
ts.sys,
|
|
41
|
-
path.dirname(tsconfigPath),
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
return fileNames.filter((fileName) => (excludeDTs ? !fileName.endsWith(".d.ts") : true));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function filterGitIgnoredFiles({
|
|
48
|
-
git,
|
|
49
|
-
filePaths,
|
|
50
|
-
}: {
|
|
51
|
-
git: SimpleGit;
|
|
52
|
-
filePaths: string[];
|
|
53
|
-
}) {
|
|
54
|
-
const promises = await Promise.all(filePaths.map((file) => isFileGitTracked(git, file)));
|
|
55
|
-
|
|
56
|
-
return filePaths.filter((_, index) => promises[index]);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function isFileGitTracked(git: SimpleGit, filePath: string) {
|
|
60
|
-
const result = await git.checkIgnore(filePath);
|
|
61
|
-
|
|
62
|
-
return result.length <= 0;
|
|
63
|
-
}
|
package/src/utils/migrate.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import jscodeshift from "jscodeshift";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
|
|
4
|
-
export interface ImportTransformers {
|
|
5
|
-
source: { startsWith: string; replaceWith?: string }[];
|
|
6
|
-
identifier: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface MigrateFileParams {
|
|
10
|
-
filePath: string;
|
|
11
|
-
jscodeshift: jscodeshift.JSCodeshift;
|
|
12
|
-
importTransformers: ImportTransformers;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function migrateFile({ filePath, jscodeshift, importTransformers }: MigrateFileParams) {
|
|
16
|
-
const file = fs.readFileSync(filePath, "utf-8");
|
|
17
|
-
|
|
18
|
-
const tree = jscodeshift(file);
|
|
19
|
-
const firstNode = getFirstNode({ tree, jscodeshift: jscodeshift });
|
|
20
|
-
|
|
21
|
-
migrateImportDeclarations({
|
|
22
|
-
importDeclarations: tree.find(jscodeshift.ImportDeclaration, {
|
|
23
|
-
source: {
|
|
24
|
-
value: (value: unknown) => {
|
|
25
|
-
if (typeof value !== "string") return false;
|
|
26
|
-
|
|
27
|
-
return importTransformers.source.some(({ startsWith }) => value.startsWith(startsWith));
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
importTransformers,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
migrateIdentifiers({
|
|
35
|
-
identifiers: tree.find(jscodeshift.Identifier, {
|
|
36
|
-
name: (value) => Object.keys(importTransformers.identifier).includes(value),
|
|
37
|
-
}),
|
|
38
|
-
identifierTransformers: importTransformers.identifier,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const firstNodeAfterModification = getFirstNode({ tree, jscodeshift: jscodeshift });
|
|
42
|
-
|
|
43
|
-
if (firstNode !== firstNodeAfterModification) {
|
|
44
|
-
firstNodeAfterModification.comments = firstNode.comments;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
fs.writeFileSync(filePath, tree.toSource());
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getFirstNode({
|
|
51
|
-
tree,
|
|
52
|
-
jscodeshift,
|
|
53
|
-
}: { tree: jscodeshift.Collection; jscodeshift: jscodeshift.JSCodeshift }) {
|
|
54
|
-
return tree.find(jscodeshift.Program).get("body", 0).node;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface MigrateImportDeclarationsParams {
|
|
58
|
-
importDeclarations: jscodeshift.Collection<jscodeshift.ImportDeclaration>;
|
|
59
|
-
importTransformers: ImportTransformers;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function migrateImportDeclarations({
|
|
63
|
-
importDeclarations,
|
|
64
|
-
importTransformers,
|
|
65
|
-
}: MigrateImportDeclarationsParams) {
|
|
66
|
-
importDeclarations.replaceWith((imp) => {
|
|
67
|
-
const currentSourceValue = imp.node.source.value;
|
|
68
|
-
const currentSpecifiers = imp.node.specifiers;
|
|
69
|
-
const currentImportKind = imp.node.importKind;
|
|
70
|
-
|
|
71
|
-
const newSourceValue = (() => {
|
|
72
|
-
if (typeof currentSourceValue !== "string") return currentSourceValue;
|
|
73
|
-
|
|
74
|
-
const { startsWith, replaceWith } = importTransformers.source.find(({ startsWith }) =>
|
|
75
|
-
currentSourceValue.startsWith(startsWith),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const sourceReplaced = replaceWith
|
|
79
|
-
? currentSourceValue.replace(startsWith, replaceWith)
|
|
80
|
-
: currentSourceValue;
|
|
81
|
-
|
|
82
|
-
const slashSplits = sourceReplaced.split("/");
|
|
83
|
-
|
|
84
|
-
const itemReplaced = slashSplits
|
|
85
|
-
.map((split, index) => {
|
|
86
|
-
if (index !== slashSplits.length - 1 || split in importTransformers.identifier === false)
|
|
87
|
-
return split;
|
|
88
|
-
|
|
89
|
-
return importTransformers.identifier[split];
|
|
90
|
-
})
|
|
91
|
-
.join("/");
|
|
92
|
-
|
|
93
|
-
return itemReplaced;
|
|
94
|
-
})();
|
|
95
|
-
|
|
96
|
-
const newSpecifiers = currentSpecifiers.map((currentSpecifier) => {
|
|
97
|
-
switch (currentSpecifier.type) {
|
|
98
|
-
case "ImportSpecifier": {
|
|
99
|
-
// import { IconHeart } from "some-package";
|
|
100
|
-
const currentImportedName = currentSpecifier.imported.name;
|
|
101
|
-
|
|
102
|
-
if (currentImportedName in importTransformers.identifier === false)
|
|
103
|
-
return currentSpecifier;
|
|
104
|
-
|
|
105
|
-
const newImportedName = importTransformers.identifier[currentImportedName];
|
|
106
|
-
|
|
107
|
-
const hasNoChange = newImportedName === currentImportedName;
|
|
108
|
-
|
|
109
|
-
if (hasNoChange) return currentSpecifier;
|
|
110
|
-
|
|
111
|
-
// TODO
|
|
112
|
-
// impactedSpecifierCount++;
|
|
113
|
-
const newImportedIdentifier = jscodeshift.identifier(newImportedName);
|
|
114
|
-
|
|
115
|
-
// import { IconHeart as Heart } from "some-package"; 에서
|
|
116
|
-
// imported: "IconHeart", local: "Heart"
|
|
117
|
-
return jscodeshift.importSpecifier(newImportedIdentifier, currentSpecifier.local);
|
|
118
|
-
}
|
|
119
|
-
case "ImportDefaultSpecifier": {
|
|
120
|
-
// import Icon from "some-package";
|
|
121
|
-
return currentSpecifier;
|
|
122
|
-
}
|
|
123
|
-
case "ImportNamespaceSpecifier": {
|
|
124
|
-
// import * as Icon from "some-package";
|
|
125
|
-
return currentSpecifier;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const newImportDeclaration = jscodeshift.importDeclaration(
|
|
131
|
-
newSpecifiers,
|
|
132
|
-
jscodeshift.literal(newSourceValue),
|
|
133
|
-
currentImportKind,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
return newImportDeclaration;
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
interface MigrateIdentifiersParams {
|
|
141
|
-
identifiers: jscodeshift.Collection<jscodeshift.Identifier>;
|
|
142
|
-
identifierTransformers: ImportTransformers["identifier"];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function migrateIdentifiers({
|
|
146
|
-
identifiers,
|
|
147
|
-
identifierTransformers,
|
|
148
|
-
}: MigrateIdentifiersParams) {
|
|
149
|
-
identifiers.replaceWith((identifier) => {
|
|
150
|
-
const currentName = identifier.node.name;
|
|
151
|
-
|
|
152
|
-
if (currentName in identifierTransformers === false) return identifier;
|
|
153
|
-
|
|
154
|
-
return jscodeshift.identifier(identifierTransformers[currentName]);
|
|
155
|
-
});
|
|
156
|
-
}
|