@toss/tds-mobile-migration 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +43 -0
- package/dist/cli.js.map +1 -0
- package/dist/tds-v2/codemod/commands/badge-props-rename.cjs +81 -0
- package/dist/tds-v2/codemod/commands/badge-size-rename.cjs +89 -0
- package/dist/tds-v2/codemod/commands/boardrow-rightarrow-rename.cjs +89 -0
- package/dist/tds-v2/codemod/commands/bottomcta-component-rename.cjs +70 -0
- package/dist/tds-v2/codemod/commands/button-export-rename.cjs +120 -0
- package/dist/tds-v2/codemod/commands/button-props-rename.cjs +205 -0
- package/dist/tds-v2/codemod/commands/button-size-rename.cjs +209 -0
- package/dist/tds-v2/codemod/commands/iconbutton-label-to-aria-label.cjs +84 -0
- package/dist/tds-v2/codemod/commands/listrow-verticalpadding-rename.cjs +101 -0
- package/dist/tds-v2/codemod/commands/textbutton-typography-to-size.cjs +229 -0
- package/dist/tds-v2/codemod/commands/top-subtitle-props-rename.cjs +66 -0
- package/dist/tds-v2/index.d.ts +2 -0
- package/dist/tds-v2/index.d.ts.map +1 -0
- package/dist/tds-v2/index.js +2 -0
- package/dist/tds-v2/index.js.map +1 -0
- package/dist/tds-v2/runCodemod.d.ts +5 -0
- package/dist/tds-v2/runCodemod.d.ts.map +1 -0
- package/dist/tds-v2/runCodemod.js +37 -0
- package/dist/tds-v2/runCodemod.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/v2/codemod/commands/provider.cjs +97 -0
- package/dist/v2/index.d.ts +2 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/index.js +2 -0
- package/dist/v2/index.js.map +1 -0
- package/dist/v2/runCodemod.d.ts +5 -0
- package/dist/v2/runCodemod.d.ts.map +1 -0
- package/dist/v2/runCodemod.js +37 -0
- package/dist/v2/runCodemod.js.map +1 -0
- package/package.json +23 -0
package/cli.js
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runCodemod as runMobileV2Codemod } from './tds-v2/index.js';
|
|
4
|
+
import { runCodemod as runMobileV2CodemodForAIT } from './v2/index.js';
|
|
5
|
+
const program = new Command();
|
|
6
|
+
const TDS_V2_BREAKING_CHANGE_COMPONENTS = [
|
|
7
|
+
'textbutton-typography-to-size',
|
|
8
|
+
'badge-props-rename',
|
|
9
|
+
'top-subtitle-props-rename',
|
|
10
|
+
'badge-size-rename',
|
|
11
|
+
'boardrow-rightarrow-rename',
|
|
12
|
+
'bottomcta-component-rename',
|
|
13
|
+
'button-export-rename',
|
|
14
|
+
'button-size-rename',
|
|
15
|
+
'button-props-rename',
|
|
16
|
+
'iconbutton-label-to-aria-label',
|
|
17
|
+
'listrow-verticalpadding-rename',
|
|
18
|
+
];
|
|
19
|
+
const AIT_CODEMODS = ['provider'];
|
|
20
|
+
program
|
|
21
|
+
.command('tds-v2 [destination]')
|
|
22
|
+
.option('-g --glob <glob>', 'Glob for files upon which to apply the migration', '{src,pages}/**/*.{ts,tsx}')
|
|
23
|
+
.action(async (_, { glob }) => {
|
|
24
|
+
try {
|
|
25
|
+
console.log(chalk.green('BC가 담긴 컴포넌트 마이그레이션을 진행해요'));
|
|
26
|
+
for (const component of TDS_V2_BREAKING_CHANGE_COMPONENTS) {
|
|
27
|
+
console.log(chalk.green(`BC Migration: ${component} 마이그레이션을 진행해요`));
|
|
28
|
+
await runMobileV2Codemod(component, { glob, packageName: '@toss/tds-mobile' });
|
|
29
|
+
}
|
|
30
|
+
for (const component of AIT_CODEMODS) {
|
|
31
|
+
console.log(chalk.green(`AIT Migration: ${component} 마이그레이션을 진행해요`));
|
|
32
|
+
await runMobileV2CodemodForAIT(component, { glob, packageName: '@toss/tds-mobile-ait' });
|
|
33
|
+
}
|
|
34
|
+
console.log(chalk.green('--------------------------------\n\n'));
|
|
35
|
+
console.log(chalk.green('v2 마이그레이션 작업들이 완료되었어요!'));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(error);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
program.parse(process.argv);
|
|
43
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,UAAU,IAAI,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,MAAM,iCAAiC,GAAG;IACxC,+BAA+B;IAC/B,oBAAoB;IACpB,2BAA2B;IAC3B,mBAAmB;IACnB,4BAA4B;IAC5B,4BAA4B;IAC5B,sBAAsB;IACtB,oBAAoB;IACpB,qBAAqB;IACrB,gCAAgC;IAChC,gCAAgC;CACjC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,UAAU,CAAC,CAAC;AAElC,OAAO;KACJ,OAAO,CAAC,sBAAsB,CAAC;KAC/B,MAAM,CAAC,kBAAkB,EAAE,kDAAkD,EAAE,2BAA2B,CAAC;KAC3G,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,EAAoB,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACrD,KAAK,MAAM,SAAS,IAAI,iCAAiC,EAAE,CAAC;YAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,SAAS,eAAe,CAAC,CAAC,CAAC;YACpE,MAAM,kBAAkB,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACjF,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,YAAY,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,SAAS,eAAe,CAAC,CAAC,CAAC;YACrE,MAAM,wBAAwB,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;QAEjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {import('jscodeshift').Transform}
|
|
3
|
+
*/
|
|
4
|
+
const transform = (fileInfo, { jscodeshift: j }, options) => {
|
|
5
|
+
const root = j(fileInfo.source);
|
|
6
|
+
|
|
7
|
+
// package name을 options에서 가져와요 (기본값: @tds/mobile)
|
|
8
|
+
const packageName = options['package-name'] || '@tds/mobile';
|
|
9
|
+
|
|
10
|
+
// packageName에서 import된 컴포넌트들을 추적하는 Set
|
|
11
|
+
const tdsImportedComponents = new Set();
|
|
12
|
+
|
|
13
|
+
// packageName import 구문들을 찾아서 컴포넌트 이름들을 수집해요
|
|
14
|
+
root
|
|
15
|
+
.find(j.ImportDeclaration, {
|
|
16
|
+
source: { value: packageName },
|
|
17
|
+
})
|
|
18
|
+
.forEach(path => {
|
|
19
|
+
path.value.specifiers.forEach(specifier => {
|
|
20
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
21
|
+
tdsImportedComponents.add(specifier.local.name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Badge 관련 컴포넌트가 packageName에서 import되지 않았다면 변환하지 않아요
|
|
27
|
+
if (!tdsImportedComponents.has('Badge') && !tdsImportedComponents.has('Paragraph') && !tdsImportedComponents.has('Top')) {
|
|
28
|
+
return fileInfo.source;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// prop 이름 매핑 규칙
|
|
32
|
+
const propNameMap = {
|
|
33
|
+
type: 'color',
|
|
34
|
+
style: 'variant',
|
|
35
|
+
htmlStyle: 'style',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// JSX 컴포넌트의 prop 이름 변경 공통 함수
|
|
39
|
+
function transformPropNames(openingElement) {
|
|
40
|
+
const attributes = openingElement.attributes;
|
|
41
|
+
|
|
42
|
+
// 모든 속성을 순회하면서 매핑된 prop 이름 변경
|
|
43
|
+
attributes.forEach(attr => {
|
|
44
|
+
if (attr.type === 'JSXAttribute' && propNameMap[attr.name.name]) {
|
|
45
|
+
attr.name.name = propNameMap[attr.name.name];
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 모든 JSX 요소를 순회해요
|
|
51
|
+
root.find(j.JSXElement).forEach(path => {
|
|
52
|
+
const openingElement = path.value.openingElement;
|
|
53
|
+
const elementName = openingElement.name;
|
|
54
|
+
|
|
55
|
+
// Badge 직접 사용 - packageName에서 import된 경우에만
|
|
56
|
+
if (elementName.name === 'Badge' && tdsImportedComponents.has('Badge')) {
|
|
57
|
+
transformPropNames(openingElement);
|
|
58
|
+
}
|
|
59
|
+
// 컴파운드 컴포넌트들
|
|
60
|
+
else if (elementName.type === 'JSXMemberExpression') {
|
|
61
|
+
const objectName = elementName.object.name;
|
|
62
|
+
const propertyName = elementName.property.name;
|
|
63
|
+
|
|
64
|
+
// Paragraph.Badge - Paragraph가 packageName에서 import된 경우에만
|
|
65
|
+
if (objectName === 'Paragraph' && propertyName === 'Badge' && tdsImportedComponents.has('Paragraph')) {
|
|
66
|
+
transformPropNames(openingElement);
|
|
67
|
+
}
|
|
68
|
+
// Top.SubtitleBadges - Top이 packageName에서 import된 경우에만
|
|
69
|
+
else if (objectName === 'Top' && propertyName === 'SubtitleBadges' && tdsImportedComponents.has('Top')) {
|
|
70
|
+
transformPropNames(openingElement);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return root.toSource({ quote: 'double' });
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const parser = 'tsx';
|
|
79
|
+
|
|
80
|
+
module.exports = transform;
|
|
81
|
+
module.exports.parser = parser;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {import('jscodeshift').Transform}
|
|
3
|
+
*/
|
|
4
|
+
const transform = (fileInfo, { jscodeshift: j }, options) => {
|
|
5
|
+
const root = j(fileInfo.source);
|
|
6
|
+
|
|
7
|
+
// package name을 options에서 가져와요 (기본값: @tds/mobile)
|
|
8
|
+
const packageName = options['package-name'] || '@tds/mobile';
|
|
9
|
+
|
|
10
|
+
// packageName에서 import된 컴포넌트들을 추적하는 Set
|
|
11
|
+
const tdsImportedComponents = new Set();
|
|
12
|
+
|
|
13
|
+
// packageName import 구문들을 찾아서 컴포넌트 이름들을 수집해요
|
|
14
|
+
root
|
|
15
|
+
.find(j.ImportDeclaration, {
|
|
16
|
+
source: { value: packageName },
|
|
17
|
+
})
|
|
18
|
+
.forEach(path => {
|
|
19
|
+
path.value.specifiers.forEach(specifier => {
|
|
20
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
21
|
+
tdsImportedComponents.add(specifier.local.name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Badge 관련 컴포넌트가 packageName에서 import되지 않았다면 변환하지 않아요
|
|
27
|
+
if (!tdsImportedComponents.has('Badge') && !tdsImportedComponents.has('Paragraph') && !tdsImportedComponents.has('Top')) {
|
|
28
|
+
return fileInfo.source;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// size 값 매핑 규칙
|
|
32
|
+
const sizeValueMap = {
|
|
33
|
+
tiny: 'xsmall',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// JSX 컴포넌트의 size prop 값 변경 공통 함수
|
|
37
|
+
function transformSizeValues(openingElement) {
|
|
38
|
+
const attributes = openingElement.attributes;
|
|
39
|
+
|
|
40
|
+
// size 속성 찾아서 값 변경
|
|
41
|
+
attributes.forEach(attr => {
|
|
42
|
+
if (attr.type === 'JSXAttribute' && attr.name.name === 'size') {
|
|
43
|
+
let sizeValue = null;
|
|
44
|
+
|
|
45
|
+
// 속성 값 추출해요 - StringLiteral과 Literal 둘 다 처리
|
|
46
|
+
if (attr.value && (attr.value.type === 'Literal' || attr.value.type === 'StringLiteral')) {
|
|
47
|
+
sizeValue = attr.value.value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 매핑된 값이 있으면 변경해요
|
|
51
|
+
if (sizeValue && sizeValueMap[sizeValue]) {
|
|
52
|
+
attr.value.value = sizeValueMap[sizeValue];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 모든 JSX 요소를 순회해요
|
|
59
|
+
root.find(j.JSXElement).forEach(path => {
|
|
60
|
+
const openingElement = path.value.openingElement;
|
|
61
|
+
const elementName = openingElement.name;
|
|
62
|
+
|
|
63
|
+
// Badge 직접 사용 - packageName에서 import된 경우에만
|
|
64
|
+
if (elementName.name === 'Badge' && tdsImportedComponents.has('Badge')) {
|
|
65
|
+
transformSizeValues(openingElement);
|
|
66
|
+
}
|
|
67
|
+
// 컴파운드 컴포넌트들
|
|
68
|
+
else if (elementName.type === 'JSXMemberExpression') {
|
|
69
|
+
const objectName = elementName.object.name;
|
|
70
|
+
const propertyName = elementName.property.name;
|
|
71
|
+
|
|
72
|
+
// Paragraph.Badge - Paragraph가 packageName에서 import된 경우에만
|
|
73
|
+
if (objectName === 'Paragraph' && propertyName === 'Badge' && tdsImportedComponents.has('Paragraph')) {
|
|
74
|
+
transformSizeValues(openingElement);
|
|
75
|
+
}
|
|
76
|
+
// Top.SubtitleBadges - Top이 packageName에서 import된 경우에만
|
|
77
|
+
else if (objectName === 'Top' && propertyName === 'SubtitleBadges' && tdsImportedComponents.has('Top')) {
|
|
78
|
+
transformSizeValues(openingElement);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return root.toSource({ quote: 'double' });
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const parser = 'tsx';
|
|
87
|
+
|
|
88
|
+
module.exports = transform;
|
|
89
|
+
module.exports.parser = parser;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {import('jscodeshift').Transform}
|
|
3
|
+
*/
|
|
4
|
+
const transform = (fileInfo, { jscodeshift: j }, options) => {
|
|
5
|
+
const root = j(fileInfo.source);
|
|
6
|
+
|
|
7
|
+
// package name을 options에서 가져와요 (기본값: @tds/mobile)
|
|
8
|
+
const packageName = options['package-name'] || '@tds/mobile';
|
|
9
|
+
|
|
10
|
+
// packageName에서 import된 컴포넌트들을 추적하는 Set
|
|
11
|
+
const tdsImportedComponents = new Set();
|
|
12
|
+
|
|
13
|
+
// packageName import 구문들을 찾아서 컴포넌트 이름들을 수집해요
|
|
14
|
+
root
|
|
15
|
+
.find(j.ImportDeclaration, {
|
|
16
|
+
source: { value: packageName },
|
|
17
|
+
})
|
|
18
|
+
.forEach(path => {
|
|
19
|
+
path.value.specifiers.forEach(specifier => {
|
|
20
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
21
|
+
tdsImportedComponents.add(specifier.local.name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// BoardRow 컴포넌트가 packageName에서 import되지 않았다면 변환하지 않아요
|
|
27
|
+
if (!tdsImportedComponents.has('BoardRow')) {
|
|
28
|
+
return fileInfo.source;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 1. JSX 컴포넌트 이름 변경: BoardRow.RightArrow → BoardRow.ArrowIcon
|
|
32
|
+
root.find(j.JSXElement).forEach(path => {
|
|
33
|
+
const openingElement = path.value.openingElement;
|
|
34
|
+
const closingElement = path.value.closingElement;
|
|
35
|
+
|
|
36
|
+
// JSXMemberExpression 체크 (BoardRow.RightArrow)
|
|
37
|
+
if (openingElement.name.type === 'JSXMemberExpression') {
|
|
38
|
+
const objectName = openingElement.name.object.name;
|
|
39
|
+
const propertyName = openingElement.name.property.name;
|
|
40
|
+
|
|
41
|
+
// BoardRow.RightArrow - BoardRow가 packageName에서 import된 경우에만
|
|
42
|
+
if (objectName === 'BoardRow' && propertyName === 'RightArrow' && tdsImportedComponents.has('BoardRow')) {
|
|
43
|
+
// opening element 변경
|
|
44
|
+
openingElement.name.property.name = 'ArrowIcon';
|
|
45
|
+
|
|
46
|
+
// closing element도 변경 (있는 경우)
|
|
47
|
+
if (closingElement && closingElement.name.type === 'JSXMemberExpression') {
|
|
48
|
+
closingElement.name.property.name = 'ArrowIcon';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 2. TypeScript 타입 이름 변경: BoardRowRightArrowProps → BoardRowArrowIconProps
|
|
55
|
+
root.find(j.Identifier, { name: 'BoardRowRightArrowProps' }).forEach(path => {
|
|
56
|
+
path.node.name = 'BoardRowArrowIconProps';
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 3. TSTypeReference에서도 변경 (타입 참조에서)
|
|
60
|
+
root.find(j.TSTypeReference).forEach(path => {
|
|
61
|
+
if (path.node.typeName && path.node.typeName.name === 'BoardRowRightArrowProps') {
|
|
62
|
+
path.node.typeName.name = 'BoardRowArrowIconProps';
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 4. Import/Export에서도 변경
|
|
67
|
+
root.find(j.ImportSpecifier).forEach(path => {
|
|
68
|
+
if (path.node.imported.name === 'BoardRowRightArrowProps') {
|
|
69
|
+
path.node.imported.name = 'BoardRowArrowIconProps';
|
|
70
|
+
// local name도 같이 변경 (as로 rename 안한 경우)
|
|
71
|
+
if (path.node.local.name === 'BoardRowRightArrowProps') {
|
|
72
|
+
path.node.local.name = 'BoardRowArrowIconProps';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
root.find(j.ExportSpecifier).forEach(path => {
|
|
78
|
+
if (path.node.exported.name === 'BoardRowRightArrowProps') {
|
|
79
|
+
path.node.exported.name = 'BoardRowArrowIconProps';
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return root.toSource({ quote: 'double' });
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const parser = 'tsx';
|
|
87
|
+
|
|
88
|
+
module.exports = transform;
|
|
89
|
+
module.exports.parser = parser;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {import('jscodeshift').Transform}
|
|
3
|
+
*/
|
|
4
|
+
const transform = (fileInfo, { jscodeshift: j }, options) => {
|
|
5
|
+
const root = j(fileInfo.source);
|
|
6
|
+
|
|
7
|
+
// package name을 options에서 가져와요 (기본값: @tds/mobile)
|
|
8
|
+
const packageName = options['package-name'] || '@tds/mobile';
|
|
9
|
+
|
|
10
|
+
// packageName에서 import된 컴포넌트들을 추적하는 Set
|
|
11
|
+
const tdsImportedComponents = new Set();
|
|
12
|
+
|
|
13
|
+
// packageName import 구문들을 찾아서 컴포넌트 이름들을 수집해요
|
|
14
|
+
root
|
|
15
|
+
.find(j.ImportDeclaration, {
|
|
16
|
+
source: { value: packageName },
|
|
17
|
+
})
|
|
18
|
+
.forEach(path => {
|
|
19
|
+
path.value.specifiers.forEach(specifier => {
|
|
20
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
21
|
+
tdsImportedComponents.add(specifier.local.name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// BottomCTA 또는 FixedBottomCTA 컴포넌트가 packageName에서 import되지 않았다면 변환하지 않아요
|
|
27
|
+
if (!tdsImportedComponents.has('BottomCTA') && !tdsImportedComponents.has('FixedBottomCTA')) {
|
|
28
|
+
return fileInfo.source;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 컴포넌트 이름 매핑 규칙
|
|
32
|
+
const componentNameMap = {
|
|
33
|
+
TypeA: 'Single',
|
|
34
|
+
TypeB: 'Double',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// 모든 JSX 요소를 순회해요
|
|
38
|
+
root.find(j.JSXElement).forEach(path => {
|
|
39
|
+
const openingElement = path.value.openingElement;
|
|
40
|
+
const closingElement = path.value.closingElement;
|
|
41
|
+
|
|
42
|
+
// 컴파운드 컴포넌트인지 확인해요 (BottomCTA.TypeA, BottomCTA.TypeB, FixedBottomCTA.TypeA, FixedBottomCTA.TypeB)
|
|
43
|
+
if (openingElement.name && openingElement.name.type === 'JSXMemberExpression') {
|
|
44
|
+
const objectName = openingElement.name.object.name;
|
|
45
|
+
const propertyName = openingElement.name.property.name;
|
|
46
|
+
|
|
47
|
+
// BottomCTA 또는 FixedBottomCTA 컴포넌트의 TypeA, TypeB인 경우에만 처리해요 - packageName에서 import된 경우에만
|
|
48
|
+
if (
|
|
49
|
+
(objectName === 'BottomCTA' || objectName === 'FixedBottomCTA') &&
|
|
50
|
+
componentNameMap[propertyName] &&
|
|
51
|
+
tdsImportedComponents.has(objectName)
|
|
52
|
+
) {
|
|
53
|
+
// opening element 이름 변경해요
|
|
54
|
+
openingElement.name.property.name = componentNameMap[propertyName];
|
|
55
|
+
|
|
56
|
+
// closing element도 있으면 변경해요
|
|
57
|
+
if (closingElement && closingElement.name && closingElement.name.type === 'JSXMemberExpression') {
|
|
58
|
+
closingElement.name.property.name = componentNameMap[propertyName];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return root.toSource({ quote: 'single' });
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const parser = 'tsx';
|
|
68
|
+
|
|
69
|
+
module.exports = transform;
|
|
70
|
+
module.exports.parser = parser;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {import('jscodeshift').Transform}
|
|
3
|
+
*/
|
|
4
|
+
const transform = (fileInfo, { jscodeshift: j }, options) => {
|
|
5
|
+
const root = j(fileInfo.source);
|
|
6
|
+
|
|
7
|
+
// package name을 options에서 가져와요 (기본값: @tds/mobile)
|
|
8
|
+
const packageName = options['package-name'] || '@tds/mobile';
|
|
9
|
+
|
|
10
|
+
// packageName에서 import하는 export들을 추적하는 Set
|
|
11
|
+
const tdsImportedExports = new Set();
|
|
12
|
+
|
|
13
|
+
// export 이름 매핑 규칙
|
|
14
|
+
const exportNameMap = {
|
|
15
|
+
TDSButtonStyle: 'TDSButtonVariant',
|
|
16
|
+
TDSButtonType: 'TDSButtonColor',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const identifiersToRename = new Map();
|
|
20
|
+
let hasChanges = false;
|
|
21
|
+
|
|
22
|
+
// packageName에서 import하는 구문들을 찾아서 처리해요
|
|
23
|
+
root
|
|
24
|
+
.find(j.ImportDeclaration, {
|
|
25
|
+
source: { value: packageName },
|
|
26
|
+
})
|
|
27
|
+
.forEach(path => {
|
|
28
|
+
path.value.specifiers.forEach(specifier => {
|
|
29
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
30
|
+
// named import 처리 (import { TDSButtonStyle, TDSButtonType } from packageName)
|
|
31
|
+
if (specifier.imported && specifier.imported.name) {
|
|
32
|
+
const importedName = specifier.imported.name;
|
|
33
|
+
const localName = specifier.local.name;
|
|
34
|
+
|
|
35
|
+
// export 이름을 Set에 추가해요
|
|
36
|
+
tdsImportedExports.add(importedName);
|
|
37
|
+
|
|
38
|
+
if (exportNameMap[importedName]) {
|
|
39
|
+
// import 시 이름 변경해요
|
|
40
|
+
specifier.imported.name = exportNameMap[importedName];
|
|
41
|
+
|
|
42
|
+
// alias가 없는 경우에만 local name도 변경하고 rename 대상에 추가해요
|
|
43
|
+
if (localName === importedName) {
|
|
44
|
+
specifier.local.name = exportNameMap[importedName];
|
|
45
|
+
// alias가 없는 경우에만 rename 대상으로 추가해요
|
|
46
|
+
identifiersToRename.set(localName, exportNameMap[importedName]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
hasChanges = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// type import도 처리해요 (import type { TDSButtonStyle } from packageName)
|
|
57
|
+
root
|
|
58
|
+
.find(j.ImportDeclaration, {
|
|
59
|
+
source: { value: packageName },
|
|
60
|
+
importKind: 'type',
|
|
61
|
+
})
|
|
62
|
+
.forEach(path => {
|
|
63
|
+
path.value.specifiers.forEach(specifier => {
|
|
64
|
+
if (specifier.type === 'ImportSpecifier' && specifier.imported && specifier.imported.name) {
|
|
65
|
+
const importedName = specifier.imported.name;
|
|
66
|
+
const localName = specifier.local.name;
|
|
67
|
+
|
|
68
|
+
// export 이름을 Set에 추가해요
|
|
69
|
+
tdsImportedExports.add(importedName);
|
|
70
|
+
|
|
71
|
+
if (exportNameMap[importedName]) {
|
|
72
|
+
specifier.imported.name = exportNameMap[importedName];
|
|
73
|
+
|
|
74
|
+
// alias가 없는 경우에만 local name도 변경하고 rename 대상에 추가해요
|
|
75
|
+
if (localName === importedName) {
|
|
76
|
+
specifier.local.name = exportNameMap[importedName];
|
|
77
|
+
// alias가 없는 경우에만 rename 대상으로 추가해요
|
|
78
|
+
identifiersToRename.set(localName, exportNameMap[importedName]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
hasChanges = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Button 관련 export가 packageName에서 import되지 않았다면 변환하지 않아요
|
|
88
|
+
if (!tdsImportedExports.has('TDSButtonStyle') && !tdsImportedExports.has('TDSButtonType')) {
|
|
89
|
+
return fileInfo.source;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// import 외의 일반 코드에서 Button 관련 식별자 이름 변경 적용해요
|
|
93
|
+
identifiersToRename.forEach((newName, oldName) => {
|
|
94
|
+
root
|
|
95
|
+
.find(j.Identifier, { name: oldName })
|
|
96
|
+
.filter(path => {
|
|
97
|
+
// 이미 import 구문에서 처리된 경우 건너뛰어요
|
|
98
|
+
if (path.parent.value.type === 'ImportSpecifier') return false;
|
|
99
|
+
// 멤버 접근의 프로퍼티인 경우 건너뛰어요 (예: obj.TDSButtonStyle)
|
|
100
|
+
if (
|
|
101
|
+
path.parent.value.type === 'MemberExpression' &&
|
|
102
|
+
path.parent.value.property === path.node &&
|
|
103
|
+
!path.parent.value.computed
|
|
104
|
+
) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
})
|
|
109
|
+
.forEach(path => {
|
|
110
|
+
path.node.name = newName;
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return hasChanges ? root.toSource({ quote: 'double' }) : fileInfo.source;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const parser = 'tsx';
|
|
118
|
+
|
|
119
|
+
module.exports = transform;
|
|
120
|
+
module.exports.parser = parser;
|