@simplysm/sd-cli 11.1.14 → 11.1.16

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.
Files changed (50) hide show
  1. package/dist/build-tools/SdLinter.d.ts +2 -2
  2. package/dist/build-tools/SdLinter.js +8 -17
  3. package/dist/build-tools/SdLinter.js.map +1 -1
  4. package/dist/build-tools/SdNgBundler.d.ts +2 -2
  5. package/dist/build-tools/SdNgBundler.js +97 -44
  6. package/dist/build-tools/SdNgBundler.js.map +1 -1
  7. package/dist/build-tools/SdTsBundler.js +19 -4
  8. package/dist/build-tools/SdTsBundler.js.map +1 -1
  9. package/dist/builders/SdCliClientBuilder.d.ts +1 -0
  10. package/dist/builders/SdCliClientBuilder.js +18 -21
  11. package/dist/builders/SdCliClientBuilder.js.map +1 -1
  12. package/dist/builders/SdCliJsLibLinter.js +3 -3
  13. package/dist/builders/SdCliJsLibLinter.js.map +1 -1
  14. package/dist/builders/SdCliServerBuilder.d.ts +1 -5
  15. package/dist/builders/SdCliServerBuilder.js +37 -30
  16. package/dist/builders/SdCliServerBuilder.js.map +1 -1
  17. package/dist/builders/SdCliTsLibBuilder.js +1 -2
  18. package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
  19. package/dist/bundle-plugins/sdLoadedFilesPlugin.d.ts +4 -0
  20. package/dist/bundle-plugins/sdLoadedFilesPlugin.js +15 -0
  21. package/dist/bundle-plugins/sdLoadedFilesPlugin.js.map +1 -0
  22. package/dist/bundle-plugins/sdNgPlugin.d.ts +13 -0
  23. package/dist/bundle-plugins/sdNgPlugin.js +267 -0
  24. package/dist/bundle-plugins/sdNgPlugin.js.map +1 -0
  25. package/dist/bundle-plugins/sdTscPlugin.d.ts +5 -0
  26. package/dist/bundle-plugins/sdTscPlugin.js +21 -0
  27. package/dist/bundle-plugins/sdTscPlugin.js.map +1 -0
  28. package/dist/commons.d.ts +1 -1
  29. package/dist/entry/SdCliProject.js +5 -1
  30. package/dist/entry/SdCliProject.js.map +1 -1
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/utils/SdCliBuildResultUtil.js +1 -1
  35. package/dist/utils/SdCliBuildResultUtil.js.map +1 -1
  36. package/package.json +16 -16
  37. package/src/build-tools/SdLinter.ts +10 -18
  38. package/src/build-tools/SdNgBundler.ts +103 -52
  39. package/src/build-tools/SdTsBundler.ts +24 -4
  40. package/src/builders/SdCliClientBuilder.ts +27 -26
  41. package/src/builders/SdCliJsLibLinter.ts +3 -3
  42. package/src/builders/SdCliServerBuilder.ts +40 -35
  43. package/src/builders/SdCliTsLibBuilder.ts +1 -3
  44. package/src/bundle-plugins/sdLoadedFilesPlugin.ts +19 -0
  45. package/src/bundle-plugins/sdNgPlugin.ts +408 -0
  46. package/src/bundle-plugins/sdTscPlugin.ts +25 -0
  47. package/src/commons.ts +1 -1
  48. package/src/entry/SdCliProject.ts +5 -2
  49. package/src/index.ts +3 -0
  50. package/src/utils/SdCliBuildResultUtil.ts +1 -1
