@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webmate-studio/builder",
3
- "version": "0.1.0",
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 '../../parser/src/index.js';
5
- import { loadConfig, logger } from '../../core/src/index.js';
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 '../../cli/src/utils/bundler.js';
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
+ }