@simplysm/sd-cli 12.9.42 → 12.10.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.
Files changed (45) hide show
  1. package/dist/entry/sd-cli-electron.js +11 -14
  2. package/dist/entry/sd-cli-electron.js.map +1 -1
  3. package/dist/entry/sd-cli-postinstall.js +1 -1
  4. package/dist/entry/sd-cli-postinstall.js.map +1 -1
  5. package/dist/fix/convert-extends-sd-modal-base-to-interface.d.ts +1 -0
  6. package/dist/fix/convert-extends-sd-modal-base-to-interface.js +289 -0
  7. package/dist/fix/convert-extends-sd-modal-base-to-interface.js.map +1 -0
  8. package/dist/fix/convert-modal-show-params.d.ts +1 -0
  9. package/dist/fix/convert-modal-show-params.js +32 -0
  10. package/dist/fix/convert-modal-show-params.js.map +1 -0
  11. package/dist/fix/convert-sd-angular-symbol-names.js +1 -0
  12. package/dist/fix/convert-sd-angular-symbol-names.js.map +1 -1
  13. package/dist/fix/convert-sd-sheet-bindings-inInline-template.js +6 -1
  14. package/dist/fix/convert-sd-sheet-bindings-inInline-template.js.map +1 -1
  15. package/dist/fix/core/convert-symbol.js +1 -0
  16. package/dist/fix/core/convert-symbol.js.map +1 -1
  17. package/dist/fix/core/remove-named-import.d.ts +1 -0
  18. package/dist/fix/core/remove-named-import.js +14 -0
  19. package/dist/fix/core/remove-named-import.js.map +1 -0
  20. package/dist/fix/core/remove-symbol.d.ts +1 -0
  21. package/dist/fix/core/remove-symbol.js +76 -0
  22. package/dist/fix/core/remove-symbol.js.map +1 -0
  23. package/dist/fix/remove-sd-angular-symbol-names.d.ts +1 -0
  24. package/dist/fix/remove-sd-angular-symbol-names.js +9 -0
  25. package/dist/fix/remove-sd-angular-symbol-names.js.map +1 -0
  26. package/dist/pkg-builders/client/sd-ng.bundler.js +1 -1
  27. package/dist/pkg-builders/client/sd-ng.bundler.js.map +1 -1
  28. package/dist/sd-cli-entry.js +12 -14
  29. package/dist/sd-cli-entry.js.map +1 -1
  30. package/dist/sd-cli.js +54 -38
  31. package/dist/sd-cli.js.map +1 -1
  32. package/package.json +12 -12
  33. package/src/entry/sd-cli-electron.ts +34 -38
  34. package/src/entry/sd-cli-postinstall.ts +1 -1
  35. package/src/fix/convert-extends-sd-modal-base-to-interface.ts +324 -0
  36. package/src/fix/convert-modal-show-params.ts +41 -0
  37. package/src/fix/convert-sd-angular-symbol-names.ts +1 -0
  38. package/src/fix/convert-sd-sheet-bindings-inInline-template.ts +32 -29
  39. package/src/fix/core/convert-symbol.ts +2 -0
  40. package/src/fix/core/remove-named-import.ts +14 -0
  41. package/src/fix/core/remove-symbol.ts +94 -0
  42. package/src/fix/remove-sd-angular-symbol-names.ts +11 -0
  43. package/src/pkg-builders/client/sd-ng.bundler.ts +1 -1
  44. package/src/sd-cli-entry.ts +170 -167
  45. package/src/sd-cli.ts +66 -44
