@webmate-studio/builder 0.2.125 → 0.2.127
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 +1 -1
- package/src/bundler.js +9 -1
- package/src/html-cleaner.js +29 -29
package/package.json
CHANGED
package/src/bundler.js
CHANGED
|
@@ -54,6 +54,7 @@ export async function bundleIsland(islandPath, outputPath, options = {}) {
|
|
|
54
54
|
target,
|
|
55
55
|
outfile: outputPath,
|
|
56
56
|
platform: 'browser',
|
|
57
|
+
...(format === 'iife' ? { globalName: '__IslandExport' } : {}),
|
|
57
58
|
charset: 'utf8', // Force UTF-8 output to prevent base64 corruption (esbuild bug #1501)
|
|
58
59
|
// Loaders for different file types
|
|
59
60
|
// NOTE: All image formats are handled by custom plugin above (image-base64-dataurl)
|
|
@@ -205,7 +206,7 @@ export async function bundleComponentIslands(componentDir, outputDir, options =
|
|
|
205
206
|
|
|
206
207
|
// Find all island files (JS, JSX, Svelte, Vue, etc.)
|
|
207
208
|
const files = await fs.readdir(islandsDir);
|
|
208
|
-
const
|
|
209
|
+
const allIslandFiles = files.filter((f) =>
|
|
209
210
|
f.endsWith('.js') ||
|
|
210
211
|
f.endsWith('.jsx') ||
|
|
211
212
|
f.endsWith('.svelte') ||
|
|
@@ -214,6 +215,13 @@ export async function bundleComponentIslands(componentDir, outputDir, options =
|
|
|
214
215
|
f.endsWith('.tsx')
|
|
215
216
|
);
|
|
216
217
|
|
|
218
|
+
// Skip .svelte files that have a matching .js wrapper (the .js is the entry point)
|
|
219
|
+
const jsNames = new Set(allIslandFiles.filter(f => f.endsWith('.js')).map(f => f.replace(/\.js$/, '')));
|
|
220
|
+
const islandFiles = allIslandFiles.filter(f => {
|
|
221
|
+
if (f.endsWith('.svelte') && jsNames.has(f.replace(/\.svelte$/, ''))) return false;
|
|
222
|
+
return true;
|
|
223
|
+
});
|
|
224
|
+
|
|
217
225
|
if (islandFiles.length === 0) {
|
|
218
226
|
return { islands: [], success: true };
|
|
219
227
|
}
|
package/src/html-cleaner.js
CHANGED
|
@@ -47,53 +47,53 @@ export function cleanComponentHTML(html, islands = []) {
|
|
|
47
47
|
* @returns {string} HTML with transformed islands
|
|
48
48
|
*/
|
|
49
49
|
function transformIslandsToDataAttributes(html, availableIslands = []) {
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
return html.replace(/<([A-Z][a-zA-Z0-9]*)\s+([^>]*)\/>/g, (match, tagName, attrsString) => {
|
|
53
|
-
// Check if this is a registered island
|
|
54
|
-
if (!availableIslands.includes(tagName)) {
|
|
55
|
-
return match; // Not an island, keep as-is
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Convert island name to kebab-case for Custom Element tag
|
|
59
|
-
// SwiperTest → swiper-test
|
|
60
|
-
// MyAwesomeComponent → my-awesome-component
|
|
50
|
+
// Convert PascalCase island name to kebab-case Custom Element tag
|
|
51
|
+
function toKebabTag(tagName) {
|
|
61
52
|
let kebabTag = tagName
|
|
62
53
|
.replace(/([A-Z])/g, '-$1')
|
|
63
54
|
.toLowerCase()
|
|
64
55
|
.replace(/^-/, '');
|
|
65
|
-
|
|
66
|
-
// Svelte requires custom element names to be hyphenated
|
|
67
|
-
// If there's no hyphen, add a prefix to make it valid
|
|
68
|
-
// IMPORTANT: Must match the prefix logic in injectSvelteOptions()
|
|
56
|
+
// Custom elements must contain a hyphen
|
|
69
57
|
if (!kebabTag.includes('-')) {
|
|
70
|
-
kebabTag = 'wm-' + kebabTag;
|
|
58
|
+
kebabTag = 'wm-' + kebabTag;
|
|
71
59
|
}
|
|
60
|
+
return kebabTag;
|
|
61
|
+
}
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
// Parse attributes string into HTML attributes
|
|
64
|
+
function parseAttrs(attrsString) {
|
|
74
65
|
const attrs = [];
|
|
75
|
-
|
|
76
|
-
// Match prop={value} or prop="value" patterns
|
|
77
66
|
const attrPattern = /([a-z][a-zA-Z0-9]*)\s*=\s*(?:\{([^}]+)\}|"([^"]*)"|'([^']*)')/g;
|
|
78
67
|
let attrMatch;
|
|
79
|
-
|
|
80
68
|
while ((attrMatch = attrPattern.exec(attrsString)) !== null) {
|
|
81
69
|
const propName = attrMatch[1];
|
|
82
|
-
// Get value from whichever group matched (curly braces, double quotes, or single quotes)
|
|
83
70
|
const propValue = attrMatch[2] || attrMatch[3] || attrMatch[4];
|
|
84
|
-
|
|
85
|
-
// If it was in curly braces, keep the braces for runtime evaluation
|
|
86
|
-
// Otherwise it's a literal string
|
|
87
71
|
const value = attrMatch[2] ? `{${propValue}}` : propValue;
|
|
88
|
-
|
|
89
|
-
// Add as HTML attribute
|
|
90
72
|
attrs.push(`${propName}="${value}"`);
|
|
91
73
|
}
|
|
74
|
+
return attrs.length > 0 ? ' ' + attrs.join(' ') : '';
|
|
75
|
+
}
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
77
|
+
// 1. Self-closing: <ComponentName attrs... />
|
|
78
|
+
html = html.replace(/<([A-Z][a-zA-Z0-9]*)\s+([^>]*)\/>/g, (match, tagName, attrsString) => {
|
|
79
|
+
if (!availableIslands.includes(tagName)) return match;
|
|
80
|
+
const kebabTag = toKebabTag(tagName);
|
|
81
|
+
return `<${kebabTag}${parseAttrs(attrsString)}></${kebabTag}>`;
|
|
96
82
|
});
|
|
83
|
+
|
|
84
|
+
// 2. Opening + closing with children: <ComponentName attrs>...children...</ComponentName>
|
|
85
|
+
for (const islandName of availableIslands) {
|
|
86
|
+
const openPattern = new RegExp(`<${islandName}(\\s[^>]*)?>`, 'g');
|
|
87
|
+
const closePattern = new RegExp(`</${islandName}>`, 'g');
|
|
88
|
+
const kebabTag = toKebabTag(islandName);
|
|
89
|
+
|
|
90
|
+
html = html.replace(openPattern, (match, attrsString) => {
|
|
91
|
+
return `<${kebabTag}${parseAttrs(attrsString || '')}>`;
|
|
92
|
+
});
|
|
93
|
+
html = html.replace(closePattern, `</${kebabTag}>`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return html;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|