@webmate-studio/builder 0.1.0 → 0.1.2
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.
- package/package.json +12 -2
- package/src/build.js +3 -3
- package/src/bundler.js +163 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webmate-studio/builder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Webmate Studio Component Builder",
|
|
6
6
|
"keywords": [
|
|
@@ -24,11 +24,21 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@webmate-studio/core": "^0.1.0",
|
|
28
|
+
"@webmate-studio/parser": "^0.1.0",
|
|
29
|
+
"alpinejs": "^3.15.0",
|
|
27
30
|
"dom-serializer": "^2.0.0",
|
|
28
31
|
"esbuild": "^0.19.0",
|
|
32
|
+
"esbuild-svelte": "^0.9.3",
|
|
29
33
|
"glob": "^10.3.0",
|
|
30
34
|
"htmlparser2": "^9.0.0",
|
|
35
|
+
"lit": "^3.3.1",
|
|
36
|
+
"preact": "^10.27.2",
|
|
37
|
+
"react": "^19.2.0",
|
|
38
|
+
"react-dom": "^19.2.0",
|
|
39
|
+
"svelte": "^5.41.2",
|
|
31
40
|
"tailwindcss": "^4.1.0",
|
|
32
|
-
"@tailwindcss/cli": "^4.1.0"
|
|
41
|
+
"@tailwindcss/cli": "^4.1.0",
|
|
42
|
+
"vue": "^3.5.22"
|
|
33
43
|
}
|
|
34
44
|
}
|
package/src/build.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, cpSync } from 'fs';
|
|
2
2
|
import { join, dirname, relative, extname, basename } from 'path';
|
|
3
3
|
import { glob } from 'glob';
|
|
4
|
-
import { parseComponent } from '
|
|
5
|
-
import { loadConfig, logger } from '
|
|
4
|
+
import { parseComponent } from '@webmate-studio/parser';
|
|
5
|
+
import { loadConfig, logger } from '@webmate-studio/core';
|
|
6
6
|
import { cleanComponentHTML, extractStyles } from './html-cleaner.js';
|
|
7
7
|
import { generateManifest } from './manifest.js';
|
|
8
8
|
import { generateComponentCSS } from './tailwind-generator-v4.js';
|
|
9
|
-
import { bundleComponentIslands } from '
|
|
9
|
+
import { bundleComponentIslands } from './bundler.js';
|
|
10
10
|
import { parseDocument } from 'htmlparser2';
|
|
11
11
|
import { DomUtils } from 'htmlparser2';
|
|
12
12
|
import render from 'dom-serializer';
|
package/src/bundler.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
import esbuildSvelte from 'esbuild-svelte';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Bundle island JavaScript files with esbuild
|
|
9
|
+
* Supports: Vanilla JS, React, Preact, Svelte, Alpine, Lit, Vue, Solid
|
|
10
|
+
*/
|
|
11
|
+
export async function bundleIsland(islandPath, outputPath, options = {}) {
|
|
12
|
+
const {
|
|
13
|
+
minify = true,
|
|
14
|
+
sourcemap = true,
|
|
15
|
+
target = 'es2020',
|
|
16
|
+
format = 'esm'
|
|
17
|
+
} = options;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Resolve paths for dependencies from builder package
|
|
21
|
+
const builderNodeModules = path.resolve(import.meta.dirname, '../node_modules');
|
|
22
|
+
|
|
23
|
+
// Determine if this file should use JSX loader
|
|
24
|
+
// Only use JSX for .jsx files (React/Preact), not for .js files (Lit/Alpine/Vue/Vanilla)
|
|
25
|
+
const useJsxLoader = islandPath.endsWith('.jsx');
|
|
26
|
+
|
|
27
|
+
const result = await esbuild.build({
|
|
28
|
+
entryPoints: [islandPath],
|
|
29
|
+
bundle: true,
|
|
30
|
+
minify,
|
|
31
|
+
sourcemap,
|
|
32
|
+
format,
|
|
33
|
+
target,
|
|
34
|
+
outfile: outputPath,
|
|
35
|
+
platform: 'browser',
|
|
36
|
+
// Only enable JSX for .jsx files (React/Preact)
|
|
37
|
+
loader: useJsxLoader ? {
|
|
38
|
+
'.jsx': 'jsx',
|
|
39
|
+
'.ts': 'tsx',
|
|
40
|
+
'.tsx': 'tsx'
|
|
41
|
+
} : {
|
|
42
|
+
'.ts': 'tsx',
|
|
43
|
+
'.tsx': 'tsx'
|
|
44
|
+
},
|
|
45
|
+
// Use automatic JSX runtime (React 17+) only for JSX files
|
|
46
|
+
jsx: useJsxLoader ? 'automatic' : undefined,
|
|
47
|
+
// JSX import source (Preact uses preact, React uses react)
|
|
48
|
+
jsxImportSource: useJsxLoader ? 'preact' : undefined,
|
|
49
|
+
// Alias vue to the full build (with template compiler)
|
|
50
|
+
alias: {
|
|
51
|
+
'vue': 'vue/dist/vue.esm-bundler.js'
|
|
52
|
+
},
|
|
53
|
+
// Define Vue feature flags
|
|
54
|
+
define: {
|
|
55
|
+
'__VUE_OPTIONS_API__': 'true',
|
|
56
|
+
'__VUE_PROD_DEVTOOLS__': 'false',
|
|
57
|
+
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': 'false'
|
|
58
|
+
},
|
|
59
|
+
plugins: [
|
|
60
|
+
// Svelte support
|
|
61
|
+
esbuildSvelte({
|
|
62
|
+
compilerOptions: {
|
|
63
|
+
css: 'injected' // Inject CSS into JS
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
],
|
|
67
|
+
// Don't bundle browser globals
|
|
68
|
+
external: [],
|
|
69
|
+
// Add builder's node_modules to resolve path so framework runtimes can be found
|
|
70
|
+
nodePaths: [builderNodeModules],
|
|
71
|
+
// Log level
|
|
72
|
+
logLevel: 'warning'
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
outputPath,
|
|
78
|
+
size: (await fs.stat(outputPath)).size,
|
|
79
|
+
warnings: result.warnings
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: error.message,
|
|
85
|
+
errors: error.errors
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Bundle all islands in a component directory
|
|
92
|
+
*/
|
|
93
|
+
export async function bundleComponentIslands(componentDir, outputDir) {
|
|
94
|
+
const islandsDir = path.join(componentDir, 'islands');
|
|
95
|
+
|
|
96
|
+
// Check if islands directory exists
|
|
97
|
+
try {
|
|
98
|
+
await fs.access(islandsDir);
|
|
99
|
+
} catch {
|
|
100
|
+
// No islands directory
|
|
101
|
+
return { islands: [], success: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Find all .js files in islands directory
|
|
105
|
+
const files = await fs.readdir(islandsDir);
|
|
106
|
+
const islandFiles = files.filter((f) => f.endsWith('.js'));
|
|
107
|
+
|
|
108
|
+
if (islandFiles.length === 0) {
|
|
109
|
+
return { islands: [], success: true };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create output directory
|
|
113
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
114
|
+
|
|
115
|
+
const results = [];
|
|
116
|
+
|
|
117
|
+
for (const islandFile of islandFiles) {
|
|
118
|
+
const inputPath = path.join(islandsDir, islandFile);
|
|
119
|
+
const outputPath = path.join(outputDir, islandFile);
|
|
120
|
+
|
|
121
|
+
console.log(pc.dim(` Bundling ${islandFile}...`));
|
|
122
|
+
|
|
123
|
+
const result = await bundleIsland(inputPath, outputPath);
|
|
124
|
+
|
|
125
|
+
if (result.success) {
|
|
126
|
+
const sizeKb = (result.size / 1024).toFixed(2);
|
|
127
|
+
console.log(pc.green(` ✓ ${islandFile} → ${sizeKb}kb`));
|
|
128
|
+
} else {
|
|
129
|
+
console.log(pc.red(` ✗ ${islandFile} failed:`));
|
|
130
|
+
console.log(pc.red(` ${result.error}`));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
results.push({
|
|
134
|
+
file: islandFile,
|
|
135
|
+
...result
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const allSuccess = results.every((r) => r.success);
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
islands: results,
|
|
143
|
+
success: allSuccess
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get total bundle size for all islands
|
|
149
|
+
*/
|
|
150
|
+
export function getTotalBundleSize(bundleResults) {
|
|
151
|
+
return bundleResults.islands.reduce((total, island) => {
|
|
152
|
+
return total + (island.size || 0);
|
|
153
|
+
}, 0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Format bundle size in human-readable format
|
|
158
|
+
*/
|
|
159
|
+
export function formatSize(bytes) {
|
|
160
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
161
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)}KB`;
|
|
162
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)}MB`;
|
|
163
|
+
}
|