@@ -0,0 +1,408 @@
1
+ import esbuild from "esbuild";
2
+ import {FsUtil} from "@simplysm/sd-core-node";
3
+ import ts from "typescript";
4
+ import path from "path";
5
+ import {convertTypeScriptDiagnostic} from "@angular-devkit/build-angular/src/tools/esbuild/angular/diagnostics";
6
+ import {AngularCompilerHost} from "@angular-devkit/build-angular/src/tools/esbuild/angular/angular-host";
7
+ import {
8
+ ComponentStylesheetBundler
9
+ } from "@angular-devkit/build-angular/src/tools/esbuild/angular/component-stylesheets";
10
+ import {transformSupportedBrowsersToTargets} from "@angular-devkit/build-angular/src/tools/esbuild/utils";
11
+ import browserslist from "browserslist";
12
+ import {StringUtil} from "@simplysm/sd-core-common";
13
+ import {NgtscProgram, OptimizeFor} from "@angular/compiler-cli";
14
+ import {createHash} from "crypto";
15
+ import transformJavaScript from "@angular-devkit/build-angular/src/tools/esbuild/javascript-transformer-worker";
16
+
17
+ export function sdNgPlugin(conf: {
18
+ pkgPath: string;
19
+ dev: boolean;
20
+ modifiedFileSet: Set<string>;
21
+ result: INgResultCache;
22
+ }): esbuild.Plugin {
23
+ const tsConfigPath = path.resolve(conf.pkgPath, "tsconfig.json");
24
+ const tsConfig = FsUtil.readJson(tsConfigPath);
25
+ const parsedTsConfig = ts.parseJsonConfigFileContent(tsConfig, ts.sys, conf.pkgPath, {
26
+ ...tsConfig.angularCompilerOptions,
27
+ declaration: false
28
+ });
29
+
30
+ const sourceFileCache = new Map<string, ts.SourceFile>();
31
+ const referencingMap = new Map<string, Set<string>>();
32
+
33
+ let ngProgram: NgtscProgram | undefined;
34
+ let builder: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined;
35
+
36
+ let resultCache: IResultCache = {
37
+ watchFileSet: new Set<string>(),
38
+ affectedFileSet: new Set<string>(),
39
+ outputPrepareMap: new Map<string, string>(),
40
+ additionalResultMap: new Map<string, IAdditionalResult>()
41
+ };
42
+
43
+ let stylesheetBundler: ComponentStylesheetBundler | undefined;
44
+
45
+ function createCompilerHost() {
46
+ const compilerHost: AngularCompilerHost = ts.createIncrementalCompilerHost(parsedTsConfig.options);
47
+ compilerHost.readResource = (fileName: string) => {
48
+ return compilerHost.readFile(fileName) ?? "";
49
+ };
50
+
51
+ compilerHost.transformResource = async (data: string, context: {
52
+ type: string,
53
+ containingFile: string,
54
+ resourceFile: any
55
+ }) => {
56
+ if (context.type !== "style") {
57
+ return null;
58
+ }
59
+
60
+ const stylesheetResult = context.resourceFile != null
61
+ ? await stylesheetBundler!.bundleFile(context.resourceFile)
62
+ : await stylesheetBundler!.bundleInline(
63
+ data,
64
+ context.containingFile,
65
+ "scss",
66
+ );
67
+
68
+ resultCache.watchFileSet.add(path.normalize(context.containingFile));
69
+
70
+ if (stylesheetResult.referencedFiles) {
71
+ for (const referencedFile of stylesheetResult.referencedFiles) {
72
+ const referencingMapValSet = referencingMap.getOrCreate(path.normalize(referencedFile), new Set<string>());
73
+ referencingMapValSet.add(path.normalize(context.containingFile));
74
+ }
75
+
76
+ resultCache.watchFileSet.adds(...Array.from(stylesheetResult.referencedFiles.values()).map(item => path.normalize(item)));
77
+ }
78
+
79
+ resultCache.additionalResultMap.set(path.normalize(context.resourceFile ?? context.containingFile), {
80
+ outputFiles: stylesheetResult.resourceFiles,
81
+ metafile: stylesheetResult.metafile,
82
+ errors: stylesheetResult.errors,
83
+ warnings: stylesheetResult.warnings
84
+ });
85
+
86
+ return StringUtil.isNullOrEmpty(stylesheetResult.contents) ? null : {content: stylesheetResult.contents};
87
+ };
88
+
89
+ compilerHost.getModifiedResourceFiles = () => {
90
+ return conf.modifiedFileSet;
91
+ };
92
+
93
+ const baseGetSourceFile = compilerHost.getSourceFile;
94
+ compilerHost.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile, ...args) => {
95
+ if (!shouldCreateNewSourceFile && sourceFileCache.has(path.normalize(fileName))) {
96
+ return sourceFileCache.get(path.normalize(fileName));
97
+ }
98
+
99
+ const file = baseGetSourceFile.call(
100
+ compilerHost,
101
+ fileName,
102
+ languageVersionOrOptions,
103
+ onError,
104
+ true,
105
+ ...args,
106
+ );
107
+
108
+ if (file) {
109
+ sourceFileCache.set(path.normalize(fileName), file);
110
+ }
111
+
112
+ return file;
113
+ };
114
+
115
+ return compilerHost;
116
+ }
117
+
118
+ function findAffectedFileSet() {
119
+ const affectedFileSet = new Set<string>();
120
+
121
+ while (true) {
122
+ const result = builder!.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => {
123
+ if (ngProgram?.compiler.ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
124
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
125
+ const originalSourceFile = sourceFileCache.get(originalFilename);
126
+ if (originalSourceFile) {
127
+ affectedFileSet.add(path.normalize(originalSourceFile.fileName));
128
+ }
129
+
130
+ return true;
131
+ }
132
+
133
+ return false;
134
+ });
135
+
136
+ if (!result) {
137
+ break;
138
+ }
139
+
140
+ affectedFileSet.add(path.normalize((result.affected as ts.SourceFile).fileName));
141
+ }
142
+
143
+ return affectedFileSet;
144
+ }
145
+
146
+ return {
147
+ name: "sd-ng",
148
+ setup: (build: esbuild.PluginBuild) => {
149
+ //-- stylesheetBundler
150
+ const browserTarget = transformSupportedBrowsersToTargets(browserslist("defaults and fully supports es6-module"));
151
+ stylesheetBundler = new ComponentStylesheetBundler(
152
+ {
153
+ workspaceRoot: conf.pkgPath,
154
+ optimization: !conf.dev,
155
+ sourcemap: conf.dev ? 'inline' : false,
156
+ outputNames: {bundles: '[name]', media: 'media/[name]'},
157
+ includePaths: [],
158
+ externalDependencies: [],
159
+ target: browserTarget,
160
+ preserveSymlinks: false,
161
+ tailwindConfiguration: undefined
162
+ },
163
+ conf.dev
164
+ );
165
+
166
+ //-- compilerHost
167
+ const compilerHost = createCompilerHost();
168
+
169
+ //-- vars
170
+
171
+ //---------------------------
172
+
173
+ build.onStart(async () => {
174
+ //-- modified
175
+ stylesheetBundler!.invalidate(conf.modifiedFileSet);
176
+ for (const modifiedFile of conf.modifiedFileSet) {
177
+ sourceFileCache.delete(modifiedFile);
178
+
179
+ if (referencingMap.has(modifiedFile)) {
180
+ for (const referencingFile of referencingMap.get(modifiedFile)!) {
181
+ sourceFileCache.delete(referencingFile);
182
+ }
183
+ }
184
+ }
185
+ referencingMap.clear();
186
+
187
+ //-- init resultCache
188
+
189
+ resultCache = {
190
+ watchFileSet: new Set<string>(),
191
+ affectedFileSet: new Set<string>(),
192
+ outputPrepareMap: new Map<string, string>(),
193
+ additionalResultMap: new Map<string, IAdditionalResult>()
194
+ };
195
+
196
+ //-- createBuilder
197
+
198
+ ngProgram = new NgtscProgram(
199
+ parsedTsConfig.fileNames,
200
+ parsedTsConfig.options,
201
+ compilerHost,
202
+ ngProgram
203
+ );
204
+ const ngCompiler = ngProgram.compiler;
205
+ const program = ngProgram.getTsProgram();
206
+
207
+ const baseGetSourceFiles = program.getSourceFiles;
208
+ program.getSourceFiles = function (...parameters) {
209
+ const files: readonly (ts.SourceFile & { version?: string })[] = baseGetSourceFiles(...parameters);
210
+
211
+ for (const file of files) {
212
+ if (file.version === undefined) {
213
+ file.version = createHash("sha256").update(file.text).digest("hex");
214
+ }
215
+ }
216
+
217
+ return files;
218
+ };
219
+
220
+ builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
221
+ program,
222
+ compilerHost,
223
+ builder
224
+ );
225
+
226
+ await ngCompiler.analyzeAsync();
227
+
228
+ //-- affectedFilePathSet
229
+
230
+ resultCache.affectedFileSet.adds(...findAffectedFileSet());
231
+
232
+ // Deps -> refMap
233
+ builder.getSourceFiles().filter(sf => !ngCompiler.ignoreForEmit.has(sf))
234
+ .forEach(sf => {
235
+ resultCache.watchFileSet.add(sf.fileName);
236
+
237
+ const deps = ngCompiler.getResourceDependencies(sf);
238
+ for (const dep of deps) {
239
+ const ref = referencingMap.getOrCreate(dep, new Set<string>());
240
+ ref.add(dep);
241
+
242
+ resultCache.watchFileSet.add(dep);
243
+ }
244
+ });
245
+
246
+ // refMap, modFile -> affectedFileSet
247
+ for (const modifiedFile of conf.modifiedFileSet) {
248
+ resultCache.affectedFileSet.adds(...referencingMap.get(modifiedFile) ?? []);
249
+ }
250
+
251
+ //-- diagnostics / build
252
+
253
+ const diagnostics: ts.Diagnostic[] = [];
254
+
255
+ diagnostics.push(
256
+ ...builder.getConfigFileParsingDiagnostics(),
257
+ ...ngCompiler.getOptionDiagnostics(),
258
+ ...builder.getOptionsDiagnostics(),
259
+ ...builder.getGlobalDiagnostics()
260
+ );
261
+
262
+ for (const affectedFile of resultCache.affectedFileSet) {
263
+ const affectedSourceFile = sourceFileCache.get(path.normalize(affectedFile));
264
+ if (!affectedSourceFile || ngCompiler.ignoreForDiagnostics.has(affectedSourceFile)) {
265
+ continue;
266
+ }
267
+
268
+ diagnostics.push(
269
+ ...builder.getSyntacticDiagnostics(affectedSourceFile),
270
+ ...builder.getSemanticDiagnostics(affectedSourceFile)
271
+ );
272
+
273
+ if (affectedSourceFile.isDeclarationFile) {
274
+ continue;
275
+ }
276
+
277
+ diagnostics.push(
278
+ ...ngCompiler.getDiagnosticsForFile(affectedSourceFile, OptimizeFor.WholeProgram),
279
+ );
280
+ }
281
+
282
+ //-- prepare emit cache
283
+ while (true) {
284
+ const affectedFileResult = builder.emitNextAffectedFile((fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
285
+ if (!sourceFiles || sourceFiles.length === 0) {
286
+ compilerHost.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
287
+ return;
288
+ }
289
+
290
+ const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
291
+ if (ngCompiler.ignoreForEmit.has(sourceFile)) {
292
+ return;
293
+ }
294
+
295
+ ngCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
296
+ resultCache.outputPrepareMap.set(path.normalize(sourceFile.fileName), text);
297
+ }, undefined, undefined, ngProgram.compiler.prepareEmit().transformers);
298
+
299
+ if (!affectedFileResult) {
300
+ break;
301
+ }
302
+
303
+ diagnostics.push(...affectedFileResult.result.diagnostics);
304
+ }
305
+
306
+ //-- return err/warn
307
+
308
+ return {
309
+ errors: [
310
+ ...diagnostics.filter(item => item.category === ts.DiagnosticCategory.Error).map(item => convertTypeScriptDiagnostic(ts, item)),
311
+ ...Array.from(resultCache.additionalResultMap.values()).flatMap(item => item.errors)
312
+ ].filterExists(),
313
+ warnings: [
314
+ ...diagnostics.filter(item => item.category !== ts.DiagnosticCategory.Error).map(item => convertTypeScriptDiagnostic(ts, item)),
315
+ ...Array.from(resultCache.additionalResultMap.values()).flatMap(item => item.warnings)
316
+ ],
317
+ };
318
+ });
319
+
320
+ build.onLoad({filter: /\.ts$/}, async (args) => {
321
+ const contents = resultCache.outputPrepareMap.get(path.normalize(args.path));
322
+
323
+ const {sideEffects} = await build.resolve(args.path, {
324
+ kind: 'import-statement',
325
+ resolveDir: build.initialOptions.absWorkingDir ?? '',
326
+ });
327
+
328
+ const newContents = await transformJavaScript["default"]({
329
+ filename: args.path,
330
+ data: contents!,
331
+ sourcemap: conf.dev,
332
+ thirdPartySourcemaps: conf.dev,
333
+ advancedOptimizations: true,
334
+ skipLinker: true,
335
+ sideEffects,
336
+ jit: false
337
+ });
338
+
339
+ return {contents: newContents, loader: "js"};
340
+ });
341
+
342
+ build.onLoad(
343
+ {filter: /\.[cm]?js$/},
344
+ async (args) => {
345
+ const {sideEffects} = await build.resolve(args.path, {
346
+ kind: 'import-statement',
347
+ resolveDir: build.initialOptions.absWorkingDir ?? '',
348
+ });
349
+
350
+ const contents = await FsUtil.readFileAsync(args.path);
351
+
352
+ const newContents = await transformJavaScript["default"]({
353
+ filename: args.path,
354
+ data: contents!,
355
+ sourcemap: conf.dev,
356
+ thirdPartySourcemaps: conf.dev,
357
+ advancedOptimizations: true,
358
+ skipLinker: false,
359
+ sideEffects,
360
+ jit: false
361
+ });
362
+
363
+ return {
364
+ contents: newContents,
365
+ loader: 'js',
366
+ };
367
+ }
368
+ );
369
+
370
+ build.onEnd((result) => {
371
+ for (const {outputFiles, metafile} of resultCache.additionalResultMap.values()) {
372
+ result.outputFiles?.push(...outputFiles);
373
+
374
+ if (result.metafile && metafile) {
375
+ result.metafile.inputs = {...result.metafile.inputs, ...metafile.inputs};
376
+ result.metafile.outputs = {...result.metafile.outputs, ...metafile.outputs};
377
+ }
378
+ }
379
+
380
+ conf.result.watchFileSet = resultCache.watchFileSet;
381
+ conf.result.affectedFileSet = resultCache.affectedFileSet;
382
+ conf.result.outputFiles = result.outputFiles;
383
+ conf.result.metafile = result.metafile;
384
+ });
385
+ }
386
+ };
387
+ }
388
+
389
+ interface IResultCache {
390
+ watchFileSet: Set<string>;
391
+ affectedFileSet: Set<string>;
392
+ outputPrepareMap: Map<string, string>;
393
+ additionalResultMap: Map<string, IAdditionalResult>;
394
+ }
395
+
396
+ interface IAdditionalResult {
397
+ outputFiles: esbuild.OutputFile[];
398
+ metafile?: esbuild.Metafile;
399
+ errors?: esbuild.PartialMessage[];
400
+ warnings: esbuild.PartialMessage[];
401
+ }
402
+
403
+ export interface INgResultCache {
404
+ watchFileSet?: Set<string>;
405
+ affectedFileSet?: Set<string>;
406
+ outputFiles?: esbuild.OutputFile[];
407
+ metafile?: esbuild.Metafile;
408
+ }
@@ -0,0 +1,25 @@
1
+ import esbuild from "esbuild";
2
+ import {FsUtil} from "@simplysm/sd-core-node";
3
+ import ts from "typescript";
4
+ import path from "path";
5
+
6
+ export function sdTscPlugin(conf: {
7
+ parsedTsconfig: ts.ParsedCommandLine;
8
+ }): esbuild.Plugin {
9
+ return {
10
+ name: "sd-tsc",
11
+ setup: (build: esbuild.PluginBuild) => {
12
+ if (conf.parsedTsconfig.options.emitDecoratorMetadata) {
13
+ build.onLoad({filter: /\.ts$/}, async (args) => {
14
+
15
+ const fileContent = await FsUtil.readFileAsync(args.path);
16
+ const program = ts.transpileModule(fileContent, {
17
+ compilerOptions: conf.parsedTsconfig.options,
18
+ fileName: path.basename(args.path),
19
+ });
20
+ return {contents: program.outputText};
21
+ });
22
+ }
23
+ }
24
+ };
25
+ }
package/src/commons.ts CHANGED
@@ -49,7 +49,7 @@ export interface ISdCliPackageBuildResult {
49
49
  code: string | undefined;
50
50
  severity: "error" | "warning" | "suggestion" | "message";
51
51
  message: string;
52
- type: "build" | "lint" | "style" | undefined;
52
+ type: "build" | "lint" | "style" | "check" | undefined;
53
53
  }
