@simplysm/sd-cli 12.13.13 → 12.13.15
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/sd-cli-electron.js +1 -1
- package/dist/entry/sd-cli-electron.js.map +1 -1
- package/dist/entry/sd-cli-project.d.ts +2 -0
- package/dist/entry/sd-cli-project.js +11 -3
- package/dist/entry/sd-cli-project.js.map +1 -1
- package/dist/pkg-builders/client/sd-client.build-runner.d.ts +1 -1
- package/dist/pkg-builders/client/sd-client.build-runner.js +74 -50
- package/dist/pkg-builders/client/sd-client.build-runner.js.map +1 -1
- package/dist/pkg-builders/client/sd-ng.bundler.d.ts +2 -0
- package/dist/pkg-builders/client/sd-ng.bundler.js +19 -20
- package/dist/pkg-builders/client/sd-ng.bundler.js.map +1 -1
- package/dist/pkg-builders/client/sd-ng.plugin-creator.d.ts +2 -0
- package/dist/pkg-builders/client/sd-ng.plugin-creator.js +94 -33
- package/dist/pkg-builders/client/sd-ng.plugin-creator.js.map +1 -1
- package/dist/pkg-builders/commons/build-runner.base.d.ts +4 -2
- package/dist/pkg-builders/commons/build-runner.base.js +45 -25
- package/dist/pkg-builders/commons/build-runner.base.js.map +1 -1
- package/dist/pkg-builders/lib/sd-js-lib.build-runner.d.ts +1 -1
- package/dist/pkg-builders/lib/sd-js-lib.build-runner.js +14 -4
- package/dist/pkg-builders/lib/sd-js-lib.build-runner.js.map +1 -1
- package/dist/pkg-builders/lib/sd-ts-lib.build-runner.d.ts +1 -1
- package/dist/pkg-builders/lib/sd-ts-lib.build-runner.js +21 -40
- package/dist/pkg-builders/lib/sd-ts-lib.build-runner.js.map +1 -1
- package/dist/pkg-builders/lib/sd-ts-lib.builder.d.ts +1 -1
- package/dist/pkg-builders/lib/sd-ts-lib.builder.js +3 -1
- package/dist/pkg-builders/lib/sd-ts-lib.builder.js.map +1 -1
- package/dist/pkg-builders/sd-multi.build-runner.js +116 -86
- package/dist/pkg-builders/sd-multi.build-runner.js.map +1 -1
- package/dist/pkg-builders/server/sd-server.build-runner.d.ts +1 -1
- package/dist/pkg-builders/server/sd-server.build-runner.js +9 -5
- package/dist/pkg-builders/server/sd-server.build-runner.js.map +1 -1
- package/dist/pkg-builders/server/sd-server.bundler.d.ts +2 -0
- package/dist/pkg-builders/server/sd-server.bundler.js +3 -1
- package/dist/pkg-builders/server/sd-server.bundler.js.map +1 -1
- package/dist/pkg-builders/server/sd-server.plugin-creator.d.ts +2 -0
- package/dist/pkg-builders/server/sd-server.plugin-creator.js +4 -1
- package/dist/pkg-builders/server/sd-server.plugin-creator.js.map +1 -1
- package/dist/sd-cli-entry.js +10 -0
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.js +8 -6
- package/dist/sd-cli.js.map +1 -1
- package/dist/ts-compiler/sd-dependency-analyzer.d.ts +8 -1
- package/dist/ts-compiler/sd-dependency-analyzer.js +128 -63
- package/dist/ts-compiler/sd-dependency-analyzer.js.map +1 -1
- package/dist/ts-compiler/sd-dependency-cache.d.ts +0 -34
- package/dist/ts-compiler/sd-dependency-cache.js +73 -119
- package/dist/ts-compiler/sd-dependency-cache.js.map +1 -1
- package/dist/ts-compiler/sd-style-bundler.d.ts +16 -0
- package/dist/ts-compiler/sd-style-bundler.js +118 -0
- package/dist/ts-compiler/sd-style-bundler.js.map +1 -0
- package/dist/ts-compiler/sd-ts-compiler.js +418 -235
- package/dist/ts-compiler/sd-ts-compiler.js.map +1 -1
- package/dist/types/build-runner.types.d.ts +2 -0
- package/dist/types/config.types.d.ts +2 -0
- package/dist/types/ts-compiler.types.d.ts +2 -0
- package/dist/types/worker.types.d.ts +1 -1
- package/dist/workers/build-runner.worker.js +1 -1
- package/dist/workers/build-runner.worker.js.map +1 -1
- package/dist/workers/server.worker.js +2 -2
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +13 -12
- package/src/entry/sd-cli-electron.ts +2 -1
- package/src/entry/sd-cli-project.ts +13 -3
- package/src/pkg-builders/client/sd-client.build-runner.ts +93 -54
- package/src/pkg-builders/client/sd-ng.bundler.ts +79 -85
- package/src/pkg-builders/client/sd-ng.plugin-creator.ts +119 -39
- package/src/pkg-builders/commons/build-runner.base.ts +78 -33
- package/src/pkg-builders/lib/sd-js-lib.build-runner.ts +24 -7
- package/src/pkg-builders/lib/sd-ts-lib.build-runner.ts +36 -42
- package/src/pkg-builders/lib/sd-ts-lib.builder.ts +4 -0
- package/src/pkg-builders/sd-multi.build-runner.ts +137 -108
- package/src/pkg-builders/server/sd-server.build-runner.ts +10 -4
- package/src/pkg-builders/server/sd-server.bundler.ts +5 -1
- package/src/pkg-builders/server/sd-server.plugin-creator.ts +10 -5
- package/src/sd-cli-entry.ts +10 -0
- package/src/sd-cli.ts +8 -6
- package/src/ts-compiler/sd-dependency-analyzer.ts +161 -86
- package/src/ts-compiler/sd-dependency-cache.ts +118 -99
- package/src/ts-compiler/sd-style-bundler.ts +150 -0
- package/src/ts-compiler/sd-ts-compiler.ts +559 -296
- package/src/types/build-runner.types.ts +2 -0
- package/src/types/config.types.ts +3 -0
- package/src/types/ts-compiler.types.ts +15 -11
- package/src/types/worker.types.ts +7 -10
- package/src/workers/build-runner.worker.ts +9 -5
- package/src/workers/server.worker.ts +2 -2
- package/tests/deps/sd-dependency-cache.spec.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { FsUtils, PathUtils, SdLogger } from "@simplysm/sd-core-node";
|
|
4
|
-
import { StringUtils
|
|
4
|
+
import { StringUtils } from "@simplysm/sd-core-common";
|
|
5
5
|
import { NgtscProgram, OptimizeFor } from "@angular/compiler-cli";
|
|
6
6
|
import { replaceBootstrap } from "@angular/build/src/tools/angular/transformers/jit-bootstrap-transformer";
|
|
7
7
|
import { SdCliPerformanceTimer } from "../utils/sd-cli-performance-time";
|
|
@@ -10,21 +10,23 @@ import { createWorkerTransformer } from "@angular/build/src/tools/angular/transf
|
|
|
10
10
|
import { SdDependencyCache } from "./sd-dependency-cache";
|
|
11
11
|
import { SdDependencyAnalyzer } from "./sd-dependency-analyzer";
|
|
12
12
|
import { FlatESLint } from "eslint/use-at-your-own-risk";
|
|
13
|
-
import {
|
|
14
|
-
import { transformSupportedBrowsersToTargets } from "@angular/build/src/tools/esbuild/utils";
|
|
15
|
-
import browserslist from "browserslist";
|
|
13
|
+
import { SdStyleBundler } from "./sd-style-bundler";
|
|
16
14
|
export class SdTsCompiler {
|
|
17
15
|
#logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
|
|
18
16
|
#isForAngular;
|
|
19
|
-
#
|
|
17
|
+
#styleBundler;
|
|
20
18
|
#ngProgram;
|
|
21
19
|
#program;
|
|
22
20
|
// 빌드정보 캐싱
|
|
23
|
-
#
|
|
21
|
+
#cache = {
|
|
22
|
+
dep: new SdDependencyCache(),
|
|
23
|
+
type: new WeakMap(),
|
|
24
|
+
prop: new WeakMap(),
|
|
25
|
+
declFiles: new WeakMap(),
|
|
26
|
+
ngOrg: new Map(),
|
|
27
|
+
};
|
|
24
28
|
#sourceFileCacheMap = new Map();
|
|
25
29
|
#emittedFilesCacheMap = new Map();
|
|
26
|
-
// 빌드결과 캐싱
|
|
27
|
-
#stylesheetBundlingResultMap = new Map();
|
|
28
30
|
#perf;
|
|
29
31
|
constructor(_opt) {
|
|
30
32
|
this._opt = _opt;
|
|
@@ -32,28 +34,9 @@ export class SdTsCompiler {
|
|
|
32
34
|
const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
|
|
33
35
|
const tsconfig = FsUtils.readJson(tsconfigPath);
|
|
34
36
|
this.#isForAngular = Boolean(tsconfig.angularCompilerOptions);
|
|
35
|
-
this
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
inlineFonts: true,
|
|
39
|
-
preserveSymlinks: false,
|
|
40
|
-
sourcemap: this._opt.isDevMode ? "inline" : false,
|
|
41
|
-
outputNames: { bundles: "[name]", media: "media/[name]" },
|
|
42
|
-
includePaths: [],
|
|
43
|
-
// sass:
|
|
44
|
-
externalDependencies: [],
|
|
45
|
-
target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
|
|
46
|
-
tailwindConfiguration: undefined,
|
|
47
|
-
/*postcssConfiguration: {
|
|
48
|
-
plugins: [["css-has-pseudo"]],
|
|
49
|
-
},*/
|
|
50
|
-
// publicPath:
|
|
51
|
-
cacheOptions: {
|
|
52
|
-
enabled: true,
|
|
53
|
-
path: ".cache/angular",
|
|
54
|
-
basePath: ".cache",
|
|
55
|
-
},
|
|
56
|
-
}, "scss", this._opt.isDevMode);
|
|
37
|
+
if (!this._opt.isNoEmit) {
|
|
38
|
+
this.#styleBundler = new SdStyleBundler(this._opt.pkgPath, this._opt.isDevMode, this._opt.watchScopePathSet);
|
|
39
|
+
}
|
|
57
40
|
}
|
|
58
41
|
#parseTsConfig() {
|
|
59
42
|
const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
|
|
@@ -61,6 +44,32 @@ export class SdTsCompiler {
|
|
|
61
44
|
const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._opt.pkgPath, {
|
|
62
45
|
...tsconfig.angularCompilerOptions,
|
|
63
46
|
...this._opt.additionalOptions,
|
|
47
|
+
...(this._opt.isEmitOnly
|
|
48
|
+
? {
|
|
49
|
+
// typescript
|
|
50
|
+
noEmitOnError: false,
|
|
51
|
+
strict: false,
|
|
52
|
+
noImplicitAny: false,
|
|
53
|
+
noImplicitThis: false,
|
|
54
|
+
strictNullChecks: false,
|
|
55
|
+
strictFunctionTypes: false,
|
|
56
|
+
strictBindCallApply: false,
|
|
57
|
+
strictPropertyInitialization: false,
|
|
58
|
+
useUnknownInCatchVariables: false,
|
|
59
|
+
exactOptionalPropertyTypes: false,
|
|
60
|
+
noUncheckedIndexedAccess: false,
|
|
61
|
+
noUnusedLocals: false,
|
|
62
|
+
noUnusedParameters: false,
|
|
63
|
+
skipLibCheck: true,
|
|
64
|
+
checkJs: false,
|
|
65
|
+
alwaysStrict: false,
|
|
66
|
+
// angular
|
|
67
|
+
strictTemplates: false,
|
|
68
|
+
strictInjectionParameters: false,
|
|
69
|
+
strictInputAccessModifiers: false,
|
|
70
|
+
strictStandalone: false,
|
|
71
|
+
}
|
|
72
|
+
: {}),
|
|
64
73
|
});
|
|
65
74
|
const distPath = PathUtils.norm(parsedTsconfig.options.outDir ?? path.resolve(this._opt.pkgPath, "dist"));
|
|
66
75
|
return {
|
|
@@ -93,108 +102,53 @@ export class SdTsCompiler {
|
|
|
93
102
|
compilerHost.readResource = (fileName) => {
|
|
94
103
|
return compilerHost.readFile(fileName) ?? "";
|
|
95
104
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
if (!this._opt.isNoEmit) {
|
|
106
|
+
compilerHost.transformResource = async (data, context) => {
|
|
107
|
+
if (context.type !== "style") {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const styleBundleResult = await this.#styleBundler.bundleAsync(data, PathUtils.norm(context.containingFile), context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined);
|
|
111
|
+
if (!styleBundleResult.cached && !StringUtils.isNullOrEmpty(styleBundleResult.contents)) {
|
|
112
|
+
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), context.containingFile);
|
|
113
|
+
const outAbsPath = PathUtils.norm(compilerOptions.outDir, relPath.replace(/\.ts$/, ".css"));
|
|
114
|
+
const cache = this.#emittedFilesCacheMap.getOrCreate(PathUtils.norm(context.containingFile), []);
|
|
115
|
+
cache.remove((item) => item.outAbsPath === outAbsPath);
|
|
116
|
+
cache.push({ outAbsPath, text: styleBundleResult.contents });
|
|
117
|
+
}
|
|
118
|
+
return StringUtils.isNullOrEmpty(styleBundleResult.contents)
|
|
119
|
+
? null
|
|
120
|
+
: { content: "" /*styleBundleResult.contents*/ };
|
|
121
|
+
};
|
|
122
|
+
}
|
|
105
123
|
compilerHost.getModifiedResourceFiles = () => {
|
|
106
124
|
return new Set(Array.from(modifiedFileSet).map((item) => PathUtils.posix(item)));
|
|
107
125
|
};
|
|
108
126
|
}
|
|
109
127
|
return compilerHost;
|
|
110
128
|
}
|
|
111
|
-
// private async _getOrCreateStyleBundleWorkerAsync() {
|
|
112
|
-
// if (this._stylesheetBundlingWorker) {
|
|
113
|
-
// return this._stylesheetBundlingWorker;
|
|
114
|
-
// }
|
|
115
|
-
//
|
|
116
|
-
// this._stylesheetBundlingWorker = new SdWorker<TStyleBundlerWorkerType>(
|
|
117
|
-
// import.meta.resolve("../workers/style-bundler.worker"),
|
|
118
|
-
// );
|
|
119
|
-
//
|
|
120
|
-
// await this._stylesheetBundlingWorker.run(
|
|
121
|
-
// "prepare",
|
|
122
|
-
// [this._opt.pkgPath, this._opt.isDevMode],
|
|
123
|
-
// );
|
|
124
|
-
//
|
|
125
|
-
// return this._stylesheetBundlingWorker;
|
|
126
|
-
// }
|
|
127
|
-
async #bundleStylesheetAsync(data, containingFile, resourceFile = null) {
|
|
128
|
-
// containingFile: 포함된 파일 (.ts)
|
|
129
|
-
// resourceFile: 외부 리소스 파일 (styleUrls로 입력하지 않고 styles에 직접 입력한 경우 null)
|
|
130
|
-
// referencedFiles: import한 외부 scss 파일 혹은 woff파일등 외부 파일
|
|
131
|
-
// this.#debug(`bundle stylesheet...(${containingFile}, ${resourceFile})`);
|
|
132
|
-
return await this.#perf.run("스타일 번들링", async () => {
|
|
133
|
-
const fileNPath = PathUtils.norm(resourceFile ?? containingFile);
|
|
134
|
-
if (this.#stylesheetBundlingResultMap.has(fileNPath)) {
|
|
135
|
-
return this.#stylesheetBundlingResultMap.get(fileNPath);
|
|
136
|
-
}
|
|
137
|
-
try {
|
|
138
|
-
// const result = await new SdSassEmbeddedBundler(this._opt.isDevMode)
|
|
139
|
-
// .bundleAsync(data, resourceFile ?? containingFile);
|
|
140
|
-
// const worker = this._stylesheetBundlingWorker!;
|
|
141
|
-
// const result = await worker.run("bundle", [data, containingFile, resourceFile]);
|
|
142
|
-
const result = resourceFile != null
|
|
143
|
-
? await this.#stylesheetBundler.bundleFile(resourceFile)
|
|
144
|
-
: await this.#stylesheetBundler.bundleInline(data, containingFile, "scss");
|
|
145
|
-
for (const referencedFile of result.referencedFiles ?? []) {
|
|
146
|
-
// for (const referencedFile of result.referencedFiles) {
|
|
147
|
-
// 참조하는 파일과 참조된 파일 사이의 의존성 관계 추가
|
|
148
|
-
this.#depCache.addImport(fileNPath, PathUtils.norm(referencedFile), 0);
|
|
149
|
-
}
|
|
150
|
-
this.#stylesheetBundlingResultMap.set(fileNPath, result);
|
|
151
|
-
// this._stylesheetBundlingResultMap.set(fileNPath, {
|
|
152
|
-
// errors: undefined,
|
|
153
|
-
// warnings: [],
|
|
154
|
-
// ...result,
|
|
155
|
-
// });
|
|
156
|
-
//
|
|
157
|
-
// return {
|
|
158
|
-
// errors: undefined,
|
|
159
|
-
// warnings: [],
|
|
160
|
-
// ...result,
|
|
161
|
-
// };
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
const result = {
|
|
166
|
-
errors: [
|
|
167
|
-
{
|
|
168
|
-
text: `스타일 번들링 실패: ${err.message ?? "알 수 없는 오류"}`,
|
|
169
|
-
location: { file: containingFile },
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
warnings: [],
|
|
173
|
-
};
|
|
174
|
-
this.#stylesheetBundlingResultMap.set(fileNPath, result);
|
|
175
|
-
return result;
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
129
|
async compileAsync(modifiedFileSet) {
|
|
180
130
|
this.#perf = new SdCliPerformanceTimer("esbuild compile");
|
|
181
131
|
const prepareResult = await this.#prepareAsync(modifiedFileSet);
|
|
182
132
|
const [globalStyleSheet, buildResult, lintResults] = await Promise.all([
|
|
183
|
-
this.#buildGlobalStyleAsync(),
|
|
133
|
+
this._opt.isNoEmit ? undefined : this.#buildGlobalStyleAsync(),
|
|
184
134
|
this.#build(prepareResult),
|
|
185
|
-
this.#lintAsync(prepareResult),
|
|
135
|
+
this._opt.isEmitOnly ? [] : this.#lintAsync(prepareResult),
|
|
136
|
+
]);
|
|
137
|
+
const affectedFileSet = new Set([
|
|
138
|
+
...prepareResult.affectedFileSet,
|
|
139
|
+
...prepareResult.styleAffectedFileSet,
|
|
186
140
|
]);
|
|
187
141
|
this.#debug(`빌드 완료됨`, this.#perf.toString());
|
|
188
|
-
this.#debug(`영향 받은 파일: ${
|
|
142
|
+
this.#debug(`영향 받은 파일: ${affectedFileSet.size}개`);
|
|
189
143
|
this.#debug(`감시 중인 파일: ${prepareResult.watchFileSet.size}개`);
|
|
190
144
|
return {
|
|
191
145
|
messages: [
|
|
192
146
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromTsDiag(buildResult.diagnostics),
|
|
193
147
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromEslint(lintResults),
|
|
194
148
|
],
|
|
195
|
-
affectedFileSet:
|
|
149
|
+
affectedFileSet: affectedFileSet,
|
|
196
150
|
watchFileSet: prepareResult.watchFileSet,
|
|
197
|
-
stylesheetBundlingResultMap: this.#
|
|
151
|
+
stylesheetBundlingResultMap: this.#styleBundler?.getResultCache() ?? new Map(),
|
|
198
152
|
emittedFilesCacheMap: this.#emittedFilesCacheMap,
|
|
199
153
|
emitFileSet: new Set([...buildResult.emitFileSet, globalStyleSheet].filterExists()),
|
|
200
154
|
};
|
|
@@ -204,47 +158,37 @@ export class SdTsCompiler {
|
|
|
204
158
|
const tsconfig = this.#parseTsConfig();
|
|
205
159
|
if (modifiedFileSet.size !== 0) {
|
|
206
160
|
this.#debug(`캐시 무효화 및 초기화 중...`);
|
|
207
|
-
await Wait.time(100);
|
|
208
161
|
// this._perf.run("캐시 무효화 및 초기화", () => {
|
|
209
162
|
this.#perf.run("캐시 무효화 및 초기화", () => {
|
|
210
|
-
//
|
|
211
|
-
const affectedFileMap = this.#depCache.getAffectedFileMap(modifiedFileSet);
|
|
212
|
-
const affectedFiles = Array.from(affectedFileMap.values()).mapMany((item) => Array.from(item));
|
|
213
|
-
const getTreeText = (node, indent = "") => {
|
|
214
|
-
let result = indent + node.fileNPath + "\n";
|
|
215
|
-
for (const child of node.children) {
|
|
216
|
-
result += getTreeText(child, indent + " ");
|
|
217
|
-
}
|
|
218
|
-
return result;
|
|
219
|
-
};
|
|
220
|
-
const affectedFileTree = this.#depCache.getAffectedFileTree(modifiedFileSet);
|
|
221
|
-
this.#debug(`영향받은 기존파일: ${affectedFileTree.map((item) => getTreeText(item)).join("\n")}`.trim());
|
|
222
|
-
// 스타일 번들러에서 영향받은 파일 관련 항목 무효화 (.scss만 해당)
|
|
223
|
-
this.#stylesheetBundler.invalidate(new Set([...modifiedFileSet, ...affectedFiles.filter((item) => item.endsWith(".scss"))]));
|
|
224
|
-
// await worker.run("invalidate", [affectedFileSet]);
|
|
225
|
-
// 소스파일은 변경된 파일들로 무효화 (스타일 변화시 타고들어가야됨)
|
|
163
|
+
// 소스파일은 변경된 파일들로 무효화
|
|
226
164
|
for (const modifiedFile of modifiedFileSet) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
165
|
+
this.#sourceFileCacheMap.delete(modifiedFile);
|
|
166
|
+
}
|
|
167
|
+
// 스타일 번들러 무효화 (transformResource 재실행 땜에 필요할듯)
|
|
168
|
+
if (this.#styleBundler) {
|
|
169
|
+
const styleAffectedFileSet = this.#styleBundler.invalidate(modifiedFileSet);
|
|
170
|
+
// 스타일 변경된 파일들로 소스파일 무효화
|
|
171
|
+
for (const styleAffectedFile of styleAffectedFileSet) {
|
|
172
|
+
this.#sourceFileCacheMap.delete(styleAffectedFile);
|
|
234
173
|
}
|
|
235
174
|
}
|
|
175
|
+
// angular origin 파일 매핑은 변경된 파일들로 무효화
|
|
176
|
+
for (const modifiedFile of modifiedFileSet) {
|
|
177
|
+
this.#cache.ngOrg.delete(modifiedFile);
|
|
178
|
+
}
|
|
179
|
+
// 기존 의존성에 의해 영향받는 파일들 계산
|
|
180
|
+
const affectedFileMap = this.#cache.dep.getAffectedFileMap(modifiedFileSet);
|
|
181
|
+
const affectedFileSet = new Set(Array.from(affectedFileMap.values()).mapMany((item) => Array.from(item)));
|
|
236
182
|
// 의존성 캐시에서 영향받은 파일 관련 항목 무효화 (Affected더라도 SF는 동일하므로, modifiedFileSet만 넣어도될듯?)
|
|
237
183
|
// 250715: sourceFile 타입체크를 다시 해야해서 affected로 넣는게 맞는듯.
|
|
238
|
-
this.#
|
|
239
|
-
//
|
|
240
|
-
for (const
|
|
241
|
-
this.#emittedFilesCacheMap.delete(
|
|
242
|
-
this.#stylesheetBundlingResultMap.delete(affectedFile);
|
|
184
|
+
this.#cache.dep.invalidates(affectedFileSet);
|
|
185
|
+
// 결과물이 바뀌어야 하는 캐시 모두 무효화 (modified만 다시쓰면될듯..)
|
|
186
|
+
for (const modifiedFile of modifiedFileSet) {
|
|
187
|
+
this.#emittedFilesCacheMap.delete(modifiedFile);
|
|
243
188
|
}
|
|
244
189
|
});
|
|
245
190
|
}
|
|
246
191
|
this.#debug(`ts.Program 생성 중...`);
|
|
247
|
-
await Wait.time(300);
|
|
248
192
|
const compilerHost = this.#perf.run("ts.CompilerHost 생성", () => {
|
|
249
193
|
return this.#createCompilerHost(tsconfig.options, modifiedFileSet);
|
|
250
194
|
});
|
|
@@ -258,29 +202,41 @@ export class SdTsCompiler {
|
|
|
258
202
|
}
|
|
259
203
|
});
|
|
260
204
|
this.#debug(`ts.Program 생성`);
|
|
261
|
-
await Wait.time(300);
|
|
262
205
|
if (this.#ngProgram) {
|
|
263
206
|
await this.#perf.run("Angular 템플릿 분석", async () => {
|
|
264
207
|
await this.#ngProgram.compiler.analyzeAsync();
|
|
265
208
|
});
|
|
266
209
|
}
|
|
267
|
-
this
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
210
|
+
if (!this._opt.isEmitOnly) {
|
|
211
|
+
this.#debug(`새 의존성 분석 중...`);
|
|
212
|
+
this.#perf.run("새 의존성 분석", () => {
|
|
213
|
+
// SdTsDependencyAnalyzer를 통해 의존성 분석 및 SdDepCache 업데이트
|
|
214
|
+
SdDependencyAnalyzer.analyze(this.#program, compilerHost, this._opt.watchScopePathSet, this.#cache);
|
|
215
|
+
});
|
|
216
|
+
this.#debug(`새 의존성 분석(Angular) 중...`);
|
|
271
217
|
// Angular 리소스 의존성 추가
|
|
272
218
|
if (this.#ngProgram) {
|
|
273
|
-
|
|
219
|
+
this.#perf.run("새 의존성 분석(Angular)", () => {
|
|
220
|
+
SdDependencyAnalyzer.analyzeAngularResources(this.#ngProgram, this._opt.watchScopePathSet, this.#cache.dep);
|
|
221
|
+
});
|
|
274
222
|
}
|
|
275
|
-
}
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
223
|
+
}
|
|
224
|
+
const allFiles = this.#program.getSourceFiles().map((item) => PathUtils.norm(item.fileName));
|
|
225
|
+
const watchFileSet = new Set(allFiles.filter((item) => this._opt.watchScopePathSet.inScope(item)));
|
|
226
|
+
let affectedFileSet;
|
|
227
|
+
if (modifiedFileSet.size === 0) {
|
|
228
|
+
affectedFileSet = new Set(allFiles);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const affectedFileMap = this.#cache.dep.getAffectedFileMap(modifiedFileSet);
|
|
232
|
+
this.#debug("영향받은 파일:", affectedFileMap);
|
|
233
|
+
affectedFileSet = new Set(Array.from(affectedFileMap.values()).mapMany((item) => Array.from(item)));
|
|
234
|
+
}
|
|
280
235
|
return {
|
|
281
236
|
tsconfig,
|
|
282
237
|
compilerHost,
|
|
283
238
|
affectedFileSet,
|
|
239
|
+
styleAffectedFileSet: this.#styleBundler?.getAffectedFileSet(affectedFileSet) ?? new Set(),
|
|
284
240
|
watchFileSet,
|
|
285
241
|
};
|
|
286
242
|
}
|
|
@@ -314,7 +270,7 @@ export class SdTsCompiler {
|
|
|
314
270
|
this.#debug(`전역 스타일 번들링 중...`);
|
|
315
271
|
await this.#perf.run("전역 스타일 번들링", async () => {
|
|
316
272
|
const data = await FsUtils.readFileAsync(this._opt.globalStyleFilePath);
|
|
317
|
-
const stylesheetBundlingResult = await this.#
|
|
273
|
+
const stylesheetBundlingResult = await this.#styleBundler.bundleAsync(data, this._opt.globalStyleFilePath, this._opt.globalStyleFilePath);
|
|
318
274
|
const emitFileInfos = this.#emittedFilesCacheMap.getOrCreate(this._opt.globalStyleFilePath, []);
|
|
319
275
|
emitFileInfos.push({
|
|
320
276
|
outAbsPath: PathUtils.norm(this._opt.pkgPath, path
|
|
@@ -330,99 +286,323 @@ export class SdTsCompiler {
|
|
|
330
286
|
#build(prepareResult) {
|
|
331
287
|
const emitFileSet = new Set();
|
|
332
288
|
const diagnostics = [];
|
|
333
|
-
this
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
this.#debug(`개별 파일 진단 수집 중...`);
|
|
341
|
-
for (const affectedFile of prepareResult.affectedFileSet) {
|
|
342
|
-
if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath))
|
|
343
|
-
continue;
|
|
344
|
-
const affectedSourceFile = this.#program.getSourceFile(affectedFile);
|
|
345
|
-
if (!affectedSourceFile ||
|
|
346
|
-
(this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))) {
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
// this.#debug(`get diagnostics of file ${affectedFile}...`);
|
|
350
|
-
this.#perf.run("개별 파일 진단 수집", () => {
|
|
351
|
-
diagnostics.push(...this.#program.getSyntacticDiagnostics(affectedSourceFile), ...this.#program.getSemanticDiagnostics(affectedSourceFile));
|
|
289
|
+
if (!this._opt.isEmitOnly) {
|
|
290
|
+
this.#debug(`프로그램 진단 수집 중...`);
|
|
291
|
+
this.#perf.run("프로그램 진단 수집", () => {
|
|
292
|
+
diagnostics.push(...this.#program.getConfigFileParsingDiagnostics(), ...this.#program.getOptionsDiagnostics(), ...this.#program.getGlobalDiagnostics());
|
|
293
|
+
if (this.#ngProgram) {
|
|
294
|
+
diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
|
|
295
|
+
}
|
|
352
296
|
});
|
|
353
|
-
|
|
354
|
-
this.#perf.run("개별 파일 진단 수집(Angular)", () => {
|
|
355
|
-
if (affectedSourceFile.isDeclarationFile)
|
|
356
|
-
return;
|
|
357
|
-
diagnostics.push(...this.#ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram));
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
this.#perf.run("파일 출력 (emit)", () => {
|
|
362
|
-
this.#debug(`파일 출력 준비 중...`);
|
|
363
|
-
let transformers = {};
|
|
364
|
-
if (this.#ngProgram) {
|
|
365
|
-
const angularTransfomers = this.#ngProgram.compiler.prepareEmit().transformers;
|
|
366
|
-
(transformers.before ??= []).push(...(angularTransfomers.before ?? []));
|
|
367
|
-
(transformers.after ??= []).push(...(angularTransfomers.after ?? []));
|
|
368
|
-
(transformers.afterDeclarations ??= []).push(...(angularTransfomers.afterDeclarations ?? []));
|
|
369
|
-
(transformers.before ??= []).push(replaceBootstrap(() => this.#program.getTypeChecker()));
|
|
370
|
-
(transformers.before ??= []).push(createWorkerTransformer((file, importer) => {
|
|
371
|
-
const fullPath = path.resolve(path.dirname(importer), file);
|
|
372
|
-
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
|
|
373
|
-
return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
|
|
374
|
-
}));
|
|
375
|
-
}
|
|
376
|
-
this.#debug(`파일 출력 중...`);
|
|
377
|
-
// affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
|
|
378
|
-
// 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
|
|
379
|
-
// 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
|
|
297
|
+
this.#debug(`개별 파일 진단 수집 중...`);
|
|
380
298
|
for (const affectedFile of prepareResult.affectedFileSet) {
|
|
381
|
-
if (
|
|
382
|
-
continue;
|
|
383
|
-
const sf = this.#program.getSourceFile(affectedFile);
|
|
384
|
-
if (!sf || sf.isDeclarationFile)
|
|
299
|
+
if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath))
|
|
385
300
|
continue;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
continue;
|
|
390
|
-
// 번들이 아닌 외부패키지는 보통 emit안해도 됨
|
|
391
|
-
// but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
|
|
392
|
-
if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
|
|
301
|
+
const affectedSourceFile = this.#program.getSourceFile(affectedFile);
|
|
302
|
+
if (!affectedSourceFile ||
|
|
303
|
+
(this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))) {
|
|
393
304
|
continue;
|
|
394
305
|
}
|
|
395
|
-
this.#
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile))
|
|
306
|
+
// this.#debug(`get diagnostics of file ${affectedFile}...`);
|
|
307
|
+
this.#perf.run("개별 파일 진단 수집", () => {
|
|
308
|
+
diagnostics.push(...this.#program.getSyntacticDiagnostics(affectedSourceFile), ...this.#program.getSemanticDiagnostics(affectedSourceFile));
|
|
309
|
+
});
|
|
310
|
+
if (this.#ngProgram) {
|
|
311
|
+
this.#perf.run("개별 파일 진단 수집(Angular)", () => {
|
|
312
|
+
if (affectedSourceFile.isDeclarationFile)
|
|
403
313
|
return;
|
|
404
|
-
this.#ngProgram.compiler.
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
|
|
408
|
-
const real = this.#convertOutputToReal(fileName, prepareResult.tsconfig.distPath, text);
|
|
409
|
-
emitFileInfoCaches.push({
|
|
410
|
-
outAbsPath: real.filePath,
|
|
411
|
-
text: real.text,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
emitFileInfoCaches.push({ text });
|
|
416
|
-
}
|
|
417
|
-
emitFileSet.add(PathUtils.norm(sourceFile.fileName));
|
|
418
|
-
}, undefined, undefined, transformers);
|
|
314
|
+
diagnostics.push(...this.#ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram));
|
|
315
|
+
});
|
|
316
|
+
}
|
|
419
317
|
}
|
|
420
|
-
}
|
|
318
|
+
}
|
|
319
|
+
if (!this._opt.isNoEmit) {
|
|
320
|
+
this.#perf.run("파일 출력 (emit)", () => {
|
|
321
|
+
this.#debug(`파일 출력 준비 중...`);
|
|
322
|
+
let transformers = {};
|
|
323
|
+
if (this.#ngProgram) {
|
|
324
|
+
const angularTransfomers = this.#ngProgram.compiler.prepareEmit().transformers;
|
|
325
|
+
(transformers.before ??= []).push(...(angularTransfomers.before ?? []));
|
|
326
|
+
(transformers.after ??= []).push(...(angularTransfomers.after ?? []));
|
|
327
|
+
(transformers.afterDeclarations ??= []).push(...(angularTransfomers.afterDeclarations ?? []));
|
|
328
|
+
(transformers.before ??= []).push(replaceBootstrap(() => this.#program.getTypeChecker()));
|
|
329
|
+
(transformers.before ??= []).push(createWorkerTransformer((file, importer) => {
|
|
330
|
+
const fullPath = path.resolve(path.dirname(importer), file);
|
|
331
|
+
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
|
|
332
|
+
return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
|
|
333
|
+
}));
|
|
334
|
+
(transformers.before ??= []).push(this.#createExternalizeComponentStylesTransformer());
|
|
335
|
+
}
|
|
336
|
+
this.#debug(`파일 출력 중...`);
|
|
337
|
+
// affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
|
|
338
|
+
// 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
|
|
339
|
+
// 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
|
|
340
|
+
for (const affectedFile of [
|
|
341
|
+
...prepareResult.affectedFileSet /*,
|
|
342
|
+
...prepareResult.styleAffectedFileSet,*/,
|
|
343
|
+
]) {
|
|
344
|
+
if (this.#emittedFilesCacheMap
|
|
345
|
+
.get(affectedFile)
|
|
346
|
+
?.some((item) => !item.outAbsPath?.endsWith(".css")))
|
|
347
|
+
continue;
|
|
348
|
+
const sf = this.#program.getSourceFile(affectedFile);
|
|
349
|
+
if (!sf || sf.isDeclarationFile)
|
|
350
|
+
continue;
|
|
351
|
+
if (this.#ngProgram?.compiler.ignoreForEmit.has(sf))
|
|
352
|
+
continue;
|
|
353
|
+
if (this.#ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf))
|
|
354
|
+
continue;
|
|
355
|
+
// 번들이 아닌 외부패키지는 보통 emit안해도 됨
|
|
356
|
+
// but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
|
|
357
|
+
if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
this.#program.emit(sf, (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
|
|
361
|
+
if (!sourceFiles || sourceFiles.length === 0) {
|
|
362
|
+
prepareResult.compilerHost.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
|
|
366
|
+
if (this.#ngProgram) {
|
|
367
|
+
if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile))
|
|
368
|
+
return;
|
|
369
|
+
this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
|
|
370
|
+
}
|
|
371
|
+
const emitFileInfoCaches = this.#emittedFilesCacheMap.getOrCreate(PathUtils.norm(sourceFile.fileName), []);
|
|
372
|
+
if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
|
|
373
|
+
const real = this.#convertOutputToReal(fileName, prepareResult.tsconfig.distPath, text);
|
|
374
|
+
emitFileInfoCaches.push({
|
|
375
|
+
outAbsPath: real.filePath,
|
|
376
|
+
text: this.#removeOutputDevModeLine(real.text),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
emitFileInfoCaches.push({ text });
|
|
381
|
+
}
|
|
382
|
+
emitFileSet.add(PathUtils.norm(sourceFile.fileName));
|
|
383
|
+
}, undefined, undefined, transformers);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
421
387
|
return {
|
|
422
388
|
emitFileSet,
|
|
423
389
|
diagnostics,
|
|
424
390
|
};
|
|
425
391
|
}
|
|
392
|
+
#createExternalizeComponentStylesTransformer() {
|
|
393
|
+
const f = ts.factory;
|
|
394
|
+
/*function makeEnsureStyleFunc() {
|
|
395
|
+
// function __sdEnsureStyle(href) { ... }
|
|
396
|
+
const hrefParam = f.createParameterDeclaration(undefined, undefined, "href");
|
|
397
|
+
|
|
398
|
+
// const d = document;
|
|
399
|
+
const declD = f.createVariableStatement(
|
|
400
|
+
undefined,
|
|
401
|
+
f.createVariableDeclarationList(
|
|
402
|
+
[f.createVariableDeclaration("d", undefined, undefined, f.createIdentifier("document"))],
|
|
403
|
+
ts.NodeFlags.Const,
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// let link = d.querySelector(`link[data-sd-style="${href}"]`);
|
|
408
|
+
const tpl = f.createTemplateExpression(f.createTemplateHead('link[data-sd-style="'), [
|
|
409
|
+
f.createTemplateSpan(f.createIdentifier("href"), f.createTemplateTail('"]')),
|
|
410
|
+
]);
|
|
411
|
+
const declLink = f.createVariableStatement(
|
|
412
|
+
undefined,
|
|
413
|
+
f.createVariableDeclarationList(
|
|
414
|
+
[
|
|
415
|
+
f.createVariableDeclaration(
|
|
416
|
+
"link",
|
|
417
|
+
undefined,
|
|
418
|
+
undefined,
|
|
419
|
+
f.createCallExpression(
|
|
420
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "querySelector"),
|
|
421
|
+
undefined,
|
|
422
|
+
[tpl],
|
|
423
|
+
),
|
|
424
|
+
),
|
|
425
|
+
],
|
|
426
|
+
ts.NodeFlags.Let,
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
// if (link) return;
|
|
431
|
+
const ifReturn = f.createIfStatement(
|
|
432
|
+
f.createIdentifier("link"),
|
|
433
|
+
f.createBlock([f.createReturnStatement()], true),
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// link = d.createElement('link');
|
|
437
|
+
const mkLink = f.createExpressionStatement(
|
|
438
|
+
f.createBinaryExpression(
|
|
439
|
+
f.createIdentifier("link"),
|
|
440
|
+
ts.SyntaxKind.EqualsToken,
|
|
441
|
+
f.createCallExpression(
|
|
442
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "createElement"),
|
|
443
|
+
undefined,
|
|
444
|
+
[f.createStringLiteral("link")],
|
|
445
|
+
),
|
|
446
|
+
),
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// link.rel = 'stylesheet';
|
|
450
|
+
const setRel = f.createExpressionStatement(
|
|
451
|
+
f.createBinaryExpression(
|
|
452
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "rel"),
|
|
453
|
+
ts.SyntaxKind.EqualsToken,
|
|
454
|
+
f.createStringLiteral("stylesheet"),
|
|
455
|
+
),
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// link.setAttribute('data-sd-style', href);
|
|
459
|
+
const setData = f.createExpressionStatement(
|
|
460
|
+
f.createCallExpression(
|
|
461
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "setAttribute"),
|
|
462
|
+
undefined,
|
|
463
|
+
[f.createStringLiteral("data-sd-style"), f.createIdentifier("href")],
|
|
464
|
+
),
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
// link.href = href;
|
|
468
|
+
const setHref = f.createExpressionStatement(
|
|
469
|
+
f.createBinaryExpression(
|
|
470
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "href"),
|
|
471
|
+
ts.SyntaxKind.EqualsToken,
|
|
472
|
+
f.createIdentifier("href"),
|
|
473
|
+
),
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
// d.head.appendChild(link);
|
|
477
|
+
const append = f.createExpressionStatement(
|
|
478
|
+
f.createCallExpression(
|
|
479
|
+
f.createPropertyAccessExpression(
|
|
480
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "head"),
|
|
481
|
+
"appendChild",
|
|
482
|
+
),
|
|
483
|
+
undefined,
|
|
484
|
+
[f.createIdentifier("link")],
|
|
485
|
+
),
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
const body = f.createBlock(
|
|
489
|
+
[declD, declLink, ifReturn, mkLink, setRel, setData, setHref, append],
|
|
490
|
+
true,
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
return f.createFunctionDeclaration(
|
|
494
|
+
undefined,
|
|
495
|
+
undefined,
|
|
496
|
+
f.createIdentifier("__sdEnsureStyle"),
|
|
497
|
+
undefined,
|
|
498
|
+
[hrefParam],
|
|
499
|
+
undefined,
|
|
500
|
+
body,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function makeEnsureCallInStatic(href: string) {
|
|
505
|
+
return f.createExpressionStatement(
|
|
506
|
+
f.createCallExpression(
|
|
507
|
+
f.createIdentifier("__sdEnsureStyle"),
|
|
508
|
+
undefined,
|
|
509
|
+
[f.createStringLiteral(href)], // 필요하면 devBust 인자도 추가 가능
|
|
510
|
+
),
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const upsertEnsureStyleHelper = (sf: ts.SourceFile): ts.SourceFile => {
|
|
515
|
+
// 이미 있으면 스킵
|
|
516
|
+
if (
|
|
517
|
+
sf.statements.some((s) => ts.isFunctionDeclaration(s) && s.name?.text === "__sdEnsureStyle")
|
|
518
|
+
) {
|
|
519
|
+
return sf;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const fn = makeEnsureStyleFunc();
|
|
523
|
+
|
|
524
|
+
const href = path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
525
|
+
const call = makeEnsureCallInStatic(href);
|
|
526
|
+
|
|
527
|
+
if (this._opt.isForBundle) {
|
|
528
|
+
return f.updateSourceFile(sf, [fn, call, ...sf.statements]);
|
|
529
|
+
} else {
|
|
530
|
+
const importTarget = "./" + path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
531
|
+
const importDecl = f.createImportDeclaration(
|
|
532
|
+
undefined,
|
|
533
|
+
undefined,
|
|
534
|
+
f.createStringLiteral(importTarget),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
return f.updateSourceFile(sf, [fn, call, importDecl, ...sf.statements]);
|
|
538
|
+
}
|
|
539
|
+
};*/
|
|
540
|
+
function upsertEnsureStyleHelper(sf) {
|
|
541
|
+
// 이미 있으면 스킵
|
|
542
|
+
if (sf.statements.some((s) => ts.isFunctionDeclaration(s) && s.name?.text === "__sdEnsureStyle")) {
|
|
543
|
+
return sf;
|
|
544
|
+
}
|
|
545
|
+
const importTarget = "./" + path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
546
|
+
const importDecl = f.createImportDeclaration(undefined, // decorators
|
|
547
|
+
undefined, // modifiers
|
|
548
|
+
f.createStringLiteral(importTarget));
|
|
549
|
+
return f.updateSourceFile(sf, [importDecl, ...sf.statements]);
|
|
550
|
+
}
|
|
551
|
+
const removeStyleProp = (node) => {
|
|
552
|
+
const allDecorators = ts.getDecorators(node);
|
|
553
|
+
if (!allDecorators || allDecorators.length === 0)
|
|
554
|
+
return node;
|
|
555
|
+
const decoratorsUpdated = allDecorators.map((dec) => {
|
|
556
|
+
if (!ts.isCallExpression(dec.expression))
|
|
557
|
+
return dec;
|
|
558
|
+
const call = dec.expression;
|
|
559
|
+
if (!ts.isIdentifier(call.expression) || call.expression.text !== "Component")
|
|
560
|
+
return dec;
|
|
561
|
+
if (call.arguments.length !== 1)
|
|
562
|
+
return dec;
|
|
563
|
+
const arg = call.arguments[0];
|
|
564
|
+
if (!ts.isObjectLiteralExpression(arg))
|
|
565
|
+
return dec;
|
|
566
|
+
const filteredProps = arg.properties.filter((p) => {
|
|
567
|
+
if (!ts.isPropertyAssignment(p))
|
|
568
|
+
return true;
|
|
569
|
+
const name = p.name;
|
|
570
|
+
const key = ts.isIdentifier(name)
|
|
571
|
+
? name.text
|
|
572
|
+
: ts.isStringLiteralLike(name)
|
|
573
|
+
? name.text
|
|
574
|
+
: undefined;
|
|
575
|
+
return !(key === "styles" || key === "styleUrls");
|
|
576
|
+
});
|
|
577
|
+
const newArg = f.updateObjectLiteralExpression(arg, filteredProps);
|
|
578
|
+
const newCall = f.updateCallExpression(call, call.expression, call.typeArguments, [newArg]);
|
|
579
|
+
return f.updateDecorator(dec, newCall);
|
|
580
|
+
});
|
|
581
|
+
const existingModifiers = node.modifiers ?? [];
|
|
582
|
+
const modifiersWithoutOldDecos = existingModifiers.filter((m) => !ts.isDecorator(m));
|
|
583
|
+
const newModifiers = [
|
|
584
|
+
...decoratorsUpdated,
|
|
585
|
+
...modifiersWithoutOldDecos,
|
|
586
|
+
];
|
|
587
|
+
const newNode = f.updateClassDeclaration(node, newModifiers, node.name, node.typeParameters, node.heritageClauses, node.members);
|
|
588
|
+
return f.updateClassDeclaration(newNode, newNode.modifiers, newNode.name, newNode.typeParameters, newNode.heritageClauses, newNode.members);
|
|
589
|
+
};
|
|
590
|
+
return (ctx) => {
|
|
591
|
+
return (sf) => {
|
|
592
|
+
const has = this.#styleBundler.getResultCache().get(PathUtils.norm(sf.fileName));
|
|
593
|
+
if (!has)
|
|
594
|
+
return sf;
|
|
595
|
+
const realSf = upsertEnsureStyleHelper(sf);
|
|
596
|
+
function visitor(node) {
|
|
597
|
+
if (ts.isClassDeclaration(node) && Boolean(ts.getDecorators(node)?.length)) {
|
|
598
|
+
return removeStyleProp(node);
|
|
599
|
+
}
|
|
600
|
+
return ts.visitEachChild(node, visitor, ctx);
|
|
601
|
+
}
|
|
602
|
+
return ts.visitNode(realSf, visitor);
|
|
603
|
+
};
|
|
604
|
+
};
|
|
605
|
+
}
|
|
426
606
|
#convertOutputToReal(filePath, distPath, text) {
|
|
427
607
|
let realFilePath = PathUtils.norm(filePath);
|
|
428
608
|
let realText = text;
|
|
@@ -438,6 +618,9 @@ export class SdTsCompiler {
|
|
|
438
618
|
}
|
|
439
619
|
return { filePath: realFilePath, text: realText };
|
|
440
620
|
}
|
|
621
|
+
#removeOutputDevModeLine(str) {
|
|
622
|
+
return str.replace(/\(\(\) => \{ \(typeof ngDevMode === "undefined" \|\| ngDevMode\) && i0.ɵsetClassDebugInfo\(.*, \{ className: ".*", filePath: ".*", lineNumber: [0-9]* }\); }\)\(\);/, "");
|
|
623
|
+
}
|
|
441
624
|
#debug(...msg) {
|
|
442
625
|
this.#logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
|
|
443
626
|
}
|