@simplysm/sd-cli 12.5.20 → 12.5.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.
@@ -0,0 +1,117 @@
1
+ import ts from "typescript";
2
+ import path from "path";
3
+ import { SdTsCompiler } from "../build-tools/SdTsCompiler";
4
+ import { convertTypeScriptDiagnostic } from "@angular/build/src/tools/esbuild/angular/diagnostics";
5
+ import postcss from "postcss";
6
+ import { FsUtil } from "@simplysm/sd-core-node";
7
+ import postcssUrl from "postcss-url";
8
+ import postcssHas from "css-has-pseudo";
9
+ import autoprefixer from "autoprefixer";
10
+ export function sdReactPlugin(conf) {
11
+ return {
12
+ name: "sd-ng-compiler",
13
+ setup: (build) => {
14
+ const compiler = new SdTsCompiler({
15
+ pkgPath: conf.pkgPath,
16
+ additionalOptions: { declaration: false },
17
+ isDevMode: conf.dev,
18
+ isForBundle: true,
19
+ watchScopePaths: conf.watchScopePaths
20
+ });
21
+ let buildResult;
22
+ const outputContentsCacheMap = new Map();
23
+ //---------------------------
24
+ build.onStart(async () => {
25
+ compiler.invalidate(conf.modifiedFileSet);
26
+ for (const modifiedFile of conf.modifiedFileSet) {
27
+ outputContentsCacheMap.delete(modifiedFile);
28
+ }
29
+ buildResult = await compiler.buildAsync();
30
+ conf.result.watchFileSet = buildResult.watchFileSet;
31
+ conf.result.affectedFileSet = buildResult.affectedFileSet;
32
+ conf.result.program = buildResult.program;
33
+ //-- return err/warn
34
+ return {
35
+ errors: [
36
+ ...buildResult.typescriptDiagnostics
37
+ .filter((item) => item.category === ts.DiagnosticCategory.Error)
38
+ .map((item) => convertTypeScriptDiagnostic(ts, item)),
39
+ ...Array.from(buildResult.stylesheetBundlingResultMap.values()).flatMap((item) => item.errors),
40
+ ].filterExists(),
41
+ warnings: [
42
+ ...buildResult.typescriptDiagnostics
43
+ .filter((item) => item.category !== ts.DiagnosticCategory.Error)
44
+ .map((item) => convertTypeScriptDiagnostic(ts, item)),
45
+ // ...Array.from(buildResult.stylesheetResultMap.values()).flatMap(item => item.warnings)
46
+ ],
47
+ };
48
+ });
49
+ build.onLoad({ filter: /\.tsx?$/ }, (args) => {
50
+ const output = outputContentsCacheMap.get(path.normalize(args.path));
51
+ if (output != null) {
52
+ return { contents: output, loader: "js" };
53
+ }
54
+ const emittedJsFile = buildResult.emittedFilesCacheMap.get(path.normalize(args.path))?.last();
55
+ if (!emittedJsFile) {
56
+ throw new Error(`ts 빌더 결과 emit 파일이 존재하지 않습니다. ${args.path}`);
57
+ }
58
+ const contents = emittedJsFile.text;
59
+ outputContentsCacheMap.set(path.normalize(args.path), contents);
60
+ return { contents, loader: "js" };
61
+ });
62
+ build.onLoad({ filter: /\.[cm]?jsx?$/ }, (args) => {
63
+ conf.result.watchFileSet.add(path.normalize(args.path));
64
+ return null;
65
+ });
66
+ build.onLoad({ filter: /\.css$/ }, async (args) => {
67
+ conf.result.watchFileSet.add(path.normalize(args.path));
68
+ const output = outputContentsCacheMap.get(path.normalize(args.path));
69
+ if (output != null) {
70
+ return {
71
+ contents: output,
72
+ loader: "file",
73
+ };
74
+ }
75
+ const css = await FsUtil.readFileAsync(path.normalize(args.path));
76
+ const result = await postcss()
77
+ .use(autoprefixer({
78
+ overrideBrowserslist: ["Chrome > 78"]
79
+ }))
80
+ .use(postcssUrl({
81
+ url: "copy",
82
+ }))
83
+ .use(postcssHas())
84
+ .process(css, {
85
+ from: path.normalize(args.path),
86
+ to: path.resolve(conf.pkgPath, "dist", "media", path.basename(args.path)),
87
+ });
88
+ outputContentsCacheMap.set(path.normalize(args.path), result.css);
89
+ return { contents: result.css, loader: "file" };
90
+ });
91
+ build.onLoad({
92
+ filter: new RegExp("(" +
93
+ Object.keys(build.initialOptions.loader)
94
+ .map((item) => "\\" + item)
95
+ .join("|") +
96
+ ")$"),
97
+ }, (args) => {
98
+ conf.result.watchFileSet.add(path.normalize(args.path));
99
+ return null;
100
+ });
101
+ build.onEnd((result) => {
102
+ for (const { outputFiles, metafile } of buildResult.stylesheetBundlingResultMap.values()) {
103
+ result.outputFiles = result.outputFiles ?? [];
104
+ result.outputFiles.push(...outputFiles);
105
+ if (result.metafile && metafile) {
106
+ result.metafile.inputs = { ...result.metafile.inputs, ...metafile.inputs };
107
+ result.metafile.outputs = { ...result.metafile.outputs, ...metafile.outputs };
108
+ }
109
+ }
110
+ conf.result.outputFiles = result.outputFiles;
111
+ conf.result.metafile = result.metafile;
112
+ conf.modifiedFileSet.clear();
113
+ });
114
+ },
115
+ };
116
+ }
117
+ //# sourceMappingURL=sdReactPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdReactPlugin.js","sourceRoot":"","sources":["../../src/bundle-plugins/sdReactPlugin.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export * from "./build-tools/SdCliNgRoutesFileGenerator";
4
4
  export * from "./build-tools/SdLinter";