@@ -0,0 +1,324 @@
1
+ /* eslint-disable no-console */
2
+ import * as path from "path";
3
+ import { SyntaxKind } from "ts-morph";
4
+ import getTsMorphSourceFiles from "./core/get-ts-morph-source-files";
5
+ import removeNamedImport from "./core/remove-named-import";
6
+
7
+ export function convertExtendsSdModalBaseToInterface() {
8
+ const sourceFiles = getTsMorphSourceFiles();
9
+
10
+ let totalChanged = 0;
11
+
12
+ for (const sourceFile of sourceFiles) {
13
+ let changed = false;
14
+ const relPath = path.basename(sourceFile.getFilePath());
15
+
16
+ const modalClass = sourceFile.getClasses().find((classDecl) => {
17
+ const extendsClause = classDecl
18
+ .getHeritageClauses()
19
+ .find((h) => h.getToken() === SyntaxKind.ExtendsKeyword);
20
+ if (!extendsClause) return false;
21
+ const typeNodes = extendsClause.getTypeNodes();
22
+ return typeNodes.some((typeNode) =>
23
+ /^SdModalBase\s*<[^,>]+,\s*[^>]+\s*>$/.test(typeNode.getText()),
24
+ );
25
+ });
26
+
27
+ if (!modalClass) continue;
28
+
29
+ const extendsClause = modalClass
30
+ .getHeritageClauses()
31
+ .find((h) => h.getToken() === SyntaxKind.ExtendsKeyword);
32
+ if (!extendsClause) continue;
33
+ const typeNodes = extendsClause.getTypeNodes();
34
+ const match = typeNodes[0]?.getText().match(/^SdModalBase\s*<([^,>]+),\s*([^>]+)\s*>$/);
35
+ if (!match) continue;
36
+ const paramTypeName = match[1].trim();
37
+ const modalOutputTypeText = match[2].trim();
38
+ const paramInterface = sourceFile.getInterface(paramTypeName);
39
+ if (!paramInterface) continue;
40
+ let props = paramInterface.getProperties();
41
+ let paramNames = props.map((p) => p.getName());
42
+
43
+ const onlyISharedData =
44
+ props.length === 0 &&
45
+ paramInterface.getExtends().some(e =>
46
+ e.getText().includes("ISharedDataModalInputParam")
47
+ );
48
+
49
+ if (onlyISharedData) {
50
+ props = [
51
+ {
52
+ getName: () => "selectedItemKeys",
53
+ hasQuestionToken: () => false,
54
+ getTypeNode: () => ({ getText: () => "any[]" })
55
+ },
56
+ {
57
+ getName: () => "selectMode",
58
+ hasQuestionToken: () => true,
59
+ getTypeNode: () => ({ getText: () => `"single" | "multi"` })
60
+ }
61
+ ] as any;
62
+ paramNames = ["selectedItemKeys", "selectMode"];
63
+ }
64
+
65
+ // 1. property/class 필드/close 추가
66
+ let lastPropIdx = 0;
67
+ for (const prop of props) {
68
+ const propName = prop.getName();
69
+ const isOptional = prop.hasQuestionToken();
70
+ const propType = prop.getTypeNode()?.getText() ?? "any";
71
+ const initializer = `input${isOptional ? "" : ".required"}<${propType}>()`;
72
+ if (!modalClass.getProperty(propName)) {
73
+ modalClass.insertProperty(lastPropIdx, { name: propName, initializer });
74
+ lastPropIdx++;
75
+ changed = true;
76
+ }
77
+ }
78
+ if (!modalClass.getProperty("close")) {
79
+ modalClass.insertProperty(lastPropIdx, { name: "close", initializer: `output<${modalOutputTypeText}>()` });
80
+ changed = true;
81
+ }
82
+
83
+ // 2. extends → implements 변환
84
+ let implementsText: string;
85
+ if (modalOutputTypeText === "ISharedDataModalOutputResult") {
86
+ implementsText = "ISdSelectModal";
87
+ } else {
88
+ implementsText = `ISdModal<${modalOutputTypeText}>`;
89
+ }
90
+ if (!modalClass.getImplements().some(i => i.getText() === implementsText)) {
91
+ modalClass.addImplements(implementsText);
92
+ changed = true;
93
+ }
94
+
95
+ // 3. super() 삭제(생성자)
96
+ const ctor = modalClass.getConstructors().first();
97
+ if (ctor) {
98
+ ctor.getStatements().forEach((stmt) => {
99
+ if (stmt.getKind() === SyntaxKind.ExpressionStatement) {
100
+ const expr = stmt.asKind(SyntaxKind.ExpressionStatement)?.getExpression();
101
+ if (expr && expr.getKind() === SyntaxKind.CallExpression) {
102
+ const callExpr = expr.asKind(SyntaxKind.CallExpression);
103
+ if (
104
+ callExpr?.getExpression().getText() === "super" &&
105
+ callExpr.getArguments().length === 0
106
+ ) {
107
+ stmt.remove();
108
+ changed = true;
109
+ }
110
+ }
111
+ }
112
+ });
113
+ }
114
+
115
+ // 4. this.open() 전체 삭제(클래스 전체)
116
+ modalClass
117
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
118
+ .filter(
119
+ (expr) =>
120
+ expr.getExpression().getKind() === SyntaxKind.PropertyAccessExpression &&
121
+ expr.getExpression().getText() === "this.open",
122
+ )
123
+ .forEach((expr) => {
124
+ const parentStmt = expr.getFirstAncestorByKind(SyntaxKind.ExpressionStatement);
125
+ if (parentStmt) {
126
+ parentStmt.remove();
127
+ changed = true;
128
+ }
129
+ });
130
+
131
+ // 5. params().xxx → xxx() 및 this.params().xxx → this.xxx() AST 치환
132
+ modalClass.forEachDescendant((node) => {
133
+ if (node.getKind() === SyntaxKind.PropertyAccessExpression) {
134
+ const propAccess = node.asKind(SyntaxKind.PropertyAccessExpression)!;
135
+ const propName = propAccess.getName();
136
+ if (!paramNames.includes(propName)) return;
137
+ const expr = propAccess.getExpression();
138
+
139
+ // params().xxx
140
+ if (expr.getKind() === SyntaxKind.CallExpression) {
141
+ const callExpr = expr.asKind(SyntaxKind.CallExpression)!;
142
+ const innerExpr = callExpr.getExpression();
143
+
144
+ // a) 단독 params()
145
+ if (innerExpr.getKind() === SyntaxKind.Identifier && innerExpr.getText() === "params") {
146
+ propAccess.replaceWithText(`${propName}()`);
147
+ changed = true;
148
+ }
149
+ // b) this.params()
150
+ else if (innerExpr.getKind() === SyntaxKind.PropertyAccessExpression) {
151
+ const propExpr = innerExpr.asKind(SyntaxKind.PropertyAccessExpression)!;
152
+ if (
153
+ propExpr.getExpression().getKind() === SyntaxKind.ThisKeyword &&
154
+ propExpr.getName() === "params"
155
+ ) {
156
+ propAccess.replaceWithText(`this.${propName}()`);
157
+ changed = true;
158
+ }
159
+ }
160
+ }
161
+ }
162
+ });
163
+
164
+ // 6. this.close(...) → this.close.emit(...)
165
+ modalClass.forEachDescendant((node) => {
166
+ if (node.getKind() === SyntaxKind.CallExpression) {
167
+ const callExpr = node.asKind(SyntaxKind.CallExpression)!;
168
+ const expr = callExpr.getExpression();
169
+ if (
170
+ expr.getKind() === SyntaxKind.PropertyAccessExpression &&
171
+ expr.asKind(SyntaxKind.PropertyAccessExpression)!.getExpression().getKind() ===
172
+ SyntaxKind.ThisKeyword
173
+ ) {
174
+ const methodName = expr.asKind(SyntaxKind.PropertyAccessExpression)!.getName();
175
+ if (methodName === "close") {
176
+ expr.asKind(SyntaxKind.PropertyAccessExpression)!.replaceWithText(`this.close.emit`);
177
+ changed = true;
178
+ }
179
+ }
180
+ }
181
+ });
182
+
183
+ // 7. @Component template: params().xxx → xxx() 치환
184
+ const componentDecorator = modalClass.getDecorator("Component");
185
+ if (componentDecorator) {
186
+ const arg = componentDecorator.getArguments().first();
187
+ if (arg && arg.getKind() === SyntaxKind.ObjectLiteralExpression) {
188
+ const objLit = arg.asKind(SyntaxKind.ObjectLiteralExpression)!;
189
+ const tplProp = objLit.getProperty("template");
190
+ if (
191
+ tplProp &&
192
+ tplProp.getKind() === SyntaxKind.PropertyAssignment &&
193
+ tplProp.asKind(SyntaxKind.PropertyAssignment)!.getInitializer()
194
+ ) {
195
+ const propAssign = tplProp.asKind(SyntaxKind.PropertyAssignment)!;
196
+ const init = propAssign.getInitializerOrThrow();
197
+ let tplText = init.getText();
198
+ let tplTextRaw = tplText;
199
+ for (const paramName of paramNames) {
200
+ const paramsPattern = new RegExp(`params\\(\\)\\.${paramName}\\b`, "g");
201
+ tplTextRaw = tplTextRaw.replace(paramsPattern, `${paramName}()`);
202
+ }
203
+ if (tplTextRaw !== tplText) {
204
+ propAssign.setInitializer(tplTextRaw);
205
+ changed = true;
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ // 8. import 관리
212
+ const sdAngularImport = sourceFile.getImportDeclaration(
213
+ d => d.getModuleSpecifierValue() === "@simplysm/sd-angular"
214
+ );
215
+ if (modalOutputTypeText === "ISharedDataModalOutputResult") {
216
+ // ISdSelectModal 추가
217
+ if (sdAngularImport) {
218
+ const namedImports = sdAngularImport.getNamedImports();
219
+ // ISdSelectModal 없으면 추가
220
+ if (!namedImports.some(n => n.getName() === "ISdSelectModal")) {
221
+ sdAngularImport.addNamedImport("ISdSelectModal");
222
+ }
223
+ // ISdModal 제거
224
+ namedImports
225
+ .filter(n => n.getName() === "ISdModal")
226
+ .forEach(n => n.remove());
227
+ } else {
228
+ sourceFile.addImportDeclaration({
229
+ namedImports: ["ISdSelectModal"],
230
+ moduleSpecifier: "@simplysm/sd-angular"
231
+ });
232
+ }
233
+ } else {
234
+ // ISdModal 추가
235
+ if (sdAngularImport) {
236
+ const namedImports = sdAngularImport.getNamedImports();
237
+ // ISdModal 없으면 추가
238
+ if (!namedImports.some(n => n.getName() === "ISdModal")) {
239
+ sdAngularImport.addNamedImport("ISdModal");
240
+ }
241
+ // ISdSelectModal 제거
242
+ namedImports
243
+ .filter(n => n.getName() === "ISdSelectModal")
244
+ .forEach(n => n.remove());
245
+ } else {
246
+ sourceFile.addImportDeclaration({
247
+ namedImports: ["ISdModal"],
248
+ moduleSpecifier: "@simplysm/sd-angular"
249
+ });
250
+ }
251
+ }
252
+
253
+ const ngCoreImport = sourceFile.getImportDeclaration(
254
+ (d) => d.getModuleSpecifierValue() === "@angular/core",
255
+ );
256
+ if (ngCoreImport) {
257
+ const imports = ngCoreImport.getNamedImports().map((n) => n.getName());
258
+ if (!imports.includes("input")) {
259
+ ngCoreImport.addNamedImport("input");
260
+ changed = true;
261
+ }
262
+ if (!imports.includes("output")) {
263
+ ngCoreImport.addNamedImport("output");
264
+ changed = true;
265
+ }
266
+ } else {
267
+ sourceFile.addImportDeclaration({
268
+ namedImports: ["input", "output"],
269
+ moduleSpecifier: "@angular/core",
270
+ });
271
+ changed = true;
272
+ }
273
+
274
+ // 9. $effect([this.params], ...) → $effect([], ...) (생성자)
275
+ if (ctor) {
276
+ ctor.forEachDescendant((node) => {
277
+ if (node.getKind() === SyntaxKind.CallExpression) {
278
+ const callExpr = node.asKind(SyntaxKind.CallExpression)!;
279
+ const expr = callExpr.getExpression();
280
+ if (expr.getKind() === SyntaxKind.Identifier && expr.getText() === "$effect") {
281
+ const args = callExpr.getArguments();
282
+ if (
283
+ args.length >= 1 &&
284
+ args[0].getKind() === SyntaxKind.ArrayLiteralExpression &&
285
+ args[0].asKind(SyntaxKind.ArrayLiteralExpression)!.getElements().length === 1
286
+ ) {
287
+ const arrElem = args[0].asKind(SyntaxKind.ArrayLiteralExpression)!.getElements()[0];
288
+ if (
289
+ arrElem.getKind() === SyntaxKind.PropertyAccessExpression &&
290
+ arrElem.getText() === "this.params"
291
+ ) {
292
+ args[0].replaceWithText("[]");
293
+ changed = true;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ });
299
+ }
300
+
301
+ // 10. interface 선언 삭제
302
+ paramInterface.remove();
303
+
304
+ // 11. extends 삭제 (항상 마지막)
305
+ modalClass.removeExtends();
306
+
307
+ // 11. import 삭제
308
+ removeNamedImport(sourceFile, "@simplysm/sd-angular", "SdModalBase");
309
+ removeNamedImport(sourceFile, "@simplysm/sd-angular", "ISharedDataModalInputParam");
310
+ // removeNamedImport(sourceFile, "@simplysm/sd-angular", "ISharedDataModalOutputResult");
311
+
312
+ // 12. 저장 및 간결 로그 출력
313
+ if (changed) {
314
+ sourceFile.saveSync();
315
+ totalChanged++;
316
+ console.log(`[modal-updated] ${relPath} :: 모달기반 변경 완료`);
317
+ }
318
+ }
319
+ if (totalChanged > 0) {
320
+ console.log(`\n[완료] 모달변환 완료 (총 ${totalChanged}개)`);
321
+ } else {
322
+ console.log(`[완료] 변환 대상 없음`);
323
+ }
324
+ }
@@ -0,0 +1,41 @@
1
+ import { Node, SyntaxKind } from "ts-morph";
2
+ import getTsMorphSourceFiles from "./core/get-ts-morph-source-files";
3
+
4
+ export default function convertShowAsyncCall() {
5
+ const sourceFiles = getTsMorphSourceFiles();
6
+
7
+ for (const sourceFile of sourceFiles) {
8
+ const callExprs = sourceFile
9
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
10
+ .filter((callExpr) => {
11
+ const expr = callExpr.getExpression();
12
+ return (
13
+ Node.isPropertyAccessExpression(expr) &&
14
+ expr.getName() === "showAsync" &&
15
+ expr.getExpression().getText() === "this._sdModal"
16
+ );
17
+ });
18
+
19
+ for (const callExpr of callExprs) {
20
+ const args = callExpr.getArguments();
21
+ if (args.length < 2 || args.length > 3) continue;
22
+
23
+ const [typeArg, titleArg, inputsArg] = args;
24
+
25
+ // 반드시 교체 전에 텍스트만 추출
26
+ const typeText = typeArg.getText();
27
+ const titleText = titleArg.getText();
28
+ const inputsText = inputsArg.getText();
29
+
30
+ const props: string[] = [`type: ${typeText}`, `title: ${titleText}`, `inputs: ${inputsText}`];
31
+
32
+ const objectLiteralText = `{
33
+ ${props.join(",\n ")}
34
+ }`;
35
+
36
+ callExpr.replaceWithText(`this._sdModal.showAsync(${objectLiteralText})`);
37
+ }
38
+
39
+ sourceFile.saveSync();
40
+ }
41
+ }
@@ -11,6 +11,7 @@ export default function convertSdAngularSymbolNames() {
11
11
  "@simplysm/sd-angular#useActivatedRouteManager": "@simplysm/sd-angular#useQueryParamMapSignal",
12
12
  "@simplysm/sd-angular#ISdSheetColumnOrderingVM": "@simplysm/sd-angular#ISdSortingDef",
13
13
  "@simplysm/sd-angular#ISortingDef": "@simplysm/sd-angular#ISdSortingDef",
14
+ "@simplysm/sd-angular#ISharedDataModalOutputResult": "@simplysm/sd-angular#ISelectModalOutputResult",
14
15
 
15
16
  "@angular/core#signal": "@simplysm/sd-angular#$signal",
16
17
  "@angular/core#computed": "@simplysm/sd-angular#$computed",
@@ -21,50 +21,53 @@ export default function convertSdSheetBindingsSafely() {
21
21
  if (!initializer) continue;
22
22
 
23
23
  let rawTemplate: string | undefined;
24
- if (initializer.isKind(SyntaxKind.NoSubstitutionTemplateLiteral) || initializer.isKind(
25
- SyntaxKind.StringLiteral)) {
24
+ if (
25
+ initializer.isKind(SyntaxKind.NoSubstitutionTemplateLiteral) ||
26
+ initializer.isKind(SyntaxKind.StringLiteral)
27
+ ) {
26
28
  rawTemplate = initializer.getLiteralText();
27
- }
28
- else if (initializer.isKind(SyntaxKind.TemplateExpression)) {
29
+ } else if (initializer.isKind(SyntaxKind.TemplateExpression)) {
29
30
  rawTemplate = initializer.getFullText().slice(1, -1);
30
- }
31
- else continue;
31
+ } else continue;
32
32
 
33
33
  // 정규식으로 <sd-sheet> 안에서만 바인딩 속성 치환
34
- let newTemplate = rawTemplate.replace(
35
- /<sd-sheet([\s\S]*?)>/g,
36
- (match) =>
37
- match
38
- .replace(/\[\(page\)\]/g, "[(currentPage)]")
39
- .replace(/\[pageLength\]/g, "[totalPageCount]")
40
- .replace(/\[\(ordering\)\]/g, "[(sorts)]")
41
- .replace(/\[pageItemCount\]/g, "[itemsPerPage]"),
34
+ let newTemplate = rawTemplate.replace(/<sd-sheet([\s\S]*?)>/g, (match) =>
35
+ match
36
+ .replace(/\[\(page\)\]/g, "[(currentPage)]")
37
+ .replace(/\[pageLength\]/g, "[totalPageCount]")
38
+ .replace(/\[\(ordering\)\]/g, "[(sorts)]")
39
+ .replace(/\[pageItemCount\]/g, "[itemsPerPage]"),
40
+ );
41
+
42
+ newTemplate = newTemplate.replace(/<sd-pagination([\s\S]*?)>/g, (match) =>
43
+ match
44
+ .replace(/\[\(page\)\]/g, "[(currentPage)]")
45
+ .replace(/\[pageLength\]/g, "[totalPageCount]")
46
+ .replace(/\[displayPageLength\]/g, "[visiblePageCount]"),
42
47
  );
43
48
 
44
- newTemplate = newTemplate.replace(
45
- /<sd-pagination([\s\S]*?)>/g,
46
- (match) =>
47
- match
48
- .replace(/\[\(page\)\]/g, "[(currentPage)]")
49
- .replace(/\[pageLength\]/g, "[totalPageCount]")
50
- .replace(/\[displayPageLength\]/g, "[visiblePageCount]"),
49
+ newTemplate = newTemplate.replace(/<sd-sheet-column([\s\S]*?)>/g, (match) =>
50
+ match
51
+ .replace(/\[disableOrdering\]/g, "[disableSorting]")
52
+ .replace(/\sdisableOrdering/g, " disableSorting"),
51
53
  );
52
54
 
53
- newTemplate = newTemplate.replace(
54
- /<sd-sheet-column([\s\S]*?)>/g,
55
- (match) =>
56
- match
57
- .replace(/\[disableOrdering\]/g, "[disableSorting]")
58
- .replace(/\sdisableOrdering/g, " disableSorting"),
55
+ newTemplate = newTemplate.replace(/<ng-template([\s\S]*?)>/g, (match) =>
56
+ match
57
+ .replace(/target="content"/g, "#content")
58
+ .replace(/target="topbar"/g, "#pageTopbar")
59
+ .replace(/target="bottom"/g, "#modalBottom"),
59
60
  );
60
61
 
61
62
  if (rawTemplate !== newTemplate) {
62
63
  initializer.replaceWithText("`" + newTemplate + "`");
63
- console.log(`[template-updated] ${sourceFile.getBaseName()} :: 바인딩 속성 안전하게 변경 완료`);
64
+ console.log(
65
+ `[template-updated] ${sourceFile.getBaseName()} :: 바인딩 속성 안전하게 변경 완료`,
66
+ );
64
67
  sourceFile.saveSync();
65
68
  }
66
69
  }
67
70
  }
68
71
 
69
72
  console.log("[완료] 정규식 기반 안전한 sd-sheet 바인딩 속성 변경 완료");
70
- }
73
+ }
@@ -1,3 +1,5 @@
1
+ /* eslint-disable no-console */
2
+
1
3
  import { Identifier, SyntaxKind } from "ts-morph";
2
4
  import getTsMortphSourceFiles from "./get-ts-morph-source-files";
3
5
 
@@ -0,0 +1,14 @@
1
+ export default function removeNamedImport(sourceFile, moduleSpecifier, symbolName) {
2
+ const importDecl = sourceFile.getImportDeclaration(
3
+ (d) => d.getModuleSpecifierValue() === moduleSpecifier,
4
+ );
5
+ if (importDecl == null) return;
6
+ importDecl
7
+ .getNamedImports()
8
+ .filter((n) => n.getName() === symbolName)
9
+ .forEach((n) => n.remove());
10
+ // 만약 named import가 모두 사라지면 import문 자체 삭제
11
+ if (importDecl.getNamedImports().length === 0) {
12
+ importDecl.remove();
13
+ }
14
+ }
@@ -0,0 +1,94 @@
1
+ /* eslint-disable no-console */
2
+ import {
3
+ SyntaxKind,
4
+ TypeReferenceNode,
5
+ Identifier,
6
+ ArrayLiteralExpression
7
+ } from "ts-morph";
8
+ import getTsMortphSourceFiles from "./get-ts-morph-source-files";
9
+
10
+ function parseTargets(symbols: string[]) {
11
+ return symbols.map((symbol) => {
12
+ const [importSource, symbolName] = symbol.split("#");
13
+ return { importSource, symbolName };
14
+ });
15
+ }
16
+
17
+ export default function removeSymbols(symbols: string[]) {
18
+ const targets = parseTargets(symbols);
19
+ const sourceFiles = getTsMortphSourceFiles();
20
+
21
+ for (const sourceFile of sourceFiles) {
22
+ let changed = false;
23
+
24
+ for (const { importSource, symbolName } of targets) {
25
+ // 1. Import 제거
26
+ for (const importDecl of sourceFile.getImportDeclarations()) {
27
+ if (importDecl.getModuleSpecifierValue() !== importSource) continue;
28
+
29
+ for (const namedImport of importDecl.getNamedImports()) {
30
+ if (namedImport.getName() === symbolName) {
31
+ console.log(`[import] ${sourceFile.getBaseName()} :: ${importSource} :: removed '${symbolName}'`);
32
+ namedImport.remove();
33
+ changed = true;
34
+ }
35
+ }
36
+
37
+ if (importDecl.getNamedImports().length === 0) {
38
+ console.log(`[import] ${sourceFile.getBaseName()} :: removed empty import from '${importSource}'`);
39
+ importDecl.remove();
40
+ changed = true;
41
+ }
42
+ }
43
+
44
+ // 2. 타입 참조 제거
45
+ sourceFile.forEachDescendant((node) => {
46
+ if (node.getKind() !== SyntaxKind.TypeReference) return;
47
+
48
+ const typeNode = node as TypeReferenceNode;
49
+ const typeName = typeNode.getTypeName().getText();
50
+ if (typeName !== symbolName) return;
51
+
52
+ const decl = node.getFirstAncestor((a) =>
53
+ a.getKind() === SyntaxKind.VariableStatement ||
54
+ a.getKind() === SyntaxKind.Parameter ||
55
+ a.getKind() === SyntaxKind.PropertyDeclaration
56
+ );
57
+
58
+ if (decl) {
59
+ console.log(`[type] ${sourceFile.getBaseName()} :: removed type usage of '${symbolName}'`);
60
+
61
+ const removable = decl.asKind(SyntaxKind.VariableStatement)
62
+ ?? decl.asKind(SyntaxKind.Parameter)
63
+ ?? decl.asKind(SyntaxKind.PropertyDeclaration);
64
+
65
+ if (removable) {
66
+ removable.remove();
67
+ changed = true;
68
+ }
69
+ }
70
+ });
71
+
72
+ // 3. ArrayLiteral 내 사용 제거 (예: imports: [SdButtonControl])
73
+ sourceFile.forEachDescendant((node) => {
74
+ if (node.getKind() !== SyntaxKind.ArrayLiteralExpression) return;
75
+
76
+ const arr = node as ArrayLiteralExpression;
77
+ const elements = arr.getElements();
78
+
79
+ for (const el of elements) {
80
+ if (el.getKind() === SyntaxKind.Identifier && (el as Identifier).getText() === symbolName) {
81
+ console.log(`[usage] ${sourceFile.getBaseName()} :: removed '${symbolName}' from array`);
82
+ arr.removeElement(el);
83
+ changed = true;
84
+ break;
85
+ }
86
+ }
87
+ });
88
+ }
89
+
90
+ if (changed) {
91
+ sourceFile.saveSync();
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,11 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import removeSymbols from "./core/remove-symbol";
4
+
5
+ export default function removeSdAngularSymbolNames() {
6
+ removeSymbols([
7
+ "@simplysm/sd-angular#TemplateTargetDirective",
8
+ ]);
9
+
10
+ console.log("[완료] SdAngular 심볼 삭제 완료");
11
+ }
@@ -44,7 +44,7 @@ import { INpmConfig } from "../../types/common-configs.types";
44
44
  import { ISdClientBuilderCordovaConfig } from "../../types/config.types";
45
45
  import { ISdCliNgPluginResultCache } from "../../types/build-plugin.types";
46
46
  import { ISdBuildMessage } from "../../types/build.types";
47
- import nodeModule from "node:module";
47
+ import nodeModule from "module";
48
48
  import { ScopePathSet } from "../commons/scope-path";
49
49
 
50
50
  export class SdNgBundler {