54
54
 
55
55
  export interface ISdCliConfig {
@@ -485,16 +485,19 @@ export class SdCliProject {
485
485
  logger.info("모든 빌드가 완료되었습니다.");
486
486
  }
487
487
 
488
+ // TODO: npm:piscina??
488
489
  private static async _prepareClusterAsync(): Promise<cp.ChildProcess> {
489
490
  const logger = Logger.get(["simplysm", "sd-cli", "SdCliProject", "_runBuildClusterAsync"]);
490
-
491
491
  return await new Promise<cp.ChildProcess>(async (resolve, reject) => {
492
492
  const cluster = cp.fork(
493
493
  fileURLToPath(await import.meta.resolve!("../build-cluster")),
494
494
  [],
495
495
  {
496
496
  stdio: ["pipe", "pipe", "pipe", "ipc"],
497
- env: process.env
497
+ env: {
498
+ ...process.env,
499
+ // "NG_BUILD_PARALLEL_TS": "0"
500
+ }
498
501
  }
499
502
  );
500
503
 
package/src/index.ts CHANGED
@@ -10,6 +10,9 @@ export * from "./builders/SdCliClientBuilder";
10
10
  export * from "./builders/SdCliJsLibLinter";
11
11
  export * from "./builders/SdCliServerBuilder";
12
12
  export * from "./builders/SdCliTsLibBuilder";
13
+ export * from "./bundle-plugins/sdLoadedFilesPlugin";
14
+ export * from "./bundle-plugins/sdNgPlugin";
15
+ export * from "./bundle-plugins/sdTscPlugin";
13
16
  export * from "./commons";
14
17
  export * from "./entry/SdCliElectron";
15
18
  export * from "./entry/SdCliLocalUpdate";
@@ -25,7 +25,7 @@ export class SdCliBuildResultUtil {
25
25
  code,
26
26
  severity,
27
27
  message,
28
- type: "build" as const
28
+ type
29
29
  };
30
30
  }
31
31