5
5
  export * from "./build-tools/SdNgBundler";
6
6
  export * from "./build-tools/SdNgBundlerContext";
7
+ export * from "./build-tools/SdReactBundler";
8
+ export * from "./build-tools/SdReactBundlerContext";
7
9
  export * from "./build-tools/SdServerBundler";
8
10
  export * from "./build-tools/SdTsCompiler";
9
11
  export * from "./build-tools/SdTsLibBundler";
@@ -12,6 +14,7 @@ export * from "./builders/SdCliJsLibLinter";
12
14
  export * from "./builders/SdCliServerBuilder";
13
15
  export * from "./builders/SdCliTsLibBuilder";
14
16
  export * from "./bundle-plugins/sdNgPlugin";
17
+ export * from "./bundle-plugins/sdReactPlugin";
15
18
  export * from "./bundle-plugins/sdServerPlugin";
16
19
  export * from "./commons";
17
20
  export * from "./entry/SdCliElectron";
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ export * from "./build-tools/SdCliNgRoutesFileGenerator";
4
4
  export * from "./build-tools/SdLinter";
5
5
  export * from "./build-tools/SdNgBundler";
6
6
  export * from "./build-tools/SdNgBundlerContext";
7
+ export * from "./build-tools/SdReactBundler";
8
+ export * from "./build-tools/SdReactBundlerContext";
7
9
  export * from "./build-tools/SdServerBundler";
8
10
  export * from "./build-tools/SdTsCompiler";
9
11
  export * from "./build-tools/SdTsLibBundler";
@@ -12,6 +14,7 @@ export * from "./builders/SdCliJsLibLinter";
12
14
  export * from "./builders/SdCliServerBuilder";
13
15
  export * from "./builders/SdCliTsLibBuilder";
14
16
  export * from "./bundle-plugins/sdNgPlugin";
17
+ export * from "./bundle-plugins/sdReactPlugin";
15
18
  export * from "./bundle-plugins/sdServerPlugin";
16
19
  export * from "./commons";
17
20
  export * from "./entry/SdCliElectron";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-cli",
3
- "version": "12.5.20",
3
+ "version": "12.5.21",
4
4
  "description": "심플리즘 패키지 - CLI",
5
5
  "author": "김석래",
