@reliverse/build 2.2.7

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 (75) hide show
  1. package/dist/impl/assets.d.ts +28 -0
  2. package/dist/impl/assets.js +188 -0
  3. package/dist/impl/cache.d.ts +25 -0
  4. package/dist/impl/cache.js +209 -0
  5. package/dist/impl/constants.d.ts +44 -0
  6. package/dist/impl/constants.js +45 -0
  7. package/dist/impl/debug.d.ts +30 -0
  8. package/dist/impl/debug.js +150 -0
  9. package/dist/impl/dependency-tracker.d.ts +23 -0
  10. package/dist/impl/dependency-tracker.js +113 -0
  11. package/dist/impl/dev-server.d.ts +24 -0
  12. package/dist/impl/dev-server.js +360 -0
  13. package/dist/impl/dts-generator.d.ts +26 -0
  14. package/dist/impl/dts-generator.js +514 -0
  15. package/dist/impl/go-build.d.ts +10 -0
  16. package/dist/impl/go-build.js +350 -0
  17. package/dist/impl/html-processor.d.ts +21 -0
  18. package/dist/impl/html-processor.js +167 -0
  19. package/dist/impl/impl.d.ts +0 -0
  20. package/dist/impl/impl.js +0 -0
  21. package/dist/impl/plugins/asset-optimization.d.ts +2 -0
  22. package/dist/impl/plugins/asset-optimization.js +114 -0
  23. package/dist/impl/plugins/bundle-analyzer.d.ts +2 -0
  24. package/dist/impl/plugins/bundle-analyzer.js +156 -0
  25. package/dist/impl/plugins/css-modules.d.ts +2 -0
  26. package/dist/impl/plugins/css-modules.js +19 -0
  27. package/dist/impl/plugins/index.d.ts +21 -0
  28. package/dist/impl/plugins/index.js +65 -0
  29. package/dist/impl/plugins/performance.d.ts +2 -0
  30. package/dist/impl/plugins/performance.js +62 -0
  31. package/dist/impl/plugins/react-refresh.d.ts +2 -0
  32. package/dist/impl/plugins/react-refresh.js +33 -0
  33. package/dist/impl/plugins/svg-as-react.d.ts +2 -0
  34. package/dist/impl/plugins/svg-as-react.js +18 -0
  35. package/dist/impl/plugins/typescript-declarations.d.ts +2 -0
  36. package/dist/impl/plugins/typescript-declarations.js +48 -0
  37. package/dist/impl/plugins/worker.d.ts +2 -0
  38. package/dist/impl/plugins/worker.js +20 -0
  39. package/dist/impl/presets.d.ts +10 -0
  40. package/dist/impl/presets.js +196 -0
  41. package/dist/impl/providers/mkdist/loader.d.ts +4 -0
  42. package/dist/impl/providers/mkdist/loader.js +26 -0
  43. package/dist/impl/providers/mkdist/loaders/js.d.ts +2 -0
  44. package/dist/impl/providers/mkdist/loaders/js.js +50 -0
  45. package/dist/impl/providers/mkdist/loaders/loaders-mod.d.ts +9 -0
  46. package/dist/impl/providers/mkdist/loaders/loaders-mod.js +22 -0
  47. package/dist/impl/providers/mkdist/make.d.ts +11 -0
  48. package/dist/impl/providers/mkdist/make.js +230 -0
  49. package/dist/impl/providers/mkdist/utils/dts.d.ts +11 -0
  50. package/dist/impl/providers/mkdist/utils/dts.js +117 -0
  51. package/dist/impl/providers/mkdist/utils/fs.d.ts +1 -0
  52. package/dist/impl/providers/mkdist/utils/fs.js +15 -0
  53. package/dist/impl/providers/mkdist-dts.d.ts +24 -0
  54. package/dist/impl/providers/mkdist-dts.js +8 -0
  55. package/dist/impl/tsconfig-validator.d.ts +35 -0
  56. package/dist/impl/tsconfig-validator.js +184 -0
  57. package/dist/impl/type-guards.d.ts +20 -0
  58. package/dist/impl/type-guards.js +147 -0
  59. package/dist/impl/types.d.ts +322 -0
  60. package/dist/impl/types.js +0 -0
  61. package/dist/impl/utils/go-build-handler.d.ts +12 -0
  62. package/dist/impl/utils/go-build-handler.js +83 -0
  63. package/dist/impl/utils/log-extraction.d.ts +25 -0
  64. package/dist/impl/utils/log-extraction.js +24 -0
  65. package/dist/impl/utils/package-filtering.d.ts +5 -0
  66. package/dist/impl/utils/package-filtering.js +22 -0
  67. package/dist/impl/utils/rebuild-queue.d.ts +38 -0
  68. package/dist/impl/utils/rebuild-queue.js +110 -0
  69. package/dist/impl/validation.d.ts +9 -0
  70. package/dist/impl/validation.js +332 -0
  71. package/dist/impl/watch.d.ts +21 -0
  72. package/dist/impl/watch.js +144 -0
  73. package/dist/mod.d.ts +17 -0
  74. package/dist/mod.js +1390 -0
  75. package/package.json +42 -0
