@simplysm/sd-cli 12.5.6 → 12.5.8

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 (51) hide show
  1. package/dist/build-tools/SdCliIndexFileGenerator.js +4 -7
  2. package/dist/build-tools/SdCliIndexFileGenerator.js.map +1 -1
  3. package/dist/build-tools/SdCliNgRoutesFileGenerator.d.ts +2 -2
  4. package/dist/build-tools/SdCliNgRoutesFileGenerator.js +22 -13
  5. package/dist/build-tools/SdCliNgRoutesFileGenerator.js.map +1 -1
  6. package/dist/build-tools/SdLinter.js +2 -1
  7. package/dist/build-tools/SdLinter.js.map +1 -1
  8. package/dist/build-tools/SdNgBundler.d.ts +0 -1
  9. package/dist/build-tools/SdNgBundler.js +4 -2
  10. package/dist/build-tools/SdNgBundler.js.map +1 -1
  11. package/dist/build-tools/SdReactBundler.d.ts +24 -0
  12. package/dist/build-tools/SdReactBundler.js +251 -0
  13. package/dist/build-tools/SdReactBundler.js.map +1 -0
  14. package/dist/build-tools/SdReactBundlerContext.d.ts +14 -0
  15. package/dist/build-tools/SdReactBundlerContext.js +59 -0
  16. package/dist/build-tools/SdReactBundlerContext.js.map +1 -0
  17. package/dist/build-tools/SdTsCompiler.js +32 -29
  18. package/dist/build-tools/SdTsCompiler.js.map +1 -1
  19. package/dist/build-tools/SdTsLibBundler.js +4 -3
  20. package/dist/build-tools/SdTsLibBundler.js.map +1 -1
  21. package/dist/builders/SdCliClientBuilder.js +53 -32
  22. package/dist/builders/SdCliClientBuilder.js.map +1 -1
  23. package/dist/builders/SdCliTsLibBuilder.js +8 -11
  24. package/dist/builders/SdCliTsLibBuilder.js.map +1 -1
  25. package/dist/bundle-plugins/sdNgPlugin.js +21 -11
  26. package/dist/bundle-plugins/sdNgPlugin.js.map +1 -1
  27. package/dist/bundle-plugins/sdReactPlugin.d.ts +15 -0
  28. package/dist/bundle-plugins/sdReactPlugin.js +111 -0
  29. package/dist/bundle-plugins/sdReactPlugin.js.map +1 -0
  30. package/dist/commons.d.ts +1 -2
  31. package/dist/entry/SdCliProject.js +1 -0
  32. package/dist/entry/SdCliProject.js.map +1 -1
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +3 -0
  35. package/dist/index.js.map +1 -1
  36. package/package.json +12 -5
  37. package/src/build-tools/SdCliIndexFileGenerator.ts +10 -12
  38. package/src/build-tools/SdCliNgRoutesFileGenerator.ts +33 -22
  39. package/src/build-tools/SdLinter.ts +2 -1
  40. package/src/build-tools/SdNgBundler.ts +4 -3
  41. package/src/build-tools/SdReactBundler.ts +325 -0
  42. package/src/build-tools/SdReactBundlerContext.ts +71 -0
  43. package/src/build-tools/SdTsCompiler.ts +146 -129
  44. package/src/build-tools/SdTsLibBundler.ts +9 -13
  45. package/src/builders/SdCliClientBuilder.ts +108 -69
  46. package/src/builders/SdCliTsLibBuilder.ts +35 -32
  47. package/src/bundle-plugins/sdNgPlugin.ts +59 -56
  48. package/src/bundle-plugins/sdReactPlugin.ts +157 -0
  49. package/src/commons.ts +1 -2
  50. package/src/entry/SdCliProject.ts +1 -0
  51. package/src/index.ts +3 -0
@@ -1,22 +1,20 @@
1
1
  import path from "path";
2
- import {FsUtil, PathUtil, SdFsWatcher} from "@simplysm/sd-core-node";
3
- import {StringUtil} from "@simplysm/sd-core-common";
2
+ import { FsUtil, PathUtil, SdFsWatcher } from "@simplysm/sd-core-node";
3
+ import { StringUtil } from "@simplysm/sd-core-common";
4
4
 
