@simplysm/sd-cli 12.8.18 → 12.8.21
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-cordova.d.ts +30 -0
- package/dist/entry/sd-cli-cordova.js +309 -249
- package/dist/entry/sd-cli-cordova.js.map +1 -1
- package/dist/entry/sd-cli-project.d.ts +1 -1
- package/dist/entry/sd-cli-project.js +8 -9
- package/dist/entry/sd-cli-project.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pkg-builders/client/sd-ng.bundler.d.ts +15 -1
- package/dist/pkg-builders/client/sd-ng.bundler.js +60 -70
- package/dist/pkg-builders/client/sd-ng.bundler.js.map +1 -1
- package/dist/pkg-builders/client/sd-ng.plugin-creator.js +49 -29
- package/dist/pkg-builders/client/sd-ng.plugin-creator.js.map +1 -1
- package/dist/pkg-builders/lib/sd-ts-lib.builder.js +7 -4
- package/dist/pkg-builders/lib/sd-ts-lib.builder.js.map +1 -1
- package/dist/ts-compiler/sd-ts-compiler.d.ts +24 -2
- package/dist/ts-compiler/sd-ts-compiler.js +267 -575
- package/dist/ts-compiler/sd-ts-compiler.js.map +1 -1
- package/dist/ts-compiler/sd-ts-dependency-analyzer.d.ts +10 -0
- package/dist/ts-compiler/sd-ts-dependency-analyzer.js +140 -0
- package/dist/ts-compiler/sd-ts-dependency-analyzer.js.map +1 -0
- package/dist/types/ts-compiler.types.d.ts +12 -7
- package/dist/types/worker.types.d.ts +13 -0
- package/dist/utils/sd-cli-performance-time.js +1 -1
- package/package.json +10 -12
- package/src/entry/sd-cli-cordova.ts +394 -281
- package/src/entry/sd-cli-project.ts +11 -23
- package/src/index.ts +1 -0
- package/src/pkg-builders/client/sd-ng.bundler.ts +70 -69
- package/src/pkg-builders/client/sd-ng.plugin-creator.ts +47 -27
- package/src/pkg-builders/lib/sd-ts-lib.builder.ts +14 -7
- package/src/ts-compiler/sd-ts-compiler.ts +334 -705
- package/src/ts-compiler/sd-ts-dependency-analyzer.ts +176 -0
- package/src/types/ts-compiler.types.ts +11 -6
- package/src/types/worker.types.ts +7 -6
- package/src/utils/sd-cli-performance-time.ts +1 -1
|
@@ -16,26 +16,24 @@ import { SdCliPerformanceTimer } from "../utils/sd-cli-performance-time";
|
|
|
16
16
|
import { SdCliConvertMessageUtils } from "../utils/sd-cli-convert-message.utils";
|
|
17
17
|
import {
|
|
18
18
|
ISdTsCompilerResult,
|
|
19
|
-
IStylesheetBundlingResult,
|
|
20
19
|
SdTsCompilerOptions,
|
|
20
|
+
TStylesheetBundlingResult,
|
|
21
21
|
} from "../types/ts-compiler.types";
|
|
22
22
|
import { ISdBuildMessage } from "../types/build.types";
|
|
23
23
|
import {
|
|
24
24
|
createWorkerTransformer,
|
|
25
25
|
} from "@angular/build/src/tools/angular/transformers/web-worker-transformer";
|
|
26
26
|
import { ESLint } from "eslint";
|
|
27
|
+
import { SdTsDependencyAnalyzer } from "./sd-ts-dependency-analyzer";
|
|
27
28
|
|
|
28
29
|
export class SdTsCompiler {
|
|
29
|
-
|
|
30
|
+
private _logger = SdLogger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
readonly #isForAngular: boolean;
|
|
32
|
+
private _isForAngular: boolean;
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
readonly #sourceFileCacheMap = new Map<TNormPath, ts.SourceFile>();
|
|
38
|
-
readonly #emittedFilesCacheMap = new Map<
|
|
34
|
+
private _revDepCacheMap = new Map<TNormPath, Set<TNormPath>>();
|
|
35
|
+
private _sourceFileCacheMap = new Map<TNormPath, ts.SourceFile>();
|
|
36
|
+
private _emittedFilesCacheMap = new Map<
|
|
39
37
|
TNormPath,
|
|
40
38
|
{
|
|
41
39
|
outAbsPath?: TNormPath;
|
|
@@ -43,73 +41,103 @@ export class SdTsCompiler {
|
|
|
43
41
|
}[]
|
|
44
42
|
>();
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
readonly #compilerHost: ts.CompilerHost | AngularCompilerHost;
|
|
44
|
+
private _stylesheetBundler: ComponentStylesheetBundler | undefined;
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
private _ngProgram: NgtscProgram | undefined;
|
|
47
|
+
private _program: ts.Program | undefined;
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
private _modifiedFileSet = new Set<TNormPath>();
|
|
50
|
+
private _affectedFileSet = new Set<TNormPath>();
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
private _watchFileSet = new Set<TNormPath>();
|
|
53
|
+
private _stylesheetBundlingResultMap = new Map<TNormPath, TStylesheetBundlingResult>();
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
readonly #distPath: TNormPath;
|
|
60
|
-
readonly #globalStyleFilePath?: TNormPath;
|
|
61
|
-
readonly #watchScopePaths: TNormPath[];
|
|
55
|
+
private _perf!: SdCliPerformanceTimer;
|
|
62
56
|
|
|
63
|
-
readonly
|
|
57
|
+
constructor(private readonly _opt: SdTsCompilerOptions) {
|
|
58
|
+
this._debug("초기화 중...");
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// #processWebWorker?: (workerFile: string, containingFile: string) => string;
|
|
70
|
-
|
|
71
|
-
constructor(opt: SdTsCompilerOptions) {
|
|
72
|
-
this.#pkgPath = opt.pkgPath;
|
|
73
|
-
this.#globalStyleFilePath = opt.globalStyleFilePath;
|
|
74
|
-
this.#isForBundle = opt.isForBundle;
|
|
75
|
-
this.#watchScopePaths = opt.watchScopePaths;
|
|
76
|
-
// this.#processWebWorker = opt.processWebWorker;
|
|
60
|
+
const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
|
|
61
|
+
const tsconfig = FsUtils.readJson(tsconfigPath);
|
|
62
|
+
this._isForAngular = Boolean(tsconfig.angularCompilerOptions);
|
|
77
63
|
|
|
78
|
-
this
|
|
64
|
+
if (this._isForAngular) {
|
|
65
|
+
//-- stylesheetBundler
|
|
66
|
+
this._stylesheetBundler = new ComponentStylesheetBundler(
|
|
67
|
+
{
|
|
68
|
+
workspaceRoot: this._opt.pkgPath,
|
|
69
|
+
optimization: !this._opt.isDevMode,
|
|
70
|
+
inlineFonts: true,
|
|
71
|
+
preserveSymlinks: false,
|
|
72
|
+
sourcemap: this._opt.isDevMode ? "inline" : false,
|
|
73
|
+
outputNames: { bundles: "[name]", media: "media/[name]" },
|
|
74
|
+
includePaths: [],
|
|
75
|
+
externalDependencies: [],
|
|
76
|
+
target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
|
|
77
|
+
postcssConfiguration: {
|
|
78
|
+
plugins: [["css-has-pseudo"]],
|
|
79
|
+
},
|
|
80
|
+
tailwindConfiguration: undefined,
|
|
81
|
+
cacheOptions: {
|
|
82
|
+
enabled: true,
|
|
83
|
+
path: ".cache/angular",
|
|
84
|
+
basePath: ".cache",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
"scss",
|
|
88
|
+
this._opt.isDevMode,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
79
92
|
|
|
80
|
-
|
|
93
|
+
private _parseTsConfig(): ITsConfigInfo {
|
|
94
|
+
const config = this._loadTsConfig();
|
|
95
|
+
const compilerHost = this._createCompilerHost(config.options);
|
|
96
|
+
return {
|
|
97
|
+
...config,
|
|
98
|
+
compilerHost,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
81
101
|
|
|
82
|
-
|
|
102
|
+
private _loadTsConfig() {
|
|
103
|
+
const tsconfigPath = path.resolve(this._opt.pkgPath, "tsconfig.json");
|
|
83
104
|
const tsconfig = FsUtils.readJson(tsconfigPath);
|
|
84
|
-
|
|
85
|
-
this.#parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, opt.pkgPath, {
|
|
105
|
+
const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, this._opt.pkgPath, {
|
|
86
106
|
...tsconfig.angularCompilerOptions,
|
|
87
|
-
...
|
|
107
|
+
...this._opt.additionalOptions,
|
|
88
108
|
});
|
|
89
109
|
|
|
90
|
-
|
|
91
|
-
|
|
110
|
+
const distPath = PathUtils.norm(parsedTsconfig.options.outDir ?? path.resolve(
|
|
111
|
+
this._opt.pkgPath,
|
|
92
112
|
"dist",
|
|
93
113
|
));
|
|
94
114
|
|
|
95
|
-
|
|
115
|
+
return {
|
|
116
|
+
fileNames: parsedTsconfig.fileNames,
|
|
117
|
+
options: parsedTsconfig.options,
|
|
118
|
+
distPath: distPath,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
96
121
|
|
|
97
|
-
|
|
122
|
+
private _createCompilerHost(compilerOptions: ts.CompilerOptions) {
|
|
123
|
+
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
98
124
|
|
|
99
|
-
const baseGetSourceFile =
|
|
100
|
-
|
|
125
|
+
const baseGetSourceFile = compilerHost.getSourceFile;
|
|
126
|
+
compilerHost.getSourceFile = (
|
|
101
127
|
fileName: string,
|
|
102
128
|
languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
103
129
|
onError?: ((message: string) => void) | undefined,
|
|
104
130
|
shouldCreateNewSourceFile?: boolean,
|
|
105
131
|
...args
|
|
106
132
|
): ts.SourceFile | undefined => {
|
|
107
|
-
|
|
108
|
-
|
|
133
|
+
const fileNPath = PathUtils.norm(fileName);
|
|
134
|
+
|
|
135
|
+
if (!shouldCreateNewSourceFile && this._sourceFileCacheMap.has(fileNPath)) {
|
|
136
|
+
return this._sourceFileCacheMap.get(fileNPath);
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
const sf: ts.SourceFile | undefined = baseGetSourceFile.call(
|
|
112
|
-
|
|
140
|
+
compilerHost,
|
|
113
141
|
fileName,
|
|
114
142
|
languageVersionOrOptions,
|
|
115
143
|
onError,
|
|
@@ -118,55 +146,27 @@ export class SdTsCompiler {
|
|
|
118
146
|
);
|
|
119
147
|
|
|
120
148
|
if (sf) {
|
|
121
|
-
this
|
|
149
|
+
this._sourceFileCacheMap.set(fileNPath, sf);
|
|
122
150
|
}
|
|
123
151
|
else {
|
|
124
|
-
this
|
|
152
|
+
this._sourceFileCacheMap.delete(fileNPath);
|
|
125
153
|
}
|
|
126
154
|
|
|
127
155
|
return sf;
|
|
128
156
|
};
|
|
129
157
|
|
|
130
|
-
const baseReadFile =
|
|
131
|
-
|
|
132
|
-
this
|
|
133
|
-
return baseReadFile.call(
|
|
158
|
+
const baseReadFile = compilerHost.readFile;
|
|
159
|
+
compilerHost.readFile = (fileName) => {
|
|
160
|
+
this._watchFileSet.add(PathUtils.norm(fileName));
|
|
161
|
+
return baseReadFile.call(compilerHost, fileName);
|
|
134
162
|
};
|
|
135
163
|
|
|
136
|
-
if (this
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
workspaceRoot: opt.pkgPath,
|
|
141
|
-
optimization: !opt.isDevMode,
|
|
142
|
-
inlineFonts: true,
|
|
143
|
-
preserveSymlinks: false,
|
|
144
|
-
sourcemap: opt.isDevMode ? "inline" : false,
|
|
145
|
-
outputNames: { bundles: "[name]", media: "media/[name]" },
|
|
146
|
-
includePaths: [],
|
|
147
|
-
externalDependencies: [],
|
|
148
|
-
target: transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"])),
|
|
149
|
-
postcssConfiguration: {
|
|
150
|
-
plugins: [["css-has-pseudo"]],
|
|
151
|
-
},
|
|
152
|
-
tailwindConfiguration: undefined,
|
|
153
|
-
cacheOptions: {
|
|
154
|
-
enabled: true,
|
|
155
|
-
path: ".cache/angular",
|
|
156
|
-
basePath: ".cache",
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
"scss",
|
|
160
|
-
opt.isDevMode,
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
//-- compilerHost
|
|
164
|
-
|
|
165
|
-
(this.#compilerHost as AngularCompilerHost).readResource = (fileName: string) => {
|
|
166
|
-
return this.#compilerHost.readFile(fileName) ?? "";
|
|
164
|
+
if (this._isForAngular) {
|
|
165
|
+
(compilerHost as AngularCompilerHost).readResource = (fileName: string) => {
|
|
166
|
+
return compilerHost.readFile(fileName) ?? "";
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
-
(
|
|
169
|
+
(compilerHost as AngularCompilerHost).transformResource = async (
|
|
170
170
|
data: string,
|
|
171
171
|
context: {
|
|
172
172
|
type: string;
|
|
@@ -178,69 +178,88 @@ export class SdTsCompiler {
|
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
const
|
|
181
|
+
const stylesheetBundlingResult = await this._bundleStylesheetAsync(
|
|
182
182
|
data,
|
|
183
183
|
PathUtils.norm(context.containingFile),
|
|
184
184
|
context.resourceFile != null ? PathUtils.norm(context.resourceFile) : undefined,
|
|
185
185
|
);
|
|
186
186
|
|
|
187
|
-
return StringUtils.isNullOrEmpty(contents)
|
|
187
|
+
return StringUtils.isNullOrEmpty(stylesheetBundlingResult.contents)
|
|
188
|
+
? null
|
|
189
|
+
: { content: stylesheetBundlingResult.contents };
|
|
188
190
|
};
|
|
189
191
|
|
|
190
|
-
(
|
|
191
|
-
return new Set(Array.from(this
|
|
192
|
+
(compilerHost as AngularCompilerHost).getModifiedResourceFiles = () => {
|
|
193
|
+
return new Set(Array.from(this._modifiedFileSet).map((item) => PathUtils.posix(item)));
|
|
192
194
|
};
|
|
193
195
|
}
|
|
196
|
+
|
|
197
|
+
return compilerHost;
|
|
194
198
|
}
|
|
195
199
|
|
|
196
|
-
async
|
|
200
|
+
private async _bundleStylesheetAsync(
|
|
197
201
|
data: string,
|
|
198
202
|
containingFile: TNormPath,
|
|
199
203
|
resourceFile: TNormPath | null = null,
|
|
200
|
-
) {
|
|
204
|
+
): Promise<TStylesheetBundlingResult> {
|
|
201
205
|
// containingFile: 포함된 파일 (.ts)
|
|
202
206
|
// resourceFile: 외부 리소스 파일 (styleUrls로 입력하지 않고 styles에 직접 입력한 경우 null)
|
|
203
207
|
// referencedFiles: import한 외부 scss 파일 혹은 woff파일등 외부 파일
|
|
204
208
|
|
|
205
209
|
// this.#debug(`bundle stylesheet...(${containingFile}, ${resourceFile})`);
|
|
206
210
|
|
|
207
|
-
return await this
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
211
|
+
return await this._perf.run("스타일 번들링", async () => {
|
|
212
|
+
const fileNPath = PathUtils.norm(resourceFile ?? containingFile);
|
|
213
|
+
if (this._stylesheetBundlingResultMap.has(fileNPath)) {
|
|
214
|
+
return this._stylesheetBundlingResultMap.get(fileNPath)!;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const result =
|
|
219
|
+
resourceFile != null
|
|
220
|
+
? await this._stylesheetBundler!.bundleFile(resourceFile)
|
|
221
|
+
: await this._stylesheetBundler!.bundleInline(data, containingFile, "scss");
|
|
222
|
+
|
|
223
|
+
if (result.referencedFiles) {
|
|
224
|
+
for (const referencedFile of result.referencedFiles) {
|
|
225
|
+
const depCacheSet = this._revDepCacheMap.getOrCreate(
|
|
226
|
+
PathUtils.norm(referencedFile),
|
|
227
|
+
new Set<TNormPath>(),
|
|
228
|
+
);
|
|
229
|
+
depCacheSet.add(fileNPath);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this._watchFileSet.adds(
|
|
233
|
+
...Array.from(result.referencedFiles.values())
|
|
234
|
+
.map((item) => PathUtils.norm(item)),
|
|
218
235
|
);
|
|
219
|
-
depCacheSet.add(resourceFile ?? containingFile);
|
|
220
236
|
}
|
|
221
237
|
|
|
222
|
-
this
|
|
223
|
-
...Array.from(stylesheetResult.referencedFiles.values())
|
|
224
|
-
.map((item) => PathUtils.norm(item)),
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
this.#stylesheetBundlingResultMap.set(PathUtils.norm(resourceFile ?? containingFile), {
|
|
229
|
-
outputFiles: stylesheetResult.errors == null ? stylesheetResult.outputFiles : undefined,
|
|
230
|
-
metafile: stylesheetResult.errors == null ? stylesheetResult.metafile : undefined,
|
|
231
|
-
errors: stylesheetResult.errors,
|
|
232
|
-
warnings: stylesheetResult.warnings,
|
|
233
|
-
});
|
|
238
|
+
this._stylesheetBundlingResultMap.set(fileNPath, result);
|
|
234
239
|
|
|
235
|
-
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
const result = {
|
|
244
|
+
errors: [
|
|
245
|
+
{
|
|
246
|
+
text: `스타일 번들링 실패: ${err.message ?? "알 수 없는 오류"}`,
|
|
247
|
+
location: { file: containingFile },
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
warnings: [],
|
|
251
|
+
};
|
|
252
|
+
this._stylesheetBundlingResultMap.set(fileNPath, result);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
236
255
|
});
|
|
237
256
|
}
|
|
238
257
|
|
|
239
|
-
async compileAsync(modifiedFileSet: Set<TNormPath>): Promise<ISdTsCompilerResult> {
|
|
240
|
-
this
|
|
258
|
+
public async compileAsync(modifiedFileSet: Set<TNormPath>): Promise<ISdTsCompilerResult> {
|
|
259
|
+
this._perf = new SdCliPerformanceTimer("esbuild compile");
|
|
241
260
|
|
|
242
|
-
this
|
|
243
|
-
this
|
|
261
|
+
this._modifiedFileSet = new Set(modifiedFileSet);
|
|
262
|
+
this._affectedFileSet = new Set<TNormPath>();
|
|
244
263
|
|
|
245
264
|
/*for (const mod of modifiedFileSet) {
|
|
246
265
|
const workerImporters = this.#workerRevDependencyCacheMap.get(mod);
|
|
@@ -251,11 +270,18 @@ export class SdTsCompiler {
|
|
|
251
270
|
}
|
|
252
271
|
}*/
|
|
253
272
|
|
|
254
|
-
const
|
|
273
|
+
const tsconf = this._parseTsConfig();
|
|
255
274
|
|
|
256
|
-
const
|
|
275
|
+
const prepareResult = await this._prepareAsync(tsconf);
|
|
257
276
|
|
|
258
|
-
|
|
277
|
+
const [buildResult, lintResults] = await Promise.all([
|
|
278
|
+
this._buildAsync(tsconf),
|
|
279
|
+
this._lintAsync(),
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
this._debug(`빌드 완료됨`, this._perf.toString());
|
|
283
|
+
this._debug(`영향 받은 파일: ${this._affectedFileSet.size}개`);
|
|
284
|
+
this._debug(`감시 중인 파일: ${this._watchFileSet.size}개`);
|
|
259
285
|
|
|
260
286
|
return {
|
|
261
287
|
messages: [
|
|
@@ -263,210 +289,147 @@ export class SdTsCompiler {
|
|
|
263
289
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromTsDiag(buildResult.diagnostics),
|
|
264
290
|
...SdCliConvertMessageUtils.convertToBuildMessagesFromEslint(lintResults),
|
|
265
291
|
],
|
|
266
|
-
watchFileSet: this
|
|
267
|
-
affectedFileSet: this
|
|
268
|
-
stylesheetBundlingResultMap: this
|
|
269
|
-
emittedFilesCacheMap: this
|
|
292
|
+
watchFileSet: this._watchFileSet,
|
|
293
|
+
affectedFileSet: this._affectedFileSet,
|
|
294
|
+
stylesheetBundlingResultMap: this._stylesheetBundlingResultMap,
|
|
295
|
+
emittedFilesCacheMap: this._emittedFilesCacheMap,
|
|
270
296
|
emitFileSet: buildResult.emitFileSet,
|
|
271
297
|
};
|
|
272
298
|
}
|
|
273
299
|
|
|
274
|
-
async
|
|
275
|
-
if (this
|
|
276
|
-
this
|
|
300
|
+
private async _prepareAsync(tsconf: ITsConfigInfo) {
|
|
301
|
+
if (this._modifiedFileSet.size !== 0) {
|
|
302
|
+
this._debug(`영향 받은 파일 추적 중... (구 의존성)`);
|
|
303
|
+
|
|
304
|
+
this._perf.run("영향 받은 파일 추적", () => {
|
|
305
|
+
for (const modifiedFile of this._modifiedFileSet) {
|
|
306
|
+
this._affectedFileSet.add(modifiedFile);
|
|
307
|
+
|
|
308
|
+
this._affectedFileSet.adds(...(this._revDepCacheMap.get(modifiedFile) ?? []));
|
|
277
309
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
310
|
+
// .d.ts → .js 대응
|
|
311
|
+
if (modifiedFile.endsWith(".d.ts")) {
|
|
312
|
+
const jsEquivalent = PathUtils.norm(modifiedFile.replace(/\.d\.ts$/, ".js"));
|
|
313
|
+
this._affectedFileSet.add(jsEquivalent);
|
|
314
|
+
}
|
|
283
315
|
}
|
|
284
316
|
});
|
|
285
317
|
|
|
286
|
-
this
|
|
318
|
+
this._debug(`캐시 무효화 및 초기화 중...`);
|
|
287
319
|
|
|
288
|
-
this
|
|
289
|
-
this
|
|
320
|
+
this._perf.run("캐시 무효화 및 초기화", () => {
|
|
321
|
+
this._stylesheetBundler?.invalidate(this._affectedFileSet);
|
|
290
322
|
|
|
291
|
-
for (const affectedFile of this
|
|
292
|
-
this
|
|
293
|
-
this
|
|
294
|
-
this
|
|
295
|
-
this
|
|
296
|
-
|
|
323
|
+
for (const affectedFile of this._affectedFileSet) {
|
|
324
|
+
this._emittedFilesCacheMap.delete(affectedFile);
|
|
325
|
+
this._sourceFileCacheMap.delete(affectedFile);
|
|
326
|
+
this._stylesheetBundlingResultMap.delete(affectedFile);
|
|
327
|
+
this._watchFileSet.delete(affectedFile);
|
|
328
|
+
|
|
329
|
+
for (const [key, deps] of this._revDepCacheMap.entries()) {
|
|
330
|
+
if (key === affectedFile) {
|
|
331
|
+
this._revDepCacheMap.delete(key);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
297
334
|
|
|
298
|
-
|
|
299
|
-
|
|
335
|
+
if (deps.has(affectedFile)) {
|
|
336
|
+
deps.delete(affectedFile);
|
|
337
|
+
if (deps.size === 0) {
|
|
338
|
+
this._revDepCacheMap.delete(key);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
300
343
|
});
|
|
301
344
|
}
|
|
302
345
|
|
|
303
|
-
this
|
|
346
|
+
this._debug(`ts.Program 생성 중...`);
|
|
304
347
|
|
|
305
|
-
this
|
|
306
|
-
if (this
|
|
307
|
-
this
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this
|
|
348
|
+
this._perf.run("ts.Program 생성", () => {
|
|
349
|
+
if (this._isForAngular) {
|
|
350
|
+
this._ngProgram = new NgtscProgram(
|
|
351
|
+
tsconf.fileNames,
|
|
352
|
+
tsconf.options,
|
|
353
|
+
tsconf.compilerHost,
|
|
354
|
+
this._ngProgram,
|
|
312
355
|
);
|
|
313
|
-
this
|
|
356
|
+
this._program = this._ngProgram.getTsProgram();
|
|
314
357
|
}
|
|
315
358
|
else {
|
|
316
|
-
this
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
this
|
|
359
|
+
this._program = ts.createProgram(
|
|
360
|
+
tsconf.fileNames,
|
|
361
|
+
tsconf.options,
|
|
362
|
+
tsconf.compilerHost,
|
|
363
|
+
this._program,
|
|
321
364
|
);
|
|
322
365
|
}
|
|
323
366
|
});
|
|
324
367
|
|
|
325
|
-
if (this
|
|
326
|
-
await this
|
|
327
|
-
await this
|
|
368
|
+
if (this._ngProgram) {
|
|
369
|
+
await this._perf.run("Angular 템플릿 분석", async () => {
|
|
370
|
+
await this._ngProgram!.compiler.analyzeAsync();
|
|
328
371
|
});
|
|
329
372
|
}
|
|
330
373
|
|
|
331
|
-
|
|
332
|
-
if (sf.fileName.endsWith(".ngtypecheck.ts")) {
|
|
333
|
-
const orgFileName = sf.fileName.slice(0, -15) + ".ts";
|
|
334
|
-
return this.#program!.getSourceFile(orgFileName);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return sf;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const sourceFileSet = new Set(
|
|
341
|
-
this.#program!.getSourceFiles()
|
|
342
|
-
.map((sf) => getOrgSourceFile(sf))
|
|
343
|
-
.filterExists(),
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
this.#debug(`get new deps...`);
|
|
374
|
+
this._debug(`새 의존성 분석 중...`);
|
|
347
375
|
|
|
348
376
|
const messages: ISdBuildMessage[] = [];
|
|
377
|
+
const analysed1 = this._perf.run("새 의존성 분석", () => {
|
|
378
|
+
const analysed = SdTsDependencyAnalyzer.analyze(
|
|
379
|
+
this._program!,
|
|
380
|
+
tsconf.compilerHost,
|
|
381
|
+
this._opt.watchScopePaths,
|
|
382
|
+
);
|
|
383
|
+
messages.push(...analysed.messages);
|
|
349
384
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
TNormPath
|
|
353
|
-
{
|
|
354
|
-
fileName: TNormPath;
|
|
355
|
-
importName: string;
|
|
356
|
-
exportName?: string;
|
|
357
|
-
}[]
|
|
358
|
-
>();
|
|
359
|
-
for (const sf of sourceFileSet) {
|
|
360
|
-
if (!this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(
|
|
361
|
-
sf.fileName,
|
|
362
|
-
scopePath,
|
|
363
|
-
))) {
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const refs = this.#findDeps(sf);
|
|
368
|
-
messages.push(...refs.filter((item) => "severity" in item));
|
|
369
|
-
depMap.set(
|
|
370
|
-
PathUtils.norm(sf.fileName),
|
|
371
|
-
refs
|
|
372
|
-
.filter((item) => "fileName" in item)
|
|
373
|
-
.filter((item) =>
|
|
374
|
-
this.#watchScopePaths.some((scopePath) => PathUtils.isChildPath(
|
|
375
|
-
item.fileName,
|
|
376
|
-
scopePath,
|
|
377
|
-
)),
|
|
378
|
-
),
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const allDepMap = new Map<TNormPath, Set<TNormPath>>();
|
|
383
|
-
const getAllDeps = (fileName: TNormPath, prevSet?: Set<TNormPath>) => {
|
|
384
|
-
if (allDepMap.has(fileName)) {
|
|
385
|
-
return allDepMap.get(fileName)!;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const result = new Set<TNormPath>();
|
|
389
|
-
|
|
390
|
-
const deps = depMap.get(fileName) ?? [];
|
|
391
|
-
result.adds(...deps.map((item) => item.fileName));
|
|
385
|
+
for (const sf of analysed.sourceFileSet) {
|
|
386
|
+
const filePath = PathUtils.norm(sf.fileName);
|
|
387
|
+
const deps = analysed.allDepMap.get(filePath) ?? new Set<TNormPath>();
|
|
392
388
|
|
|
393
389
|
for (const dep of deps) {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
if (dep.importName === "*") {
|
|
397
|
-
for (const targetRefItem of targetDeps.filter((item) => item.exportName != null)) {
|
|
398
|
-
if (prevSet?.has(targetRefItem.fileName)) continue;
|
|
399
|
-
|
|
400
|
-
result.add(targetRefItem.fileName);
|
|
401
|
-
result.adds(...getAllDeps(
|
|
402
|
-
targetRefItem.fileName,
|
|
403
|
-
new Set<TNormPath>(prevSet).adds(...result),
|
|
404
|
-
));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
for (const targetRefItem of targetDeps.filter((item) => item.exportName
|
|
409
|
-
=== dep.importName)) {
|
|
410
|
-
if (prevSet?.has(targetRefItem.fileName)) continue;
|
|
411
|
-
|
|
412
|
-
result.add(targetRefItem.fileName);
|
|
413
|
-
result.adds(...getAllDeps(
|
|
414
|
-
targetRefItem.fileName,
|
|
415
|
-
new Set<TNormPath>(prevSet).adds(...result),
|
|
416
|
-
));
|
|
417
|
-
}
|
|
418
|
-
}
|
|
390
|
+
const depCache = this._revDepCacheMap.getOrCreate(dep, new Set<TNormPath>());
|
|
391
|
+
depCache.add(filePath);
|
|
419
392
|
}
|
|
420
393
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
for (const sf of sourceFileSet) {
|
|
425
|
-
const deps = getAllDeps(PathUtils.norm(sf.fileName));
|
|
426
|
-
allDepMap.set(PathUtils.norm(sf.fileName), deps);
|
|
427
|
-
|
|
428
|
-
for (const dep of getAllDeps(PathUtils.norm(sf.fileName))) {
|
|
429
|
-
const depCache = this.#revDependencyCacheMap.getOrCreate(dep, new Set<TNormPath>());
|
|
430
|
-
depCache.add(PathUtils.norm(sf.fileName));
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (this.#ngProgram) {
|
|
434
|
-
if (this.#ngProgram.compiler.ignoreForEmit.has(sf)) {
|
|
394
|
+
if (this._ngProgram) {
|
|
395
|
+
if (this._ngProgram.compiler.ignoreForEmit.has(sf)) {
|
|
435
396
|
continue;
|
|
436
397
|
}
|
|
437
398
|
|
|
438
|
-
for (const dep of this
|
|
439
|
-
const ref = this
|
|
399
|
+
for (const dep of this._ngProgram.compiler.getResourceDependencies(sf)) {
|
|
400
|
+
const ref = this._revDepCacheMap.getOrCreate(
|
|
440
401
|
PathUtils.norm(dep),
|
|
441
402
|
new Set<TNormPath>(),
|
|
442
403
|
);
|
|
443
|
-
ref.add(
|
|
404
|
+
ref.add(filePath);
|
|
444
405
|
}
|
|
445
406
|
}
|
|
446
407
|
}
|
|
408
|
+
|
|
409
|
+
return analysed;
|
|
447
410
|
});
|
|
448
411
|
|
|
449
|
-
if (this
|
|
450
|
-
this
|
|
412
|
+
if (this._modifiedFileSet.size === 0) {
|
|
413
|
+
this._debug(`영향 받은 파일 추가 중... (새 의존성)`);
|
|
451
414
|
|
|
452
|
-
this
|
|
453
|
-
for (const sf of sourceFileSet) {
|
|
454
|
-
if (!this
|
|
415
|
+
this._perf.run("영향 받은 파일 추가 중 (새 의존성)", () => {
|
|
416
|
+
for (const sf of analysed1.sourceFileSet) {
|
|
417
|
+
if (!this._opt.watchScopePaths.some((scopePath) => PathUtils.isChildPath(
|
|
455
418
|
sf.fileName,
|
|
456
419
|
scopePath,
|
|
457
420
|
))) {
|
|
458
421
|
continue;
|
|
459
422
|
}
|
|
460
423
|
|
|
461
|
-
this
|
|
424
|
+
this._affectedFileSet.add(PathUtils.norm(sf.fileName));
|
|
462
425
|
}
|
|
463
426
|
});
|
|
464
427
|
}
|
|
465
428
|
|
|
466
|
-
for (const dep of this
|
|
467
|
-
if (this
|
|
468
|
-
this
|
|
469
|
-
...Array.from(this
|
|
429
|
+
for (const dep of this._revDepCacheMap.keys()) {
|
|
430
|
+
if (this._modifiedFileSet.has(dep)) {
|
|
431
|
+
this._affectedFileSet.adds(
|
|
432
|
+
...Array.from(this._revDepCacheMap.get(dep)!).mapMany((item) =>
|
|
470
433
|
[
|
|
471
434
|
item,
|
|
472
435
|
// .d.ts면 .js파일도 affected에 추가
|
|
@@ -478,22 +441,8 @@ export class SdTsCompiler {
|
|
|
478
441
|
|
|
479
442
|
// dep이 emit된적이 없으면 affected에 추가해야함.
|
|
480
443
|
// dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
|
|
481
|
-
if (!this
|
|
482
|
-
this
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (this.#ngProgram) {
|
|
487
|
-
for (const dep of this.#resourceDependencyCacheMap.keys()) {
|
|
488
|
-
if (this.#modifiedFileSet.has(dep)) {
|
|
489
|
-
this.#affectedFileSet.adds(...this.#resourceDependencyCacheMap.get(dep)!);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// dep이 emit된적이 없으면 affected에 추가해야함.
|
|
493
|
-
// dep파일이 추가된후 기존 파일에서 import하면 dep파일이 affected에 포함이 안되는 현상 때문
|
|
494
|
-
if (!this.#emittedFilesCacheMap.has(dep)) {
|
|
495
|
-
this.#affectedFileSet.add(dep);
|
|
496
|
-
}
|
|
444
|
+
if (!this._emittedFilesCacheMap.has(dep)) {
|
|
445
|
+
this._affectedFileSet.add(dep);
|
|
497
446
|
}
|
|
498
447
|
}
|
|
499
448
|
|
|
@@ -502,9 +451,9 @@ export class SdTsCompiler {
|
|
|
502
451
|
};
|
|
503
452
|
}
|
|
504
453
|
|
|
505
|
-
async
|
|
506
|
-
const lintFilePaths = Array.from(this
|
|
507
|
-
.filter((item) => PathUtils.isChildPath(item, this
|
|
454
|
+
private async _lintAsync() {
|
|
455
|
+
const lintFilePaths = Array.from(this._affectedFileSet)
|
|
456
|
+
.filter((item) => PathUtils.isChildPath(item, this._opt.pkgPath))
|
|
508
457
|
.filter((item) => (
|
|
509
458
|
(!item.endsWith(".d.ts") && item.endsWith(".ts")) ||
|
|
510
459
|
item.endsWith(".js")
|
|
@@ -516,14 +465,14 @@ export class SdTsCompiler {
|
|
|
516
465
|
}
|
|
517
466
|
|
|
518
467
|
const linter = new ESLint({
|
|
519
|
-
cwd: this
|
|
468
|
+
cwd: this._opt.pkgPath,
|
|
520
469
|
cache: false,
|
|
521
470
|
overrideConfig: {
|
|
522
471
|
languageOptions: {
|
|
523
472
|
parserOptions: {
|
|
524
473
|
// parser: tseslint.parser,
|
|
525
474
|
project: null,
|
|
526
|
-
programs: [this
|
|
475
|
+
programs: [this._program],
|
|
527
476
|
},
|
|
528
477
|
},
|
|
529
478
|
},
|
|
@@ -538,57 +487,52 @@ export class SdTsCompiler {
|
|
|
538
487
|
// ]);
|
|
539
488
|
}
|
|
540
489
|
|
|
541
|
-
async
|
|
490
|
+
private async _buildAsync(tsconf: ITsConfigInfo) {
|
|
542
491
|
const emitFileSet = new Set<TNormPath>();
|
|
543
492
|
const diagnostics: ts.Diagnostic[] = [];
|
|
544
493
|
|
|
545
|
-
this
|
|
494
|
+
this._debug(`프로그램 진단 수집 중...`);
|
|
546
495
|
|
|
547
|
-
this
|
|
496
|
+
this._perf.run("프로그램 진단 수집", () => {
|
|
548
497
|
diagnostics.push(
|
|
549
|
-
...this
|
|
550
|
-
...this
|
|
551
|
-
...this
|
|
498
|
+
...this._program!.getConfigFileParsingDiagnostics(),
|
|
499
|
+
...this._program!.getOptionsDiagnostics(),
|
|
500
|
+
...this._program!.getGlobalDiagnostics(),
|
|
552
501
|
);
|
|
553
502
|
|
|
554
|
-
if (this
|
|
555
|
-
diagnostics.push(...this
|
|
503
|
+
if (this._ngProgram) {
|
|
504
|
+
diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
|
|
556
505
|
}
|
|
557
506
|
});
|
|
558
507
|
|
|
559
|
-
this
|
|
508
|
+
this._debug(`개별 파일 진단 수집 중...`);
|
|
560
509
|
|
|
561
|
-
for (const affectedFile of this
|
|
562
|
-
if (!PathUtils.isChildPath(affectedFile, this
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const affectedSourceFile = this.#program!.getSourceFile(affectedFile);
|
|
510
|
+
for (const affectedFile of this._affectedFileSet) {
|
|
511
|
+
if (!PathUtils.isChildPath(affectedFile, this._opt.pkgPath)) continue;
|
|
567
512
|
|
|
513
|
+
const affectedSourceFile = this._program!.getSourceFile(affectedFile);
|
|
568
514
|
if (
|
|
569
515
|
!affectedSourceFile ||
|
|
570
|
-
(this
|
|
516
|
+
(this._ngProgram && this._ngProgram.compiler.ignoreForDiagnostics.has(affectedSourceFile))
|
|
571
517
|
) {
|
|
572
518
|
continue;
|
|
573
519
|
}
|
|
574
520
|
|
|
575
521
|
// this.#debug(`get diagnostics of file ${affectedFile}...`);
|
|
576
522
|
|
|
577
|
-
this
|
|
523
|
+
this._perf.run("개별 파일 진단 수집", () => {
|
|
578
524
|
diagnostics.push(
|
|
579
|
-
...this
|
|
580
|
-
...this
|
|
525
|
+
...this._program!.getSyntacticDiagnostics(affectedSourceFile),
|
|
526
|
+
...this._program!.getSemanticDiagnostics(affectedSourceFile),
|
|
581
527
|
);
|
|
582
528
|
});
|
|
583
529
|
|
|
584
|
-
if (this
|
|
585
|
-
this
|
|
586
|
-
if (affectedSourceFile.isDeclarationFile)
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
530
|
+
if (this._ngProgram) {
|
|
531
|
+
this._perf.run("개별 파일 진단 수집(Angular)", () => {
|
|
532
|
+
if (affectedSourceFile.isDeclarationFile) return;
|
|
589
533
|
|
|
590
534
|
diagnostics.push(
|
|
591
|
-
...this
|
|
535
|
+
...this._ngProgram!.compiler.getDiagnosticsForFile(
|
|
592
536
|
affectedSourceFile,
|
|
593
537
|
OptimizeFor.WholeProgram,
|
|
594
538
|
),
|
|
@@ -597,163 +541,50 @@ export class SdTsCompiler {
|
|
|
597
541
|
}
|
|
598
542
|
}
|
|
599
543
|
|
|
600
|
-
this
|
|
601
|
-
this
|
|
544
|
+
this._perf.run("파일 출력 (emit)", () => {
|
|
545
|
+
this._debug(`파일 출력 준비 중...`);
|
|
602
546
|
|
|
603
547
|
let transformers: ts.CustomTransformers = {};
|
|
604
548
|
|
|
605
|
-
if (this
|
|
549
|
+
if (this._ngProgram) {
|
|
606
550
|
transformers = {
|
|
607
551
|
...transformers,
|
|
608
|
-
...this
|
|
552
|
+
...this._ngProgram.compiler.prepareEmit().transformers,
|
|
609
553
|
};
|
|
610
|
-
(transformers.before ??= []).push(replaceBootstrap(() => this
|
|
554
|
+
(transformers.before ??= []).push(replaceBootstrap(() => this._program!.getTypeChecker()));
|
|
611
555
|
(transformers.before ??= []).push(
|
|
612
556
|
createWorkerTransformer((file, importer) => {
|
|
613
557
|
const fullPath = path.resolve(path.dirname(importer), file);
|
|
614
|
-
const relPath = path.relative(path.resolve(this
|
|
558
|
+
const relPath = path.relative(path.resolve(this._opt.pkgPath, "src"), fullPath);
|
|
615
559
|
return relPath.replace(/\.ts$/, "").replaceAll("\\", "/") + ".js";
|
|
616
560
|
}),
|
|
617
561
|
);
|
|
618
562
|
}
|
|
619
|
-
// (transformers.before ??= []).push(transformKeys(this.#program));
|
|
620
|
-
|
|
621
|
-
/*const fixImportTransformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
|
|
622
|
-
return (sf) => {
|
|
623
|
-
const shouldAppendJs = (importText: string): string | undefined => {
|
|
624
|
-
const resolved = ts.resolveModuleName(
|
|
625
|
-
importText,
|
|
626
|
-
sf.fileName,
|
|
627
|
-
this.#program!.getCompilerOptions(),
|
|
628
|
-
ts.sys,
|
|
629
|
-
);
|
|
630
|
-
|
|
631
|
-
const resolvedInfo = resolved.resolvedModule;
|
|
632
|
-
if (!resolvedInfo) return undefined;
|
|
633
|
-
|
|
634
|
-
const resolvedFileName = resolvedInfo.resolvedFileName;
|
|
635
|
-
|
|
636
|
-
// ① .ts / .tsx / .js / .jsx 만 대상
|
|
637
|
-
if (!/\.(d\.ts|ts|tsx|js|jsx)$/i.test(resolvedFileName)) return undefined;
|
|
638
|
-
|
|
639
|
-
// ② 사용자가 .js, .mjs, .json 등 명시한 경우 무시
|
|
640
|
-
if (/\.[mc]?js$|\.json$/i.test(importText)) return undefined;
|
|
641
|
-
|
|
642
|
-
// 3. import 경로의 마지막 부분이 파일명(확장자 제외)과 같으면 → .js 붙여야 함
|
|
643
|
-
const importLastName = importText.split("/").pop();
|
|
644
|
-
const resolvedFileNameOnly = path.basename(resolvedFileName)
|
|
645
|
-
.replace(/\.(d\.ts|ts|tsx|js|jsx)$/, "");
|
|
646
563
|
|
|
647
|
-
|
|
648
|
-
return importText + ".js";
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// 4. 그렇지 않으면 → index.ts 같은 루트 패키지 import → .js 붙이지 않음
|
|
652
|
-
return undefined;
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
const visitor: ts.Visitor = (node): ts.Node => {
|
|
656
|
-
// import { x } from "./foo"
|
|
657
|
-
if (
|
|
658
|
-
ts.isImportDeclaration(node) &&
|
|
659
|
-
ts.isStringLiteral(node.moduleSpecifier)
|
|
660
|
-
) {
|
|
661
|
-
const newPath = shouldAppendJs(node.moduleSpecifier.text);
|
|
662
|
-
if (newPath != null) {
|
|
663
|
-
return ts.factory.updateImportDeclaration(
|
|
664
|
-
node,
|
|
665
|
-
node.modifiers,
|
|
666
|
-
node.importClause,
|
|
667
|
-
ts.factory.createStringLiteral(newPath),
|
|
668
|
-
undefined,
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// export * from "./bar"
|
|
674
|
-
if (
|
|
675
|
-
ts.isExportDeclaration(node) &&
|
|
676
|
-
node.moduleSpecifier &&
|
|
677
|
-
ts.isStringLiteral(node.moduleSpecifier)
|
|
678
|
-
) {
|
|
679
|
-
const newPath = shouldAppendJs(node.moduleSpecifier.text);
|
|
680
|
-
if (newPath != null) {
|
|
681
|
-
return ts.factory.updateExportDeclaration(
|
|
682
|
-
node,
|
|
683
|
-
node.modifiers,
|
|
684
|
-
node.isTypeOnly,
|
|
685
|
-
node.exportClause,
|
|
686
|
-
ts.factory.createStringLiteral(newPath),
|
|
687
|
-
undefined,
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// dynamic import("./baz")
|
|
693
|
-
if (
|
|
694
|
-
ts.isCallExpression(node) &&
|
|
695
|
-
node.expression.kind === ts.SyntaxKind.ImportKeyword &&
|
|
696
|
-
node.arguments.length === 1 &&
|
|
697
|
-
ts.isStringLiteral(node.arguments[0])
|
|
698
|
-
) {
|
|
699
|
-
const newPath = shouldAppendJs(node.arguments[0].text);
|
|
700
|
-
if (newPath != null) {
|
|
701
|
-
return ts.factory.updateCallExpression(
|
|
702
|
-
node,
|
|
703
|
-
node.expression,
|
|
704
|
-
undefined,
|
|
705
|
-
[ts.factory.createStringLiteral(newPath)],
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
return ts.visitEachChild(node, visitor, context);
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
return ts.visitNode(sf, visitor) as ts.SourceFile;
|
|
714
|
-
};
|
|
715
|
-
};
|
|
716
|
-
(transformers.before ??= []).push(fixImportTransformer);*/
|
|
717
|
-
|
|
718
|
-
this.#debug(`emit for files...`);
|
|
564
|
+
this._debug(`파일 출력 중...`);
|
|
719
565
|
|
|
720
566
|
// affected에 새로 추가된 파일은 포함되지 않는 현상이 있어 sourceFileSet으로 바꿈
|
|
721
567
|
// 비교해보니, 딱히 getSourceFiles라서 더 느려지는것 같지는 않음
|
|
722
568
|
// 그래도 affected로 다시 테스트 (조금이라도 더 빠르게)
|
|
723
|
-
for (const affectedFile of this
|
|
724
|
-
if (this
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
569
|
+
for (const affectedFile of this._affectedFileSet) {
|
|
570
|
+
if (this._emittedFilesCacheMap.has(affectedFile)) continue;
|
|
727
571
|
|
|
728
|
-
const sf = this
|
|
729
|
-
if (!sf)
|
|
730
|
-
|
|
731
|
-
|
|
572
|
+
const sf = this._program!.getSourceFile(affectedFile);
|
|
573
|
+
if (!sf || sf.isDeclarationFile) continue;
|
|
574
|
+
if (this._ngProgram?.compiler.ignoreForEmit.has(sf)) continue;
|
|
575
|
+
if (this._ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) continue;
|
|
732
576
|
|
|
733
|
-
|
|
577
|
+
// 번들이 아닌 외부패키지는 보통 emit안해도 됨
|
|
578
|
+
// but esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
|
|
579
|
+
if (!this._opt.isForBundle && !PathUtils.isChildPath(sf.fileName, this._opt.pkgPath)) {
|
|
734
580
|
continue;
|
|
735
581
|
}
|
|
736
582
|
|
|
737
|
-
|
|
738
|
-
continue;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if (this.#ngProgram?.compiler.incrementalCompilation.safeToSkipEmit(sf)) {
|
|
742
|
-
continue;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// esbuild를 통해 bundle로 묶어야 하는놈들은 모든 output이 있어야 함.
|
|
746
|
-
if (!this.#isForBundle) {
|
|
747
|
-
if (!PathUtils.isChildPath(sf.fileName, this.#pkgPath)) {
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
this.#program!.emit(
|
|
583
|
+
this._program!.emit(
|
|
753
584
|
sf,
|
|
754
585
|
(fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
|
|
755
586
|
if (!sourceFiles || sourceFiles.length === 0) {
|
|
756
|
-
|
|
587
|
+
tsconf.compilerHost.writeFile(
|
|
757
588
|
fileName,
|
|
758
589
|
text,
|
|
759
590
|
writeByteOrderMark,
|
|
@@ -765,44 +596,22 @@ export class SdTsCompiler {
|
|
|
765
596
|
}
|
|
766
597
|
|
|
767
598
|
const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
|
|
768
|
-
if (this
|
|
769
|
-
if (this
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
this.#ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
|
|
599
|
+
if (this._ngProgram) {
|
|
600
|
+
if (this._ngProgram.compiler.ignoreForEmit.has(sourceFile)) return;
|
|
601
|
+
this._ngProgram.compiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
|
|
773
602
|
}
|
|
774
603
|
|
|
775
|
-
const emitFileInfoCaches = this
|
|
776
|
-
sourceFile.fileName),
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
PathUtils.isChildPath(
|
|
783
|
-
realFilePath,
|
|
784
|
-
path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"),
|
|
785
|
-
)
|
|
786
|
-
) {
|
|
787
|
-
realFilePath = PathUtils.norm(
|
|
788
|
-
this.#distPath,
|
|
789
|
-
path.relative(
|
|
790
|
-
path.resolve(this.#distPath, path.basename(this.#pkgPath), "src"),
|
|
791
|
-
realFilePath,
|
|
792
|
-
),
|
|
793
|
-
);
|
|
794
|
-
|
|
795
|
-
if (fileName.endsWith(".js.map")) {
|
|
796
|
-
const sourceMapContents = JSON.parse(realText);
|
|
797
|
-
// remove "../../"
|
|
798
|
-
sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
|
|
799
|
-
realText = JSON.stringify(sourceMapContents);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
604
|
+
const emitFileInfoCaches = this._emittedFilesCacheMap.getOrCreate(
|
|
605
|
+
PathUtils.norm(sourceFile.fileName),
|
|
606
|
+
[],
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
if (PathUtils.isChildPath(sourceFile.fileName, this._opt.pkgPath)) {
|
|
610
|
+
const real = this._convertOutputToReal(fileName, tsconf.distPath, text);
|
|
802
611
|
|
|
803
612
|
emitFileInfoCaches.push({
|
|
804
|
-
outAbsPath:
|
|
805
|
-
text:
|
|
613
|
+
outAbsPath: real.filePath,
|
|
614
|
+
text: real.text,
|
|
806
615
|
});
|
|
807
616
|
}
|
|
808
617
|
else {
|
|
@@ -820,32 +629,32 @@ export class SdTsCompiler {
|
|
|
820
629
|
|
|
821
630
|
//-- global style
|
|
822
631
|
if (
|
|
823
|
-
this
|
|
824
|
-
FsUtils.exists(this
|
|
825
|
-
!this
|
|
632
|
+
this._opt.globalStyleFilePath != null &&
|
|
633
|
+
FsUtils.exists(this._opt.globalStyleFilePath) &&
|
|
634
|
+
!this._emittedFilesCacheMap.has(this._opt.globalStyleFilePath)
|
|
826
635
|
) {
|
|
827
|
-
this
|
|
636
|
+
this._debug(`전역 스타일 번들링 중...`);
|
|
828
637
|
|
|
829
|
-
await this
|
|
830
|
-
const data = FsUtils.readFile(this
|
|
831
|
-
const
|
|
638
|
+
await this._perf.run("전역 스타일 번들링", async () => {
|
|
639
|
+
const data = FsUtils.readFile(this._opt.globalStyleFilePath!);
|
|
640
|
+
const stylesheetBundlingResult = await this._bundleStylesheetAsync(
|
|
832
641
|
data,
|
|
833
|
-
this
|
|
834
|
-
this
|
|
642
|
+
this._opt.globalStyleFilePath!,
|
|
643
|
+
this._opt.globalStyleFilePath,
|
|
835
644
|
);
|
|
836
|
-
const emitFileInfos = this
|
|
837
|
-
this
|
|
645
|
+
const emitFileInfos = this._emittedFilesCacheMap.getOrCreate(
|
|
646
|
+
this._opt.globalStyleFilePath!,
|
|
838
647
|
[],
|
|
839
648
|
);
|
|
840
649
|
emitFileInfos.push({
|
|
841
650
|
outAbsPath: PathUtils.norm(
|
|
842
|
-
this
|
|
843
|
-
path.relative(path.resolve(this
|
|
651
|
+
this._opt.pkgPath,
|
|
652
|
+
path.relative(path.resolve(this._opt.pkgPath, "src"), this._opt.globalStyleFilePath!)
|
|
844
653
|
.replace(/\.scss$/, ".css"),
|
|
845
654
|
),
|
|
846
|
-
text: contents,
|
|
655
|
+
text: stylesheetBundlingResult.contents ?? "",
|
|
847
656
|
});
|
|
848
|
-
emitFileSet.add(this
|
|
657
|
+
emitFileSet.add(this._opt.globalStyleFilePath!);
|
|
849
658
|
});
|
|
850
659
|
}
|
|
851
660
|
|
|
@@ -855,215 +664,35 @@ export class SdTsCompiler {
|
|
|
855
664
|
};
|
|
856
665
|
}
|
|
857
666
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
#findDeps(sf: ts.SourceFile) {
|
|
863
|
-
const deps: ({
|
|
864
|
-
fileName: TNormPath;
|
|
865
|
-
importName: string;
|
|
866
|
-
exportName?: string;
|
|
867
|
-
} | ISdBuildMessage)[] = [];
|
|
868
|
-
|
|
869
|
-
const tc = this.#program!.getTypeChecker();
|
|
870
|
-
|
|
871
|
-
const visit = (node: ts.Node) => {
|
|
872
|
-
if (ts.isExportDeclaration(node)) {
|
|
873
|
-
if (node.moduleSpecifier) {
|
|
874
|
-
const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
|
|
875
|
-
if (!moduleSymbol) {
|
|
876
|
-
const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
877
|
-
deps.push({
|
|
878
|
-
filePath: PathUtils.norm(sf.fileName),
|
|
879
|
-
line: pos.line,
|
|
880
|
-
char: pos.character,
|
|
881
|
-
code: undefined,
|
|
882
|
-
severity: "error",
|
|
883
|
-
message: "export moduleSymbol not found",
|
|
884
|
-
type: "deps",
|
|
885
|
-
});
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
667
|
+
private _convertOutputToReal(filePath: string, distPath: string, text: string) {
|
|
668
|
+
let realFilePath = PathUtils.norm(filePath);
|
|
669
|
+
let realText = text;
|
|
888
670
|
|
|
889
|
-
|
|
890
|
-
if (!decls) {
|
|
891
|
-
const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
892
|
-
deps.push({
|
|
893
|
-
filePath: PathUtils.norm(sf.fileName),
|
|
894
|
-
line: pos.line,
|
|
895
|
-
char: pos.character,
|
|
896
|
-
code: undefined,
|
|
897
|
-
severity: "error",
|
|
898
|
-
message: "export decls not found",
|
|
899
|
-
type: "deps",
|
|
900
|
-
});
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
671
|
+
const srcRelBasePath = path.resolve(distPath, path.basename(this._opt.pkgPath), "src");
|
|
903
672
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
for (const el of namedBindings.elements) {
|
|
907
|
-
for (const decl of decls) {
|
|
908
|
-
deps.push({
|
|
909
|
-
fileName: PathUtils.norm(decl.getSourceFile().fileName),
|
|
910
|
-
importName: el.name.text,
|
|
911
|
-
exportName: el.propertyName?.text ?? el.name.text,
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
else {
|
|
917
|
-
if (!moduleSymbol.exports) {
|
|
918
|
-
const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
919
|
-
deps.push({
|
|
920
|
-
filePath: PathUtils.norm(sf.fileName),
|
|
921
|
-
line: pos.line,
|
|
922
|
-
char: pos.character,
|
|
923
|
-
code: undefined,
|
|
924
|
-
severity: "error",
|
|
925
|
-
message: "moduleSymbol exports not found",
|
|
926
|
-
type: "deps",
|
|
927
|
-
});
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
673
|
+
if (PathUtils.isChildPath(realFilePath, srcRelBasePath)) {
|
|
674
|
+
realFilePath = PathUtils.norm(distPath, path.relative(srcRelBasePath, realFilePath));
|
|
930
675
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
exportName: key.toString(),
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
}
|
|
676
|
+
// source map 위치 정확히 찾아가기
|
|
677
|
+
if (filePath.endsWith(".js.map")) {
|
|
678
|
+
const sourceMapContents = JSON.parse(realText);
|
|
679
|
+
sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6); // remove "../../"
|
|
680
|
+
realText = JSON.stringify(sourceMapContents);
|
|
942
681
|
}
|
|
943
|
-
|
|
944
|
-
const moduleSymbol = tc.getSymbolAtLocation(node.moduleSpecifier);
|
|
945
|
-
if (!moduleSymbol) {
|
|
946
|
-
if (ts.isStringLiteral(node.moduleSpecifier)
|
|
947
|
-
&& node.moduleSpecifier.text.startsWith("./")) {
|
|
948
|
-
deps.push({
|
|
949
|
-
fileName: PathUtils.norm(path.resolve(
|
|
950
|
-
path.dirname(sf.fileName),
|
|
951
|
-
node.moduleSpecifier.text + ".ts",
|
|
952
|
-
)),
|
|
953
|
-
importName: "*",
|
|
954
|
-
});
|
|
955
|
-
|
|
956
|
-
// const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
957
|
-
// deps.push({
|
|
958
|
-
// filePath: PathUtil.norm(sf.fileName),
|
|
959
|
-
// line: pos.line,
|
|
960
|
-
// char: pos.character,
|
|
961
|
-
// code: undefined,
|
|
962
|
-
// severity: "error",
|
|
963
|
-
// message: `import moduleSymbol not found (${node.moduleSpecifier.text})`,
|
|
964
|
-
// type: "deps",
|
|
965
|
-
// });
|
|
966
|
-
// return;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/*else {
|
|
970
|
-
throw new NeverEntryError(`import moduleSymbol: ${sf.fileName} ${node.moduleSpecifier["text"]}`);
|
|
971
|
-
}*/
|
|
972
|
-
}
|
|
973
|
-
else {
|
|
974
|
-
const decls = moduleSymbol.getDeclarations();
|
|
975
|
-
if (!decls) {
|
|
976
|
-
const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
977
|
-
deps.push({
|
|
978
|
-
filePath: PathUtils.norm(sf.fileName),
|
|
979
|
-
line: pos.line,
|
|
980
|
-
char: pos.character,
|
|
981
|
-
code: undefined,
|
|
982
|
-
severity: "error",
|
|
983
|
-
message: `import decls not found (${moduleSymbol.name})`,
|
|
984
|
-
type: "deps",
|
|
985
|
-
});
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
const namedBindings = node.importClause?.namedBindings;
|
|
990
|
-
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
991
|
-
for (const el of namedBindings.elements) {
|
|
992
|
-
for (const decl of decls) {
|
|
993
|
-
deps.push({
|
|
994
|
-
fileName: PathUtils.norm(decl.getSourceFile().fileName),
|
|
995
|
-
importName: el.name.text,
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
for (const decl of decls) {
|
|
1002
|
-
deps.push({
|
|
1003
|
-
fileName: PathUtils.norm(decl.getSourceFile().fileName),
|
|
1004
|
-
importName: "*",
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
1012
|
-
if (ts.isStringLiteral(node.arguments[0]) && node.arguments[0].text.startsWith("./")) {
|
|
1013
|
-
|
|
1014
|
-
const moduleSymbol = tc.getSymbolAtLocation(node.arguments[0]);
|
|
1015
|
-
if (!moduleSymbol) {
|
|
1016
|
-
deps.push({
|
|
1017
|
-
fileName: PathUtils.norm(path.resolve(
|
|
1018
|
-
path.dirname(sf.fileName),
|
|
1019
|
-
node.arguments[0].text + ".ts",
|
|
1020
|
-
)),
|
|
1021
|
-
importName: "*",
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
|
-
// const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
1025
|
-
// deps.push({
|
|
1026
|
-
// filePath: PathUtil.norm(sf.fileName),
|
|
1027
|
-
// line: pos.line,
|
|
1028
|
-
// char: pos.character,
|
|
1029
|
-
// code: undefined,
|
|
1030
|
-
// severity: "error",
|
|
1031
|
-
// message: `import() moduleSymbol not found (${node.arguments[0].text})`,
|
|
1032
|
-
// type: "deps",
|
|
1033
|
-
// });
|
|
1034
|
-
// return;
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
const decls = moduleSymbol.getDeclarations();
|
|
1038
|
-
if (!decls) {
|
|
1039
|
-
const pos = ts.getLineAndCharacterOfPosition(sf, node.getStart());
|
|
1040
|
-
deps.push({
|
|
1041
|
-
filePath: PathUtils.norm(sf.fileName),
|
|
1042
|
-
line: pos.line,
|
|
1043
|
-
char: pos.character,
|
|
1044
|
-
code: undefined,
|
|
1045
|
-
severity: "error",
|
|
1046
|
-
message: `import() decls not found (${node.arguments[0].text})`,
|
|
1047
|
-
type: "deps",
|
|
1048
|
-
});
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
for (const decl of decls) {
|
|
1053
|
-
deps.push({
|
|
1054
|
-
fileName: PathUtils.norm(decl.getSourceFile().fileName),
|
|
1055
|
-
importName: "*",
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
node.forEachChild(visit);
|
|
1063
|
-
};
|
|
682
|
+
}
|
|
1064
683
|
|
|
1065
|
-
|
|
684
|
+
return { filePath: realFilePath, text: realText };
|
|
685
|
+
}
|
|
1066
686
|
|
|
1067
|
-
|
|
687
|
+
private _debug(...msg: any[]): void {
|
|
688
|
+
this._logger.debug(`[${path.basename(this._opt.pkgPath)}]`, ...msg);
|
|
1068
689
|
}
|
|
1069
690
|
}
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
interface ITsConfigInfo {
|
|
694
|
+
fileNames: string[];
|
|
695
|
+
options: ts.CompilerOptions;
|
|
696
|
+
compilerHost: ts.CompilerHost;
|
|
697
|
+
distPath: string;
|
|
698
|
+
}
|