6
6
  "repository": {
@@ -21,10 +21,11 @@
21
21
  "@angular/compiler": "^18.2.7",
22
22
  "@angular/compiler-cli": "^18.2.7",
23
23
  "@electron/rebuild": "^3.6.2",
24
- "@simplysm/sd-core-common": "12.5.20",
25
- "@simplysm/sd-core-node": "12.5.20",
26
- "@simplysm/sd-service-server": "12.5.20",
27
- "@simplysm/sd-storage": "12.5.20",
24
+ "@simplysm/sd-core-common": "12.5.21",
25
+ "@simplysm/sd-core-node": "12.5.21",
26
+ "@simplysm/sd-service-server": "12.5.21",
27
+ "@simplysm/sd-storage": "12.5.21",
28
+ "autoprefixer": "^10.4.20",
28
29
  "browserslist": "^4.24.0",
29
30
  "cordova": "^12.0.0",
30
31
  "css-has-pseudo": "^7.0.0",
@@ -36,11 +37,13 @@
36
37
  "jszip": "^3.10.1",
37
38
  "node-stdlib-browser": "^1.2.1",
38
39
  "postcss": "^8.4.47",
40
+ "postcss-url": "^10.1.3",
39
41
  "rxjs": "^7.8.1",
40
42
  "sass": "^1.79.4",
41
43
  "semver": "^7.6.3",
42
44
  "specifier-resolution-node": "^1.1.4",
43
45
  "typescript": "~5.5.4",
46
+ "workbox-build": "^7.1.1",
44
47
  "xml2js": "^0.6.2",
45
48
  "yargs": "^17.7.2"
46
49
  },