5
5
  export class SdCliNgRoutesFileGenerator {
6
- public static async watchAsync(pkgPath: string): Promise<void> {
6
+ public static async watchAsync(pkgPath: string, noLazyRoute?: boolean): Promise<void> {
7
7
  const routesFilePath = path.resolve(pkgPath, "src/routes.ts");
8
8
  let cache = FsUtil.exists(routesFilePath) ? FsUtil.readFile(routesFilePath) : undefined;
9
9
 
10
- SdFsWatcher
11
- .watch([path.resolve(pkgPath, "src")])
12
- .onChange({}, async () => {
13
- cache = await this.runAsync(pkgPath, cache);
14
- });
10
+ SdFsWatcher.watch([path.resolve(pkgPath, "src")]).onChange({}, async () => {
11
+ cache = await this.runAsync(pkgPath, cache, noLazyRoute);
12
+ });
15
13
 
16
- cache = await this.runAsync(pkgPath, cache);
14
+ cache = await this.runAsync(pkgPath, cache, noLazyRoute);
17
15
  }
18
16
 
19
- public static async runAsync(pkgPath: string, cache?: string): Promise<string> {
17
+ public static async runAsync(pkgPath: string, cache?: string, noLazyRoute?: boolean): Promise<string> {
20
18
  const appDirPath = path.resolve(pkgPath, "src/app");
21
19
  const routesFilePath = path.resolve(pkgPath, "src/routes.ts");
22
20
 
@@ -25,20 +23,23 @@ export class SdCliNgRoutesFileGenerator {
25
23
  const filePaths = await FsUtil.globAsync(path.resolve(appDirPath, "**/*Page.ts"));
26
24
  for (const filePath of filePaths.orderBy()) {
27
25
  const relModulePath = PathUtil.posix(path.relative(appDirPath, filePath)).slice(0, -3);
28
- const codes = relModulePath.slice(0, -4).split("/").map(item => StringUtil.toKebabCase(item));
26
+ const codes = relModulePath
27
+ .slice(0, -4)
28
+ .split("/")
29
+ .map((item) => StringUtil.toKebabCase(item));
29
30
 
30
31
  let cursorItem!: { relModulePath?: string; children: TInfo };
31
32
  let cursor = result;
32
33
 
33
34
  for (const code of codes) {
34
- cursorItem = cursor.getOrCreate(code, {children: new Map()});
35
+ cursorItem = cursor.getOrCreate(code, { children: new Map() });
35
36
  cursor = cursorItem.children;
36
37
  }
37
38
 
38
39
  cursorItem.relModulePath = relModulePath;
39
40
  }
40
41
 
41
-
42
+ const imports: string[] = [];
42
43
  const fn = (currInfo: TInfo, indent: number): string => {
43
44
  const indentStr = new Array(indent).fill(" ").join("");
44
45
 
@@ -47,8 +48,14 @@ export class SdCliNgRoutesFileGenerator {
47
48
  cont += indentStr + "{\n";
48
49
  cont += indentStr + ` path: "${key}",\n`;
49
50
  if (val.relModulePath != null) {
50
- cont += indentStr + ` loadComponent: () => import("./app/${val.relModulePath}").then((m) => m.${path.basename(val.relModulePath)}),\n`;
51
- cont += indentStr + ` canDeactivate: [sdCanDeactivateGuard],\n`;
51
+ if (noLazyRoute) {
52
+ cont += indentStr + ` component: ${path.basename(val.relModulePath)},\n`;
53
+ imports.push(`import { ${path.basename(val.relModulePath)} } from "./app/${val.relModulePath}";`);
54
+ } else {
55
+ cont +=
56
+ indentStr +
57
+ ` loadComponent: () => import("./app/${val.relModulePath}").then((m) => m.${path.basename(val.relModulePath)}),\n`;
58
+ }
52
59
  }
53
60
  if (val.children.size > 0) {
54
61
  cont += indentStr + ` children: [\n`;
@@ -60,12 +67,13 @@ export class SdCliNgRoutesFileGenerator {
60
67
  return cont.trimEnd();
61
68
  };
62
69
 
70
+ const routes = fn(result, 2);
71
+
63
72
  const content = `
64
- import {sdCanDeactivateGuard} from "@simplysm/sd-angular";
65
73
  import {Routes} from "@angular/router";
66
-
74
+ ${imports.join("\n")}
67
75
  export const routes: Routes = [
68
- ${fn(result, 2)}
76
+ ${routes}
69
77
  ];`.trim();
70
78
  if (content !== cache) {
71
79
  await FsUtil.writeFileAsync(routesFilePath, content);
@@ -74,7 +82,10 @@ ${fn(result, 2)}
74
82
  }
75
83
  }
76
84
 
77
- type TInfo = Map<string, {
78
- relModulePath?: string;
79
- children: TInfo
80
- }>;
85
+ type TInfo = Map<
86
+ string,
87
+ {
88
+ relModulePath?: string;
89
+ children: TInfo;
90
+ }
91
+ >;
@@ -12,6 +12,7 @@ export abstract class SdLinter {
12
12
  .filter(
13
13
  (item) =>
14
14
  (!item.endsWith(".d.ts") && item.endsWith(".ts")) ||
15
+ item.endsWith(".tsx") ||
15
16
  item.endsWith(".js")
16
17
  )
17
18
  .filter((item) => FsUtil.exists(item));
@@ -26,7 +27,7 @@ export abstract class SdLinter {
26
27
  cache: false,
27
28
  overrideConfig: [
28
29
  {
29
- files: ["**/*.ts"],
30
+ files: ["**/*.ts", "**/*.tsx"],
30
31
  languageOptions: {
31
32
  parserOptions: {
32
33
  programs: [tsProgram],
@@ -74,7 +74,7 @@ export class SdNgBundler {
74
74
  this.#mainFilePath = path.resolve(opt.pkgPath, "src/main.ts");
75
75
  this.#tsConfigFilePath = path.resolve(opt.pkgPath, "tsconfig.json");
76
76
  this.#swConfFilePath = path.resolve(opt.pkgPath, "ngsw-config.json");
77
- this.#browserTarget = transformSupportedBrowsersToTargets(browserslist(opt.browserslist ?? ["last 2 Chrome versions", "last 2 Edge versions"]));
77
+ this.#browserTarget = transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"]));
78
78
  this.#indexHtmlFilePath = path.resolve(opt.pkgPath, "src/index.html");
79
79
  this.#pkgName = path.basename(opt.pkgPath);
80
80
  this.#baseHref = opt.builderType === "web" ? `/${this.#pkgName}/` : opt.dev ? `/${this.#pkgName}/${opt.builderType}/` : ``;
@@ -219,7 +219,8 @@ export class SdNgBundler {
219
219
  program: this.#ngResultCache.program,
220
220
  watchFileSet: new Set([
221
221
  ...this.#ngResultCache.watchFileSet!,
222
- ...this.#styleLoadResultCache.watchFiles
222
+ ...this.#styleLoadResultCache.watchFiles,
223
+ this.#indexHtmlFilePath
223
224
  ]),
224
225
  affectedFileSet: this.#ngResultCache.affectedFileSet!,
225
226
  results
@@ -274,6 +275,7 @@ export class SdNgBundler {
274
275
  if (value.type === 'script') {
275
276
  hints.push({url: key, mode: 'modulepreload' as const});
276
277
  }
278
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
277
279
  else if (value.type === 'style') {
278
280
  hints.push({url: key, mode: 'preload' as const, as: 'style'});
279
281
  }
@@ -606,5 +608,4 @@ interface IOptions {
606
608
  builderType: string;
607
609
  env: Record<string, string> | undefined;
608
610
  cordovaConfig: ISdCliClientBuilderCordovaConfig | undefined;
609
- browserslist: string[] | undefined;
610
611
  }
@@ -0,0 +1,325 @@
1
+ import path from "path";
2
+ import { FsUtil, Logger, PathUtil } from "@simplysm/sd-core-node";
3
+ import { fileURLToPath } from "url";
4
+ import nodeStdLibBrowser from "node-stdlib-browser";
5
+ import nodeStdLibBrowserPlugin from "node-stdlib-browser/helpers/esbuild/plugin";
6
+ import { INpmConfig, ISdCliClientBuilderCordovaConfig, ISdCliPackageBuildResult } from "../commons";
7
+ import ts from "typescript";
8
+ import { SdReactBundlerContext } from "./SdReactBundlerContext";
9
+ import { IReactPluginResultCache, sdReactPlugin } from "../bundle-plugins/sdReactPlugin";
10
+ import { transformSupportedBrowsersToTargets } from "@angular/build/src/tools/esbuild/utils";
11
+ import browserslist from "browserslist";
12
+ import { glob } from "glob";
13
+
14
+ export class SdReactBundler {
15
+ readonly #logger = Logger.get(["simplysm", "sd-cli", "SdNgBundler"]);
16
+
17
+ readonly #modifiedFileSet = new Set<string>();
18
+ readonly #compileResultCache: IReactPluginResultCache = {
19
+ affectedFileSet: new Set<string>(),
20
+ watchFileSet: new Set<string>(),
21
+ };
22
+ #contexts: SdReactBundlerContext[] | undefined;
23
+
24
+ readonly #outputCache = new Map<string, string | number>();
25
+
26
+ readonly #opt: IOptions;
27
+
28
+ readonly #pkgNpmConf: INpmConfig;
29
+ readonly #indexFilePath: string;
30
+ readonly #tsConfigFilePath: string;
31
+ readonly #browserTarget: string[];
32
+
33
+ public constructor(opt: IOptions) {
34
+ this.#opt = opt;
35
+ this.#pkgNpmConf = FsUtil.readJson(path.resolve(opt.pkgPath, "package.json"));
36
+ this.#indexFilePath = path.resolve(opt.pkgPath, "src/index.tsx");
37
+ this.#tsConfigFilePath = path.resolve(opt.pkgPath, "tsconfig.json");
38
+ this.#browserTarget = transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"]));
39
+ }
40
+
41
+ public markForChanges(filePaths: string[]): void {
42
+ for (const filePath of filePaths) {
43
+ this.#modifiedFileSet.add(path.normalize(filePath));
44
+ }
45
+ }
46
+
47
+ public async bundleAsync(): Promise<{
48
+ program?: ts.Program;
49
+ watchFileSet: Set<string>;
50
+ affectedFileSet: Set<string>;
51
+ results: ISdCliPackageBuildResult[];
52
+ }> {
53
+ this.#debug(`get contexts...`);
54
+
55
+ if (!this.#contexts) {
56
+ this.#contexts = [
57
+ this._getAppContext(),
58
+ ...(this.#opt.builderType === "electron" ? [this._getElectronMainContext()] : []),
59
+ ];
60
+ }
61
+
62
+ this.#debug(`build...`);
63
+
64
+ const bundlingResults = await this.#contexts.mapAsync(async (ctx, i) => await ctx.bundleAsync());
65
+
66
+ //-- results
67
+ const results = bundlingResults.mapMany((bundlingResult) => bundlingResult.results);
68
+
69
+ this.#debug(`convert result...`);
70
+
71
+ const outputFiles: IReactOutputFile[] = bundlingResults
72
+ .mapMany((item) => item.outputFiles ?? [])
73
+ .map((item) => ({
74
+ relPath: path.relative(this.#opt.pkgPath, item.path),
75
+ contents: item.contents,
76
+ }));
77
+
78
+ // cordova empty
79
+ if (this.#opt.builderType === "cordova" && this.#opt.cordovaConfig?.plugins) {
80
+ outputFiles.push({ relPath: "cordova-empty.js", contents: "export default {};" });
81
+ }
82
+
83
+ // index.html
84
+ let indexHtml = await FsUtil.readFileAsync(path.resolve(this.#opt.pkgPath, "src/index.html"));
85
+ indexHtml = indexHtml.replace(/<\/head>/, '<script type="module" src="index.js"></script></head>');
86
+ if (this.#opt.builderType === "cordova") {
87
+ indexHtml = indexHtml.replace(
88
+ /(.*)<\/head>/,
89
+ '<script type="module" src="cordova-entry.js"></script>\n$1</head>',
90
+ );
91
+ }
92
+ if (outputFiles.some(item => item.relPath === "index.css")) {
93
+ indexHtml = indexHtml.replace(
94
+ /(.*)<\/head>/,
95
+ '<link rel="stylesheet" href="index.css">\n$1</head>',
96
+ );
97
+ }
98
+
99
+ outputFiles.push({
100
+ relPath: "index.html",
101
+ contents: indexHtml,
102
+ });
103
+
104
+ // copy assets
105
+ outputFiles.push(
106
+ ...this.#copyAssets("src", "favicon.ico"),
107
+ ...this.#copyAssets("src", "manifest.webmanifest"),
108
+ ...this.#copyAssets("src/assets", "**/*", "assets"),
109
+ ...(this.#opt.dev && this.#opt.builderType === "cordova"
110
+ ? Object.keys(this.#opt.cordovaConfig?.platform ?? { browser: {} }).mapMany((platform) => [
111
+ ...this.#copyAssets(
112
+ `.cordova/platforms/${platform}/platform_www/plugins`,
113
+ "**/*",
114
+ `cordova-${platform}/plugins`,
115
+ ),
116
+ ...this.#copyAssets(`.cordova/platforms/${platform}/platform_www`, "cordova.js", `cordova-${platform}`),
117
+ ...this.#copyAssets(
118
+ `.cordova/platforms/${platform}/platform_www`,
119
+ "cordova_plugins.js",
120
+ `cordova-${platform}`,
121
+ ),
122
+ ...this.#copyAssets(`.cordova/platforms/${platform}/www`, "config.xml", `cordova-${platform}`),
123
+ ])
124
+ : []),
125
+ );
126
+
127
+ // TODO: extract 3rdpartylicenses
128
+ // TODO: service worker
129
+
130
+ //-- write
131
+ this.#debug(`write output files...(${outputFiles.length})`);
132
+
133
+ for (const outputFile of outputFiles) {
134
+ const distFilePath = path.resolve(this.#opt.outputPath, outputFile.relPath);
135
+ const prev = this.#outputCache.get(distFilePath);
136
+ if (prev !== Buffer.from(outputFile.contents).toString("base64")) {
137
+ await FsUtil.writeFileAsync(distFilePath, outputFile.contents);
138
+ this.#outputCache.set(distFilePath, Buffer.from(outputFile.contents).toString("base64"));
139
+ }
140
+ }
141
+
142
+ this.#debug(`번들링중 영향받은 파일`, Array.from(this.#compileResultCache.affectedFileSet!));
143
+
144
+ return {
145
+ program: this.#compileResultCache.program,
146
+ watchFileSet: new Set(this.#compileResultCache.watchFileSet),
147
+ affectedFileSet: this.#compileResultCache.affectedFileSet!,
148
+ results,
149
+ };
150
+ }
151
+
152
+ #copyAssets(srcDir: string, globPath: string, distDir?: string): IReactOutputFile[] {
153
+ const result: IReactOutputFile[] = [];
154
+ const srcGlobPath = path.resolve(this.#opt.pkgPath, srcDir, globPath);
155
+ const srcPaths = glob.sync(srcGlobPath);
156
+ for (const srcPath of srcPaths) {
157
+ if (!FsUtil.exists(srcPath)) continue;
158
+ result.push({
159
+ relPath: path.join(
160
+ ...[distDir, path.relative(path.resolve(this.#opt.pkgPath, srcDir), srcPath)].filterExists(),
161
+ ),
162
+ contents: FsUtil.readFile(srcPath),
163
+ });
164
+ }
165
+
166
+ return result;
167
+ }
168
+
169
+ private _getAppContext() {
170
+ return new SdReactBundlerContext(this.#opt.pkgPath, {
171
+ absWorkingDir: this.#opt.pkgPath,
172
+ bundle: true,
173
+ keepNames: true,
174
+ format: "esm",
175
+ assetNames: "media/[name]",
176
+ conditions: ["es2020", "es2015", "module"],
177
+ resolveExtensions: [".js", ".mjs", ".cjs", ".ts", ".tsx"],
178
+ metafile: true,
179
+ legalComments: this.#opt.dev ? "eof" : "none",
180
+ logLevel: "silent",
181
+ minifyIdentifiers: !this.#opt.dev,
182
+ minifySyntax: !this.#opt.dev,
183
+ minifyWhitespace: !this.#opt.dev,
184
+ pure: ["forwardRef"],
185
+ outdir: this.#opt.pkgPath,
186
+ outExtension: undefined,
187
+ sourcemap: true, //this.#opt.dev,
188
+ splitting: true,
189
+ chunkNames: "[name]-[hash]",
190
+ tsconfig: this.#tsConfigFilePath,
191
+ write: false,
192
+ preserveSymlinks: false,
193
+ define: {
194
+ ...(!this.#opt.dev ? { ngDevMode: "false" } : {}),
195
+ "ngJitMode": "false",
196
+ "global": "global",
197
+ "process": "process",
198
+ "Buffer": "Buffer",
199
+ "process.env.SD_VERSION": JSON.stringify(this.#pkgNpmConf.version),
200
+ "process.env.NODE_ENV": JSON.stringify(this.#opt.dev ? "development" : "production"),
201
+ ...(this.#opt.env
202
+ ? Object.keys(this.#opt.env).toObject(
203
+ (key) => `process.env.${key}`,
204
+ (key) => JSON.stringify(this.#opt.env![key]),
205
+ )
206
+ : {}),
207
+ },
208
+ platform: "browser",
209
+ mainFields: ["es2020", "es2015", "browser", "module", "main"],
210
+ entryNames: "[name]",
211
+ entryPoints: {
212
+ index: this.#indexFilePath,
213
+ },
214
+ external: ["electron"],
215
+ target: this.#browserTarget,
216
+ supported: { "async-await": false, "object-rest-spread": false },
217
+ loader: {
218
+ ".png": "file",
219
+ ".jpeg": "file",
220
+ ".jpg": "file",
221
+ ".jfif": "file",
222
+ ".gif": "file",
223
+ ".svg": "file",
224
+ ".woff": "file",
225
+ ".woff2": "file",
226
+ ".ttf": "file",
227
+ ".ttc": "file",
228
+ ".eot": "file",
229
+ ".ico": "file",
230
+ ".otf": "file",
231
+ ".csv": "file",
232
+ ".xlsx": "file",
233
+ ".xls": "file",
234
+ ".pptx": "file",
235
+ ".ppt": "file",
236
+ ".docx": "file",
237
+ ".doc": "file",
238
+ ".zip": "file",
239
+ ".pfx": "file",
240
+ ".pkl": "file",
241
+ ".mp3": "file",
242
+ ".ogg": "file",
243
+ },
244
+ inject: [PathUtil.posix(fileURLToPath(import.meta.resolve("node-stdlib-browser/helpers/esbuild/shim")))],
245
+ plugins: [
246
+ ...(this.#opt.builderType === "cordova" && this.#opt.cordovaConfig?.plugins
247
+ ? [
248
+ {
249
+ name: "cordova:plugin-empty",
250
+ setup: ({ onResolve }) => {
251
+ onResolve({ filter: new RegExp("(" + this.#opt.cordovaConfig!.plugins!.join("|") + ")") }, () => {
252
+ return {
253
+ path: `./cordova-empty.js`,
254
+ external: true,
255
+ };
256
+ });
257
+ },
258
+ },
259
+ ]
260
+ : []),
261
+ sdReactPlugin({
262
+ modifiedFileSet: this.#modifiedFileSet,
263
+ dev: this.#opt.dev,
264
+ pkgPath: this.#opt.pkgPath,
265
+ result: this.#compileResultCache,
266
+ }),
267
+ nodeStdLibBrowserPlugin(nodeStdLibBrowser),
268
+ ],
269
+ });
270
+ }
271
+
272
+ private _getElectronMainContext() {
273
+ return new SdReactBundlerContext(this.#opt.pkgPath, {
274
+ absWorkingDir: this.#opt.pkgPath,
275
+ bundle: true,
276
+ entryNames: "[name]",
277
+ assetNames: "media/[name]",
278
+ conditions: ["es2020", "es2015", "module"],
279
+ resolveExtensions: [".js", ".mjs", ".cjs", ".ts"],
280
+ metafile: true,
281
+ legalComments: this.#opt.dev ? "eof" : "none",
282
+ logLevel: "silent",
283
+ minify: !this.#opt.dev,
284
+ outdir: this.#opt.pkgPath,
285
+ sourcemap: true, //this.#opt.dev,
286
+ tsconfig: this.#tsConfigFilePath,
287
+ write: false,
288
+ preserveSymlinks: false,
289
+ external: ["electron"],
290
+ define: {
291
+ ...(!this.#opt.dev ? { ngDevMode: "false" } : {}),
292
+ "process.env.SD_VERSION": JSON.stringify(this.#pkgNpmConf.version),
293
+ "process.env.NODE_ENV": JSON.stringify(this.#opt.dev ? "development" : "production"),
294
+ ...(this.#opt.env
295
+ ? Object.keys(this.#opt.env).toObject(
296
+ (key) => `process.env.${key}`,
297
+ (key) => JSON.stringify(this.#opt.env![key]),
298
+ )
299
+ : {}),
300
+ },
301
+ platform: "node",
302
+ entryPoints: {
303
+ "electron-main": path.resolve(this.#opt.pkgPath, "src/electron-main.ts"),
304
+ },
305
+ });
306
+ }
307
+
308
+ #debug(...msg: any[]): void {
309
+ this.#logger.debug(`[${path.basename(this.#opt.pkgPath)}]`, ...msg);
310
+ }
311
+ }
312
+
313
+ interface IOptions {
314
+ dev: boolean;
315
+ outputPath: string;
316
+ pkgPath: string;
317
+ builderType: string;
318
+ env: Record<string, string> | undefined;
319
+ cordovaConfig: ISdCliClientBuilderCordovaConfig | undefined;
320
+ }
321
+
322
+ interface IReactOutputFile {
323
+ relPath: string;
324
+ contents: Uint8Array | string;
325
+ }
@@ -0,0 +1,71 @@
1
+ import esbuild from "esbuild";
2
+ import path from "path";
3
+ import { ISdCliPackageBuildResult } from "../commons";
4
+ import { Logger } from "@simplysm/sd-core-node";
5
+
6
+ export class SdReactBundlerContext {
7
+ readonly #logger = Logger.get(["simplysm", "sd-cli", "SdReactBundlerContext"]);
8
+
9
+ private _context?: esbuild.BuildContext;
10
+
11
+ public constructor(
12
+ private readonly _pkgPath: string,
13
+ private readonly _esbuildOptions: esbuild.BuildOptions,
14
+ ) {}
15
+
16
+ public async bundleAsync() {
17
+ if (this._context == null) {
18
+ this._context = await esbuild.context(this._esbuildOptions);
19
+ }
20
+
21
+ let buildResult: esbuild.BuildResult;
22
+
23
+ try {
24
+ this.#debug(`rebuild...`);
25
+ buildResult = await this._context.rebuild();
26
+ this.#debug(`rebuild completed`);
27
+ } catch (err) {
28
+ if ("warnings" in err || "errors" in err) {
29
+ buildResult = err;
30
+ } else {
31
+ throw err;
32
+ }
33
+ }
34
+
35
+ this.#debug(`convert results...`);
36
+
37
+ const results = [
38
+ ...buildResult.warnings.map((warn) => ({
39
+ filePath: warn.location?.file !== undefined ? path.resolve(this._pkgPath, warn.location.file) : undefined,
40
+ line: warn.location?.line,
41
+ char: warn.location?.column,
42
+ code: warn.text.slice(0, warn.text.indexOf(":")),
43
+ severity: "warning",
44
+ message: `${warn.pluginName ? `(${warn.pluginName}) ` : ""} ${warn.text.slice(warn.text.indexOf(":") + 1)}`,
45
+ type: "build",
46
+ })),
47
+ ...buildResult.errors.map((err) => ({
48
+ filePath: err.location?.file !== undefined ? path.resolve(this._pkgPath, err.location.file) : undefined,
49
+ line: err.location?.line,
50
+ char: err.location?.column !== undefined ? err.location.column + 1 : undefined,
51
+ code: err.text.slice(0, err.text.indexOf(":")),
52
+ severity: "error",
53
+ message: `${err.pluginName ? `(${err.pluginName}) ` : ""} ${err.text.slice(err.text.indexOf(":") + 1)}`,
54
+ type: "build",
55
+ })),
56
+ ] as ISdCliPackageBuildResult[];
57
+
58
+ return {
59
+ results,
60
+ outputFiles: buildResult.outputFiles,
61
+ metafile: buildResult.metafile,
62
+ };
63
+ }
64
+
65
+ #debug(...msg: any[]): void {
66
+ this.#logger.debug(
67
+ `[${path.basename(this._pkgPath)}] (${Object.keys(this._esbuildOptions.entryPoints as Record<string, any>).join(", ")})`,
68
+ ...msg,
69
+ );
70
+ }
71
+ }