@webmate-studio/builder 0.2.21 → 0.2.22
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/build-service.js +89 -2
- package/package.json +1 -1
package/build-service.js
CHANGED
|
@@ -62,13 +62,72 @@ async function installDependencies(componentDir, packageJson) {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Auto-inject <svelte:options> for Web Component registration
|
|
67
|
+
* Converts filename to kebab-case tag name (e.g., SwiperTest.svelte → swiper-test)
|
|
68
|
+
* Generates props definition from component.json metadata
|
|
69
|
+
*/
|
|
70
|
+
function injectSvelteOptions(filename, content, componentMetadata) {
|
|
71
|
+
// Convert filename to kebab-case tag name
|
|
72
|
+
// SwiperTest.svelte → swiper-test
|
|
73
|
+
// MyAwesomeComponent.svelte → my-awesome-component
|
|
74
|
+
const componentName = filename.replace('.svelte', '');
|
|
75
|
+
const tagName = componentName
|
|
76
|
+
.replace(/([A-Z])/g, '-$1') // Insert hyphen before capital letters
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replace(/^-/, ''); // Remove leading hyphen
|
|
79
|
+
|
|
80
|
+
// Generate props definition from component.json
|
|
81
|
+
const props = {};
|
|
82
|
+
if (componentMetadata.props) {
|
|
83
|
+
// Props can be either an object or an array
|
|
84
|
+
const propsData = Array.isArray(componentMetadata.props)
|
|
85
|
+
? componentMetadata.props
|
|
86
|
+
: Object.entries(componentMetadata.props).map(([name, config]) => ({ name, ...config }));
|
|
87
|
+
|
|
88
|
+
for (const prop of propsData) {
|
|
89
|
+
// Map prop types to Svelte types
|
|
90
|
+
const typeMap = {
|
|
91
|
+
'text': 'String',
|
|
92
|
+
'string': 'String',
|
|
93
|
+
'number': 'Number',
|
|
94
|
+
'boolean': 'Boolean',
|
|
95
|
+
'array': 'Array',
|
|
96
|
+
'object': 'Object'
|
|
97
|
+
};
|
|
98
|
+
const svelteType = typeMap[prop.type?.toLowerCase()] || 'String';
|
|
99
|
+
props[prop.name] = { type: svelteType };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Generate <svelte:options> block
|
|
104
|
+
const propsJson = JSON.stringify(props, null, 3).replace(/^/gm, '\t\t\t'); // Indent props
|
|
105
|
+
const svelteOptions = `<svelte:options
|
|
106
|
+
customElement={{
|
|
107
|
+
tag: "${tagName}",
|
|
108
|
+
shadow: "none",
|
|
109
|
+
props: ${propsJson}
|
|
110
|
+
}}
|
|
111
|
+
/>\n\n`;
|
|
112
|
+
|
|
113
|
+
// Check if <svelte:options> already exists
|
|
114
|
+
if (content.includes('<svelte:options')) {
|
|
115
|
+
console.log(`[Build Service] ℹ️ ${filename} already has <svelte:options>, skipping auto-inject`);
|
|
116
|
+
return content;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Inject at the top of the file (before <script> or template)
|
|
120
|
+
console.log(`[Build Service] ✓ Auto-injected <svelte:options> for ${tagName}`);
|
|
121
|
+
return svelteOptions + content;
|
|
122
|
+
}
|
|
123
|
+
|
|
65
124
|
/**
|
|
66
125
|
* Build a component (islands + CSS)
|
|
67
126
|
* @param {Object} payload - Build request payload
|
|
68
127
|
* @returns {Object} Build result
|
|
69
128
|
*/
|
|
70
129
|
async function buildComponent(payload) {
|
|
71
|
-
const { packageJson, islands, html, componentName = 'component' } = payload;
|
|
130
|
+
const { packageJson, islands, html, assets, componentMetadata, componentName = 'component' } = payload;
|
|
72
131
|
|
|
73
132
|
// Create temporary directory
|
|
74
133
|
const tmpDir = await mkdtemp(join(tmpdir(), 'wm-build-'));
|
|
@@ -81,6 +140,28 @@ async function buildComponent(payload) {
|
|
|
81
140
|
await installDependencies(tmpDir, packageJson);
|
|
82
141
|
}
|
|
83
142
|
|
|
143
|
+
// Write assets to disk BEFORE building islands (islands may reference them)
|
|
144
|
+
if (assets && assets.length > 0) {
|
|
145
|
+
const assetsDir = join(tmpDir, 'assets');
|
|
146
|
+
await mkdir(assetsDir, { recursive: true });
|
|
147
|
+
|
|
148
|
+
for (const asset of assets) {
|
|
149
|
+
const assetPath = join(assetsDir, asset.file);
|
|
150
|
+
|
|
151
|
+
// Convert base64 back to Buffer for binary files
|
|
152
|
+
// Text files can be written directly as UTF-8
|
|
153
|
+
let content;
|
|
154
|
+
if (asset.encoding === 'base64') {
|
|
155
|
+
content = Buffer.from(asset.content, 'base64');
|
|
156
|
+
} else {
|
|
157
|
+
content = asset.content; // UTF-8 string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await writeFile(assetPath, content);
|
|
161
|
+
}
|
|
162
|
+
console.log(`[Build Service] Wrote ${assets.length} asset(s)`);
|
|
163
|
+
}
|
|
164
|
+
|
|
84
165
|
// Build islands
|
|
85
166
|
const bundledIslands = [];
|
|
86
167
|
if (islands && islands.length > 0) {
|
|
@@ -91,8 +172,14 @@ async function buildComponent(payload) {
|
|
|
91
172
|
const inputPath = join(islandsDir, island.file);
|
|
92
173
|
const outputPath = join(islandsDir, island.file.replace(/\.(jsx?|tsx?|svelte|vue)$/, '.js'));
|
|
93
174
|
|
|
175
|
+
// Auto-inject <svelte:options> for Svelte islands
|
|
176
|
+
let content = island.content;
|
|
177
|
+
if (island.file.endsWith('.svelte') && componentMetadata && componentMetadata.props) {
|
|
178
|
+
content = injectSvelteOptions(island.file, content, componentMetadata);
|
|
179
|
+
}
|
|
180
|
+
|
|
94
181
|
// Write source file
|
|
95
|
-
await writeFile(inputPath,
|
|
182
|
+
await writeFile(inputPath, content);
|
|
96
183
|
|
|
97
184
|
console.log(`[Build Service] Bundling ${island.file}...`);
|
|
98
185
|
|