@simplysm/sd-cli 12.15.68 → 12.15.69
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/dist/entry/SdCliCapacitor.d.ts +37 -0
- package/dist/entry/SdCliCapacitor.js +390 -0
- package/dist/pkg-builders/client/SdClientBuildRunner.d.ts +1 -0
- package/dist/pkg-builders/client/SdClientBuildRunner.js +14 -0
- package/dist/pkg-builders/client/SdNgBundler.js +3 -1
- package/dist/sd-cli-entry.js +18 -44
- package/dist/types/config/ISdProjectConfig.d.ts +31 -0
- package/package.json +5 -5
- package/src/entry/SdCliCapacitor.ts +560 -0
- package/src/pkg-builders/client/SdClientBuildRunner.ts +17 -0
- package/src/pkg-builders/client/SdNgBundler.ts +3 -1
- package/src/sd-cli-entry.ts +26 -56
- package/src/types/config/ISdProjectConfig.ts +34 -0
- package/dist/fix/convertPrivateToHash.d.ts +0 -1
- package/dist/fix/convertPrivateToHash.js +0 -58
- package/dist/fix/convertSdAngularSymbolNames.d.ts +0 -1
- package/dist/fix/convertSdAngularSymbolNames.js +0 -22
- package/dist/fix/core/convertSymbols.d.ts +0 -1
- package/dist/fix/core/convertSymbols.js +0 -101
- package/dist/fix/core/getTsMortphSourceFiles.d.ts +0 -1
- package/dist/fix/core/getTsMortphSourceFiles.js +0 -7
- package/dist/fix/core/removeSymbols.d.ts +0 -1
- package/dist/fix/core/removeSymbols.js +0 -76
- package/dist/fix/removeSdAngularSymbolNames.d.ts +0 -1
- package/dist/fix/removeSdAngularSymbolNames.js +0 -6
- package/dist/fix/removeUnusedImports.d.ts +0 -1
- package/dist/fix/removeUnusedImports.js +0 -41
- package/dist/fix/removeUnusedInjects.d.ts +0 -1
- package/dist/fix/removeUnusedInjects.js +0 -37
- package/dist/fix/removeUnusedProtectedReadonly.d.ts +0 -1
- package/dist/fix/removeUnusedProtectedReadonly.js +0 -57
- package/src/fix/convertPrivateToHash.ts +0 -74
- package/src/fix/convertSdAngularSymbolNames.ts +0 -27
- package/src/fix/core/convertSymbols.ts +0 -135
- package/src/fix/core/getTsMortphSourceFiles.ts +0 -9
- package/src/fix/core/removeSymbols.ts +0 -102
- package/src/fix/removeSdAngularSymbolNames.ts +0 -9
- package/src/fix/removeUnusedImports.ts +0 -50
- package/src/fix/removeUnusedInjects.ts +0 -50
- package/src/fix/removeUnusedProtectedReadonly.ts +0 -69
package/src/sd-cli-entry.ts
CHANGED
|
@@ -12,12 +12,7 @@ import { SdCliElectron } from "./entry/SdCliElectron";
|
|
|
12
12
|
import { SdCliLocalUpdate } from "./entry/SdCliLocalUpdate";
|
|
13
13
|
import { SdCliPostInstall } from "./entry/SdCliPostInstall";
|
|
14
14
|
import { SdCliProject } from "./entry/SdCliProject";
|
|
15
|
-
import
|
|
16
|
-
import removeSdAngularSymbolNames from "./fix/removeSdAngularSymbolNames";
|
|
17
|
-
import convertSdAngularSymbolNames from "./fix/convertSdAngularSymbolNames";
|
|
18
|
-
import { removeUnusedInjects } from "./fix/removeUnusedInjects";
|
|
19
|
-
import removeUnusedProtectedReadonly from "./fix/removeUnusedProtectedReadonly";
|
|
20
|
-
import { removeUnusedImports } from "./fix/removeUnusedImports";
|
|
15
|
+
import { SdCliCapacitor } from "./entry/SdCliCapacitor";
|
|
21
16
|
|
|
22
17
|
Error.stackTraceLimit = Infinity;
|
|
23
18
|
EventEmitter.defaultMaxListeners = 0;
|
|
@@ -244,6 +239,31 @@ await yargs(hideBin(process.argv))
|
|
|
244
239
|
}),
|
|
245
240
|
async (argv) => await SdCliCordova.runWebviewOnDeviceAsync(argv),
|
|
246
241
|
)
|
|
242
|
+
.command(
|
|
243
|
+
"run-capacitor <platform> <package> [url]",
|
|
244
|
+
"변경감지중인 플랫폼을 Capacitor 디바이스에 앱 형태로 띄웁니다.",
|
|
245
|
+
(cmd) =>
|
|
246
|
+
cmd
|
|
247
|
+
.version(false)
|
|
248
|
+
.hide("help")
|
|
249
|
+
.hide("debug")
|
|
250
|
+
.positional("platform", {
|
|
251
|
+
type: "string",
|
|
252
|
+
describe: "빌드 플랫폼(android,...)",
|
|
253
|
+
demandOption: true,
|
|
254
|
+
})
|
|
255
|
+
.positional("package", {
|
|
256
|
+
type: "string",
|
|
257
|
+
describe: "패키지명",
|
|
258
|
+
demandOption: true,
|
|
259
|
+
})
|
|
260
|
+
.positional("url", {
|
|
261
|
+
type: "string",
|
|
262
|
+
describe: "Webview로 오픈할 URL",
|
|
263
|
+
demandOption: true,
|
|
264
|
+
}),
|
|
265
|
+
async (argv) => await SdCliCapacitor.runWebviewOnDeviceAsync(argv),
|
|
266
|
+
)
|
|
247
267
|
.command(
|
|
248
268
|
"commit",
|
|
249
269
|
"AI를 통해 변경사항에 대한 커밋 메시지를 작성하여, 커밋 및 푸쉬를 수행합니다.",
|
|
@@ -256,56 +276,6 @@ await yargs(hideBin(process.argv))
|
|
|
256
276
|
(cmd) => cmd.version(false).hide("help").hide("debug"),
|
|
257
277
|
() => SdCliPostInstall.run(),
|
|
258
278
|
)
|
|
259
|
-
.command(
|
|
260
|
-
"fix",
|
|
261
|
-
"가능한 내용 자동 수정",
|
|
262
|
-
(cmd) =>
|
|
263
|
-
cmd
|
|
264
|
-
.version(false)
|
|
265
|
-
.hide("help")
|
|
266
|
-
.hide("debug")
|
|
267
|
-
.options({
|
|
268
|
-
library: {
|
|
269
|
-
type: "boolean",
|
|
270
|
-
describe: "simplysm 라이브러리 픽스",
|
|
271
|
-
default: false,
|
|
272
|
-
},
|
|
273
|
-
}),
|
|
274
|
-
(argv) => {
|
|
275
|
-
// GIT 사용중일 경우, 커밋되지 않은 수정사항이 있는지 확인
|
|
276
|
-
/*if (FsUtils.exists(path.resolve(process.cwd(), ".git"))) {
|
|
277
|
-
const gitStatusResult = await SdProcess.spawnAsync("git status");
|
|
278
|
-
if (gitStatusResult.includes("Changes") || gitStatusResult.includes("Untracked")) {
|
|
279
|
-
throw new Error("커밋되지 않은 정보가 있습니다. FIX오류시 롤백이 불가능하므로, 미리 커밋을 해놔야 합니다.\n" + gitStatusResult);
|
|
280
|
-
}
|
|
281
|
-
}*/
|
|
282
|
-
|
|
283
|
-
convertPrivateToHash();
|
|
284
|
-
|
|
285
|
-
//-- 심볼정리
|
|
286
|
-
removeSdAngularSymbolNames();
|
|
287
|
-
convertSdAngularSymbolNames();
|
|
288
|
-
|
|
289
|
-
//-- inject/import 정리
|
|
290
|
-
removeUnusedInjects();
|
|
291
|
-
removeUnusedProtectedReadonly();
|
|
292
|
-
removeUnusedImports();
|
|
293
|
-
|
|
294
|
-
if (argv.library) return;
|
|
295
|
-
|
|
296
|
-
// convertSdSheetBindingsSafely();
|
|
297
|
-
// convertSetupCumulateSelectedKeysToObjectParam();
|
|
298
|
-
// convertExtendsSdModalBaseToInterface();
|
|
299
|
-
// convertModalShowParams();
|
|
300
|
-
// convertExtendsSdPrintTemplateBaseToInterface();
|
|
301
|
-
// convertPrintParams();
|
|
302
|
-
// convertToUsePermsSignal();
|
|
303
|
-
// convertGetMenusToUsableMenus();
|
|
304
|
-
// convertFlatPagesToUsableFlatMenus();
|
|
305
|
-
// convertSdIconToFaIcon();
|
|
306
|
-
// convertSelectModalButtonToSelectButton();
|
|
307
|
-
},
|
|
308
|
-
)
|
|
309
279
|
.strict()
|
|
310
280
|
.recommendCommands()
|
|
311
281
|
.fail((msg, err, cmd) => {
|
|
@@ -62,6 +62,9 @@ export interface ISdClientPackageConfig {
|
|
|
62
62
|
builder?: {
|
|
63
63
|
web?: ISdClientBuilderWebConfig;
|
|
64
64
|
electron?: ISdClientBuilderElectronConfig;
|
|
65
|
+
capacitor?: ISdClientBuilderCapacitorConfig;
|
|
66
|
+
|
|
67
|
+
/** @deprecated */
|
|
65
68
|
cordova?: ISdClientBuilderCordovaConfig;
|
|
66
69
|
};
|
|
67
70
|
}
|
|
@@ -98,6 +101,7 @@ export interface ISdClientBuilderWebConfig {
|
|
|
98
101
|
env?: Record<string, string>;
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
/** @deprecated */
|
|
101
105
|
export interface ISdClientBuilderCordovaConfig {
|
|
102
106
|
appId: string;
|
|
103
107
|
appName: string;
|
|
@@ -128,6 +132,36 @@ export interface ISdClientBuilderCordovaConfig {
|
|
|
128
132
|
browserslist?: string[];
|
|
129
133
|
}
|
|
130
134
|
|
|
135
|
+
|
|
136
|
+
export interface ISdClientBuilderCapacitorConfig {
|
|
137
|
+
appId: string;
|
|
138
|
+
appName: string;
|
|
139
|
+
plugins?: Record<string, Record<string, unknown> | true>;
|
|
140
|
+
icon?: string;
|
|
141
|
+
debug?: boolean;
|
|
142
|
+
platform?: {
|
|
143
|
+
android?: {
|
|
144
|
+
config?: Record<string, string>;
|
|
145
|
+
bundle?: boolean;
|
|
146
|
+
sign?: {
|
|
147
|
+
keystore: string;
|
|
148
|
+
storePassword: string;
|
|
149
|
+
alias: string;
|
|
150
|
+
password: string;
|
|
151
|
+
keystoreType?: string;
|
|
152
|
+
};
|
|
153
|
+
sdkVersion?: number;
|
|
154
|
+
permissions?: {
|
|
155
|
+
name: string;
|
|
156
|
+
maxSdkVersion?: number;
|
|
157
|
+
ignore?: string;
|
|
158
|
+
}[];
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
env?: Record<string, string>;
|
|
162
|
+
browserslist?: string[];
|
|
163
|
+
}
|
|
164
|
+
|
|
131
165
|
export type TSdPostPublishConfig = ISdPostPublishScriptConfig;
|
|
132
166
|
|
|
133
167
|
export interface ISdPostPublishScriptConfig {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function convertPrivateToHash(): void;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { Node, SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMortphSourceFiles from "./core/getTsMortphSourceFiles";
|
|
4
|
-
export default function convertPrivateToHash() {
|
|
5
|
-
const sourceFiles = getTsMortphSourceFiles();
|
|
6
|
-
for (const sourceFile of sourceFiles) {
|
|
7
|
-
let changed = false;
|
|
8
|
-
for (const classDecl of sourceFile.getClasses()) {
|
|
9
|
-
const renameMap = new Map();
|
|
10
|
-
// 1. 선언부에서 private을 #으로 바꾸고 이름 매핑 저장
|
|
11
|
-
for (const member of classDecl.getMembers()) {
|
|
12
|
-
if (Node.isPropertyDeclaration(member) ||
|
|
13
|
-
Node.isMethodDeclaration(member) ||
|
|
14
|
-
Node.isGetAccessorDeclaration(member) ||
|
|
15
|
-
Node.isSetAccessorDeclaration(member)) {
|
|
16
|
-
if (member.hasModifier(SyntaxKind.PrivateKeyword)) {
|
|
17
|
-
const nameNode = member.getNameNode();
|
|
18
|
-
if (typeof nameNode.getText !== "function")
|
|
19
|
-
continue;
|
|
20
|
-
const oldName = nameNode.getText();
|
|
21
|
-
const newName = oldName.startsWith("_") ? `#${oldName.slice(1)}` : `#${oldName}`;
|
|
22
|
-
nameNode.replaceWithText(newName);
|
|
23
|
-
member.toggleModifier("private", false);
|
|
24
|
-
renameMap.set(oldName, newName);
|
|
25
|
-
const kind = Node.isMethodDeclaration(member)
|
|
26
|
-
? "method"
|
|
27
|
-
: Node.isGetAccessorDeclaration(member) || Node.isSetAccessorDeclaration(member)
|
|
28
|
-
? "accessor"
|
|
29
|
-
: "field";
|
|
30
|
-
console.log(`[private-${kind}] ${sourceFile.getBaseName()} :: private ${oldName} → ${newName}`);
|
|
31
|
-
changed = true;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (renameMap.size === 0)
|
|
36
|
-
continue;
|
|
37
|
-
// 2. this.xxx 접근 표현식 전환
|
|
38
|
-
classDecl.forEachDescendant((node) => {
|
|
39
|
-
const propAccess = node.asKind(SyntaxKind.PropertyAccessExpression);
|
|
40
|
-
if (propAccess) {
|
|
41
|
-
const expr = propAccess.getExpression();
|
|
42
|
-
const name = propAccess.getName();
|
|
43
|
-
if (expr.getText() === "this" && renameMap.has(name)) {
|
|
44
|
-
const newName = renameMap.get(name);
|
|
45
|
-
propAccess.replaceWithText(`this.${newName}`);
|
|
46
|
-
console.log(`[ref] ${sourceFile.getBaseName()} :: this.${name} → this.${newName}`);
|
|
47
|
-
changed = true;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
if (changed) {
|
|
53
|
-
sourceFile.saveSync();
|
|
54
|
-
console.log(`[save] ${sourceFile.getFilePath()}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
console.log("[완료] private → ECMAScript # 멤버 변환 완료");
|
|
58
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function convertSdAngularSymbolNames(): void;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import convertSymbols from "./core/convertSymbols";
|
|
3
|
-
export default function convertSdAngularSymbolNames() {
|
|
4
|
-
convertSymbols({
|
|
5
|
-
"@simplysm/sd-angular#useBgTheme": "@simplysm/sd-angular#setupBgTheme",
|
|
6
|
-
"@simplysm/sd-angular#canDeactivate": "@simplysm/sd-angular#setupCanDeactivate",
|
|
7
|
-
"@simplysm/sd-angular#useRipple": "@simplysm/sd-angular#setupRipple",
|
|
8
|
-
"@simplysm/sd-angular#useCumulateSelectedKeys": "@simplysm/sd-angular#setupCumulateSelectedKeys",
|
|
9
|
-
"@simplysm/sd-angular#injectQueryParamMap$": "@simplysm/sd-angular#useQueryParamMapSignal",
|
|
10
|
-
"@simplysm/sd-angular#useActivatedRouteManager": "@simplysm/sd-angular#useQueryParamMapSignal",
|
|
11
|
-
"@simplysm/sd-angular#ISdSheetColumnOrderingVM": "@simplysm/sd-angular#ISdSortingDef",
|
|
12
|
-
"@simplysm/sd-angular#ISortingDef": "@simplysm/sd-angular#ISdSortingDef",
|
|
13
|
-
"@simplysm/sd-angular#ISharedDataModalOutputResult": "@simplysm/sd-angular#ISelectModalOutputResult",
|
|
14
|
-
"@angular/core#signal": "@simplysm/sd-angular#$signal",
|
|
15
|
-
"@angular/core#computed": "@simplysm/sd-angular#$computed",
|
|
16
|
-
"@angular/core#effect": "@simplysm/sd-angular#$effect",
|
|
17
|
-
"@angular/core#afterRenderEffect": "@simplysm/sd-angular#$afterRenderEffect",
|
|
18
|
-
"@angular/core#afterRenderComputed": "@simplysm/sd-angular#$afterRenderComputed",
|
|
19
|
-
"@angular/core#resource": "@simplysm/sd-angular#$resource",
|
|
20
|
-
});
|
|
21
|
-
console.log("[완료] SdAngular 심볼 이름 변환 완료");
|
|
22
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function convertSymbols(raw: Record<string, string>): void;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMortphSourceFiles from "./getTsMortphSourceFiles";
|
|
4
|
-
function parseReplacements(input) {
|
|
5
|
-
return Object.entries(input).map(([oldKey, newValue]) => {
|
|
6
|
-
const [oldModule, oldName] = oldKey.split("#");
|
|
7
|
-
const [newModule, newName] = newValue.split("#");
|
|
8
|
-
return { oldModule, oldName, newModule, newName };
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
function getSourceFileModule(filePath) {
|
|
12
|
-
const match = filePath.match(/packages[\\/](.*?)[\\/]src[\\/]/);
|
|
13
|
-
return `@simplysm/${match[1]}`;
|
|
14
|
-
}
|
|
15
|
-
export default function convertSymbols(raw) {
|
|
16
|
-
const replacements = parseReplacements(raw);
|
|
17
|
-
const sourceFiles = getTsMortphSourceFiles();
|
|
18
|
-
for (const sourceFile of sourceFiles) {
|
|
19
|
-
const sourceFileModule = getSourceFileModule(sourceFile.getFilePath());
|
|
20
|
-
let changed = false;
|
|
21
|
-
const usedReplacements = new Set(); // 실제 사용된 newModule#newName 추적
|
|
22
|
-
const existingImportMap = new Map();
|
|
23
|
-
// 1. 참조 치환 (Identifier)
|
|
24
|
-
sourceFile.forEachDescendant((node) => {
|
|
25
|
-
if (node.getKind() !== SyntaxKind.Identifier)
|
|
26
|
-
return;
|
|
27
|
-
if (node.getFirstAncestorByKind(SyntaxKind.ImportSpecifier))
|
|
28
|
-
return;
|
|
29
|
-
const identifier = node;
|
|
30
|
-
const name = identifier.getText();
|
|
31
|
-
const symbol = identifier.getSymbol();
|
|
32
|
-
if (!symbol)
|
|
33
|
-
return;
|
|
34
|
-
const declarations = symbol.getDeclarations();
|
|
35
|
-
if (declarations.length === 0)
|
|
36
|
-
return;
|
|
37
|
-
const importDecl = declarations[0].getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
|
|
38
|
-
if (!importDecl)
|
|
39
|
-
return;
|
|
40
|
-
const importModule = importDecl.getModuleSpecifierValue();
|
|
41
|
-
const match = replacements.find((r) => r.oldModule === importModule && r.oldName === name);
|
|
42
|
-
if (!match || match.oldModule === sourceFileModule || match.newModule === sourceFileModule)
|
|
43
|
-
return;
|
|
44
|
-
identifier.replaceWithText(match.newName);
|
|
45
|
-
usedReplacements.add(`${match.newModule}#${match.newName}`);
|
|
46
|
-
changed = true;
|
|
47
|
-
console.log(`[ref] ${sourceFile.getBaseName()} :: ${importModule} :: ${name} → ${match.newName}`);
|
|
48
|
-
});
|
|
49
|
-
// 2. import 교체
|
|
50
|
-
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
51
|
-
const specifier = importDecl.getModuleSpecifierValue();
|
|
52
|
-
const namedImports = importDecl.getNamedImports();
|
|
53
|
-
for (const ni of namedImports) {
|
|
54
|
-
const oldName = ni.getName();
|
|
55
|
-
const match = replacements.find((r) => r.oldModule === specifier && r.oldName === oldName);
|
|
56
|
-
if (!match ||
|
|
57
|
-
match.oldModule === sourceFileModule ||
|
|
58
|
-
match.newModule === sourceFileModule) {
|
|
59
|
-
// 기존 import 유지 기록
|
|
60
|
-
if (!existingImportMap.has(specifier)) {
|
|
61
|
-
existingImportMap.set(specifier, new Set());
|
|
62
|
-
}
|
|
63
|
-
existingImportMap.get(specifier).add(oldName);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
ni.remove();
|
|
67
|
-
// 만약 모듈이 동일하다면 즉시 대체
|
|
68
|
-
if (match.oldModule === match.newModule) {
|
|
69
|
-
importDecl.addNamedImport(match.newName);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// 새로 import할 목록에 기록
|
|
73
|
-
usedReplacements.add(`${match.newModule}#${match.newName}`);
|
|
74
|
-
}
|
|
75
|
-
changed = true;
|
|
76
|
-
console.log(`[import] ${sourceFile.getBaseName()} :: ${specifier} :: ${oldName} → ${match.newName}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// 3. 필요 시 새 import 추가
|
|
81
|
-
for (const key of usedReplacements) {
|
|
82
|
-
const [newModule, newName] = key.split("#");
|
|
83
|
-
const already = existingImportMap.get(newModule)?.has(newName);
|
|
84
|
-
const existsInFile = sourceFile
|
|
85
|
-
.getImportDeclarations()
|
|
86
|
-
.some((decl) => decl.getModuleSpecifierValue() === newModule &&
|
|
87
|
-
decl.getNamedImports().some((ni) => ni.getName() === newName));
|
|
88
|
-
if (already || existsInFile)
|
|
89
|
-
continue;
|
|
90
|
-
sourceFile.addImportDeclaration({
|
|
91
|
-
moduleSpecifier: newModule,
|
|
92
|
-
namedImports: [newName],
|
|
93
|
-
});
|
|
94
|
-
changed = true;
|
|
95
|
-
console.log(`[new-import] ${sourceFile.getBaseName()} :: ${newModule} :: ${newName}`);
|
|
96
|
-
}
|
|
97
|
-
if (changed) {
|
|
98
|
-
sourceFile.saveSync();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function getTsMortphSourceFiles(): import("ts-morph").SourceFile[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function removeSymbols(symbols: string[]): void;
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMortphSourceFiles from "./getTsMortphSourceFiles";
|
|
4
|
-
function parseTargets(symbols) {
|
|
5
|
-
return symbols.map((symbol) => {
|
|
6
|
-
const [importSource, symbolName] = symbol.split("#");
|
|
7
|
-
return { importSource, symbolName };
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
export default function removeSymbols(symbols) {
|
|
11
|
-
const targets = parseTargets(symbols);
|
|
12
|
-
const sourceFiles = getTsMortphSourceFiles();
|
|
13
|
-
for (const sourceFile of sourceFiles) {
|
|
14
|
-
let changed = false;
|
|
15
|
-
for (const { importSource, symbolName } of targets) {
|
|
16
|
-
// 1. Import 제거
|
|
17
|
-
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
18
|
-
if (importDecl.getModuleSpecifierValue() !== importSource)
|
|
19
|
-
continue;
|
|
20
|
-
for (const namedImport of importDecl.getNamedImports()) {
|
|
21
|
-
if (namedImport.getName() === symbolName) {
|
|
22
|
-
console.log(`[import] ${sourceFile.getBaseName()} :: ${importSource} :: removed '${symbolName}'`);
|
|
23
|
-
namedImport.remove();
|
|
24
|
-
changed = true;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (importDecl.getNamedImports().length === 0) {
|
|
28
|
-
console.log(`[import] ${sourceFile.getBaseName()} :: removed empty import from '${importSource}'`);
|
|
29
|
-
importDecl.remove();
|
|
30
|
-
changed = true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// 2. 타입 참조 제거
|
|
34
|
-
sourceFile.forEachDescendant((node) => {
|
|
35
|
-
if (node.getKind() !== SyntaxKind.TypeReference)
|
|
36
|
-
return;
|
|
37
|
-
const typeNode = node;
|
|
38
|
-
const typeName = typeNode.getTypeName().getText();
|
|
39
|
-
if (typeName !== symbolName)
|
|
40
|
-
return;
|
|
41
|
-
const decl = node.getFirstAncestor((a) => a.getKind() === SyntaxKind.VariableStatement ||
|
|
42
|
-
a.getKind() === SyntaxKind.Parameter ||
|
|
43
|
-
a.getKind() === SyntaxKind.PropertyDeclaration);
|
|
44
|
-
if (decl) {
|
|
45
|
-
console.log(`[type] ${sourceFile.getBaseName()} :: removed type usage of '${symbolName}'`);
|
|
46
|
-
const removable = decl.asKind(SyntaxKind.VariableStatement) ??
|
|
47
|
-
decl.asKind(SyntaxKind.Parameter) ??
|
|
48
|
-
decl.asKind(SyntaxKind.PropertyDeclaration);
|
|
49
|
-
if (removable) {
|
|
50
|
-
removable.remove();
|
|
51
|
-
changed = true;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
// 3. ArrayLiteral 내 사용 제거 (예: imports: [SdButtonControl])
|
|
56
|
-
sourceFile.forEachDescendant((node) => {
|
|
57
|
-
if (node.getKind() !== SyntaxKind.ArrayLiteralExpression)
|
|
58
|
-
return;
|
|
59
|
-
const arr = node;
|
|
60
|
-
const elements = arr.getElements();
|
|
61
|
-
for (const el of elements) {
|
|
62
|
-
if (el.getKind() === SyntaxKind.Identifier &&
|
|
63
|
-
el.getText() === symbolName) {
|
|
64
|
-
console.log(`[usage] ${sourceFile.getBaseName()} :: removed '${symbolName}' from array`);
|
|
65
|
-
arr.removeElement(el);
|
|
66
|
-
changed = true;
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
if (changed) {
|
|
73
|
-
sourceFile.saveSync();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function removeSdAngularSymbolNames(): void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function removeUnusedImports(): void;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMorphSourceFiles from "./core/getTsMortphSourceFiles";
|
|
4
|
-
export function removeUnusedImports() {
|
|
5
|
-
const sourceFiles = getTsMorphSourceFiles();
|
|
6
|
-
let totalChanged = 0;
|
|
7
|
-
for (const sourceFile of sourceFiles) {
|
|
8
|
-
let changed = false;
|
|
9
|
-
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
10
|
-
// ⚠️ 사이드이펙트 import는 유지
|
|
11
|
-
if (importDecl.getNamedImports().length === 0) {
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
// ⚙️ Named import 제거
|
|
15
|
-
for (const namedImport of importDecl.getNamedImports()) {
|
|
16
|
-
const name = namedImport.getName();
|
|
17
|
-
const used = sourceFile
|
|
18
|
-
.getDescendantsOfKind(SyntaxKind.Identifier)
|
|
19
|
-
.some((id) => id.getText() === name && id !== namedImport.getNameNode());
|
|
20
|
-
if (!used) {
|
|
21
|
-
namedImport.remove();
|
|
22
|
-
changed = true;
|
|
23
|
-
console.log(`[정리됨] ${sourceFile.getBaseName()} → 미사용 import: ${name}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
// 모든 import가 제거된 경우 전체 import 선언 제거
|
|
27
|
-
if (importDecl.getNamedImports().length === 0) {
|
|
28
|
-
importDecl.remove();
|
|
29
|
-
changed = true;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (changed) {
|
|
33
|
-
sourceFile.saveSync();
|
|
34
|
-
totalChanged++;
|
|
35
|
-
console.log(`[updated] ${sourceFile.getBaseName()} :: import 정리 완료`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
console.log(totalChanged > 0
|
|
39
|
-
? `\n[완료] 미사용 import 정리 완료 (총 ${totalChanged}개)`
|
|
40
|
-
: `[완료] 정리 대상 없음`);
|
|
41
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function removeUnusedInjects(): void;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMorphSourceFiles from "./core/getTsMortphSourceFiles";
|
|
4
|
-
export function removeUnusedInjects() {
|
|
5
|
-
const sourceFiles = getTsMorphSourceFiles();
|
|
6
|
-
let totalChanged = 0;
|
|
7
|
-
for (const sourceFile of sourceFiles) {
|
|
8
|
-
let changed = false;
|
|
9
|
-
for (const cls of sourceFile.getClasses()) {
|
|
10
|
-
const injectVars = cls
|
|
11
|
-
.getInstanceProperties()
|
|
12
|
-
.filter((p) => p.isKind(SyntaxKind.PropertyDeclaration) &&
|
|
13
|
-
p.getInitializer()?.isKind(SyntaxKind.CallExpression) &&
|
|
14
|
-
p.getInitializer().getText().startsWith("inject("));
|
|
15
|
-
for (const propDecl of injectVars) {
|
|
16
|
-
const name = propDecl.getName();
|
|
17
|
-
// 클래스 전체에서 해당 변수명이 참조되고 있는지 확인
|
|
18
|
-
const references = cls
|
|
19
|
-
.getDescendants()
|
|
20
|
-
.filter((desc) => desc.getText() === name && desc !== propDecl.getNameNode());
|
|
21
|
-
if (references.length === 0) {
|
|
22
|
-
propDecl.remove();
|
|
23
|
-
changed = true;
|
|
24
|
-
console.log(`[정리됨] ${sourceFile.getBaseName()} → 미사용 inject: ${name}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (changed) {
|
|
29
|
-
sourceFile.saveSync();
|
|
30
|
-
totalChanged++;
|
|
31
|
-
console.log(`[updated] ${sourceFile.getBaseName()} :: inject 정리 완료`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
console.log(totalChanged > 0
|
|
35
|
-
? `\n[완료] 미사용 inject 정리 완료 (총 ${totalChanged}개)`
|
|
36
|
-
: `[완료] 정리 대상 없음`);
|
|
37
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function removeUnusedProtectedReadonly(): void;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { SyntaxKind } from "ts-morph";
|
|
3
|
-
import getTsMortphSourceFiles from "./core/getTsMortphSourceFiles";
|
|
4
|
-
export default function removeUnusedProtectedReadonly() {
|
|
5
|
-
const sourceFiles = getTsMortphSourceFiles();
|
|
6
|
-
for (const sourceFile of sourceFiles) {
|
|
7
|
-
const classes = sourceFile.getClasses();
|
|
8
|
-
for (const cls of classes) {
|
|
9
|
-
// @Component 데코레이터가 있는 클래스만 대상
|
|
10
|
-
const componentDecorator = cls.getDecorator("Component");
|
|
11
|
-
if (!componentDecorator)
|
|
12
|
-
continue;
|
|
13
|
-
// 템플릿 문자열 추출
|
|
14
|
-
const templateArg = componentDecorator
|
|
15
|
-
.getArguments()
|
|
16
|
-
.find((arg) => arg.getKind() === SyntaxKind.ObjectLiteralExpression);
|
|
17
|
-
if (!templateArg || !templateArg.asKind(SyntaxKind.ObjectLiteralExpression))
|
|
18
|
-
continue;
|
|
19
|
-
const templateProp = templateArg
|
|
20
|
-
.asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
|
|
21
|
-
.getProperty("template");
|
|
22
|
-
if (!templateProp || !templateProp.asKind(SyntaxKind.PropertyAssignment))
|
|
23
|
-
continue;
|
|
24
|
-
const initializer = templateProp
|
|
25
|
-
.asKindOrThrow(SyntaxKind.PropertyAssignment)
|
|
26
|
-
.getInitializerIfKind(SyntaxKind.NoSubstitutionTemplateLiteral) ??
|
|
27
|
-
templateProp
|
|
28
|
-
.asKindOrThrow(SyntaxKind.PropertyAssignment)
|
|
29
|
-
.getInitializerIfKind(SyntaxKind.TemplateExpression) ??
|
|
30
|
-
templateProp
|
|
31
|
-
.asKindOrThrow(SyntaxKind.PropertyAssignment)
|
|
32
|
-
.getInitializerIfKind(SyntaxKind.StringLiteral);
|
|
33
|
-
const templateText = initializer?.getText().slice(1, -1); // `...` 또는 '...' 제거
|
|
34
|
-
if (templateText == null)
|
|
35
|
-
continue;
|
|
36
|
-
// protected readonly 필드 중 템플릿에서 사용되지 않은 것 삭제
|
|
37
|
-
const protectedReadonlyFields = cls
|
|
38
|
-
.getProperties()
|
|
39
|
-
.filter((prop) => prop.getScope() === "protected" && prop.isReadonly() && !prop.isStatic());
|
|
40
|
-
for (const field of protectedReadonlyFields) {
|
|
41
|
-
const name = field.getName();
|
|
42
|
-
const usedInTemplate = templateText.includes(name) || new RegExp(`\\b${name}\\b`).test(templateText);
|
|
43
|
-
const identifiers = cls.getDescendantsOfKind(SyntaxKind.Identifier);
|
|
44
|
-
const usedInClass = identifiers.some((id) => {
|
|
45
|
-
if (id.getText() !== name)
|
|
46
|
-
return false;
|
|
47
|
-
return id !== field.getNameNode();
|
|
48
|
-
});
|
|
49
|
-
if (!usedInTemplate && !usedInClass) {
|
|
50
|
-
console.log(`🧹 Removing unused field: ${name} in ${sourceFile.getBaseName()}`);
|
|
51
|
-
field.remove();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
sourceFile.saveSync();
|
|
56
|
-
}
|
|
57
|
-
}
|