@simplysm/sd-cli 14.0.37 → 14.0.39

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 (107) hide show
  1. package/dist/angular/angular-build-pipeline.d.ts +1 -1
  2. package/dist/angular/angular-build-pipeline.js +1 -1
  3. package/dist/angular/client-transform-stylesheet.d.ts +1 -1
  4. package/dist/angular/client-transform-stylesheet.js +3 -3
  5. package/dist/angular/vite-angular-plugin.d.ts +2 -5
  6. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  7. package/dist/angular/vite-angular-plugin.js +19 -72
  8. package/dist/angular/vite-angular-plugin.js.map +1 -1
  9. package/dist/commands/publish/storage-publisher.js +1 -0
  10. package/dist/commands/publish/storage-publisher.js.map +1 -1
  11. package/dist/dev-server/hmr-service.d.ts +2 -0
  12. package/dist/dev-server/hmr-service.d.ts.map +1 -1
  13. package/dist/dev-server/hmr-service.js +24 -10
  14. package/dist/dev-server/hmr-service.js.map +1 -1
  15. package/dist/electron/electron.js +4 -4
  16. package/dist/electron/electron.js.map +1 -1
  17. package/dist/engines/BaseEngine.d.ts.map +1 -1
  18. package/dist/engines/BaseEngine.js +10 -1
  19. package/dist/engines/BaseEngine.js.map +1 -1
  20. package/dist/engines/EsbuildClientEngine.d.ts.map +1 -1
  21. package/dist/engines/EsbuildClientEngine.js +12 -1
  22. package/dist/engines/EsbuildClientEngine.js.map +1 -1
  23. package/dist/engines/engine-factory.d.ts +0 -4
  24. package/dist/engines/engine-factory.d.ts.map +1 -1
  25. package/dist/engines/engine-factory.js.map +1 -1
  26. package/dist/esbuild/esbuild-client-config.d.ts +0 -2
  27. package/dist/esbuild/esbuild-client-config.d.ts.map +1 -1
  28. package/dist/esbuild/esbuild-client-config.js +24 -14
  29. package/dist/esbuild/esbuild-client-config.js.map +1 -1
  30. package/dist/esbuild/esbuild-postcss-plugin.d.ts +8 -0
  31. package/dist/esbuild/esbuild-postcss-plugin.d.ts.map +1 -0
  32. package/dist/esbuild/esbuild-postcss-plugin.js +105 -0
  33. package/dist/esbuild/esbuild-postcss-plugin.js.map +1 -0
  34. package/dist/esbuild/esbuild-tsc-plugin.d.ts +23 -0
  35. package/dist/esbuild/esbuild-tsc-plugin.d.ts.map +1 -0
  36. package/dist/esbuild/esbuild-tsc-plugin.js +60 -0
  37. package/dist/esbuild/esbuild-tsc-plugin.js.map +1 -0
  38. package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
  39. package/dist/orchestrators/DevOrchestrator.js +0 -5
  40. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  41. package/dist/orchestrators/TypecheckOrchestrator.js +2 -2
  42. package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -1
  43. package/dist/sd-cli.js +2 -1
  44. package/dist/sd-cli.js.map +1 -1
  45. package/dist/utils/output-utils.d.ts.map +1 -1
  46. package/dist/utils/output-utils.js +3 -2
  47. package/dist/utils/output-utils.js.map +1 -1
  48. package/dist/workers/client.worker.d.ts.map +1 -1
  49. package/dist/workers/client.worker.js +39 -3
  50. package/dist/workers/client.worker.js.map +1 -1
  51. package/dist/workers/server-build.worker.d.ts.map +1 -1
  52. package/dist/workers/server-build.worker.js +129 -90
  53. package/dist/workers/server-build.worker.js.map +1 -1
  54. package/dist/workers/server-esbuild-context.d.ts +27 -0
  55. package/dist/workers/server-esbuild-context.d.ts.map +1 -1
  56. package/dist/workers/server-esbuild-context.js +43 -3
  57. package/dist/workers/server-esbuild-context.js.map +1 -1
  58. package/package.json +6 -4
  59. package/src/angular/angular-build-pipeline.ts +2 -2
  60. package/src/angular/client-transform-stylesheet.ts +4 -4
  61. package/src/angular/vite-angular-plugin.ts +19 -82
  62. package/src/commands/publish/storage-publisher.ts +1 -0
  63. package/src/dev-server/hmr-service.ts +28 -13
  64. package/src/electron/electron.ts +5 -5
  65. package/src/engines/BaseEngine.ts +10 -1
  66. package/src/engines/EsbuildClientEngine.ts +13 -1
  67. package/src/engines/engine-factory.ts +0 -1
  68. package/src/esbuild/esbuild-client-config.ts +27 -18
  69. package/src/esbuild/esbuild-postcss-plugin.ts +117 -0
  70. package/src/esbuild/esbuild-tsc-plugin.ts +83 -0
  71. package/src/orchestrators/DevOrchestrator.ts +0 -6
  72. package/src/orchestrators/TypecheckOrchestrator.ts +2 -2
  73. package/src/sd-cli.ts +2 -1
  74. package/src/utils/output-utils.ts +3 -2
  75. package/src/workers/client.worker.ts +40 -3
  76. package/src/workers/server-build.worker.ts +136 -97
  77. package/src/workers/server-esbuild-context.ts +59 -3
  78. package/tests/angular/_vite-angular-plugin-test-setup.ts +3 -30
  79. package/tests/angular/client-transform-stylesheet.spec.ts +1 -1
  80. package/tests/angular/vite-angular-plugin-legacy-watch.spec.ts +14 -31
  81. package/tests/angular/vite-angular-plugin-vitest.spec.ts +4 -34
  82. package/tests/angular/vite-angular-plugin.spec.ts +24 -60
  83. package/tests/commands/typecheck.spec.ts +1 -1
  84. package/tests/engines/base-engine.spec.ts +25 -0
  85. package/tests/engines/engine-adapter-isolation.spec.ts +3 -3
  86. package/tests/engines/esbuild-client-engine.acc.spec.ts +29 -0
  87. package/tests/engines/esbuild-client-engine.spec.ts +26 -0
  88. package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +349 -0
  89. package/tests/esbuild/esbuild-tsc-plugin.spec.ts +230 -0
  90. package/tests/orchestrators/build-orchestrator.spec.ts +1 -1
  91. package/tests/orchestrators/dev-orchestrator.spec.ts +1 -1
  92. package/tests/orchestrators/typecheck-orchestrator.spec.ts +1 -1
  93. package/tests/orchestrators/watch-orchestrator.spec.ts +1 -1
  94. package/tests/utils/esbuild-client-config-postcss.verify.md +6 -0
  95. package/tests/utils/esbuild-client-config.acc.spec.ts +30 -15
  96. package/tests/utils/esbuild-client-config.spec.ts +73 -11
  97. package/tests/utils/esbuild-postcss-plugin.acc.spec.ts +299 -0
  98. package/tests/utils/esbuild-postcss-plugin.spec.ts +290 -0
  99. package/tests/utils/esbuild-scss-plugin.acc.spec.ts +1 -0
  100. package/tests/utils/hmr-service-dispatcher.acc.spec.ts +70 -0
  101. package/tests/workers/client-worker-initial-build-error.verify.md +8 -0
  102. package/tests/workers/server-build-lint.spec.ts +43 -0
  103. package/tests/workers/server-build-worker-refactoring.verify.md +14 -0
  104. package/tests/workers/server-build-worker.spec.ts +122 -9
  105. package/tests/workers/server-esbuild-context-tsc.verify.md +7 -0
  106. package/tests/workers/server-esbuild-context.acc.spec.ts +156 -2
  107. package/tests/workers/server-esbuild-context.spec.ts +320 -2