@@ -0,0 +1,372 @@
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
+ import { generateSW } from "workbox-build";
14
+
15
+ export class SdReactBundler {
16
+ readonly #logger = Logger.get(["simplysm", "sd-cli", "SdNgBundler"]);
17
+
18
+ readonly #modifiedFileSet = new Set<string>();
19
+ readonly #compileResultCache: IReactPluginResultCache = {
20
+ affectedFileSet: new Set<string>(),
21
+ watchFileSet: new Set<string>()
22
+ };
23
+ #contexts: SdReactBundlerContext[] | undefined;
24
+
25
+ readonly #outputCache = new Map<string, string | number>();
26
+
27
+ readonly #opt: IOptions;
28
+
29
+ readonly #pkgNpmConf: INpmConfig;
30
+ readonly #indexFilePath: string;
31
+ readonly #tsConfigFilePath: string;
32
+ readonly #browserTarget: string[];
33
+
34
+ public constructor(opt: IOptions) {
35
+ this.#opt = opt;
36
+ this.#pkgNpmConf = FsUtil.readJson(path.resolve(opt.pkgPath, "package.json"));
37
+ this.#indexFilePath = path.resolve(opt.pkgPath, "src/index.tsx");
38
+ this.#tsConfigFilePath = path.resolve(opt.pkgPath, "tsconfig.json");
39
+ this.#browserTarget = transformSupportedBrowsersToTargets(browserslist(["Chrome > 78"]));
40
+ }
41
+
42
+ public markForChanges(filePaths: string[]): void {
43
+ for (const filePath of filePaths) {
44
+ this.#modifiedFileSet.add(path.normalize(filePath));
45
+ }
46
+ }
47
+
48
+ public async bundleAsync(): Promise<{
49
+ program?: ts.Program;
50
+ watchFileSet: Set<string>;
51
+ affectedFileSet: Set<string>;
52
+ results: ISdCliPackageBuildResult[];
53
+ }> {
54
+ this.#debug(`get contexts...`);
55
+
56
+ if (!this.#contexts) {
57
+ this.#contexts = [
58
+ this._getAppContext(),
59
+ ...(this.#opt.builderType === "electron" ? [this._getElectronMainContext()] : [])
60
+ ];
61
+ }
62
+
63
+ this.#debug(`build...`);
64
+
65
+ const bundlingResults = await this.#contexts.mapAsync(async (ctx, i) => await ctx.bundleAsync());
66
+
67
+ //-- results
68
+ const results = bundlingResults.mapMany((bundlingResult) => bundlingResult.results);
69
+
70
+ this.#debug(`convert result...`);
71
+
72
+ const outputFiles: IReactOutputFile[] = bundlingResults
73
+ .mapMany((item) => item.outputFiles ?? [])
74
+ .map((item) => ({
75
+ relPath: path.relative(this.#opt.pkgPath, item.path),
76
+ contents: item.contents
77
+ }));
78
+
79
+ // cordova empty
80
+ if (this.#opt.builderType === "cordova" && this.#opt.cordovaConfig?.plugins) {
81
+ outputFiles.push({ relPath: "cordova-empty.js", contents: "export default {};" });
82
+ }
83
+
84
+ // index.html
85
+ let indexHtml = await FsUtil.readFileAsync(path.resolve(this.#opt.pkgPath, "src/index.html"));
86
+ indexHtml = indexHtml.replace(/<\/head>/, "<script type=\"module\" src=\"index.js\"></script></head>");
87
+ if (!this.#opt.dev) {
88
+ indexHtml = indexHtml.replace(/<\/head>/, "<script src=\"registerSW.js\"></script></head>");
89
+ }
90
+ if (this.#opt.builderType === "cordova") {
91
+ indexHtml = indexHtml.replace(
92
+ /(.*)<\/head>/,
93
+ "<script type=\"module\" src=\"cordova-entry.js\"></script>\n$1</head>"
94
+ );
95
+ }
96
+ if (outputFiles.some((item) => item.relPath === "index.css")) {
97
+ indexHtml = indexHtml.replace(/(.*)<\/head>/, "<link rel=\"stylesheet\" href=\"index.css\">\n$1</head>");
98
+ }
99
+
100
+ outputFiles.push({
101
+ relPath: "index.html",
102
+ contents: indexHtml
103
+ });
104
+
105
+ // copy assets
106
+ outputFiles.push(
107
+ ...this.#copyAssets("src", "favicon.ico"),
108
+ ...this.#copyAssets("src", "manifest.webmanifest"),
109
+ ...this.#copyAssets("src/assets", "**/*", "assets"),
110
+ ...(this.#opt.dev && this.#opt.builderType === "cordova"
111
+ ? Object.keys(this.#opt.cordovaConfig?.platform ?? { browser: {} }).mapMany((platform) => [
112
+ ...this.#copyAssets(
113
+ `.cordova/platforms/${platform}/platform_www/plugins`,
114
+ "**/*",
115
+ `cordova-${platform}/plugins`
116
+ ),
117
+ ...this.#copyAssets(`.cordova/platforms/${platform}/platform_www`, "cordova.js", `cordova-${platform}`),
118
+ ...this.#copyAssets(
119
+ `.cordova/platforms/${platform}/platform_www`,
120
+ "cordova_plugins.js",
121
+ `cordova-${platform}`
122
+ ),
123
+ ...this.#copyAssets(`.cordova/platforms/${platform}/www`, "config.xml", `cordova-${platform}`)
124
+ ])
125
+ : [])
126
+ );
127
+
128
+ // TODO: extract 3rdpartylicenses
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
+ // service worker
143
+ if (!this.#opt.dev) {
144
+ try {
145
+ const swResult = await generateSW({
146
+ globDirectory: path.resolve(this.#opt.pkgPath, "dist"),
147
+ globPatterns: ["**/*"],
148
+ globIgnores: ["3rdpartylicenses.txt", "*.js.map"],
149
+ globStrict: true,
150
+ swDest: path.resolve(this.#opt.pkgPath, "dist", "sw.js")
151
+ });
152
+ swResult.warnings.map((msg) => {
153
+ results.push({
154
+ filePath: undefined,
155
+ line: undefined,
156
+ char: undefined,
157
+ code: undefined,
158
+ severity: "warning",
159
+ message: `(gen-sw) ${msg}`,
160
+ type: "build"
161
+ });
162
+ });
163
+
164
+ await FsUtil.writeFileAsync(
165
+ path.resolve(this.#opt.pkgPath, "dist", "registerSW.js"),
166
+ `
167
+ if ('serviceWorker' in navigator) {
168
+ window.addEventListener('load', () => {
169
+ navigator.serviceWorker.register('sw.js')
170
+ })
171
+ }`.trim()
172
+ );
173
+ }
174
+ catch (err) {
175
+ results.push({
176
+ filePath: undefined,
177
+ line: undefined,
178
+ char: undefined,
179
+ code: undefined,
180
+ severity: "error",
181
+ message: `(gen-sw) ${err.toString()}`,
182
+ type: "build"
183
+ });
184
+ }
185
+ }
186
+
187
+ this.#debug(`번들링중 영향받은 파일`, Array.from(this.#compileResultCache.affectedFileSet!));
188
+
189
+ return {
190
+ program: this.#compileResultCache.program,
191
+ watchFileSet: new Set(this.#compileResultCache.watchFileSet),
192
+ affectedFileSet: this.#compileResultCache.affectedFileSet!,
193
+ results
194
+ };
195
+ }
196
+
197
+ #copyAssets(srcDir: string, globPath: string, distDir?: string): IReactOutputFile[] {
198
+ const result: IReactOutputFile[] = [];
199
+ const srcGlobPath = path.resolve(this.#opt.pkgPath, srcDir, globPath);
200
+ const srcPaths = glob.sync(srcGlobPath);
201
+ for (const srcPath of srcPaths) {
202
+ if (!FsUtil.exists(srcPath)) continue;
203
+ result.push({
204
+ relPath: path.join(
205
+ ...[distDir, path.relative(path.resolve(this.#opt.pkgPath, srcDir), srcPath)].filterExists()
206
+ ),
207
+ contents: FsUtil.readFile(srcPath)
208
+ });
209
+ }
210
+
211
+ return result;
212
+ }
213
+
214
+ private _getAppContext() {
215
+ return new SdReactBundlerContext(this.#opt.pkgPath, {
216
+ absWorkingDir: this.#opt.pkgPath,
217
+ bundle: true,
218
+ keepNames: true,
219
+ format: "esm",
220
+ assetNames: "media/[name]",
221
+ conditions: ["es2020", "es2015", "module"],
222
+ resolveExtensions: [".js", ".mjs", ".cjs", ".ts", ".tsx"],
223
+ metafile: true,
224
+ legalComments: this.#opt.dev ? "eof" : "none",
225
+ logLevel: "silent",
226
+ minifyIdentifiers: !this.#opt.dev,
227
+ minifySyntax: !this.#opt.dev,
228
+ minifyWhitespace: !this.#opt.dev,
229
+ pure: ["forwardRef"],
230
+ outdir: this.#opt.pkgPath,
231
+ outExtension: undefined,
232
+ sourcemap: true, //this.#opt.dev,
233
+ splitting: true,
234
+ chunkNames: "[name]-[hash]",
235
+ tsconfig: this.#tsConfigFilePath,
236
+ write: false,
237
+ preserveSymlinks: false,
238
+ define: {
239
+ ...(!this.#opt.dev ? { ngDevMode: "false" } : {}),
240
+ "ngJitMode": "false",
241
+ "global": "global",
242
+ "process": "process",
243
+ "Buffer": "Buffer",
244
+ "process.env.SD_VERSION": JSON.stringify(this.#pkgNpmConf.version),
245
+ "process.env.NODE_ENV": JSON.stringify(this.#opt.dev ? "development" : "production"),
246
+ ...(this.#opt.env
247
+ ? Object.keys(this.#opt.env).toObject(
248
+ (key) => `process.env.${key}`,
249
+ (key) => JSON.stringify(this.#opt.env![key])
250
+ )
251
+ : {})
252
+ },
253
+ platform: "browser",
254
+ mainFields: ["es2020", "es2015", "browser", "module", "main"],
255
+ entryNames: "[name]",
256
+ entryPoints: {
257
+ index: this.#indexFilePath
258
+ },
259
+ external: ["electron"],
260
+ target: this.#browserTarget,
261
+ supported: { "async-await": false, "object-rest-spread": false },
262
+ loader: {
263
+ ".png": "file",
264
+ ".jpeg": "file",
265
+ ".jpg": "file",
266
+ ".jfif": "file",
267
+ ".gif": "file",
268
+ ".svg": "file",
269
+ ".woff": "file",
270
+ ".woff2": "file",
271
+ ".ttf": "file",
272
+ ".ttc": "file",
273
+ ".eot": "file",
274
+ ".ico": "file",
275
+ ".otf": "file",
276
+ ".csv": "file",
277
+ ".xlsx": "file",
278
+ ".xls": "file",
279
+ ".pptx": "file",
280
+ ".ppt": "file",
281
+ ".docx": "file",
282
+ ".doc": "file",
283
+ ".zip": "file",
284
+ ".pfx": "file",
285
+ ".pkl": "file",
286
+ ".mp3": "file",
287
+ ".ogg": "file"
288
+ },
289
+ inject: [PathUtil.posix(fileURLToPath(import.meta.resolve("node-stdlib-browser/helpers/esbuild/shim")))],
290
+ plugins: [
291
+ ...(this.#opt.builderType === "cordova" && this.#opt.cordovaConfig?.plugins
292
+ ? [
293
+ {
294
+ name: "cordova:plugin-empty",
295
+ setup: ({ onResolve }) => {
296
+ onResolve({ filter: new RegExp("(" + this.#opt.cordovaConfig!.plugins!.join("|") + ")") }, () => {
297
+ return {
298
+ path: `./cordova-empty.js`,
299
+ external: true
300
+ };
301
+ });
302
+ }
303
+ }
304
+ ]
305
+ : []),
306
+ sdReactPlugin({
307
+ modifiedFileSet: this.#modifiedFileSet,
308
+ dev: this.#opt.dev,
309
+ pkgPath: this.#opt.pkgPath,
310
+ result: this.#compileResultCache,
311
+ watchScopePaths: this.#opt.watchScopePaths,
312
+ }),
313
+ nodeStdLibBrowserPlugin(nodeStdLibBrowser)
314
+ ]
315
+ });
316
+ }
317
+
318
+ private _getElectronMainContext() {
319
+ return new SdReactBundlerContext(this.#opt.pkgPath, {
320
+ absWorkingDir: this.#opt.pkgPath,
321
+ bundle: true,
322
+ entryNames: "[name]",
323
+ assetNames: "media/[name]",
324
+ conditions: ["es2020", "es2015", "module"],
325
+ resolveExtensions: [".js", ".mjs", ".cjs", ".ts"],
326
+ metafile: true,
327
+ legalComments: this.#opt.dev ? "eof" : "none",
328
+ logLevel: "silent",
329
+ minify: !this.#opt.dev,
330
+ outdir: this.#opt.pkgPath,
331
+ sourcemap: true, //this.#opt.dev,
332
+ tsconfig: this.#tsConfigFilePath,
333
+ write: false,
334
+ preserveSymlinks: false,
335
+ external: ["electron"],
336
+ define: {
337
+ ...(!this.#opt.dev ? { ngDevMode: "false" } : {}),
338
+ "process.env.SD_VERSION": JSON.stringify(this.#pkgNpmConf.version),
339
+ "process.env.NODE_ENV": JSON.stringify(this.#opt.dev ? "development" : "production"),
340
+ ...(this.#opt.env
341
+ ? Object.keys(this.#opt.env).toObject(
342
+ (key) => `process.env.${key}`,
343
+ (key) => JSON.stringify(this.#opt.env![key])
344
+ )
345
+ : {})
346
+ },
347
+ platform: "node",
348
+ entryPoints: {
349
+ "electron-main": path.resolve(this.#opt.pkgPath, "src/electron-main.ts")
350
+ }
351
+ });
352
+ }
353
+
354
+ #debug(...msg: any[]): void {
355
+ this.#logger.debug(`[${path.basename(this.#opt.pkgPath)}]`, ...msg);
356
+ }
357
+ }
358
+
359
+ interface IOptions {
360
+ dev: boolean;
361
+ outputPath: string;
362
+ pkgPath: string;
363
+ builderType: string;
364
+ env: Record<string, string> | undefined;
365
+ cordovaConfig: ISdCliClientBuilderCordovaConfig | undefined;
366
+ watchScopePaths: string[];
367
+ }
368
+
369
+ interface IReactOutputFile {
370
+ relPath: string;
371
+ contents: Uint8Array | string;
372
+ }
@@ -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
+ }