@@ -0,0 +1,350 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readdir, rename, stat } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { logger } from "@reliverse/relinka";
6
+ import { lookpath } from "lookpath";
7
+ const DEFAULT_TARGETS = [
8
+ ["windows", "amd64", "dll", "win32"],
9
+ ["linux", "amd64", "so", "linux"],
10
+ ["linux", "arm64", "so", "linux"],
11
+ ["darwin", "amd64", "dylib", "darwin"],
12
+ ["darwin", "arm64", "dylib", "darwin"]
13
+ ];
14
+ async function checkDockerAvailable() {
15
+ const dockerPath = await lookpath("docker");
16
+ if (!dockerPath) {
17
+ return {
18
+ available: false,
19
+ reason: "Docker is not installed or not in PATH"
20
+ };
21
+ }
22
+ try {
23
+ const proc = Bun.spawnSync(["docker", "version"], {
24
+ stdout: "pipe",
25
+ stderr: "pipe"
26
+ });
27
+ if (proc.exitCode !== 0) {
28
+ const stderr = proc.stderr?.toString() || "";
29
+ if (stderr.includes("Cannot connect to the Docker daemon") || stderr.includes("Is the docker daemon running") || stderr.includes("dockerDesktopLinuxEngine") || stderr.includes("The system cannot find the file specified")) {
30
+ return {
31
+ available: false,
32
+ reason: "Docker daemon is not running"
33
+ };
34
+ }
35
+ return {
36
+ available: false,
37
+ reason: `Docker check failed: ${stderr.trim()}`
38
+ };
39
+ }
40
+ return { available: true };
41
+ } catch (error) {
42
+ return {
43
+ available: false,
44
+ reason: `Error checking Docker: ${error instanceof Error ? error.message : String(error)}`
45
+ };
46
+ }
47
+ }
48
+ async function findGoFiles(dir, goFiles = []) {
49
+ try {
50
+ const entries = await readdir(dir, { withFileTypes: true });
51
+ for (const entry of entries) {
52
+ const fullPath = path.join(dir, entry.name);
53
+ if (entry.isDirectory()) {
54
+ if (entry.name === "node_modules" || entry.name === "release" || entry.name === "dist" || entry.name === ".git") {
55
+ continue;
56
+ }
57
+ await findGoFiles(fullPath, goFiles);
58
+ } else if (entry.isFile() && entry.name.endsWith(".go")) {
59
+ goFiles.push(fullPath);
60
+ }
61
+ }
62
+ } catch {
63
+ }
64
+ return goFiles;
65
+ }
66
+ function getExpectedBinaryName(target, outputName) {
67
+ const [goos, goarch] = target.split("/");
68
+ let platformName = goos;
69
+ let suffix = "";
70
+ if (goos === "windows") {
71
+ platformName = "win32";
72
+ suffix = ".dll";
73
+ } else if (goos === "linux") {
74
+ platformName = "linux";
75
+ suffix = ".so";
76
+ } else if (goos === "darwin") {
77
+ platformName = "darwin";
78
+ suffix = ".dylib";
79
+ }
80
+ return `${outputName}-${platformName}-${goarch}${suffix}`;
81
+ }
82
+ async function shouldRebuildGo(packagePath, targets, outputDir, outputName) {
83
+ const releasePath = path.join(packagePath, outputDir);
84
+ if (!existsSync(releasePath)) {
85
+ return { rebuild: true, reason: "Release directory does not exist" };
86
+ }
87
+ try {
88
+ const goFiles = await findGoFiles(packagePath);
89
+ goFiles.push(path.join(packagePath, "go.mod"));
90
+ const goSumPath = path.join(packagePath, "go.sum");
91
+ if (existsSync(goSumPath)) {
92
+ goFiles.push(goSumPath);
93
+ }
94
+ let newestGoFileTime = 0;
95
+ for (const goFile of goFiles) {
96
+ if (existsSync(goFile)) {
97
+ const stats = await stat(goFile);
98
+ if (stats.mtimeMs > newestGoFileTime) {
99
+ newestGoFileTime = stats.mtimeMs;
100
+ }
101
+ }
102
+ }
103
+ const missingBinaries = [];
104
+ const outdatedBinaries = [];
105
+ for (const target of targets) {
106
+ const expectedBinary = getExpectedBinaryName(target, outputName);
107
+ let binaryPath = path.join(releasePath, expectedBinary);
108
+ if (target.startsWith("windows/") && !existsSync(binaryPath)) {
109
+ const windowsBinary = expectedBinary.replace("win32", "windows");
110
+ const windowsPath = path.join(releasePath, windowsBinary);
111
+ if (existsSync(windowsPath)) {
112
+ binaryPath = windowsPath;
113
+ } else {
114
+ const windows4Binary = expectedBinary.replace("win32", "windows-4.0");
115
+ const windows4Path = path.join(releasePath, windows4Binary);
116
+ if (existsSync(windows4Path)) {
117
+ binaryPath = windows4Path;
118
+ }
119
+ }
120
+ }
121
+ if (!existsSync(binaryPath)) {
122
+ missingBinaries.push(target);
123
+ continue;
124
+ }
125
+ const binaryStats = await stat(binaryPath);
126
+ if (binaryStats.mtimeMs < newestGoFileTime) {
127
+ outdatedBinaries.push(target);
128
+ }
129
+ }
130
+ if (missingBinaries.length > 0) {
131
+ return {
132
+ rebuild: true,
133
+ reason: `Missing Go binaries for: ${missingBinaries.join(", ")}`
134
+ };
135
+ }
136
+ if (outdatedBinaries.length > 0) {
137
+ return {
138
+ rebuild: true,
139
+ reason: `Outdated Go binaries for: ${outdatedBinaries.join(", ")}`
140
+ };
141
+ }
142
+ return { rebuild: false, reason: "All Go binaries are up-to-date" };
143
+ } catch (error) {
144
+ return {
145
+ rebuild: true,
146
+ reason: `Error checking Go binaries: ${error instanceof Error ? error.message : String(error)}`
147
+ };
148
+ }
149
+ }
150
+ async function buildGoTarget(packagePath, goos, goarch, suffix, platformName, outputName, outputDir, buildMode, ldflags, mainFile) {
151
+ const outputFileName = `${outputName}-${platformName}-${goarch}.${suffix}`;
152
+ const outputPath = path.join(packagePath, outputDir, outputFileName);
153
+ const releasePath = path.join(packagePath, outputDir);
154
+ if (!existsSync(releasePath)) {
155
+ await mkdir(releasePath, { recursive: true });
156
+ }
157
+ logger.info(`Building for ${goos}/${goarch}...`);
158
+ const env = {
159
+ ...process.env,
160
+ GOOS: goos,
161
+ GOARCH: goarch,
162
+ CGO_ENABLED: "1"
163
+ };
164
+ const proc = Bun.spawnSync(
165
+ [
166
+ "go",
167
+ "build",
168
+ `-buildmode=${buildMode}`,
169
+ `-ldflags=${ldflags}`,
170
+ "-o",
171
+ outputPath,
172
+ mainFile
173
+ ],
174
+ {
175
+ env,
176
+ cwd: packagePath
177
+ }
178
+ );
179
+ if (proc.exitCode !== 0) {
180
+ logger.error(`Failed to build for ${goos}/${goarch}`);
181
+ if (proc.stderr) {
182
+ logger.error(proc.stderr.toString());
183
+ }
184
+ return false;
185
+ }
186
+ logger.success(`\u2713 Built ${outputFileName}`);
187
+ return true;
188
+ }
189
+ async function buildWithXgo(packagePath, config, outputName) {
190
+ const xgoBase = path.join(os.homedir(), "go/bin/xgo");
191
+ const XGO = os.platform() === "win32" ? `${xgoBase}.exe` : xgoBase;
192
+ let targets;
193
+ if (config.targets) {
194
+ targets = Array.isArray(config.targets) ? config.targets.join(",") : config.targets;
195
+ } else {
196
+ targets = "linux/arm64,linux/amd64,darwin/arm64,darwin/amd64,windows/amd64";
197
+ }
198
+ const outputDir = config.outputDir ?? "release";
199
+ const releasePath = path.join(packagePath, outputDir);
200
+ const targetsArray = targets.split(",").map((t) => t.trim());
201
+ const rebuildCheck = await shouldRebuildGo(
202
+ packagePath,
203
+ targetsArray,
204
+ outputDir,
205
+ outputName
206
+ );
207
+ if (!rebuildCheck.rebuild) {
208
+ logger.info(`\u2713 ${rebuildCheck.reason}, skipping rebuild`);
209
+ return { success: true, errors: [] };
210
+ }
211
+ if (rebuildCheck.reason) {
212
+ logger.info(`Rebuilding: ${rebuildCheck.reason}`);
213
+ }
214
+ if (!existsSync(XGO)) {
215
+ const error = `xgo not found at ${XGO}. Please install: go install github.com/crazy-max/xgo@latest`;
216
+ logger.error(`Error: ${error}`);
217
+ return { success: false, errors: [error] };
218
+ }
219
+ const dockerCheck = await checkDockerAvailable();
220
+ if (!dockerCheck.available) {
221
+ logger.warn(
222
+ `\u26A0\uFE0F Skipping Go build: Docker is not installed or not running. Install Docker Desktop and ensure it's running to build Go binaries.`
223
+ );
224
+ return { success: true, errors: [] };
225
+ }
226
+ logger.info("Compiling native binaries with xgo...");
227
+ const buildMode = config.buildMode ?? "c-shared";
228
+ const ldflags = config.ldflags ?? "-s -w";
229
+ const goVersion = config.goVersion ?? "1.20.3";
230
+ if (!existsSync(releasePath)) {
231
+ await mkdir(releasePath, { recursive: true });
232
+ }
233
+ const outPath = `${outputDir}/${outputName}`;
234
+ const proc = Bun.spawnSync(
235
+ [
236
+ XGO,
237
+ "-go",
238
+ goVersion,
239
+ "-out",
240
+ outPath,
241
+ `--targets=${targets}`,
242
+ `-ldflags=${ldflags}`,
243
+ `-buildmode=${buildMode}`,
244
+ "."
245
+ ],
246
+ {
247
+ cwd: packagePath
248
+ }
249
+ );
250
+ if (proc.stdout) {
251
+ logger.info(proc.stdout.toString());
252
+ }
253
+ if (proc.exitCode !== 0) {
254
+ const error = "xgo compilation failed";
255
+ logger.error(error);
256
+ if (proc.stderr) {
257
+ const stderr = proc.stderr.toString();
258
+ logger.error(stderr);
259
+ return { success: false, errors: [error, stderr] };
260
+ }
261
+ return { success: false, errors: [error] };
262
+ }
263
+ if (existsSync(releasePath)) {
264
+ const binaries = await readdir(releasePath);
265
+ const windowsBinaries = binaries.filter(
266
+ (binary) => binary.includes("windows") && !binary.includes("win32")
267
+ );
268
+ await Promise.all(
269
+ windowsBinaries.map((binary) => {
270
+ const binaryPath = path.join(releasePath, binary);
271
+ const newPath = binaryPath.replace(/windows(-4\.0)?/g, "win32");
272
+ return rename(binaryPath, newPath);
273
+ })
274
+ );
275
+ }
276
+ return { success: true, errors: [] };
277
+ }
278
+ async function buildWithNative(packagePath, config, outputName) {
279
+ logger.info("Compiling native binaries with native Go build...");
280
+ const outputDir = config.outputDir ?? "release";
281
+ const buildMode = config.buildMode ?? "c-shared";
282
+ const ldflags = config.ldflags ?? "-s -w";
283
+ const mainFile = config.mainFile ?? "main.go";
284
+ const currentPlatform = os.platform();
285
+ let targetsToBuild = [...DEFAULT_TARGETS];
286
+ if (currentPlatform === "win32") {
287
+ targetsToBuild = targetsToBuild.filter(([goos]) => goos === "windows");
288
+ logger.info(
289
+ "Building only for Windows (CGO cross-compilation from Windows requires special setup)"
290
+ );
291
+ }
292
+ if (config.targets) {
293
+ const targetSet = new Set(
294
+ Array.isArray(config.targets) ? config.targets : [config.targets]
295
+ );
296
+ targetsToBuild = targetsToBuild.filter(
297
+ ([goos, goarch]) => targetSet.has(`${goos}/${goarch}`)
298
+ );
299
+ }
300
+ const results = await Promise.all(
301
+ targetsToBuild.map(
302
+ ([goos, goarch, suffix, platformName]) => buildGoTarget(
303
+ packagePath,
304
+ goos,
305
+ goarch,
306
+ suffix,
307
+ platformName,
308
+ outputName,
309
+ outputDir,
310
+ buildMode,
311
+ ldflags,
312
+ mainFile
313
+ ).catch((err) => {
314
+ logger.error(
315
+ `Error building ${goos}/${goarch}: ${err instanceof Error ? err.message : String(err)}`
316
+ );
317
+ return false;
318
+ })
319
+ )
320
+ );
321
+ const successCount = results.filter((r) => r === true).length;
322
+ const failCount = results.filter((r) => r === false).length;
323
+ logger.info(
324
+ `
325
+ Build complete: ${successCount} succeeded, ${failCount} failed`
326
+ );
327
+ if (successCount === 0) {
328
+ const error = "No targets built successfully";
329
+ logger.error(error);
330
+ return { success: false, errors: [error] };
331
+ }
332
+ return { success: true, errors: [] };
333
+ }
334
+ export async function buildGo(packagePath, packageName, config) {
335
+ if (config?.enable === false) {
336
+ return { success: true, errors: [] };
337
+ }
338
+ const effectiveConfig = config ?? { enable: true };
339
+ const outputName = effectiveConfig.outputName ?? packageName.replace(/^@[^/]+\//, "").replace(/[^a-zA-Z0-9-]/g, "-");
340
+ const provider = effectiveConfig.provider ?? "xgo";
341
+ if (provider !== "xgo" && provider !== "native") {
342
+ const error = `Invalid provider "${provider}". Must be "xgo" or "native".`;
343
+ logger.error(`Error: ${error}`);
344
+ return { success: false, errors: [error] };
345
+ }
346
+ if (provider === "xgo") {
347
+ return buildWithXgo(packagePath, effectiveConfig, outputName);
348
+ }
349
+ return buildWithNative(packagePath, effectiveConfig, outputName);
350
+ }
@@ -0,0 +1,21 @@
1
+ import type { PackageInfo } from "./types.js";
2
+ export interface HTMLProcessorOptions {
3
+ minify?: boolean;
4
+ injectAssets?: boolean;
5
+ publicPath?: string;
6
+ }
7
+ export declare class HTMLProcessor {
8
+ private options;
9
+ constructor(options?: HTMLProcessorOptions);
10
+ processHTML(pkg: PackageInfo, outputDir: string): Promise<void>;
11
+ private processHTMLFile;
12
+ private processScriptTags;
13
+ private processLinkTags;
14
+ private injectBuiltAssets;
15
+ private findBuiltAsset;
16
+ private findBuiltFiles;
17
+ private getRelativePath;
18
+ private getRelativePathFromOutput;
19
+ private minifyHTML;
20
+ }
21
+ export declare function processHTMLForPackage(pkg: PackageInfo, outputDir: string, options?: HTMLProcessorOptions): Promise<void>;
@@ -0,0 +1,167 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { logger } from "@reliverse/relinka";
4
+ export class HTMLProcessor {
5
+ options;
6
+ constructor(options = {}) {
7
+ this.options = {
8
+ minify: false,
9
+ injectAssets: true,
10
+ publicPath: "/",
11
+ ...options
12
+ };
13
+ }
14
+ async processHTML(pkg, outputDir) {
15
+ const htmlFiles = pkg.entryPoints.filter((ep) => ep.endsWith(".html"));
16
+ if (htmlFiles.length === 0) {
17
+ return;
18
+ }
19
+ for (const htmlFile of htmlFiles) {
20
+ await this.processHTMLFile(htmlFile, outputDir);
21
+ }
22
+ }
23
+ async processHTMLFile(htmlPath, outputDir) {
24
+ try {
25
+ const content = readFileSync(htmlPath, "utf-8");
26
+ let processedContent = content;
27
+ processedContent = await this.processScriptTags(
28
+ processedContent,
29
+ htmlPath,
30
+ outputDir
31
+ );
32
+ processedContent = await this.processLinkTags(
33
+ processedContent,
34
+ htmlPath,
35
+ outputDir
36
+ );
37
+ if (this.options.injectAssets) {
38
+ processedContent = await this.injectBuiltAssets(
39
+ processedContent,
40
+ outputDir
41
+ );
42
+ }
43
+ if (this.options.minify) {
44
+ processedContent = this.minifyHTML(processedContent);
45
+ }
46
+ const outputFileName = basename(htmlPath);
47
+ const outputPath = join(outputDir, outputFileName);
48
+ writeFileSync(outputPath, processedContent, "utf-8");
49
+ logger.info(`\u{1F4C4} Processed HTML: ${htmlPath} \u2192 ${outputPath}`);
50
+ } catch (error) {
51
+ logger.error(`Failed to process HTML file ${htmlPath}: ${error}`);
52
+ }
53
+ }
54
+ async processScriptTags(content, htmlPath, outputDir) {
55
+ const scriptRegex = /<script[^>]*src=["']([^"']+)["'][^>]*><\/script>/g;
56
+ return content.replace(scriptRegex, (match, src) => {
57
+ if (src.startsWith("./") || src.startsWith("../") || src.startsWith("/")) {
58
+ const scriptPath = resolve(dirname(htmlPath), src);
59
+ const builtScriptPath = this.findBuiltAsset(scriptPath, outputDir, [
60
+ ".js",
61
+ ".mjs"
62
+ ]);
63
+ if (builtScriptPath) {
64
+ const relativePath = this.getRelativePath(htmlPath, builtScriptPath);
65
+ return match.replace(src, relativePath);
66
+ }
67
+ }
68
+ return match;
69
+ });
70
+ }
71
+ async processLinkTags(content, htmlPath, outputDir) {
72
+ const linkRegex = /<link[^>]*href=["']([^"']+)["'][^>]*>/g;
73
+ return content.replace(linkRegex, (match, href) => {
74
+ if (href.startsWith("./") || href.startsWith("../") || href.startsWith("/")) {
75
+ const cssPath = resolve(dirname(htmlPath), href);
76
+ const builtCssPath = this.findBuiltAsset(cssPath, outputDir, [".css"]);
77
+ if (builtCssPath) {
78
+ const relativePath = this.getRelativePath(htmlPath, builtCssPath);
79
+ return match.replace(href, relativePath);
80
+ }
81
+ }
82
+ return match;
83
+ });
84
+ }
85
+ async injectBuiltAssets(content, outputDir) {
86
+ const jsFiles = this.findBuiltFiles(outputDir, [".js", ".mjs"]);
87
+ const cssFiles = this.findBuiltFiles(outputDir, [".css"]);
88
+ let injectedContent = content;
89
+ if (cssFiles.length > 0) {
90
+ const cssLinks = cssFiles.map((cssFile) => {
91
+ const relativePath = this.getRelativePathFromOutput(
92
+ cssFile,
93
+ outputDir
94
+ );
95
+ return ` <link rel="stylesheet" href="${relativePath}">`;
96
+ }).join("\n");
97
+ injectedContent = injectedContent.replace(
98
+ /<\/head>/i,
99
+ `${cssLinks}
100
+ </head>`
101
+ );
102
+ }
103
+ if (jsFiles.length > 0) {
104
+ const jsScripts = jsFiles.map((jsFile) => {
105
+ const relativePath = this.getRelativePathFromOutput(
106
+ jsFile,
107
+ outputDir
108
+ );
109
+ return ` <script src="${relativePath}"><\/script>`;
110
+ }).join("\n");
111
+ injectedContent = injectedContent.replace(
112
+ /<\/body>/i,
113
+ `${jsScripts}
114
+ </body>`
115
+ );
116
+ }
117
+ return injectedContent;
118
+ }
119
+ findBuiltAsset(originalPath, outputDir, extensions) {
120
+ const baseName = basename(
121
+ originalPath,
122
+ extensions.find((ext) => originalPath.endsWith(ext)) || ""
123
+ );
124
+ for (const ext of extensions) {
125
+ const possiblePath = join(outputDir, `${baseName}${ext}`);
126
+ if (existsSync(possiblePath)) {
127
+ return possiblePath;
128
+ }
129
+ }
130
+ return null;
131
+ }
132
+ findBuiltFiles(outputDir, extensions) {
133
+ const files = [];
134
+ try {
135
+ const { readdirSync, statSync } = require("node:fs");
136
+ const items = readdirSync(outputDir);
137
+ for (const item of items) {
138
+ const itemPath = join(outputDir, item);
139
+ const stats = statSync(itemPath);
140
+ if (stats.isFile()) {
141
+ const ext = extensions.find((e) => item.endsWith(e));
142
+ if (ext) {
143
+ files.push(itemPath);
144
+ }
145
+ }
146
+ }
147
+ } catch (_error) {
148
+ }
149
+ return files;
150
+ }
151
+ getRelativePath(fromPath, toPath) {
152
+ const fromDir = dirname(fromPath);
153
+ const relativePath = require("node:path").relative(fromDir, toPath);
154
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
155
+ }
156
+ getRelativePathFromOutput(filePath, outputDir) {
157
+ const relativePath = require("node:path").relative(outputDir, filePath);
158
+ return this.options.publicPath + relativePath.replace(/\\/g, "/");
159
+ }
160
+ minifyHTML(content) {
161
+ return content.replace(/\s+/g, " ").replace(/>\s+</g, "><").replace(/\s+>/g, ">").replace(/>\s+/g, ">").trim();
162
+ }
163
+ }
164
+ export async function processHTMLForPackage(pkg, outputDir, options = {}) {
165
+ const processor = new HTMLProcessor(options);
166
+ await processor.processHTML(pkg, outputDir);
167
+ }
File without changes
File without changes
@@ -0,0 +1,2 @@
1
+ import type { DlerPlugin } from "../types.js";
2
+ export declare const AssetOptimizationPlugin: DlerPlugin;
@@ -0,0 +1,114 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { extname, join } from "node:path";
3
+ import { logger } from "@reliverse/relinka";
4
+ export const AssetOptimizationPlugin = {
5
+ name: "asset-optimization",
6
+ setup: (buildConfig) => {
7
+ buildConfig.loader = {
8
+ ...buildConfig.loader,
9
+ ".png": "file",
10
+ ".jpg": "file",
11
+ ".jpeg": "file",
12
+ ".gif": "file",
13
+ ".svg": "file",
14
+ ".webp": "file",
15
+ ".ico": "file",
16
+ ".woff": "file",
17
+ ".woff2": "file",
18
+ ".ttf": "file",
19
+ ".eot": "file"
20
+ };
21
+ if (buildConfig.verbose) {
22
+ logger.debug("Asset optimization plugin applied");
23
+ }
24
+ },
25
+ onBuildEnd: async (result) => {
26
+ if (!result.success || result.skipped) {
27
+ return;
28
+ }
29
+ const pkg = result.package;
30
+ if (!pkg.isFrontendApp) {
31
+ return;
32
+ }
33
+ try {
34
+ await optimizeAssets(pkg);
35
+ } catch (error) {
36
+ logger.warn(`Failed to optimize assets for ${pkg.name}: ${error}`);
37
+ }
38
+ }
39
+ };
40
+ async function optimizeAssets(pkg) {
41
+ const outputDir = pkg.outputDir;
42
+ if (!existsSync(outputDir)) {
43
+ return;
44
+ }
45
+ const assetExtensions = [
46
+ ".png",
47
+ ".jpg",
48
+ ".jpeg",
49
+ ".gif",
50
+ ".svg",
51
+ ".webp",
52
+ ".ico",
53
+ ".woff",
54
+ ".woff2",
55
+ ".ttf",
56
+ ".eot"
57
+ ];
58
+ const assetFiles = [];
59
+ const glob = new Bun.Glob("**/*");
60
+ const files = Array.from(glob.scanSync({ cwd: outputDir, onlyFiles: true }));
61
+ for (const file of files) {
62
+ const ext = extname(file);
63
+ if (assetExtensions.includes(ext)) {
64
+ assetFiles.push(join(outputDir, file));
65
+ }
66
+ }
67
+ if (assetFiles.length === 0) {
68
+ return;
69
+ }
70
+ logger.info(`\u{1F5BC}\uFE0F Optimizing ${assetFiles.length} assets for ${pkg.name}`);
71
+ for (const assetFile of assetFiles) {
72
+ try {
73
+ await optimizeAsset(assetFile);
74
+ } catch (error) {
75
+ logger.warn(`Failed to optimize asset ${assetFile}: ${error}`);
76
+ }
77
+ }
78
+ }
79
+ async function optimizeAsset(assetPath) {
80
+ const ext = extname(assetPath).toLowerCase();
81
+ switch (ext) {
82
+ case ".svg":
83
+ await optimizeSVG(assetPath);
84
+ break;
85
+ case ".png":
86
+ case ".jpg":
87
+ case ".jpeg":
88
+ case ".gif":
89
+ case ".webp":
90
+ await optimizeImage(assetPath);
91
+ break;
92
+ case ".woff":
93
+ case ".woff2":
94
+ case ".ttf":
95
+ case ".eot":
96
+ await optimizeFont(assetPath);
97
+ break;
98
+ default:
99
+ break;
100
+ }
101
+ }
102
+ async function optimizeSVG(svgPath) {
103
+ const content = readFileSync(svgPath, "utf-8");
104
+ let optimized = content.replace(/\s+/g, " ").replace(/>\s+</g, "><").replace(/\s+>/g, ">").replace(/>\s+/g, ">").trim();
105
+ optimized = optimized.replace(/<!--[\s\S]*?-->/g, "");
106
+ optimized = optimized.replace(/\s+(xmlns:xlink|xlink:href)="[^"]*"/g, "");
107
+ if (optimized !== content) {
108
+ writeFileSync(svgPath, optimized, "utf-8");
109
+ }
110
+ }
111
+ async function optimizeImage(_imagePath) {
112
+ }
113
+ async function optimizeFont(_fontPath) {
114
+ }
@@ -0,0 +1,2 @@
1
+ import type { DlerPlugin } from "../types.js";
2
+ export declare const BundleAnalyzerPlugin: DlerPlugin;