@@ -1 +1 @@
1
- {"version":3,"file":"server-esbuild-context.js","sourceRoot":"","sources":["../../src/workers/server-esbuild-context.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AAYnC,wCAAwC;AACxC,IAAI,OAAyC,CAAC;AAE9C,iCAAiC;AACjC,IAAI,YAA0C,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA8B;IAChE,MAAM,WAAW,GAAG,0BAA0B,CAAC;QAC7C,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,EAAE,IAAI;KACV,CAAC,CAAC;IAEH,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;QAC9B,GAAG,WAAW;QACd,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAK3B,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAEvC,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC5B,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,MAAM,uBAAuB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEpD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QAC9C,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAA8B;IAClE,MAAM,UAAU,GAAG,OAAO,CAAC;IAC3B,OAAO,GAAG,SAAS,CAAC;IACpB,YAAY,GAAG,SAAS,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;YAAS,CAAC;QACT,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,gBAAgB,GAAG,OAAO,CAAC;IACjC,OAAO,GAAG,SAAS,CAAC;IACpB,YAAY,GAAG,SAAS,CAAC;IAEzB,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,gBAAgB,CAAC,OAAO,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,IAAI,IAAI,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"server-esbuild-context.js","sourceRoot":"","sources":["../../src/workers/server-esbuild-context.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,eAAe,EAAwB,MAAM,+BAA+B,CAAC;AAqBtF,wCAAwC;AACxC,IAAI,OAAyC,CAAC;AAE9C,iCAAiC;AACjC,IAAI,YAA0C,CAAC;AAE/C,gCAAgC;AAChC,IAAI,SAAsC,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA8B;IAChE,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,SAAS,GAAG,eAAe,CAAC;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;YACpB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;YAC1B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;YACpB,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;SACvC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,0BAA0B,CAAC;QAC7C,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,EAAE,IAAI;KACV,CAAC,CAAC;IAEH,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;QAC9B,GAAG,WAAW;QACd,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;QACpD,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAK3B,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAEvC,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC5B,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,MAAM,uBAAuB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC/C,MAAM,SAAS,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,SAAS,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEpD,OAAO;QACL,OAAO,EAAE,SAAS,CAAC,MAAM,KAAK,CAAC;QAC/B,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACpD,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAA8B;IAClE,MAAM,UAAU,GAAG,OAAO,CAAC;IAC3B,OAAO,GAAG,SAAS,CAAC;IACpB,YAAY,GAAG,SAAS,CAAC;IAEzB,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,SAAS,CAAC,mBAAmB,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;YAAS,CAAC;QACT,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,gBAAgB,GAAG,OAAO,CAAC;IACjC,OAAO,GAAG,SAAS,CAAC;IACpB,YAAY,GAAG,SAAS,CAAC;IACzB,SAAS,GAAG,SAAS,CAAC;IAEtB,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,gBAAgB,CAAC,OAAO,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,IAAI,IAAI,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,SAAS,EAAE,UAAU,EAAE,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,SAAS,EAAE,gBAAgB,EAAE,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-cli",
3
- "version": "14.0.37",
3
+ "version": "14.0.39",
4
4
  "description": "Simplysm package - CLI tool",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -19,6 +19,8 @@
19
19
  ],
20
20
  "sideEffects": false,
21
21
  "dependencies": {
22
+ "acorn": "^8.14.1",
23
+ "acorn-walk": "^8.3.4",
22
24
  "@angular/build": "^21.2.7",
23
25
  "@angular/compiler-cli": "^21.2.8",
24
26
  "@fastify/http-proxy": "^11.4.3",
@@ -38,9 +40,9 @@
38
40
  "typescript": "^5.9.3",
39
41
  "ws": "^8.20.0",
40
42
  "yargs": "^18.0.0",
41
- "@simplysm/core-common": "14.0.37",
42
- "@simplysm/core-node": "14.0.37",
43
- "@simplysm/storage": "14.0.37"
43
+ "@simplysm/core-common": "14.0.39",
44
+ "@simplysm/core-node": "14.0.39",
45
+ "@simplysm/storage": "14.0.39"
44
46
  },
45
47
  "devDependencies": {
46
48
  "@types/semver": "^7.7.1",
@@ -65,7 +65,7 @@ export interface AngularBuildPipelineOptions {
65
65
  compilerOptionsTransformer?: (opts: ts.CompilerOptions) => ts.CompilerOptions;
66
66
 
67
67
  // client 모드 전용
68
- postCssPlugins?: unknown[];
68
+ postcssPlugins?: unknown[];
69
69
  scssCacheDir?: string;
70
70
  }
71
71
 
@@ -210,7 +210,7 @@ export class AngularBuildPipeline {
210
210
  this._options.mode === "client"
211
211
  ? createClientTransformStylesheet({
212
212
  loadPaths,
213
- postCssPlugins: this._options.postCssPlugins,
213
+ postcssPlugins: this._options.postcssPlugins,
214
214
  scssErrors: this._scssErrors,
215
215
  scssDependencies: this._scssDependencies,
216
216
  cacheDir: this._options.scssCacheDir,
@@ -6,7 +6,7 @@ import { compileScssFileAsync, compileScssStringAsync } from "./scss-compiler.js
6
6
 
7
7
  export interface ClientTransformStylesheetOptions {
8
8
  loadPaths: string[];
9
- postCssPlugins?: unknown[];
9
+ postcssPlugins?: unknown[];
10
10
  scssErrors: string[];
11
11
  scssDependencies: Map<string, Set<string>>;
12
12
  /** SCSS 캐시 디렉토리 (미지정 시 캐시 비활성화) */
@@ -57,11 +57,11 @@ async function readFileHash(filePath: string): Promise<string | undefined> {
57
57
  export function createClientTransformStylesheet(
58
58
  options: ClientTransformStylesheetOptions,
59
59
  ): (data: string, containingFile: string, stylesheetFile?: string) => Promise<string | null> {
60
- const { loadPaths, postCssPlugins, scssErrors, scssDependencies, cacheDir } = options;
60
+ const { loadPaths, postcssPlugins, scssErrors, scssDependencies, cacheDir } = options;
61
61
 
62
62
  const postCssProcessor =
63
- postCssPlugins != null && postCssPlugins.length > 0
64
- ? postcss(postCssPlugins as postcss.AcceptedPlugin[])
63
+ postcssPlugins != null && postcssPlugins.length > 0
64
+ ? postcss(postcssPlugins as postcss.AcceptedPlugin[])
65
65
  : undefined;
66
66
 
67
67
  return async (
@@ -1,6 +1,4 @@
1
1
  import type { Plugin } from "vite";
2
- import { JavaScriptTransformer } from "@angular/build/private";
3
- import os from "os";
4
2
  import path from "path";
5
3
  import ts from "typescript";
6
4
  import { consola } from "consola";
@@ -11,8 +9,6 @@ import {
11
9
  AngularBuildPipeline,
12
10
  type PipelineDiagnosticResult,
13
11
  } from "./angular-build-pipeline.js";
14
- import { loadSdConfig } from "../utils/sd-config.js";
15
- import type { SdPackageConfig } from "../sd-config.types.js";
16
12
 
17
13
  const logger = consola.withTag("sd:cli:angular");
18
14
 
@@ -25,39 +21,18 @@ export interface SdAngularPluginOptions {
25
21
  /**
26
22
  * Angular AOT 컴파일을 수행하는 Vite 플러그인 (Vitest 전용).
27
23
  *
28
- * AngularBuildPipeline + JavaScriptTransformer를 관리한다.
29
- * - watchChange: 변경 파일 수집 (Vitest watch 모드용)
30
- * - buildStart: Pipeline 초기화 + 컴파일 + emit
31
- * - transform: .ts 파일에 대해 컴파일된 JS 반환 + JavaScriptTransformer 적용
32
- * - buildEnd: 리소스 정리
24
+ * AngularBuildPipeline으로 패키지의 .ts 파일을 AOT 컴파일하고,
25
+ * transform 훅에서 컴파일된 JS를 반환한다.
33
26
  */
34
27
  export function sdAngularPlugin(options: SdAngularPluginOptions): Plugin {
35
28
  let pipeline: AngularBuildPipeline | undefined;
36
29
  let sourceFileCache: AngularSourceFileCache | undefined;
37
- let jsTransformer: JavaScriptTransformer | undefined;
38
30
 
39
31
  /** Vitest watch 모드에서 변경된 파일 경로를 수집한다. buildStart 재호출 시 캐시 무효화에 사용. */
40
32
  const pendingWatchChanges = new Set<string>();
41
33
 
42
- // config() 훅에서 초기화
43
- let isDev = false;
44
- let enableSourcemap = true;
45
- let pkgConfig: SdPackageConfig | undefined;
46
34
  let resolvedPkgDir: string | undefined;
47
35
 
48
- function createJsTransformer(): JavaScriptTransformer {
49
- const maxThreads = Math.max(1, Math.floor((os.cpus().length * 2) / 3));
50
- return new JavaScriptTransformer(
51
- {
52
- sourcemap: enableSourcemap,
53
- thirdPartySourcemaps: enableSourcemap,
54
- advancedOptimizations: !isDev,
55
- jit: false,
56
- },
57
- maxThreads,
58
- );
59
- }
60
-
61
36
  return {
62
37
  name: "sd-angular",
63
38
  enforce: "pre",
@@ -66,24 +41,8 @@ export function sdAngularPlugin(options: SdAngularPluginOptions): Plugin {
66
41
  pendingWatchChanges.add(pathx.posix(id));
67
42
  },
68
43
 
69
- async config(_config: unknown, env: { mode: string }) {
70
- const cwd = process.cwd();
71
- isDev = env.mode === "development";
72
-
73
- // sd.config.ts 로딩
74
- const sdConfig = await loadSdConfig({ cwd, dev: isDev, opt: [] });
75
- const rawPkgConfig = sdConfig.packages[options.pkg];
76
- if (rawPkgConfig == null) {
77
- throw new Error(`sd.config.ts에 패키지 "${options.pkg}"가 정의되어 있지 않습니다.`);
78
- }
79
- pkgConfig = rawPkgConfig;
80
-
81
- // 패키지 디렉토리 resolve
82
- resolvedPkgDir = path.resolve(cwd, "packages", options.pkg);
83
- },
84
-
85
- configResolved(resolved: { build: { sourcemap: unknown } }) {
86
- enableSourcemap = resolved.build.sourcemap !== false || isDev;
44
+ config() {
45
+ resolvedPkgDir = path.resolve(process.cwd(), "packages", options.pkg);
87
46
  },
88
47
 
89
48
  async buildStart() {
@@ -110,12 +69,6 @@ export function sdAngularPlugin(options: SdAngularPluginOptions): Plugin {
110
69
  pendingWatchChanges.clear();
111
70
  }
112
71
 
113
- // postCssPlugins from sd.config.ts (client 패키지에만 존재)
114
- const browserSupport = pkgConfig?.target === "client"
115
- ? (pkgConfig).browserSupport
116
- : undefined;
117
- const postCssPlugins = browserSupport?.postCss?.plugins;
118
-
119
72
  // Pipeline 생성 (최초) 또는 재사용
120
73
  pipeline ??= new AngularBuildPipeline({
121
74
  mode: "client",
@@ -132,16 +85,14 @@ export function sdAngularPlugin(options: SdAngularPluginOptions): Plugin {
132
85
  noEmit: false,
133
86
  declaration: false,
134
87
  declarationMap: false,
88
+ removeComments: false,
89
+ sourceMap: false,
90
+ inlineSourceMap: true,
135
91
  rootDir: process.cwd(),
136
- ...(isDev ? { removeComments: false } : {}),
137
92
  }),
138
- postCssPlugins,
139
93
  scssCacheDir: path.join(resolvedPkgDir, ".cache", "scss"),
140
94
  });
141
95
 
142
- // JavaScriptTransformer 생성
143
- jsTransformer ??= createJsTransformer();
144
-
145
96
  // Pipeline 초기화 — 이미 초기화됐고 변경 파일이 없으면 건너뜀
146
97
  if (pipeline.getEmittedFiles().size > 0 && !hadPendingChanges) {
147
98
  logger.debug("Pipeline 이미 초기화됨, 변경 없음 — buildStart 건너뜀");
@@ -158,46 +109,32 @@ export function sdAngularPlugin(options: SdAngularPluginOptions): Plugin {
158
109
  }
159
110
  },
160
111
 
161
- async transform(_code, id) {
162
- if (jsTransformer == null) return;
163
-
112
+ transform(_code, id) {
164
113
  // query param 제거
165
114
  const cleanId = id.split("?")[0];
166
- let code = _code;
167
-
168
- // Phase 1: TS 컴파일 — .ts 파일은 Pipeline이 emit한 JS로 교체
169
- if (cleanId.endsWith(".ts")) {
170
- const normalizedId = pathx.posix(cleanId);
171
- const emittedContent = pipeline?.getEmittedFile(normalizedId);
172
- if (emittedContent == null) return;
173
- code = emittedContent;
174
- } else if (!cleanId.endsWith(".mjs") && !cleanId.endsWith(".js")) {
175
- return;
176
- }
177
115
 
178
- // Phase 2: JS 변환 — Angular Linker로 partial → full AOT 링킹 + 최적화
179
- const transformed = await jsTransformer.transformData(pathx.posix(cleanId), code, false);
180
- const transformedCode = new TextDecoder().decode(transformed);
116
+ // Pipeline이 emit한 .ts 파일만 처리
117
+ if (!cleanId.endsWith(".ts")) return;
118
+
119
+ const normalizedId = pathx.posix(cleanId);
120
+ const emittedContent = pipeline?.getEmittedFile(normalizedId);
121
+ if (emittedContent == null) return;
181
122
 
182
- // 인라인 소스맵 분리 (Rollup 경고 방지)
183
- const inlineMapMatch = transformedCode.match(
123
+ // 인라인 소스맵 분리 (Vite 호환)
124
+ const inlineMapMatch = emittedContent.match(
184
125
  /\/\/# sourceMappingURL=data:application\/json;(?:charset=utf-8;)?base64,(.+)$/m,
185
126
  );
186
127
  if (inlineMapMatch != null) {
187
128
  const mapJson = atob(inlineMapMatch[1]);
188
129
  return {
189
- code: transformedCode.slice(0, inlineMapMatch.index),
130
+ code: emittedContent.slice(0, inlineMapMatch.index),
190
131
  map: JSON.parse(mapJson),
191
132
  };
192
133
  }
193
- return { code: transformedCode, map: null };
134
+ return { code: emittedContent, map: null };
194
135
  },
195
136
 
196
- async buildEnd() {
197
- if (jsTransformer != null) {
198
- await jsTransformer.close();
199
- jsTransformer = undefined;
200
- }
137
+ buildEnd() {
201
138
  pipeline = undefined;
202
139
  },
203
140
  };
@@ -139,6 +139,7 @@ function testSshKeyAuth(
139
139
  resolve(true);
140
140
  });
141
141
  conn.on("error", () => {
142
+ conn.end();
142
143
  resolve(false);
143
144
  });
144
145
  conn.connect({
@@ -1,5 +1,8 @@
1
1
  import type http from "node:http";
2
2
  import type { IncomingMessage, ServerResponse } from "node:http";
3
+ import fs from "node:fs";
4
+ import path from "path";
5
+ import crypto from "crypto";
3
6
  import type esbuild from "esbuild";
4
7
  import { WebSocketServer, type WebSocket } from "ws";
5
8
 
@@ -10,6 +13,8 @@ export interface HmrServiceOptions {
10
13
  basePath: string;
11
14
  /** templateUpdates Map (createCompilerPlugin과 공유) */
12
15
  templateUpdates: Map<string, string>;
16
+ /** 빌드 출력 디렉토리 경로. 설정 시 파일 내용 hash로 변경 감지 (bytes 대신) */
17
+ outDir?: string;
13
18
  }
14
19
 
15
20
  export interface HmrService {
@@ -24,7 +29,7 @@ export interface HmrService {
24
29
  }
25
30
 
26
31
  export function createHmrService(options: HmrServiceOptions): HmrService {
27
- const { httpServer, basePath, templateUpdates } = options;
32
+ const { httpServer, basePath, templateUpdates, outDir } = options;
28
33
  const clients = new Set<WebSocket>();
29
34
 
30
35
  const wss = new WebSocketServer({ server: httpServer });
@@ -36,7 +41,7 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
36
41
  });
37
42
  });
38
43
 
39
- let prevOutputs: Map<string, number> | undefined;
44
+ let prevOutputs: Map<string, string> | undefined;
40
45
  let debounceTimer: ReturnType<typeof setTimeout> | undefined;
41
46
  let pendingMetafile: esbuild.Metafile | undefined;
42
47
 
@@ -49,12 +54,22 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
49
54
  }, 100);
50
55
  }
51
56
 
52
- function collectOutputs(metafile: esbuild.Metafile): Map<string, number> {
53
- const outputs = new Map<string, number>();
57
+ function collectOutputs(metafile: esbuild.Metafile): Map<string, string> {
58
+ const outputs = new Map<string, string>();
54
59
  for (const [outputPath, output] of Object.entries(metafile.outputs)) {
55
60
  const normalizedPath = outputPath.replace(/\\/g, "/");
56
61
  if (normalizedPath.endsWith(".js") || normalizedPath.endsWith(".css")) {
57
- outputs.set(normalizedPath, output.bytes);
62
+ let fingerprint = String(output.bytes);
63
+ if (outDir != null) {
64
+ try {
65
+ const filePath = path.resolve(outDir, normalizedPath);
66
+ const content = fs.readFileSync(filePath);
67
+ fingerprint = crypto.createHash("md5").update(content).digest("hex");
68
+ } catch {
69
+ // 파일 읽기 실패 시 bytes 폴백
70
+ }
71
+ }
72
+ outputs.set(normalizedPath, fingerprint);
58
73
  }
59
74
  }
60
75
  return outputs;
@@ -91,12 +106,12 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
91
106
  let cssChanged = false;
92
107
  const changedCssFiles: string[] = [];
93
108
 
94
- for (const [path, bytes] of currentOutputs) {
95
- const prevBytes = prevOutputs.get(path);
96
- if (prevBytes !== bytes) {
97
- if (path.endsWith(".css")) {
109
+ for (const [outputPath, fingerprint] of currentOutputs) {
110
+ const prevFingerprint = prevOutputs.get(outputPath);
111
+ if (prevFingerprint !== fingerprint) {
112
+ if (outputPath.endsWith(".css")) {
98
113
  cssChanged = true;
99
- changedCssFiles.push(path.split("/").pop() ?? path);
114
+ changedCssFiles.push(outputPath.split("/").pop() ?? outputPath);
100
115
  } else {
101
116
  jsChanged = true;
102
117
  }
@@ -104,9 +119,9 @@ export function createHmrService(options: HmrServiceOptions): HmrService {
104
119
  }
105
120
 
106
121
  // 삭제된 파일 체크
107
- for (const [path] of prevOutputs) {
108
- if (!currentOutputs.has(path)) {
109
- if (path.endsWith(".css")) {
122
+ for (const [outputPath] of prevOutputs) {
123
+ if (!currentOutputs.has(outputPath)) {
124
+ if (outputPath.endsWith(".css")) {
110
125
  cssChanged = true;
111
126
  } else {
112
127
  jsChanged = true;
@@ -97,7 +97,7 @@ export class Electron {
97
97
 
98
98
  let currentElectron: cpx.SpawnProcess | null = null;
99
99
  let isRestarting = false;
100
- let resolveTermination: (() => void) | null = null;
100
+ let resolveTermination: (() => void | Promise<void>) | null = null;
101
101
 
102
102
  const spawnElectron = () => {
103
103
  Electron._logger.debug("Electron 프로세스 시작");
@@ -112,7 +112,7 @@ export class Electron {
112
112
  currentElectron = null;
113
113
  if (!isRestarting && resolveTermination != null) {
114
114
  Electron._logger.info("Electron이 종료되었습니다.");
115
- resolveTermination();
115
+ void resolveTermination();
116
116
  }
117
117
  });
118
118
  };
@@ -160,13 +160,13 @@ export class Electron {
160
160
  await new Promise<void>((resolve) => {
161
161
  let disposed = false;
162
162
 
163
- const cleanup = () => {
163
+ const cleanup = async () => {
164
164
  if (disposed) return;
165
165
  disposed = true;
166
166
  Electron._logger.debug("cleanup 시작");
167
167
  process.removeListener("SIGINT", signalHandler);
168
168
  process.removeListener("SIGTERM", signalHandler);
169
- void ctx.dispose();
169
+ await ctx.dispose();
170
170
  resolve();
171
171
  };
172
172
 
@@ -175,7 +175,7 @@ export class Electron {
175
175
  const signalHandler = () => {
176
176
  Electron._logger.debug("시그널 수신, Electron 종료 중");
177
177
  if (currentElectron != null) currentElectron.kill();
178
- cleanup();
178
+ void cleanup();
179
179
  };
180
180
 
181
181
  process.once("SIGINT", signalHandler);
@@ -1,4 +1,5 @@
1
1
  import { Worker, type WorkerProxy } from "@simplysm/core-node";
2
+ import { err as errNs } from "@simplysm/core-common";
2
3
  import { consola } from "consola";
3
4
  import type { BuildResult, ResultCollector } from "../runtime/ResultCollector";
4
5
  import { stopEngineWorker } from "../runtime/engine-stop";
@@ -182,7 +183,15 @@ export abstract class BaseEngine<
182
183
  }
183
184
  });
184
185
 
185
- this._callStartWatch(output).catch(() => {
186
+ this._callStartWatch(output).catch((err: unknown) => {
187
+ logger.error(`[${this._pkg.name}] startWatch 실패:`, errNs.message(err));
188
+ this._resultCollector?.add({
189
+ name: this._pkg.name,
190
+ target: this._getTarget(),
191
+ type: "build",
192
+ status: "error",
193
+ message: errNs.message(err),
194
+ });
186
195
  resolveInitialBuild();
187
196
  });
188
197
 
@@ -121,7 +121,7 @@ export class EsbuildClientEngine implements BuildEngine {
121
121
  ? this._pkg.config.server
122
122
  : undefined;
123
123
 
124
- await this._worker!.startWatch({
124
+ const result = await this._worker!.startWatch({
125
125
  name: this._pkg.name,
126
126
  cwd: this._cwd,
127
127
  pkgDir: this._pkg.dir,
@@ -131,6 +131,18 @@ export class EsbuildClientEngine implements BuildEngine {
131
131
  pwa: this._pkg.config.pwa,
132
132
  browserSupport: this._pkg.config.browserSupport,
133
133
  });
134
+
135
+ if (!result.success) {
136
+ const errorDetail = result.errors?.join("; ") ?? "unknown error";
137
+ logger.error(`[${this._pkg.name}] 초기 빌드 실패: ${errorDetail}`);
138
+ this._resultCollector?.add({
139
+ name: this._pkg.name,
140
+ target: "client",
141
+ type: "build",
142
+ status: "error",
143
+ message: errorDetail,
144
+ });
145
+ }
134
146
  }
135
147
 
136
148
  /**
@@ -24,7 +24,6 @@ export function createBuildEngine(
24
24
  options: {
25
25
  cwd: string;
26
26
  replaceDeps?: Record<string, string>;
27
- resolvedReplaceDeps?: Array<{ packageName: string; sourcePath: string }>;
28
27
  resultCollector?: ResultCollector;
29
28
  rebuildManager?: RebuildManager;
30
29
  /** 클라이언트 빌드 출력 경로 (EsbuildClientEngine에만 적용) */
@@ -1,7 +1,9 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
+ import { createRequire } from "module";
3
4
  import esbuild from "esbuild";
4
5
  import browserslistToEsbuild from "browserslist-to-esbuild";
6
+ import type { AcceptedPlugin } from "postcss";
5
7
  import {
6
8
  createCompilerPlugin,
7
9
  SourceFileCache,
@@ -9,6 +11,7 @@ import {
9
11
  type BundleStylesheetOptions,
10
12
  } from "@angular/build/private";
11
13
  import { createScssPlugin } from "./esbuild-scss-plugin";
14
+ import { createPostcssPlugin } from "./esbuild-postcss-plugin";
12
15
 
13
16
  export interface CreateClientEsbuildOptions {
14
17
  /** 패키지 디렉토리 경로 */
@@ -29,8 +32,6 @@ export interface CreateClientEsbuildOptions {
29
32
  onEnd?: (result: esbuild.BuildResult) => void | Promise<void>;
30
33
  /** PostCSS 플러그인 ([name, options] 튜플 배열) */
31
34
  postcssPlugins?: [string, (object | string)?][];
32
- /** PostCSS 설정 기준 경로 */
33
- postcssConfigPath?: string;
34
35
  /** 빌드 출력 경로 (기본: pkgDir/dist) */
35
36
  outdir?: string;
36
37
  /** browserslist 쿼리. 미설정 시 "es2022" 기본값 */
@@ -65,16 +66,16 @@ export async function createClientEsbuildContext(
65
66
  const sourceFileCache = new SourceFileCache(cachePath);
66
67
 
67
68
  // CompilerPluginOptions
68
- const pluginOptions: CompilerPluginOptions & { browserOnlyBuild?: boolean } = {
69
+ const pluginOptions: CompilerPluginOptions = {
69
70
  tsconfig: options.tsconfig ?? path.join(options.pkgDir, "tsconfig.json"),
70
71
  sourcemap: isDev,
71
72
  advancedOptimizations: !isDev,
72
73
  thirdPartySourcemaps: isDev,
73
- incremental: true,
74
+ incremental: isDev,
74
75
  sourceFileCache,
75
76
  loadResultCache: sourceFileCache.loadResultCache,
76
77
  templateUpdates: options.templateUpdates,
77
- browserOnlyBuild: true,
78
+ includeTestMetadata: isDev,
78
79
  };
79
80
 
80
81
  // BundleStylesheetOptions
@@ -91,22 +92,27 @@ export async function createClientEsbuildContext(
91
92
  path: cachePath,
92
93
  basePath: cachePath,
93
94
  },
94
- postcssConfiguration:
95
- options.postcssPlugins != null
96
- ? {
97
- config: { plugins: options.postcssPlugins },
98
- configPath: options.postcssConfigPath ?? options.pkgDir,
99
- }
100
- : undefined,
95
+ postcssConfiguration: undefined,
101
96
  inlineStyleLanguage: "scss",
102
97
  };
103
98
 
104
99
  const angularPlugin = createCompilerPlugin(pluginOptions, styleOptions);
105
100
 
101
+ // PostCSS 플러그인 로딩 (튜플 → 인스턴스)
102
+ let loadedPostcssPlugins: AcceptedPlugin[] | undefined;
103
+ if (options.postcssPlugins != null && options.postcssPlugins.length > 0) {
104
+ const req = createRequire(path.join(options.pkgDir, "package.json"));
105
+ loadedPostcssPlugins = options.postcssPlugins.map(([name, pluginOpts]) => {
106
+ const pluginFn = req(name);
107
+ const fn = pluginFn.default ?? pluginFn;
108
+ return pluginOpts != null ? fn(pluginOpts) : fn;
109
+ });
110
+ }
111
+
106
112
  // SCSS side-effect import 처리 플러그인
107
113
  const scssPlugin = createScssPlugin({
108
114
  loadPaths: [
109
- path.join(options.pkgDir, "scss"),
115
+ path.join(options.pkgDir, "node_modules"),
110
116
  path.join(options.cwd, "node_modules"),
111
117
  ],
112
118
  });
@@ -126,10 +132,10 @@ export async function createClientEsbuildContext(
126
132
  }
127
133
 
128
134
  // 커스텀 env
135
+ // esbuild define은 정적 패턴만 치환하므로, import.meta.env 객체 자체를 주입해야
136
+ // env() 함수의 동적 접근(import.meta.env?.[key])이 동작한다.
129
137
  if (options.env != null) {
130
- for (const [key, value] of Object.entries(options.env)) {
131
- define[`import.meta.env.${key}`] = JSON.stringify(value);
132
- }
138
+ define["import.meta.env"] = JSON.stringify(options.env);
133
139
  }
134
140
 
135
141
  // import.meta.hot 폴리필 banner (Angular HMR 런타임용)
@@ -156,7 +162,7 @@ export async function createClientEsbuildContext(
156
162
  ],
157
163
  target: esbuildTarget,
158
164
  entryNames: isDev ? "[name]" : "[name]-[hash]",
159
- chunkNames: isDev ? "[name]" : "[name]-[hash]",
165
+ chunkNames: "[name]-[hash]",
160
166
  assetNames: isDev ? "[name]" : "[name]-[hash]",
161
167
  bundle: true,
162
168
  splitting: options.legacyModule !== true,
@@ -166,7 +172,7 @@ export async function createClientEsbuildContext(
166
172
  metafile: true,
167
173
  write: true,
168
174
  sourcemap: isDev ? "linked" : false,
169
- logLevel: "silent",
175
+ logLevel: isDev ? "warning" : "silent",
170
176
  tsconfig: options.tsconfig ?? path.join(options.pkgDir, "tsconfig.json"),
171
177
  define,
172
178
  banner: hmrBanner != null ? { js: hmrBanner } : undefined,
@@ -187,6 +193,9 @@ export async function createClientEsbuildContext(
187
193
  angularPlugin,
188
194
  scssPlugin,
189
195
  ...(options.plugins ?? []),
196
+ ...(loadedPostcssPlugins != null
197
+ ? [createPostcssPlugin({ plugins: loadedPostcssPlugins })]
198
+ : []),
190
199
  ...(options.legacyModule === true
191
200
  ? [
192
201
  {