@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,33 +1,37 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { FsUtils, PathUtils, SdLogger, TNormPath } 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 { AngularCompilerHost } from "@angular/build/src/tools/angular/angular-host";
|
|
7
7
|
import { replaceBootstrap } from "@angular/build/src/tools/angular/transformers/jit-bootstrap-transformer";
|
|
8
8
|
import { SdCliPerformanceTimer } from "../utils/sd-cli-performance-time";
|
|
9
9
|
import { SdCliConvertMessageUtils } from "../utils/sd-cli-convert-message.utils";
|
|
10
|
-
import { ISdTsCompilerResult, SdTsCompilerOptions
|
|
10
|
+
import { ISdTsCompilerResult, SdTsCompilerOptions } from "../types/ts-compiler.types";
|
|
11
11
|
import { createWorkerTransformer } from "@angular/build/src/tools/angular/transformers/web-worker-transformer";
|
|
12
|
-
import {
|
|
12
|
+
import { SdDependencyCache } from "./sd-dependency-cache";
|
|
13
13
|
import { SdDependencyAnalyzer } from "./sd-dependency-analyzer";
|
|
14
14
|
import { FlatESLint } from "eslint/use-at-your-own-risk";
|
|
15
|
-
import {
|
|
16
|
-
import { transformSupportedBrowsersToTargets } from "@angular/build/src/tools/esbuild/utils";
|
|
17
|
-
import browserslist from "browserslist";
|
|
15
|
+
import { SdStyleBundler } from "./sd-style-bundler";
|
|
18
16
|
|
|
19
17
|
export class SdTsCompiler {
|
|
20
18
|
#logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
|
|
21
19
|
|
|
22
20
|
#isForAngular: boolean;
|
|
23
21
|
|
|
24
|
-
#
|
|
22
|
+
#styleBundler: SdStyleBundler | undefined;
|
|
25
23
|
|
|
26
24
|
#ngProgram: NgtscProgram | undefined;
|
|
27
25
|
#program: ts.Program | undefined;
|
|
28
26
|
|
|
29
27
|
// 빌드정보 캐싱
|
|
30
|
-
#
|
|
28
|
+
#cache = {
|
|
29
|
+
dep: new SdDependencyCache(),
|
|
30
|
+
type: new WeakMap<ts.Node, ts.Type | undefined>(),
|
|
31
|
+
prop: new WeakMap<ts.Type, Map<string, ts.Symbol | undefined>>(),
|
|
32
|
+
declFiles: new WeakMap<ts.Symbol, TNormPath[]>(),
|
|
33
|
+
ngOrg: new Map<TNormPath, ts.SourceFile>(),
|
|
34
|
+
};
|
|
31
35
|
#sourceFileCacheMap = new Map<TNormPath, ts.SourceFile>();
|
|
32
36
|
#emittedFilesCacheMap = new Map<
|
|
33
37
|
TNormPath,
|
|
@@ -37,9 +41,6 @@ export class SdTsCompiler {
|
|
|
37
41
|
}[]
|
|
38
42
|
>();
|
|
39
43
|
|
|
40
|
-
// 빌드결과 캐싱
|
|
41
|
-
#stylesheetBundlingResultMap = new Map<TNormPath, TStylesheetBundlingResult>();
|
|
42
|
-
|
|
43
44
|
#perf!: SdCliPerformanceTimer;
|
|
44
45
|
|
|
45
46
|
constructor(private readonly _opt: SdTsCompilerOptions) {
|
|
@@ -49,32 +50,13 @@ export class SdTsCompiler {
|
|
|
49
50
|
const tsconfig = FsUtils.readJson(tsconfigPath);
|
|
50
51
|
this.#isForAngular = Boolean(tsconfig.angularCompilerOptions);
|
|
51
52
|
|
|
52
|
-
this
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
outputNames: { bundles: "[name]", media: "media/[name]" },
|
|
60
|
-
includePaths: [],
|
|
61
|
-
// sass:
|
|
62
|
-
externalDependencies: [],
|
|
63
|
-
target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
|
|
64
|
-
tailwindConfiguration: undefined,
|
|
65
|
-
/*postcssConfiguration: {
|
|
66
|
-
plugins: [["css-has-pseudo"]],
|
|
67
|
-
},*/
|
|
68
|
-
// publicPath:
|
|
69
|
-
cacheOptions: {
|
|
70
|
-
enabled: true,
|
|
71
|
-
path: ".cache/angular",
|
|
72
|
-
basePath: ".cache",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
"scss",
|
|
76
|
-
this._opt.isDevMode,
|
|
77
|
-
);
|
|
53
|
+
if (!this._opt.isNoEmit) {
|
|
54
|
+
this.#styleBundler = new SdStyleBundler(
|
|
55
|
+
this._opt.pkgPath,
|
|
56
|
+
this._opt.isDevMode,
|
|
57
|
+
this._opt.watchScopePathSet,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
78
60
|
}
|
|
79
61
|
|
|
80
62
|
#parseTsConfig() {
|
|
@@ -83,9 +65,38 @@ export class SdTsCompiler {
|
|
|
83
65
|
const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._opt.pkgPath, {
|
|
84
66
|
...tsconfig.angularCompilerOptions,
|
|
85
67
|
...this._opt.additionalOptions,
|
|
68
|
+
...(this._opt.isEmitOnly
|
|
69
|
+
? {
|
|
70
|
+
// typescript
|
|
71
|
+
noEmitOnError: false,
|
|
72
|
+
strict: false,
|
|
73
|
+
noImplicitAny: false,
|
|
74
|
+
noImplicitThis: false,
|
|
75
|
+
strictNullChecks: false,
|
|
76
|
+
strictFunctionTypes: false,
|
|
77
|
+
strictBindCallApply: false,
|
|
78
|
+
strictPropertyInitialization: false,
|
|
79
|
+
useUnknownInCatchVariables: false,
|
|
80
|
+
exactOptionalPropertyTypes: false,
|
|
81
|
+
noUncheckedIndexedAccess: false,
|
|
82
|
+
noUnusedLocals: false,
|
|
83
|
+
noUnusedParameters: false,
|
|
84
|
+
skipLibCheck: true,
|
|
85
|
+
checkJs: false,
|
|
86
|
+
alwaysStrict: false,
|
|
87
|
+
|
|
88
|
+
// angular
|
|
89
|
+
strictTemplates: false,
|
|
90
|
+
strictInjectionParameters: false,
|
|
91
|
+
strictInputAccessModifiers: false,
|
|
92
|
+
strictStandalone: false,
|
|
93
|
+
}
|
|
94
|
+
: {}),
|
|
86
95
|
});
|
|
87
96
|
|
|
88
|
-
const distPath = PathUtils.norm(
|
|
97
|
+
const distPath = PathUtils.norm(
|
|
98
|
+
parsedTsconfig.options.outDir ?? path.resolve(this._opt.pkgPath, "dist"),
|
|
99
|
+
);
|
|
89
100
|
|
|
90
101
|
return {
|
|
91
102
|
fileNames: parsedTsconfig.fileNames,
|
|
@@ -141,28 +152,47 @@ export class SdTsCompiler {
|
|
|
141
152
|
return compilerHost.readFile(fileName) ?? "";
|
|
142
153
|
};
|
|
143
154
|
|
|
144
|
-
(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
if (!this._opt.isNoEmit) {
|
|
156
|
+
(compilerHost as AngularCompilerHost).transformResource = async (
|
|
157
|
+
data: string,
|
|
158
|
+
context: {
|
|
159
|
+
type: string;
|
|
160
|
+
containingFile: string;
|
|
161
|
+
resourceFile: string | null;
|
|
162
|
+
},
|
|
163
|
+
) => {
|
|
164
|
+
if (context.type !== "style") {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
155
167
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
const styleBundleResult = await this.#styleBundler!.bundleAsync(
|
|
169
|
+
data,
|
|
170
|
+
PathUtils.norm(context.containingFile),
|
|
171
|
+
context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined,
|
|
172
|
+
);
|
|
161
173
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
if (!styleBundleResult.cached && !StringUtils.isNullOrEmpty(styleBundleResult.contents)) {
|
|
175
|
+
const relPath = path.relative(
|
|
176
|
+
path.resolve(this._opt.pkgPath, "src"),
|
|
177
|
+
context.containingFile,
|
|
178
|
+
);
|
|
179
|
+
const outAbsPath = PathUtils.norm(
|
|
180
|
+
compilerOptions.outDir!,
|
|
181
|
+
relPath.replace(/\.ts$/, ".css"),
|
|
182
|
+
);
|
|
183
|
+
const cache = this.#emittedFilesCacheMap.getOrCreate(
|
|
184
|
+
PathUtils.norm(context.containingFile),
|
|
185
|
+
[],
|
|
186
|
+
);
|
|
187
|
+
cache.remove((item) => item.outAbsPath === outAbsPath);
|
|
188
|
+
cache.push({ outAbsPath, text: styleBundleResult.contents });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return StringUtils.isNullOrEmpty(styleBundleResult.contents)
|
|
192
|
+
? null
|
|
193
|
+
: { content: "" /*styleBundleResult.contents*/ };
|
|
194
|
+
};
|
|
195
|
+
}
|
|
166
196
|
|
|
167
197
|
(compilerHost as AngularCompilerHost).getModifiedResourceFiles = () => {
|
|
168
198
|
return new Set(Array.from(modifiedFileSet).map((item) => PathUtils.posix(item)));
|
|
@@ -172,100 +202,24 @@ export class SdTsCompiler {
|
|
|
172
202
|
return compilerHost;
|
|
173
203
|
}
|
|
174
204
|
|
|
175
|
-
// private async _getOrCreateStyleBundleWorkerAsync() {
|
|
176
|
-
// if (this._stylesheetBundlingWorker) {
|
|
177
|
-
// return this._stylesheetBundlingWorker;
|
|
178
|
-
// }
|
|
179
|
-
//
|
|
180
|
-
// this._stylesheetBundlingWorker = new SdWorker<TStyleBundlerWorkerType>(
|
|
181
|
-
// import.meta.resolve("../workers/style-bundler.worker"),
|
|
182
|
-
// );
|
|
183
|
-
//
|
|
184
|
-
// await this._stylesheetBundlingWorker.run(
|
|
185
|
-
// "prepare",
|
|
186
|
-
// [this._opt.pkgPath, this._opt.isDevMode],
|
|
187
|
-
// );
|
|
188
|
-
//
|
|
189
|
-
// return this._stylesheetBundlingWorker;
|
|
190
|
-
// }
|
|
191
|
-
|
|
192
|
-
async #bundleStylesheetAsync(
|
|
193
|
-
data: string,
|
|
194
|
-
containingFile: TNormPath,
|
|
195
|
-
resourceFile: TNormPath | null = null,
|
|
196
|
-
): Promise<TStylesheetBundlingResult> {
|
|
197
|
-
// containingFile: 포함된 파일 (.ts)
|
|
198
|
-
// resourceFile: 외부 리소스 파일 (styleUrls로 입력하지 않고 styles에 직접 입력한 경우 null)
|
|
199
|
-
// referencedFiles: import한 외부 scss 파일 혹은 woff파일등 외부 파일
|
|
200
|
-
|
|
201
|
-
// this.#debug(`bundle stylesheet...(${containingFile}, ${resourceFile})`);
|
|
202
|
-
|
|
203
|
-
return await this.#perf.run("스타일 번들링", async () => {
|
|
204
|
-
const fileNPath = PathUtils.norm(resourceFile ?? containingFile);
|
|
205
|
-
if (this.#stylesheetBundlingResultMap.has(fileNPath)) {
|
|
206
|
-
return this.#stylesheetBundlingResultMap.get(fileNPath)!;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
// const result = await new SdSassEmbeddedBundler(this._opt.isDevMode)
|
|
211
|
-
// .bundleAsync(data, resourceFile ?? containingFile);
|
|
212
|
-
// const worker = this._stylesheetBundlingWorker!;
|
|
213
|
-
// const result = await worker.run("bundle", [data, containingFile, resourceFile]);
|
|
214
|
-
|
|
215
|
-
const result =
|
|
216
|
-
resourceFile != null
|
|
217
|
-
? await this.#stylesheetBundler.bundleFile(resourceFile)
|
|
218
|
-
: await this.#stylesheetBundler.bundleInline(data, containingFile, "scss");
|
|
219
|
-
|
|
220
|
-
for (const referencedFile of result.referencedFiles ?? []) {
|
|
221
|
-
// for (const referencedFile of result.referencedFiles) {
|
|
222
|
-
// 참조하는 파일과 참조된 파일 사이의 의존성 관계 추가
|
|
223
|
-
this.#depCache.addImport(fileNPath, PathUtils.norm(referencedFile), 0);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
this.#stylesheetBundlingResultMap.set(fileNPath, result);
|
|
227
|
-
|
|
228
|
-
// this._stylesheetBundlingResultMap.set(fileNPath, {
|
|
229
|
-
// errors: undefined,
|
|
230
|
-
// warnings: [],
|
|
231
|
-
// ...result,
|
|
232
|
-
// });
|
|
233
|
-
//
|
|
234
|
-
// return {
|
|
235
|
-
// errors: undefined,
|
|
236
|
-
// warnings: [],
|
|
237
|
-
// ...result,
|
|
238
|
-
// };
|
|
239
|
-
return result;
|
|
240
|
-
} catch (err) {
|
|
241
|
-
const result = {
|
|
242
|
-
errors: [
|
|
243
|
-
{
|
|
244
|
-
text: `스타일 번들링 실패: ${err.message ?? "알 수 없는 오류"}`,
|
|
245
|
-
location: { file: containingFile },
|
|
246
|
-
},
|
|
247
|
-
],
|
|
248
|
-
warnings: [],
|
|
249
|
-
};
|
|
250
|
-
this.#stylesheetBundlingResultMap.set(fileNPath, result);
|
|
251
|
-
return result;
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
205
|
async compileAsync(modifiedFileSet: Set<TNormPath>): Promise<ISdTsCompilerResult> {
|
|
257
206
|
this.#perf = new SdCliPerformanceTimer("esbuild compile");
|
|
258
207
|
|
|
259
208
|
const prepareResult = await this.#prepareAsync(modifiedFileSet);
|
|
260
209
|
|
|
261
210
|
const [globalStyleSheet, buildResult, lintResults] = await Promise.all([
|
|
262
|
-
this.#buildGlobalStyleAsync(),
|
|
211
|
+
this._opt.isNoEmit ? undefined : this.#buildGlobalStyleAsync(),
|
|
263
212
|
this.#build(prepareResult),
|
|
264
|
-
this.#lintAsync(prepareResult),
|
|
213
|
+
this._opt.isEmitOnly ? [] : this.#lintAsync(prepareResult),
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const affectedFileSet = new Set([
|
|
217
|
+
...prepareResult.affectedFileSet,
|
|
218
|
+
...prepareResult.styleAffectedFileSet,
|
|
265
219
|
]);
|
|
266
220
|
|
|
267
221
|
this.#debug(`빌드 완료됨`, this.#perf.toString());
|
|
268
|
-
this.#debug(`영향 받은 파일: ${
|
|
222
|
+
this.#debug(`영향 받은 파일: ${affectedFileSet.size}개`);
|
|
269
223
|
this.#debug(`감시 중인 파일: ${prepareResult.watchFileSet.size}개`);
|
|
270
224
|
|
|
271
225
|
return {
|
|
@@ -273,9 +227,9 @@ export class SdTsCompiler {
|
|
|
273
227
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromTsDiag(buildResult.diagnostics),
|
|
274
228
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromEslint(lintResults),
|
|
275
229
|
],
|
|
276
|
-
affectedFileSet:
|
|
230
|
+
affectedFileSet: affectedFileSet,
|
|
277
231
|
watchFileSet: prepareResult.watchFileSet,
|
|
278
|
-
stylesheetBundlingResultMap: this.#
|
|
232
|
+
stylesheetBundlingResultMap: this.#styleBundler?.getResultCache() ?? new Map(),
|
|
279
233
|
emittedFilesCacheMap: this.#emittedFilesCacheMap,
|
|
280
234
|
emitFileSet: new Set([...buildResult.emitFileSet, globalStyleSheet].filterExists()),
|
|
281
235
|
};
|
|
@@ -288,57 +242,46 @@ export class SdTsCompiler {
|
|
|
288
242
|
|
|
289
243
|
if (modifiedFileSet.size !== 0) {
|
|
290
244
|
this.#debug(`캐시 무효화 및 초기화 중...`);
|
|
291
|
-
await Wait.time(100);
|
|
292
245
|
|
|
293
246
|
// this._perf.run("캐시 무효화 및 초기화", () => {
|
|
294
247
|
this.#perf.run("캐시 무효화 및 초기화", () => {
|
|
295
|
-
//
|
|
296
|
-
const
|
|
297
|
-
|
|
248
|
+
// 소스파일은 변경된 파일들로 무효화
|
|
249
|
+
for (const modifiedFile of modifiedFileSet) {
|
|
250
|
+
this.#sourceFileCacheMap.delete(modifiedFile);
|
|
251
|
+
}
|
|
298
252
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
253
|
+
// 스타일 번들러 무효화 (transformResource 재실행 땜에 필요할듯)
|
|
254
|
+
if (this.#styleBundler) {
|
|
255
|
+
const styleAffectedFileSet = this.#styleBundler.invalidate(modifiedFileSet);
|
|
256
|
+
// 스타일 변경된 파일들로 소스파일 무효화
|
|
257
|
+
for (const styleAffectedFile of styleAffectedFileSet) {
|
|
258
|
+
this.#sourceFileCacheMap.delete(styleAffectedFile);
|
|
303
259
|
}
|
|
260
|
+
}
|
|
304
261
|
|
|
305
|
-
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const affectedFileTree = this.#depCache.getAffectedFileTree(modifiedFileSet);
|
|
309
|
-
this.#debug(`영향받은 기존파일: ${affectedFileTree.map((item) => getTreeText(item)).join("\n")}`.trim());
|
|
310
|
-
|
|
311
|
-
// 스타일 번들러에서 영향받은 파일 관련 항목 무효화 (.scss만 해당)
|
|
312
|
-
this.#stylesheetBundler.invalidate(
|
|
313
|
-
new Set([...modifiedFileSet, ...affectedFiles.filter((item) => item.endsWith(".scss"))]),
|
|
314
|
-
);
|
|
315
|
-
// await worker.run("invalidate", [affectedFileSet]);
|
|
316
|
-
|
|
317
|
-
// 소스파일은 변경된 파일들로 무효화 (스타일 변화시 타고들어가야됨)
|
|
262
|
+
// angular origin 파일 매핑은 변경된 파일들로 무효화
|
|
318
263
|
for (const modifiedFile of modifiedFileSet) {
|
|
319
|
-
|
|
320
|
-
for (const fileNPath of affectedFileMap.get(modifiedFile) ?? []) {
|
|
321
|
-
this.#sourceFileCacheMap.delete(fileNPath);
|
|
322
|
-
}
|
|
323
|
-
} else {
|
|
324
|
-
this.#sourceFileCacheMap.delete(modifiedFile);
|
|
325
|
-
}
|
|
264
|
+
this.#cache.ngOrg.delete(modifiedFile);
|
|
326
265
|
}
|
|
327
266
|
|
|
267
|
+
// 기존 의존성에 의해 영향받는 파일들 계산
|
|
268
|
+
const affectedFileMap = this.#cache.dep.getAffectedFileMap(modifiedFileSet);
|
|
269
|
+
const affectedFileSet = new Set(
|
|
270
|
+
Array.from(affectedFileMap.values()).mapMany((item) => Array.from(item)),
|
|
271
|
+
);
|
|
272
|
+
|
|
328
273
|
// 의존성 캐시에서 영향받은 파일 관련 항목 무효화 (Affected더라도 SF는 동일하므로, modifiedFileSet만 넣어도될듯?)
|
|
329
274
|
// 250715: sourceFile 타입체크를 다시 해야해서 affected로 넣는게 맞는듯.
|
|
330
|
-
this.#
|
|
275
|
+
this.#cache.dep.invalidates(affectedFileSet);
|
|
331
276
|
|
|
332
|
-
//
|
|
333
|
-
for (const
|
|
334
|
-
this.#emittedFilesCacheMap.delete(
|
|
335
|
-
this.#stylesheetBundlingResultMap.delete(affectedFile);
|
|
277
|
+
// 결과물이 바뀌어야 하는 캐시 모두 무효화 (modified만 다시쓰면될듯..)
|
|
278
|
+
for (const modifiedFile of modifiedFileSet) {
|
|
279
|
+
this.#emittedFilesCacheMap.delete(modifiedFile);
|
|
336
280
|
}
|
|
337
281
|
});
|
|
338
282
|
}
|
|
339
283
|
|
|
340
284
|
this.#debug(`ts.Program 생성 중...`);
|
|
341
|
-
await Wait.time(300);
|
|
342
285
|
|
|
343
286
|
const compilerHost = this.#perf.run("ts.CompilerHost 생성", () => {
|
|
344
287
|
return this.#createCompilerHost(tsconfig.options, modifiedFileSet);
|
|
@@ -346,14 +289,23 @@ export class SdTsCompiler {
|
|
|
346
289
|
|
|
347
290
|
this.#perf.run("ts.Program 생성", () => {
|
|
348
291
|
if (this.#isForAngular) {
|
|
349
|
-
this.#ngProgram = new NgtscProgram(
|
|
292
|
+
this.#ngProgram = new NgtscProgram(
|
|
293
|
+
tsconfig.fileNames,
|
|
294
|
+
tsconfig.options,
|
|
295
|
+
compilerHost,
|
|
296
|
+
this.#ngProgram,
|
|
297
|
+
);
|
|
350
298
|
this.#program = this.#ngProgram.getTsProgram();
|
|
351
299
|
} else {
|
|
352
|
-
this.#program = ts.createProgram(
|
|
300
|
+
this.#program = ts.createProgram(
|
|
301
|
+
tsconfig.fileNames,
|
|
302
|
+
tsconfig.options,
|
|
303
|
+
compilerHost,
|
|
304
|
+
this.#program,
|
|
305
|
+
);
|
|
353
306
|
}
|
|
354
307
|
});
|
|
355
308
|
this.#debug(`ts.Program 생성`);
|
|
356
|
-
await Wait.time(300);
|
|
357
309
|
|
|
358
310
|
if (this.#ngProgram) {
|
|
359
311
|
await this.#perf.run("Angular 템플릿 분석", async () => {
|
|
@@ -361,30 +313,54 @@ export class SdTsCompiler {
|
|
|
361
313
|
});
|
|
362
314
|
}
|
|
363
315
|
|
|
364
|
-
this
|
|
316
|
+
if (!this._opt.isEmitOnly) {
|
|
317
|
+
this.#debug(`새 의존성 분석 중...`);
|
|
365
318
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
319
|
+
this.#perf.run("새 의존성 분석", () => {
|
|
320
|
+
// SdTsDependencyAnalyzer를 통해 의존성 분석 및 SdDepCache 업데이트
|
|
321
|
+
SdDependencyAnalyzer.analyze(
|
|
322
|
+
this.#program!,
|
|
323
|
+
compilerHost,
|
|
324
|
+
this._opt.watchScopePathSet,
|
|
325
|
+
this.#cache,
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
this.#debug(`새 의존성 분석(Angular) 중...`);
|
|
369
330
|
|
|
370
331
|
// Angular 리소스 의존성 추가
|
|
371
332
|
if (this.#ngProgram) {
|
|
372
|
-
|
|
333
|
+
this.#perf.run("새 의존성 분석(Angular)", () => {
|
|
334
|
+
SdDependencyAnalyzer.analyzeAngularResources(
|
|
335
|
+
this.#ngProgram!,
|
|
336
|
+
this._opt.watchScopePathSet,
|
|
337
|
+
this.#cache.dep,
|
|
338
|
+
);
|
|
339
|
+
});
|
|
373
340
|
}
|
|
374
|
-
}
|
|
341
|
+
}
|
|
375
342
|
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
343
|
+
const allFiles = this.#program!.getSourceFiles().map((item) => PathUtils.norm(item.fileName));
|
|
344
|
+
const watchFileSet = new Set(
|
|
345
|
+
allFiles.filter((item) => this._opt.watchScopePathSet.inScope(item)),
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
let affectedFileSet: Set<TNormPath>;
|
|
349
|
+
if (modifiedFileSet.size === 0) {
|
|
350
|
+
affectedFileSet = new Set(allFiles);
|
|
351
|
+
} else {
|
|
352
|
+
const affectedFileMap = this.#cache.dep.getAffectedFileMap(modifiedFileSet);
|
|
353
|
+
this.#debug("영향받은 파일:", affectedFileMap);
|
|
354
|
+
affectedFileSet = new Set(
|
|
355
|
+
Array.from(affectedFileMap.values()).mapMany((item) => Array.from(item)),
|
|
356
|
+
);
|
|
357
|
+
}
|
|
383
358
|
|
|
384
359
|
return {
|
|
385
360
|
tsconfig,
|
|
386
361
|
compilerHost,
|
|
387
362
|
affectedFileSet,
|
|
363
|
+
styleAffectedFileSet: this.#styleBundler?.getAffectedFileSet(affectedFileSet) ?? new Set(),
|
|
388
364
|
watchFileSet,
|
|
389
365
|
};
|
|
390
366
|
}
|
|
@@ -425,12 +401,15 @@ export class SdTsCompiler {
|
|
|
425
401
|
|
|
426
402
|
await this.#perf.run("전역 스타일 번들링", async () => {
|
|
427
403
|
const data = await FsUtils.readFileAsync(this._opt.globalStyleFilePath!);
|
|
428
|
-
const stylesheetBundlingResult = await this.#
|
|
404
|
+
const stylesheetBundlingResult = await this.#styleBundler!.bundleAsync(
|
|
429
405
|
data,
|
|
430
406
|
this._opt.globalStyleFilePath!,
|
|
431
407
|
this._opt.globalStyleFilePath,
|
|
432
408
|
);
|
|
433
|
-
const emitFileInfos = this.#emittedFilesCacheMap.getOrCreate(
|
|
409
|
+
const emitFileInfos = this.#emittedFilesCacheMap.getOrCreate(
|
|
410
|
+
this._opt.globalStyleFilePath!,
|
|
411
|
+
[],
|
|
412
|
+
);
|
|
434
413
|
emitFileInfos.push({
|
|
435
414
|
outAbsPath: PathUtils.norm(
|
|
436
415
|
this._opt.pkgPath,
|
|
@@ -452,132 +431,408 @@ export class SdTsCompiler {
|
|
|
452
431
|
const emitFileSet = new Set<TNormPath>();
|
|
453
432
|
const diagnostics: ts.Diagnostic[] = [];
|
|
454
433
|
|
|
455
|
-
this
|
|
456
|
-
|
|
457
|
-
this.#perf.run("프로그램 진단 수집", () => {
|
|
458
|
-
diagnostics.push(
|
|
459
|
-
...this.#program!.getConfigFileParsingDiagnostics(),
|
|
460
|
-
...this.#program!.getOptionsDiagnostics(),
|
|
461
|
-
...this.#program!.getGlobalDiagnostics(),
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
if (this.#ngProgram) {
|
|
465
|
-
diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
|
|
466
|
-
}
|
|
467
|
-
});
|
|
434
|
+
if (!this._opt.isEmitOnly) {
|
|
435
|
+
this.#debug(`프로그램 진단 수집 중...`);
|
|
468
436
|
|
|
469
|
-
|
|
437
|
+
this.#perf.run("프로그램 진단 수집", () => {
|
|
438
|
+
diagnostics.push(
|
|
439
|
+
...this.#program!.getConfigFileParsingDiagnostics(),
|
|
440
|
+
...this.#program!.getOptionsDiagnostics(),
|
|
441
|
+
...this.#program!.getGlobalDiagnostics(),
|
|
442
|
+
);
|
|
470
443
|
|
|
471
|
-
|
|
472
|
-
|
|
444
|
+
if (this.#ngProgram) {
|
|
445
|
+
diagnostics.push(...this.#ngProgram.compiler.getOptionDiagnostics());
|
|
446
|
+
}
|
|
447
|
+
});
|
|
473
448
|
|
|
474
|
-
|
|
475
|
-
if (
|
|
476
|
-
!affectedSourceFile ||
|
|
477
|
-
(this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))
|
|
478
|
-
) {
|
|
479
|
-
continue;
|
|
480
|
-
}
|
|
449
|
+
this.#debug(`개별 파일 진단 수집 중...`);
|
|
481
450
|
|
|
482
|
-
|
|
451
|
+
for (const affectedFile of prepareResult.affectedFileSet) {
|
|
452
|
+
if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath)) continue;
|
|
483
453
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
)
|
|
489
|
-
|
|
454
|
+
const affectedSourceFile = this.#program!.getSourceFile(affectedFile);
|
|
455
|
+
if (
|
|
456
|
+
!affectedSourceFile ||
|
|
457
|
+
(this.#ngProgram && this.#ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))
|
|
458
|
+
) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
490
461
|
|
|
491
|
-
|
|
492
|
-
this.#perf.run("개별 파일 진단 수집(Angular)", () => {
|
|
493
|
-
if (affectedSourceFile.isDeclarationFile) return;
|
|
462
|
+
// this.#debug(`get diagnostics of file ${affectedFile}...`);
|
|
494
463
|
|
|
464
|
+
this.#perf.run("개별 파일 진단 수집", () => {
|
|
495
465
|
diagnostics.push(
|
|
496
|
-
...this.#
|
|
466
|
+
...this.#program!.getSyntacticDiagnostics(affectedSourceFile),
|
|
467
|
+
...this.#program!.getSemanticDiagnostics(affectedSourceFile),
|
|
497
468
|
);
|
|
498
469
|
});
|
|
470
|
+
|
|
471
|
+
if (this.#ngProgram) {
|
|
472
|
+
this.#perf.run("개별 파일 진단 수집(Angular)", () => {
|
|
473
|
+
if (affectedSourceFile.isDeclarationFile) return;
|
|
474
|
+
|
|
475
|
+
diagnostics.push(
|
|
476
|
+
...this.#ngProgram!.compiler.getDiagnosticsForFile(
|
|
477
|
+
affectedSourceFile,
|
|
478
|
+
OptimizeFor.WholeProgram,
|
|
479
|
+
),
|
|
480
|
+
);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
499
483
|
}
|
|
500
484
|
}
|
|
501
485
|
|
|
502
|
-
this
|
|
503
|
-
this.#
|
|
486
|
+
if (!this._opt.isNoEmit) {
|
|
487
|
+
this.#perf.run("파일 출력 (emit)", () => {
|
|
488
|
+
this.#debug(`파일 출력 준비 중...`);
|
|
504
489
|
|
|
505
|
-
|
|
490
|
+
let transformers: ts.CustomTransformers = {};
|
|
506
491
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
(transformers.before ??= []).push(
|
|
515
|
-
createWorkerTransformer((file, importer) => {
|
|
516
|
-
const fullPath = path.resolve(path.dirname(importer), file);
|
|
517
|
-
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
|
|
518
|
-
return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
|
|
519
|
-
}),
|
|
520
|
-
);
|
|
521
|
-
}
|
|
492
|
+
if (this.#ngProgram) {
|
|
493
|
+
const angularTransfomers = this.#ngProgram.compiler.prepareEmit().transformers;
|
|
494
|
+
(transformers.before ??= []).push(...(angularTransfomers.before ?? []));
|
|
495
|
+
(transformers.after ??= []).push(...(angularTransfomers.after ?? []));
|
|
496
|
+
(transformers.afterDeclarations ??= []).push(
|
|
497
|
+
...(angularTransfomers.afterDeclarations ?? []),
|
|
498
|
+
);
|
|
522
499
|
|
|
523
|
-
|
|
500
|
+
(transformers.before ??= []).push(
|
|
501
|
+
replaceBootstrap(() => this.#program!.getTypeChecker()),
|
|
502
|
+
);
|
|
503
|
+
(transformers.before ??= []).push(
|
|
504
|
+
createWorkerTransformer((file, importer) => {
|
|
505
|
+
const fullPath = path.resolve(path.dirname(importer), file);
|
|
506
|
+
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
|
|
507
|
+
return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
|
|
508
|
+
}),
|
|
509
|
+
);
|
|
524
510
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
|
|
528
|
-
for (const affectedFile of prepareResult.affectedFileSet) {
|
|
529
|
-
if (this.#emittedFilesCacheMap.has(affectedFile)) continue;
|
|
511
|
+
(transformers.before ??= []).push(this.#createExternalizeComponentStylesTransformer());
|
|
512
|
+
}
|
|
530
513
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
514
|
+
this.#debug(`파일 출력 중...`);
|
|
515
|
+
|
|
516
|
+
// affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
|
|
517
|
+
// 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
|
|
518
|
+
// 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
|
|
519
|
+
for (const affectedFile of [
|
|
520
|
+
...prepareResult.affectedFileSet /*,
|
|
521
|
+
...prepareResult.styleAffectedFileSet,*/,
|
|
522
|
+
]) {
|
|
523
|
+
if (
|
|
524
|
+
this.#emittedFilesCacheMap
|
|
525
|
+
.get(affectedFile)
|
|
526
|
+
?.some((item) => !item.outAbsPath?.endsWith(".css"))
|
|
527
|
+
)
|
|
528
|
+
continue;
|
|
529
|
+
|
|
530
|
+
const sf = this.#program!.getSourceFile(affectedFile);
|
|
531
|
+
if (!sf || sf.isDeclarationFile) continue;
|
|
532
|
+
if (this.#ngProgram?.compiler.ignoreForEmit.has(sf)) continue;
|
|
533
|
+
if (this.#ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) continue;
|
|
534
|
+
|
|
535
|
+
// 번들이 아닌 외부패키지는 보통 emit안해도 됨
|
|
536
|
+
// but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
|
|
537
|
+
if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
535
540
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
541
|
+
this.#program!.emit(
|
|
542
|
+
sf,
|
|
543
|
+
(fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
|
|
544
|
+
if (!sourceFiles || sourceFiles.length === 0) {
|
|
545
|
+
prepareResult.compilerHost.writeFile(
|
|
546
|
+
fileName,
|
|
547
|
+
text,
|
|
548
|
+
writeByteOrderMark,
|
|
549
|
+
onError,
|
|
550
|
+
sourceFiles,
|
|
551
|
+
data,
|
|
552
|
+
);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
|
|
557
|
+
if (this.#ngProgram) {
|
|
558
|
+
if (this.#ngProgram.compiler.ignoreForEmit.has(sourceFile)) return;
|
|
559
|
+
this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const emitFileInfoCaches = this.#emittedFilesCacheMap.getOrCreate(
|
|
563
|
+
PathUtils.norm(sourceFile.fileName),
|
|
564
|
+
[],
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
|
|
568
|
+
const real = this.#convertOutputToReal(
|
|
569
|
+
fileName,
|
|
570
|
+
prepareResult.tsconfig.distPath,
|
|
571
|
+
text,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
emitFileInfoCaches.push({
|
|
575
|
+
outAbsPath: real.filePath,
|
|
576
|
+
text: this.#removeOutputDevModeLine(real.text),
|
|
577
|
+
});
|
|
578
|
+
} else {
|
|
579
|
+
emitFileInfoCaches.push({ text });
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
emitFileSet.add(PathUtils.norm(sourceFile.fileName));
|
|
583
|
+
},
|
|
584
|
+
undefined,
|
|
585
|
+
undefined,
|
|
586
|
+
transformers,
|
|
587
|
+
);
|
|
540
588
|
}
|
|
589
|
+
});
|
|
590
|
+
}
|
|
541
591
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
592
|
+
return {
|
|
593
|
+
emitFileSet,
|
|
594
|
+
diagnostics,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
#createExternalizeComponentStylesTransformer() {
|
|
599
|
+
const f = ts.factory;
|
|
600
|
+
|
|
601
|
+
/*function makeEnsureStyleFunc() {
|
|
602
|
+
// function __sdEnsureStyle(href) { ... }
|
|
603
|
+
const hrefParam = f.createParameterDeclaration(undefined, undefined, "href");
|
|
604
|
+
|
|
605
|
+
// const d = document;
|
|
606
|
+
const declD = f.createVariableStatement(
|
|
607
|
+
undefined,
|
|
608
|
+
f.createVariableDeclarationList(
|
|
609
|
+
[f.createVariableDeclaration("d", undefined, undefined, f.createIdentifier("document"))],
|
|
610
|
+
ts.NodeFlags.Const,
|
|
611
|
+
),
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// let link = d.querySelector(`link[data-sd-style="${href}"]`);
|
|
615
|
+
const tpl = f.createTemplateExpression(f.createTemplateHead('link[data-sd-style="'), [
|
|
616
|
+
f.createTemplateSpan(f.createIdentifier("href"), f.createTemplateTail('"]')),
|
|
617
|
+
]);
|
|
618
|
+
const declLink = f.createVariableStatement(
|
|
619
|
+
undefined,
|
|
620
|
+
f.createVariableDeclarationList(
|
|
621
|
+
[
|
|
622
|
+
f.createVariableDeclaration(
|
|
623
|
+
"link",
|
|
624
|
+
undefined,
|
|
625
|
+
undefined,
|
|
626
|
+
f.createCallExpression(
|
|
627
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "querySelector"),
|
|
628
|
+
undefined,
|
|
629
|
+
[tpl],
|
|
630
|
+
),
|
|
631
|
+
),
|
|
632
|
+
],
|
|
633
|
+
ts.NodeFlags.Let,
|
|
634
|
+
),
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
// if (link) return;
|
|
638
|
+
const ifReturn = f.createIfStatement(
|
|
639
|
+
f.createIdentifier("link"),
|
|
640
|
+
f.createBlock([f.createReturnStatement()], true),
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
// link = d.createElement('link');
|
|
644
|
+
const mkLink = f.createExpressionStatement(
|
|
645
|
+
f.createBinaryExpression(
|
|
646
|
+
f.createIdentifier("link"),
|
|
647
|
+
ts.SyntaxKind.EqualsToken,
|
|
648
|
+
f.createCallExpression(
|
|
649
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "createElement"),
|
|
650
|
+
undefined,
|
|
651
|
+
[f.createStringLiteral("link")],
|
|
652
|
+
),
|
|
653
|
+
),
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
// link.rel = 'stylesheet';
|
|
657
|
+
const setRel = f.createExpressionStatement(
|
|
658
|
+
f.createBinaryExpression(
|
|
659
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "rel"),
|
|
660
|
+
ts.SyntaxKind.EqualsToken,
|
|
661
|
+
f.createStringLiteral("stylesheet"),
|
|
662
|
+
),
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
// link.setAttribute('data-sd-style', href);
|
|
666
|
+
const setData = f.createExpressionStatement(
|
|
667
|
+
f.createCallExpression(
|
|
668
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "setAttribute"),
|
|
669
|
+
undefined,
|
|
670
|
+
[f.createStringLiteral("data-sd-style"), f.createIdentifier("href")],
|
|
671
|
+
),
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
// link.href = href;
|
|
675
|
+
const setHref = f.createExpressionStatement(
|
|
676
|
+
f.createBinaryExpression(
|
|
677
|
+
f.createPropertyAccessExpression(f.createIdentifier("link"), "href"),
|
|
678
|
+
ts.SyntaxKind.EqualsToken,
|
|
679
|
+
f.createIdentifier("href"),
|
|
680
|
+
),
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
// d.head.appendChild(link);
|
|
684
|
+
const append = f.createExpressionStatement(
|
|
685
|
+
f.createCallExpression(
|
|
686
|
+
f.createPropertyAccessExpression(
|
|
687
|
+
f.createPropertyAccessExpression(f.createIdentifier("d"), "head"),
|
|
688
|
+
"appendChild",
|
|
689
|
+
),
|
|
690
|
+
undefined,
|
|
691
|
+
[f.createIdentifier("link")],
|
|
692
|
+
),
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
const body = f.createBlock(
|
|
696
|
+
[declD, declLink, ifReturn, mkLink, setRel, setData, setHref, append],
|
|
697
|
+
true,
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
return f.createFunctionDeclaration(
|
|
701
|
+
undefined,
|
|
702
|
+
undefined,
|
|
703
|
+
f.createIdentifier("__sdEnsureStyle"),
|
|
704
|
+
undefined,
|
|
705
|
+
[hrefParam],
|
|
706
|
+
undefined,
|
|
707
|
+
body,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function makeEnsureCallInStatic(href: string) {
|
|
712
|
+
return f.createExpressionStatement(
|
|
713
|
+
f.createCallExpression(
|
|
714
|
+
f.createIdentifier("__sdEnsureStyle"),
|
|
715
|
+
undefined,
|
|
716
|
+
[f.createStringLiteral(href)], // 필요하면 devBust 인자도 추가 가능
|
|
717
|
+
),
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const upsertEnsureStyleHelper = (sf: ts.SourceFile): ts.SourceFile => {
|
|
722
|
+
// 이미 있으면 스킵
|
|
723
|
+
if (
|
|
724
|
+
sf.statements.some((s) => ts.isFunctionDeclaration(s) && s.name?.text === "__sdEnsureStyle")
|
|
725
|
+
) {
|
|
726
|
+
return sf;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const fn = makeEnsureStyleFunc();
|
|
730
|
+
|
|
731
|
+
const href = path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
732
|
+
const call = makeEnsureCallInStatic(href);
|
|
733
|
+
|
|
734
|
+
if (this._opt.isForBundle) {
|
|
735
|
+
return f.updateSourceFile(sf, [fn, call, ...sf.statements]);
|
|
736
|
+
} else {
|
|
737
|
+
const importTarget = "./" + path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
738
|
+
const importDecl = f.createImportDeclaration(
|
|
571
739
|
undefined,
|
|
572
740
|
undefined,
|
|
573
|
-
|
|
741
|
+
f.createStringLiteral(importTarget),
|
|
574
742
|
);
|
|
743
|
+
|
|
744
|
+
return f.updateSourceFile(sf, [fn, call, importDecl, ...sf.statements]);
|
|
575
745
|
}
|
|
576
|
-
}
|
|
746
|
+
};*/
|
|
577
747
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
748
|
+
function upsertEnsureStyleHelper(sf: ts.SourceFile): ts.SourceFile {
|
|
749
|
+
// 이미 있으면 스킵
|
|
750
|
+
if (
|
|
751
|
+
sf.statements.some((s) => ts.isFunctionDeclaration(s) && s.name?.text === "__sdEnsureStyle")
|
|
752
|
+
) {
|
|
753
|
+
return sf;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const importTarget = "./" + path.basename(sf.fileName).replace(/\.ts$/, ".css");
|
|
757
|
+
const importDecl = f.createImportDeclaration(
|
|
758
|
+
undefined, // decorators
|
|
759
|
+
undefined, // modifiers
|
|
760
|
+
f.createStringLiteral(importTarget),
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
return f.updateSourceFile(sf, [importDecl, ...sf.statements]);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const removeStyleProp = (node: ts.ClassDeclaration) => {
|
|
767
|
+
const allDecorators = ts.getDecorators(node);
|
|
768
|
+
if (!allDecorators || allDecorators.length === 0) return node;
|
|
769
|
+
|
|
770
|
+
const decoratorsUpdated = allDecorators.map((dec) => {
|
|
771
|
+
if (!ts.isCallExpression(dec.expression)) return dec;
|
|
772
|
+
const call = dec.expression;
|
|
773
|
+
if (!ts.isIdentifier(call.expression) || call.expression.text !== "Component") return dec;
|
|
774
|
+
if (call.arguments.length !== 1) return dec;
|
|
775
|
+
const arg = call.arguments[0];
|
|
776
|
+
if (!ts.isObjectLiteralExpression(arg)) return dec;
|
|
777
|
+
|
|
778
|
+
const filteredProps = arg.properties.filter((p) => {
|
|
779
|
+
if (!ts.isPropertyAssignment(p)) return true;
|
|
780
|
+
const name = p.name;
|
|
781
|
+
const key = ts.isIdentifier(name)
|
|
782
|
+
? name.text
|
|
783
|
+
: ts.isStringLiteralLike(name)
|
|
784
|
+
? name.text
|
|
785
|
+
: undefined;
|
|
786
|
+
return !(key === "styles" || key === "styleUrls");
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
const newArg = f.updateObjectLiteralExpression(arg, filteredProps);
|
|
790
|
+
const newCall = f.updateCallExpression(call, call.expression, call.typeArguments, [newArg]);
|
|
791
|
+
return f.updateDecorator(dec, newCall);
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
const existingModifiers = node.modifiers ?? [];
|
|
795
|
+
const modifiersWithoutOldDecos = existingModifiers.filter((m) => !ts.isDecorator(m));
|
|
796
|
+
const newModifiers: readonly ts.ModifierLike[] = [
|
|
797
|
+
...decoratorsUpdated,
|
|
798
|
+
...modifiersWithoutOldDecos,
|
|
799
|
+
];
|
|
800
|
+
|
|
801
|
+
const newNode = f.updateClassDeclaration(
|
|
802
|
+
node,
|
|
803
|
+
newModifiers,
|
|
804
|
+
node.name,
|
|
805
|
+
node.typeParameters,
|
|
806
|
+
node.heritageClauses,
|
|
807
|
+
node.members,
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
return f.updateClassDeclaration(
|
|
811
|
+
newNode,
|
|
812
|
+
newNode.modifiers,
|
|
813
|
+
newNode.name,
|
|
814
|
+
newNode.typeParameters,
|
|
815
|
+
newNode.heritageClauses,
|
|
816
|
+
newNode.members,
|
|
817
|
+
);
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
return (ctx: ts.TransformationContext) => {
|
|
821
|
+
return (sf: ts.SourceFile) => {
|
|
822
|
+
const has = this.#styleBundler!.getResultCache().get(PathUtils.norm(sf.fileName));
|
|
823
|
+
if (!has) return sf;
|
|
824
|
+
|
|
825
|
+
const realSf = upsertEnsureStyleHelper(sf);
|
|
826
|
+
|
|
827
|
+
function visitor(node: ts.Node): ts.Node {
|
|
828
|
+
if (ts.isClassDeclaration(node) && Boolean(ts.getDecorators(node)?.length)) {
|
|
829
|
+
return removeStyleProp(node);
|
|
830
|
+
}
|
|
831
|
+
return ts.visitEachChild(node, visitor, ctx);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return ts.visitNode(realSf, visitor) as ts.SourceFile;
|
|
835
|
+
};
|
|
581
836
|
};
|
|
582
837
|
}
|
|
583
838
|
|
|
@@ -601,6 +856,13 @@ export class SdTsCompiler {
|
|
|
601
856
|
return { filePath: realFilePath, text: realText };
|
|
602
857
|
}
|
|
603
858
|
|
|
859
|
+
#removeOutputDevModeLine(str: string) {
|
|
860
|
+
return str.replace(
|
|
861
|
+
/\(\(\) => \{ \(typeof ngDevMode === "undefined" \|\| ngDevMode\) && i0.ɵsetClassDebugInfo\(.*, \{ className: ".*", filePath: ".*", lineNumber: [0-9]* }\); }\)\(\);/,
|
|
862
|
+
"",
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
604
866
|
#debug(...msg: any[]): void {
|
|
605
867
|
this.#logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
|
|
606
868
|
}
|
|
@@ -616,5 +878,6 @@ interface IPrepareResult {
|
|
|
616
878
|
tsconfig: ITsConfigInfo;
|
|
617
879
|
compilerHost: ts.CompilerHost;
|
|
618
880
|
affectedFileSet: Set<TNormPath>;
|
|
881
|
+
styleAffectedFileSet: Set<TNormPath>;
|
|
619
882
|
watchFileSet: Set<TNormPath>;
|
|
620
883
|
}
|