@webmate-studio/builder 0.2.18 → 0.2.20

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/bundler.js +46 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webmate-studio/builder",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "type": "module",
5
5
  "description": "Webmate Studio Component Builder",
6
6
  "keywords": [
package/src/bundler.js CHANGED
@@ -3,6 +3,17 @@ import esbuildSvelte from 'esbuild-svelte';
3
3
  import fs from 'fs/promises';
4
4
  import path from 'path';
5
5
  import pc from 'picocolors';
6
+ import crypto from 'crypto';
7
+
8
+ /**
9
+ * Generate content hash for asset file
10
+ * @param {Buffer} content - File content as buffer
11
+ * @param {number} length - Hash length (default: 8)
12
+ * @returns {string} - Content hash (hex)
13
+ */
14
+ function generateAssetHash(content, length = 8) {
15
+ return crypto.createHash('sha256').update(content).digest('hex').substring(0, length);
16
+ }
6
17
 
7
18
  /**
8
19
  * Bundle island JavaScript files with esbuild
@@ -42,6 +53,7 @@ export async function bundleIsland(islandPath, outputPath, options = {}) {
42
53
  target,
43
54
  outfile: outputPath,
44
55
  platform: 'browser',
56
+ charset: 'utf8', // Force UTF-8 output to prevent base64 corruption (esbuild bug #1501)
45
57
  // Loaders for different file types
46
58
  // NOTE: All image formats are handled by custom plugin above (image-base64-dataurl)
47
59
  // to generate proper data:image/...;base64,... URLs for CSS compatibility
@@ -70,39 +82,48 @@ export async function bundleIsland(islandPath, outputPath, options = {}) {
70
82
  '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': 'false'
71
83
  },
72
84
  plugins: [
73
- // Image to base64 data URL loader
74
- // esbuild's 'dataurl' loader fails for:
75
- // - SVG: generates invalid data:image/svg+xml,<?xml... (unescaped XML)
76
- // - JPG/PNG/etc: generates invalid data:image/jpeg,���� (raw binary instead of base64)
77
- // Solution: Custom loader that properly base64 encodes ALL image formats
85
+ // Asset URL resolver with content hashing
86
+ // Instead of base64 encoding (bad for large images, no caching, huge bundles),
87
+ // we use asset markers with content hashes that get resolved at runtime:
88
+ // - wm dev: /assets/{componentName}/{filename} (no hash in dev)
89
+ // - CMS: /api/components/proxy/organization-components/{uuid}/assets/{filename}.{hash}.{ext}
78
90
  {
79
- name: 'image-base64-dataurl',
91
+ name: 'asset-url-resolver',
80
92
  setup(build) {
81
93
  // Handle all image formats
82
94
  build.onLoad({ filter: /\.(svg|jpg|jpeg|png|gif|webp)$/i }, async (args) => {
83
- const ext = args.path.split('.').pop().toLowerCase();
84
- const mimeTypes = {
85
- svg: 'image/svg+xml',
86
- jpg: 'image/jpeg',
87
- jpeg: 'image/jpeg',
88
- png: 'image/png',
89
- gif: 'image/gif',
90
- webp: 'image/webp'
91
- };
92
-
93
- // Read file as binary buffer
95
+ // Read file to generate content hash
94
96
  const buffer = await fs.readFile(args.path);
95
- // Convert to base64 string (pure ASCII, safe for JavaScript)
96
- const base64 = buffer.toString('base64');
97
- const mimeType = mimeTypes[ext] || 'application/octet-stream';
97
+ const hash = generateAssetHash(buffer);
98
+
99
+ // Get relative path from component directory
100
+ // Example: /full/path/components/Hero/assets/logo.svg
101
+ // → Find "assets/" segment and extract path from there
102
+ const assetPath = args.path;
103
+ const assetsIndex = assetPath.lastIndexOf('/assets/');
104
+
105
+ if (assetsIndex === -1) {
106
+ throw new Error(`Asset must be in "assets/" directory: ${assetPath}`);
107
+ }
108
+
109
+ // Extract relative path from assets/ onwards
110
+ // Example: assets/logo.svg
111
+ const relativePath = assetPath.substring(assetsIndex + 1); // +1 to keep "assets/"
112
+
113
+ // Parse filename and extension
114
+ const filename = path.basename(relativePath);
115
+ const ext = path.extname(filename);
116
+ const nameWithoutExt = path.basename(filename, ext);
117
+ const dir = path.dirname(relativePath);
98
118
 
99
- // Build the complete data URL
100
- const dataUrl = `data:${mimeType};base64,${base64}`;
119
+ // Build hashed filename: logo.abc123.svg
120
+ const hashedFilename = `${nameWithoutExt}.${hash}${ext}`;
121
+ const hashedPath = path.join(dir, hashedFilename);
101
122
 
102
- // Return as a raw JavaScript module with template literal to preserve exact bytes
103
- // Using template literal ensures no escaping or encoding happens
123
+ // Return as asset marker (will be resolved at runtime)
124
+ // Runtime resolver will replace __ASSET__: with correct base path
104
125
  return {
105
- contents: `export default \`${dataUrl}\`;`,
126
+ contents: `export default "__ASSET__:${hashedPath}";`,
106
127
  loader: 'js'
107
128
  };
108
129
  });