@seed-design/codemod 0.0.0-alpha-20241018093322

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.
@@ -0,0 +1,147 @@
1
+ import jscodeshift from "jscodeshift";
2
+ import type { MigrateIconsOptions } from "../transforms/migrate-icons.js";
3
+ import type { Logger } from "winston";
4
+
5
+ interface MigrateImportDeclarationsParams {
6
+ importDeclarations: jscodeshift.Collection<jscodeshift.ImportDeclaration>;
7
+ match: MigrateIconsOptions["match"];
8
+ logger: Logger;
9
+ filePath: jscodeshift.FileInfo["path"];
10
+ }
11
+
12
+ export function migrateImportDeclarations({
13
+ importDeclarations,
14
+ match,
15
+ logger,
16
+ filePath,
17
+ }: MigrateImportDeclarationsParams) {
18
+ importDeclarations.replaceWith((imp) => {
19
+ const currentSourceValue = imp.node.source.value;
20
+ const currentSpecifiers = imp.node.specifiers;
21
+ const currentImportKind = imp.node.importKind;
22
+
23
+ const newSourceValue = (() => {
24
+ if (typeof currentSourceValue !== "string") return currentSourceValue;
25
+
26
+ const { startsWith, replaceWith } = match.source.find(({ startsWith }) =>
27
+ currentSourceValue.startsWith(startsWith),
28
+ );
29
+
30
+ const sourceReplaced = replaceWith
31
+ ? currentSourceValue.replace(startsWith, replaceWith)
32
+ : currentSourceValue;
33
+
34
+ const slashSplits = sourceReplaced.split("/");
35
+
36
+ // @seed-design/icon -> @seed-design/react-icon
37
+ // @seed-design/icon/IconSomething -> @seed-design/react-icon/IconSomething
38
+ // @seed-design/icon/lib/IconSomething -> @seed-design/react-icon/lib/IconSomething
39
+ const result = slashSplits
40
+ .map((split, index) => {
41
+ if (index !== slashSplits.length - 1 || split in match.identifier === false) return split;
42
+
43
+ return match.identifier[split];
44
+ })
45
+ .join("/");
46
+
47
+ return result;
48
+ })();
49
+
50
+ // import { a, b, c } from "some-package";
51
+ // a, b, c 각각 ImportSpecifier
52
+ // imported name: a, b, c, local name: a, b, c
53
+
54
+ // import { a as A, b as B, c as C } from "some-package";
55
+ // a as A, b as B, c as C 각각 ImportSpecifier
56
+ // imported name: a, b, c, local name: A, B, C
57
+
58
+ // import A from "some-package";
59
+ // A는 ImportDefaultSpecifier
60
+
61
+ // import * as A from "some-package";
62
+ // * as A는 ImportNamespaceSpecifier
63
+ // A는 local name
64
+
65
+ logger.debug(`${filePath}: source ${currentSourceValue} -> ${newSourceValue}`);
66
+
67
+ const newSpecifiers = currentSpecifiers.map((currentSpecifier) => {
68
+ switch (currentSpecifier.type) {
69
+ case "ImportSpecifier": {
70
+ const currentImportedName = currentSpecifier.imported.name;
71
+
72
+ if (currentImportedName in match.identifier === false) return currentSpecifier;
73
+
74
+ const newImportedName = match.identifier[currentImportedName];
75
+
76
+ const hasNoChange = newImportedName === currentImportedName;
77
+
78
+ if (hasNoChange) return currentSpecifier;
79
+
80
+ logger.debug(`${filePath}: imported name ${currentImportedName} -> ${newImportedName}`);
81
+
82
+ const newImportedIdentifier = jscodeshift.identifier(newImportedName);
83
+
84
+ // local name 유지하는 이유:
85
+ // import 밑에서 사용되는 실제 변수명은 migrateImportDeclaration에서 다루지 않으므로 바꾸면 곤란
86
+ return jscodeshift.importSpecifier(newImportedIdentifier, currentSpecifier.local);
87
+ }
88
+ case "ImportDefaultSpecifier": {
89
+ // import name 없으니 자연스럽게 local name 유지
90
+ return currentSpecifier;
91
+ }
92
+ case "ImportNamespaceSpecifier": {
93
+ // import name 없으니 자연스럽게 local name 유지
94
+ return currentSpecifier;
95
+ }
96
+ }
97
+ });
98
+
99
+ const newSpecifiersWithoutDuplicates = newSpecifiers.filter((specifier, index, self) => {
100
+ if (specifier.type !== "ImportSpecifier") return true;
101
+
102
+ const currentImportedName = specifier.imported.name;
103
+
104
+ return (
105
+ self.findIndex(
106
+ (s) => (s as jscodeshift.ImportSpecifier).imported.name === currentImportedName,
107
+ ) === index
108
+ );
109
+ });
110
+
111
+ const newImportDeclaration = jscodeshift.importDeclaration(
112
+ newSpecifiersWithoutDuplicates,
113
+ jscodeshift.literal(newSourceValue),
114
+ currentImportKind,
115
+ );
116
+
117
+ return newImportDeclaration;
118
+ });
119
+ }
120
+
121
+ interface MigrateIdentifiersParams {
122
+ identifiers: jscodeshift.Collection<jscodeshift.Identifier>;
123
+ identifierMatch: MigrateIconsOptions["match"]["identifier"];
124
+ logger: Logger;
125
+ filePath: jscodeshift.FileInfo["path"];
126
+ }
127
+
128
+ export function migrateIdentifiers({
129
+ identifiers,
130
+ identifierMatch,
131
+ logger,
132
+ filePath,
133
+ }: MigrateIdentifiersParams) {
134
+ identifiers.replaceWith((identifier) => {
135
+ const currentName = identifier.node.name;
136
+ if (currentName in identifierMatch === false) {
137
+ logger.error(`${filePath}: identifier ${currentName}에 대한 변환 정보 없음`);
138
+
139
+ return identifier;
140
+ }
141
+
142
+ const newName = identifierMatch[currentName];
143
+ logger.debug(`${filePath}: identifier ${currentName} -> ${newName}`);
144
+
145
+ return jscodeshift.identifier(newName);
146
+ });
147
+ }