@simplysm/sd-cli 11.0.3 → 11.0.5
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/build-cluster.js +1 -1
- package/dist/build-cluster.js.map +1 -1
- package/dist/build-tools/SdLinter.js +2 -2
- package/dist/build-tools/SdLinter.js.map +1 -1
- package/dist/build-tools/SdNgBundler.d.ts +23 -0
- package/dist/build-tools/SdNgBundler.js +331 -0
- package/dist/build-tools/SdNgBundler.js.map +1 -0
- package/dist/build-tools/SdTsBundler.d.ts +15 -0
- package/dist/build-tools/SdTsBundler.js +96 -0
- package/dist/build-tools/SdTsBundler.js.map +1 -0
- package/dist/build-tools/SdTsCompiler.d.ts +28 -0
- package/dist/build-tools/SdTsCompiler.js +212 -0
- package/dist/build-tools/SdTsCompiler.js.map +1 -0
- package/dist/builders/SdCliClientBuilder.d.ts +4 -2
- package/dist/builders/SdCliClientBuilder.js +60 -297
- package/dist/builders/SdCliClientBuilder.js.map +1 -1
- package/dist/builders/SdCliServerBuilder.js +79 -216
- package/dist/builders/SdCliServerBuilder.js.map +1 -1
- package/dist/builders/SdCliTsLibBuilder.d.ts +0 -1
- package/dist/builders/SdCliTsLibBuilder.js +35 -33
- package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
- package/dist/commons.d.ts +7 -0
- package/dist/entry/SdCliProject.js +13 -10
- package/dist/entry/SdCliProject.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/server-worker.js +3 -0
- package/dist/server-worker.js.map +1 -1
- package/dist/utils/SdCliBuildResultUtil.d.ts +1 -1
- package/dist/utils/SdCliBuildResultUtil.js +4 -4
- package/dist/utils/SdCliBuildResultUtil.js.map +1 -1
- package/package.json +14 -12
- package/src/build-cluster.ts +1 -1
- package/src/build-tools/SdLinter.ts +2 -2
- package/src/build-tools/SdNgBundler.ts +404 -0
- package/src/build-tools/SdTsBundler.ts +106 -0
- package/src/build-tools/SdTsCompiler.ts +304 -0
- package/src/builders/SdCliClientBuilder.ts +75 -322
- package/src/builders/SdCliServerBuilder.ts +95 -243
- package/src/builders/SdCliTsLibBuilder.ts +39 -40
- package/src/commons.ts +6 -0
- package/src/entry/SdCliProject.ts +17 -12
- package/src/index.ts +3 -1
- package/src/server-worker.ts +3 -0
- package/src/utils/SdCliBuildResultUtil.ts +4 -4
- package/dist/build-tools/SdTsIncrementalBuilder.d.ts +0 -31
- package/dist/build-tools/SdTsIncrementalBuilder.js +0 -126
- package/dist/build-tools/SdTsIncrementalBuilder.js.map +0 -1
- package/src/build-tools/SdTsIncrementalBuilder.ts +0 -207
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import {SdCliBuildResultUtil} from "../utils/SdCliBuildResultUtil";
|
|
4
|
+
import {FsUtil, Logger, PathUtil} from "@simplysm/sd-core-node";
|
|
5
|
+
import {ISdCliPackageBuildResult, ITsConfig} from "../commons";
|
|
6
|
+
import {NgtscProgram} from "@angular/compiler-cli";
|
|
7
|
+
import {createHash} from "crypto";
|
|
8
|
+
import {fileURLToPath, pathToFileURL} from "url";
|
|
9
|
+
import * as sass from "sass";
|
|
10
|
+
|
|
11
|
+
export class SdTsCompiler {
|
|
12
|
+
private readonly _logger = Logger.get(["simplysm", "sd-cli", "SdTsCompiler"]);
|
|
13
|
+
|
|
14
|
+
private readonly _parsedTsConfig: ts.ParsedCommandLine;
|
|
15
|
+
private readonly _writeFileCache = new Map<string, string>();
|
|
16
|
+
private readonly _compilerHost: ts.CompilerHost;
|
|
17
|
+
private _program?: ts.Program;
|
|
18
|
+
private _builder?: ts.SemanticDiagnosticsBuilderProgram;
|
|
19
|
+
|
|
20
|
+
//-- for ng
|
|
21
|
+
private readonly _isForAngular: boolean;
|
|
22
|
+
private _ngProgram?: NgtscProgram;
|
|
23
|
+
private readonly _styleDepsCache = new Map<string, Set<string>>();
|
|
24
|
+
private _markedChanges: string[] = [];
|
|
25
|
+
|
|
26
|
+
public get program(): ts.Program {
|
|
27
|
+
if (!this._program) {
|
|
28
|
+
throw new Error("TS 프로그램 NULL");
|
|
29
|
+
}
|
|
30
|
+
return this._program;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public constructor(private readonly _opt: {
|
|
34
|
+
pkgPath: string,
|
|
35
|
+
emit: boolean;
|
|
36
|
+
emitDts: boolean;
|
|
37
|
+
globalStyle: boolean;
|
|
38
|
+
}) {
|
|
39
|
+
//-- tsconfig
|
|
40
|
+
const tsConfigFilePath = path.resolve(_opt.pkgPath, "tsconfig.json");
|
|
41
|
+
const tsConfig = FsUtil.readJson(tsConfigFilePath) as ITsConfig;
|
|
42
|
+
this._parsedTsConfig = ts.parseJsonConfigFileContent(
|
|
43
|
+
tsConfig,
|
|
44
|
+
ts.sys,
|
|
45
|
+
_opt.pkgPath,
|
|
46
|
+
{
|
|
47
|
+
...tsConfig.angularCompilerOptions ?? {},
|
|
48
|
+
..._opt.emitDts !== undefined ? {declaration: _opt.emitDts} : {}
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
//-- vars
|
|
53
|
+
this._isForAngular = Boolean(tsConfig.angularCompilerOptions);
|
|
54
|
+
|
|
55
|
+
//-- host
|
|
56
|
+
this._compilerHost = ts.createIncrementalCompilerHost(this._parsedTsConfig.options);
|
|
57
|
+
if (tsConfig.angularCompilerOptions) {
|
|
58
|
+
this._compilerHost["readResource"] = (fileName: string) => {
|
|
59
|
+
return this._compilerHost.readFile(fileName);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
this._compilerHost["transformResource"] = async (data: string, context: {
|
|
63
|
+
type: string,
|
|
64
|
+
containingFile: string,
|
|
65
|
+
resourceFile: any
|
|
66
|
+
}) => {
|
|
67
|
+
if (context.resourceFile != null || context.type !== "style") {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const scssResult = await sass.compileStringAsync(data, {
|
|
73
|
+
url: new URL((context.containingFile as string) + ".scss"),
|
|
74
|
+
importer: {
|
|
75
|
+
findFileUrl: (url) => pathToFileURL(url)
|
|
76
|
+
},
|
|
77
|
+
logger: sass.Logger.silent
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const styleContent = scssResult.css.toString();
|
|
81
|
+
|
|
82
|
+
const deps = scssResult.loadedUrls.slice(1).map((item) => path.resolve(fileURLToPath(item.href)));
|
|
83
|
+
for (const dep of deps) {
|
|
84
|
+
const depCache = this._styleDepsCache.getOrCreate(dep, new Set<string>());
|
|
85
|
+
depCache.add(path.resolve(context.containingFile));
|
|
86
|
+
}
|
|
87
|
+
return {content: styleContent};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
this._logger.error("scss 파싱 에러", err);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public markChanges(changes: string[]): void {
|
|
98
|
+
this._markedChanges.push(...changes);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public async buildAsync(): Promise<{
|
|
102
|
+
affectedFilePaths: string[];
|
|
103
|
+
results: ISdCliPackageBuildResult[];
|
|
104
|
+
}> {
|
|
105
|
+
const markedChanges = this._markedChanges;
|
|
106
|
+
this._markedChanges = [];
|
|
107
|
+
|
|
108
|
+
const distPath = path.resolve(this._opt.pkgPath, "dist");
|
|
109
|
+
const srcFilePaths = await FsUtil.globAsync(path.resolve(this._opt.pkgPath, "src/**/*.{ts,tsx}"));
|
|
110
|
+
const srcFilePathSet = new Set<string>(srcFilePaths);
|
|
111
|
+
|
|
112
|
+
if (this._isForAngular) {
|
|
113
|
+
this._ngProgram = new NgtscProgram(
|
|
114
|
+
srcFilePaths,
|
|
115
|
+
this._parsedTsConfig.options,
|
|
116
|
+
this._compilerHost,
|
|
117
|
+
this._ngProgram
|
|
118
|
+
);
|
|
119
|
+
this._program = this._ngProgram.getTsProgram();
|
|
120
|
+
|
|
121
|
+
const baseGetSourceFiles = this._program.getSourceFiles;
|
|
122
|
+
this._program.getSourceFiles = function (...parameters) {
|
|
123
|
+
const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
|
|
124
|
+
|
|
125
|
+
for (const file of files) {
|
|
126
|
+
if (file.version === undefined) {
|
|
127
|
+
file.version = createHash("sha256").update(file.text).digest("hex");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return files;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this._program = ts.createProgram(
|
|
136
|
+
srcFilePaths,
|
|
137
|
+
this._parsedTsConfig.options,
|
|
138
|
+
this._compilerHost,
|
|
139
|
+
this._program
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this._builder = ts.createSemanticDiagnosticsBuilderProgram(
|
|
144
|
+
this._program,
|
|
145
|
+
this._compilerHost,
|
|
146
|
+
this._builder
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const diagnostics: ts.Diagnostic[] = [];
|
|
150
|
+
const affectedFilePaths: string[] = [];
|
|
151
|
+
|
|
152
|
+
if (this._ngProgram) {
|
|
153
|
+
diagnostics.push(...this._ngProgram.compiler.getOptionDiagnostics());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
diagnostics.push(
|
|
157
|
+
...this._builder.getOptionsDiagnostics(),
|
|
158
|
+
...this._builder.getGlobalDiagnostics(),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (this._ngProgram) {
|
|
162
|
+
await this._ngProgram.compiler.analyzeAsync();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 확인중...`);
|
|
166
|
+
while (true) {
|
|
167
|
+
let affectedSourceFile: ts.SourceFile | undefined;
|
|
168
|
+
|
|
169
|
+
const semanticResult = this._builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
|
|
170
|
+
//-- ngtypecheck의 org파일 포함 (ngtypecheck 파일는 무시)
|
|
171
|
+
if (this._ngProgram?.compiler.ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith(".ngtypecheck.ts")) {
|
|
172
|
+
const orgFileName = sourceFile.fileName.slice(0, -15) + ".ts";
|
|
173
|
+
const orgSourceFile = this._builder!.getSourceFile(orgFileName);
|
|
174
|
+
if (orgSourceFile) {
|
|
175
|
+
affectedSourceFile = orgSourceFile;
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
//-- 소스폴더 파일 포함
|
|
181
|
+
else if (srcFilePathSet.has(path.resolve(sourceFile.fileName))) {
|
|
182
|
+
affectedSourceFile = sourceFile;
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//-- 나머지 무시
|
|
187
|
+
else {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
if (!semanticResult || !affectedSourceFile) break;
|
|
192
|
+
diagnostics.push(...semanticResult.result);
|
|
193
|
+
|
|
194
|
+
if ("fileName" in affectedSourceFile) {
|
|
195
|
+
affectedFilePaths.push(path.resolve(affectedSourceFile.fileName));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this._isForAngular) {
|
|
200
|
+
for (const markedChange of markedChanges) {
|
|
201
|
+
const depsSet = this._styleDepsCache.get(markedChange);
|
|
202
|
+
if (depsSet) {
|
|
203
|
+
affectedFilePaths.push(...depsSet);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
affectedFilePaths.distinctThis();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const globalStyleFilePath = path.resolve(this._opt.pkgPath, "src/styles.scss");
|
|
210
|
+
if (this._opt.globalStyle && FsUtil.exists(globalStyleFilePath) && markedChanges.includes(globalStyleFilePath)) {
|
|
211
|
+
affectedFilePaths.push(globalStyleFilePath);
|
|
212
|
+
affectedFilePaths.distinctThis();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"}...`);
|
|
216
|
+
|
|
217
|
+
for (const affectedFilePath of affectedFilePaths) {
|
|
218
|
+
if (this._opt.globalStyle && affectedFilePath === globalStyleFilePath) {
|
|
219
|
+
try {
|
|
220
|
+
const content = await FsUtil.readFileAsync(affectedFilePath);
|
|
221
|
+
const scssResult = await sass.compileStringAsync(content, {
|
|
222
|
+
url: new URL(affectedFilePath),
|
|
223
|
+
importer: {
|
|
224
|
+
findFileUrl: (url) => pathToFileURL(url)
|
|
225
|
+
},
|
|
226
|
+
logger: sass.Logger.silent
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const deps = scssResult.loadedUrls.slice(1).map((item) => path.resolve(fileURLToPath(item.href)));
|
|
230
|
+
for (const dep of deps) {
|
|
231
|
+
const depCache = this._styleDepsCache.getOrCreate(dep, new Set<string>());
|
|
232
|
+
depCache.add(affectedFilePath);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (this._opt.emit) {
|
|
236
|
+
const outFilePath = path.resolve(this._opt.pkgPath, path.basename(affectedFilePath, path.extname(affectedFilePath)) + ".css");
|
|
237
|
+
|
|
238
|
+
this._writeFile(outFilePath, scssResult.css.toString());
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
this._logger.error(err);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const affectedSourceFile = this._builder.getSourceFile(affectedFilePath);
|
|
247
|
+
if (!affectedSourceFile) continue;
|
|
248
|
+
|
|
249
|
+
const emitResult = this._builder.emit(affectedSourceFile,
|
|
250
|
+
(filePath, data) => {
|
|
251
|
+
let realFilePath = filePath;
|
|
252
|
+
let realData = data;
|
|
253
|
+
if (PathUtil.isChildPath(realFilePath, path.resolve(distPath, path.basename(this._opt.pkgPath), "src"))) {
|
|
254
|
+
realFilePath = path.resolve(distPath, path.relative(path.resolve(distPath, path.basename(this._opt.pkgPath), "src"), realFilePath));
|
|
255
|
+
|
|
256
|
+
if (filePath.endsWith(".js.map")) {
|
|
257
|
+
const sourceMapContents = JSON.parse(realData);
|
|
258
|
+
// remove "../../"
|
|
259
|
+
sourceMapContents.sources[0] = sourceMapContents.sources[0].slice(6);
|
|
260
|
+
realData = JSON.stringify(sourceMapContents);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this._writeFile(realFilePath, realData);
|
|
265
|
+
},
|
|
266
|
+
undefined,
|
|
267
|
+
!this._opt.emit,
|
|
268
|
+
{...this._ngProgram?.compiler.prepareEmit().transformers ?? {}}
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
diagnostics.push(...emitResult.diagnostics);
|
|
272
|
+
|
|
273
|
+
diagnostics.push(...this._builder.getSyntacticDiagnostics(affectedSourceFile));
|
|
274
|
+
|
|
275
|
+
if (
|
|
276
|
+
this._ngProgram &&
|
|
277
|
+
!affectedSourceFile.isDeclarationFile &&
|
|
278
|
+
!this._ngProgram.compiler.ignoreForEmit.has(affectedSourceFile) &&
|
|
279
|
+
!this._ngProgram.compiler.incrementalCompilation.safeToSkipEmit(affectedSourceFile)
|
|
280
|
+
) {
|
|
281
|
+
diagnostics.push(
|
|
282
|
+
...this._ngProgram.compiler.getDiagnosticsForFile(affectedSourceFile, 1)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this._logger.debug(`[${path.basename(this._opt.pkgPath)}] 영향받는 파일 ${this._opt.emit ? "EMIT" : "CHECK"} 완료`, affectedFilePaths);
|
|
289
|
+
|
|
290
|
+
const buildResults = diagnostics.map((item) => SdCliBuildResultUtil.convertFromTsDiag(item, this._opt.emit ? "build" : "check"));
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
affectedFilePaths: affectedFilePaths.filter((item) => !item.endsWith(".scss")),
|
|
294
|
+
results: buildResults
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private _writeFile(filePath: string, data: string): void {
|
|
299
|
+
if (this._writeFileCache.get(filePath) !== data) {
|
|
300
|
+
this._compilerHost.writeFile(filePath, data, false);
|
|
301
|
+
}
|
|
302
|
+
this._writeFileCache.set(filePath, data);
|
|
303
|
+
}
|
|
304
|
+
}
|