sibujs 4.12.0 → 4.13.1
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/dist/browser.cjs +71 -18
- package/dist/browser.js +4 -4
- package/dist/build.cjs +6618 -6178
- package/dist/build.d.cts +554 -415
- package/dist/build.d.ts +554 -415
- package/dist/build.js +2091 -1735
- package/dist/cdn.dev.global.js +12 -12
- package/dist/cdn.full.dev.global.js +13 -13
- package/dist/cdn.full.global.js +12 -12
- package/dist/cdn.global.js +12 -12
- package/dist/{chunk-WFRQ6D7H.js → chunk-22OPSWMF.js} +1 -1
- package/dist/{chunk-ZJCHY2IB.js → chunk-2NC56RGY.js} +1 -1
- package/dist/{chunk-NB7TFSJE.js → chunk-2O6JJCUR.js} +4 -4
- package/dist/{chunk-B2Y5BJSP.js → chunk-4UTGDVSA.js} +70 -16
- package/dist/{chunk-NAVLCP4S.js → chunk-5RIBLY2N.js} +4 -4
- package/dist/{chunk-T5O2CUHV.js → chunk-5XUGJM3J.js} +1 -1
- package/dist/{chunk-HXT2RNH5.js → chunk-ADZHPN2B.js} +1 -1
- package/dist/chunk-B36ADEIR.js +21 -0
- package/dist/{chunk-DFZL3AX7.js → chunk-C73VBPEF.js} +9 -5
- package/dist/{chunk-WH4U2VUE.js → chunk-CPQKWCJ2.js} +13 -5
- package/dist/{chunk-LEUEKK4G.js → chunk-EU7T7EB3.js} +2 -2
- package/dist/{chunk-QLG5IK7F.js → chunk-G3Z2FEBR.js} +4 -4
- package/dist/{chunk-ZPY2UAKM.js → chunk-J4X7ICH4.js} +3 -3
- package/dist/{chunk-XX4BRMWF.js → chunk-K4LNUT5L.js} +1 -1
- package/dist/{chunk-CI4W6XAU.js → chunk-KC62VURU.js} +3 -3
- package/dist/chunk-N5TBYXHW.js +17 -0
- package/dist/{chunk-653KGQS2.js → chunk-OIKD7YDY.js} +2 -2
- package/dist/{chunk-LYTUTXQP.js → chunk-PBJYCE2Z.js} +46 -4
- package/dist/{chunk-EMB5PG7V.js → chunk-QC3NP47A.js} +1 -1
- package/dist/{chunk-IHA62NGJ.js → chunk-QKFVMIAV.js} +1 -1
- package/dist/{chunk-LXBMVGVB.js → chunk-QZ47OZ25.js} +3 -3
- package/dist/{chunk-ZTYCCBU3.js → chunk-VFPLWUT7.js} +44 -26
- package/dist/{chunk-RK7EPQPG.js → chunk-YFTBCCVH.js} +1 -1
- package/dist/data.cjs +71 -18
- package/dist/data.js +5 -5
- package/dist/datePicker-DxuxWpgF.d.cts +285 -0
- package/dist/datePicker-DxuxWpgF.d.ts +285 -0
- package/dist/devtools.cjs +71 -18
- package/dist/devtools.js +4 -4
- package/dist/ecosystem.cjs +82 -21
- package/dist/ecosystem.js +7 -7
- package/dist/extras.cjs +131 -23
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +21 -20
- package/dist/index.cjs +115 -37
- package/dist/index.d.cts +59 -5
- package/dist/index.d.ts +59 -5
- package/dist/index.js +10 -10
- package/dist/motion.cjs +69 -16
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +71 -18
- package/dist/patterns.js +5 -5
- package/dist/performance.cjs +69 -16
- package/dist/performance.js +4 -4
- package/dist/plugins.cjs +1421 -996
- package/dist/plugins.d.cts +132 -3
- package/dist/plugins.d.ts +132 -3
- package/dist/plugins.js +262 -11
- package/dist/routeMiddleware-B1sp4P7a.d.ts +688 -0
- package/dist/routeMiddleware-CedQTsLK.d.cts +688 -0
- package/dist/ssr.cjs +3607 -3524
- package/dist/ssr.d.cts +20 -671
- package/dist/ssr.d.ts +20 -671
- package/dist/ssr.js +15 -7
- package/dist/testing.cjs +71 -18
- package/dist/testing.js +2 -2
- package/dist/ui.cjs +78 -18
- package/dist/ui.js +8 -7
- package/dist/widgets.cjs +1352 -952
- package/dist/widgets.d.cts +100 -268
- package/dist/widgets.d.ts +100 -268
- package/dist/widgets.js +316 -7
- package/package.json +9 -1
package/dist/build.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
index_exports
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-VFPLWUT7.js";
|
|
4
|
+
import "./chunk-5XUGJM3J.js";
|
|
5
5
|
import "./chunk-2F4PSJ5Z.js";
|
|
6
6
|
import "./chunk-AM6DZLBM.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-ADZHPN2B.js";
|
|
8
|
+
import "./chunk-22OPSWMF.js";
|
|
9
9
|
import "./chunk-62BDYGAG.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-CPQKWCJ2.js";
|
|
11
|
+
import "./chunk-K4LNUT5L.js";
|
|
12
12
|
import "./chunk-F2UWOZ6J.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-YFTBCCVH.js";
|
|
14
14
|
import "./chunk-7ZHH77QA.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-QC3NP47A.js";
|
|
16
16
|
import {
|
|
17
17
|
isEventHandlerAttr,
|
|
18
18
|
isHtmlContentAttribute,
|
|
@@ -20,78 +20,352 @@ import {
|
|
|
20
20
|
} from "./chunk-EXXSN3BW.js";
|
|
21
21
|
import "./chunk-7XHATCIH.js";
|
|
22
22
|
import "./chunk-5HZXGZ6T.js";
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-QKFVMIAV.js";
|
|
24
|
+
import "./chunk-4UTGDVSA.js";
|
|
25
25
|
import "./chunk-NYNYSPK7.js";
|
|
26
26
|
|
|
27
|
-
// src/build/
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
// src/build/analyzer.ts
|
|
28
|
+
var moduleSizes = {
|
|
29
|
+
// Core modules
|
|
30
|
+
"core/html": 1800,
|
|
31
|
+
"core/mount": 120,
|
|
32
|
+
"core/each": 450,
|
|
33
|
+
"core/slots": 200,
|
|
34
|
+
"core/fragment": 180,
|
|
35
|
+
"core/catch": 280,
|
|
36
|
+
"core/portal": 350,
|
|
37
|
+
"core/directives": 520,
|
|
38
|
+
"core/dynamic": 380,
|
|
39
|
+
"core/head": 300,
|
|
40
|
+
"core/ssr": 1200,
|
|
41
|
+
"core/customElement": 650,
|
|
42
|
+
"core/scopedStyle": 480,
|
|
43
|
+
"core/domRecycler": 420,
|
|
44
|
+
"core/bundleOptimize": 200,
|
|
45
|
+
"core/compiled": 350,
|
|
46
|
+
"core/normalize": 280,
|
|
47
|
+
"core/reactiveAttr": 320,
|
|
48
|
+
"core/componentProps": 400,
|
|
49
|
+
"core/contracts": 350,
|
|
50
|
+
"core/worker": 550,
|
|
51
|
+
"core/wasm": 480,
|
|
52
|
+
"core/concurrent": 600,
|
|
53
|
+
"core/microfrontend": 700,
|
|
54
|
+
"core/chunkLoader": 450,
|
|
55
|
+
"core/versioning": 300,
|
|
56
|
+
"core/ecosystem": 250,
|
|
57
|
+
// Signals
|
|
58
|
+
"core/signal": 280,
|
|
59
|
+
"core/effect": 350,
|
|
60
|
+
"core/derived": 250,
|
|
61
|
+
"core/watch": 300,
|
|
62
|
+
"core/store": 380,
|
|
63
|
+
"core/ref": 150,
|
|
64
|
+
"core/array": 420,
|
|
65
|
+
"core/deepSignal": 500,
|
|
66
|
+
"core/lifecycle": 300,
|
|
67
|
+
"core/context": 350,
|
|
68
|
+
"core/persist": 400,
|
|
69
|
+
"core/hoc": 280,
|
|
70
|
+
"core/transition": 600,
|
|
71
|
+
"core/form": 750,
|
|
72
|
+
"core/globalStore": 450,
|
|
73
|
+
"core/machine": 550,
|
|
74
|
+
"core/optimistic": 380,
|
|
75
|
+
"core/timeTravel": 500,
|
|
76
|
+
"core/scheduler": 350,
|
|
77
|
+
"core/plugin": 300,
|
|
78
|
+
"core/virtualList": 800,
|
|
79
|
+
"core/intersection": 350,
|
|
80
|
+
"core/inputMask": 450,
|
|
81
|
+
"core/a11y": 500,
|
|
82
|
+
"core/debug": 400,
|
|
83
|
+
"core/serviceWorker": 350,
|
|
84
|
+
"core/composable": 280,
|
|
85
|
+
// Reactivity
|
|
86
|
+
"reactivity/signal": 100,
|
|
87
|
+
"reactivity/track": 250,
|
|
88
|
+
"reactivity/batch": 200,
|
|
89
|
+
"reactivity/bindAttribute": 300,
|
|
90
|
+
"reactivity/bindChildNode": 250,
|
|
91
|
+
"reactivity/bindTextNode": 200,
|
|
92
|
+
// Components
|
|
93
|
+
"components/ErrorBoundary": 350,
|
|
94
|
+
"components/Loading": 250,
|
|
95
|
+
// Plugins
|
|
96
|
+
"plugins/i18n": 600,
|
|
97
|
+
"plugins/router": 900,
|
|
98
|
+
"plugins/routerSSR": 500,
|
|
99
|
+
// Build utilities
|
|
100
|
+
"build/vite": 800,
|
|
101
|
+
"build/webpack": 750,
|
|
102
|
+
"build/cdn": 400,
|
|
103
|
+
"build/declarations": 500,
|
|
104
|
+
"build/analyzer": 600,
|
|
105
|
+
// Testing
|
|
106
|
+
"testing/index": 700,
|
|
107
|
+
"testing/e2e": 500
|
|
108
|
+
};
|
|
109
|
+
function formatBytes(bytes) {
|
|
110
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
111
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
112
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
30
113
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
114
|
+
function analyzeBundle(modules) {
|
|
115
|
+
const sizes = {};
|
|
116
|
+
let totalSize = 0;
|
|
117
|
+
for (const [name, source] of Object.entries(modules)) {
|
|
118
|
+
let byteLength;
|
|
119
|
+
if (typeof TextEncoder !== "undefined") {
|
|
120
|
+
byteLength = new TextEncoder().encode(source).length;
|
|
121
|
+
} else {
|
|
122
|
+
byteLength = source.length;
|
|
123
|
+
}
|
|
124
|
+
sizes[name] = byteLength;
|
|
125
|
+
totalSize += byteLength;
|
|
35
126
|
}
|
|
36
|
-
|
|
127
|
+
const sorted = Object.entries(sizes).map(([name, size]) => ({
|
|
128
|
+
name,
|
|
129
|
+
size,
|
|
130
|
+
percentage: totalSize > 0 ? size / totalSize * 100 : 0
|
|
131
|
+
})).sort((a, b) => b.size - a.size);
|
|
132
|
+
return {
|
|
133
|
+
totalSize,
|
|
134
|
+
moduleSizes: sizes,
|
|
135
|
+
sorted,
|
|
136
|
+
report() {
|
|
137
|
+
const lines = [
|
|
138
|
+
"=== SibuJS Bundle Analysis ===",
|
|
139
|
+
"",
|
|
140
|
+
`Total size: ${formatBytes(totalSize)}`,
|
|
141
|
+
`Modules: ${sorted.length}`,
|
|
142
|
+
"",
|
|
143
|
+
"Module breakdown:",
|
|
144
|
+
"-".repeat(60)
|
|
145
|
+
];
|
|
146
|
+
for (const entry of sorted) {
|
|
147
|
+
const sizeStr = formatBytes(entry.size).padStart(10);
|
|
148
|
+
const pctStr = `${entry.percentage.toFixed(1)}%`.padStart(6);
|
|
149
|
+
const bar = generateBar(entry.percentage, 20);
|
|
150
|
+
lines.push(` ${sizeStr} ${pctStr} ${bar} ${entry.name}`);
|
|
151
|
+
}
|
|
152
|
+
lines.push("-".repeat(60));
|
|
153
|
+
return lines.join("\n");
|
|
154
|
+
}
|
|
155
|
+
};
|
|
37
156
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!json.includes(LINE_SEPARATOR) && !json.includes(PARAGRAPH_SEPARATOR)) return json;
|
|
43
|
-
const bs = "\\";
|
|
44
|
-
return json.split(LINE_SEPARATOR).join(`${bs}u2028`).split(PARAGRAPH_SEPARATOR).join(`${bs}u2029`);
|
|
157
|
+
function generateBar(percentage, width) {
|
|
158
|
+
const filled = Math.round(percentage / 100 * width);
|
|
159
|
+
const empty = width - filled;
|
|
160
|
+
return `[${"#".repeat(filled)}${".".repeat(empty)}]`;
|
|
45
161
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
162
|
+
function estimateImportSize(imports) {
|
|
163
|
+
const breakdown = {};
|
|
164
|
+
let estimated = 0;
|
|
165
|
+
const implicitDeps = ["reactivity/signal", "reactivity/track"];
|
|
166
|
+
const allImports = [.../* @__PURE__ */ new Set([...implicitDeps, ...imports])];
|
|
167
|
+
for (const importPath of allImports) {
|
|
168
|
+
const size = moduleSizes[importPath];
|
|
169
|
+
if (size !== void 0) {
|
|
170
|
+
breakdown[importPath] = size;
|
|
171
|
+
estimated += size;
|
|
172
|
+
} else {
|
|
173
|
+
const avgSize = 350;
|
|
174
|
+
breakdown[importPath] = avgSize;
|
|
175
|
+
estimated += avgSize;
|
|
176
|
+
}
|
|
52
177
|
}
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
for (const edit of edits) {
|
|
59
|
-
out += code.slice(pos, edit.start) + edit.text;
|
|
60
|
-
pos = edit.end;
|
|
178
|
+
const sortedEntries = Object.entries(breakdown).sort(([, a], [, b]) => b - a);
|
|
179
|
+
const lines = [`Estimated bundle size: ${formatBytes(estimated)} (minified + gzipped)`, ""];
|
|
180
|
+
for (const [modulePath, size] of sortedEntries) {
|
|
181
|
+
const known = moduleSizes[modulePath] !== void 0;
|
|
182
|
+
lines.push(` ${modulePath}: ${formatBytes(size)}${known ? "" : " (estimated)"}`);
|
|
61
183
|
}
|
|
62
|
-
|
|
184
|
+
if (imports.length > 0) {
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push("Note: Actual sizes may vary based on tree-shaking and build configuration.");
|
|
187
|
+
lines.push("Implicit dependencies (reactivity/signal, reactivity/track) are included.");
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
estimated,
|
|
191
|
+
breakdown,
|
|
192
|
+
formatted: lines.join("\n")
|
|
193
|
+
};
|
|
63
194
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
195
|
+
|
|
196
|
+
// src/build/cdn.ts
|
|
197
|
+
var PACKAGE_NAME = "sibujs";
|
|
198
|
+
var SUBPATH_ENTRIES = [
|
|
199
|
+
"data",
|
|
200
|
+
"browser",
|
|
201
|
+
"patterns",
|
|
202
|
+
"motion",
|
|
203
|
+
"ui",
|
|
204
|
+
"widgets",
|
|
205
|
+
"ssr",
|
|
206
|
+
"devtools",
|
|
207
|
+
"performance",
|
|
208
|
+
"ecosystem",
|
|
209
|
+
"plugins",
|
|
210
|
+
"build",
|
|
211
|
+
"testing",
|
|
212
|
+
"extras"
|
|
213
|
+
];
|
|
214
|
+
function registerGlobal() {
|
|
215
|
+
if (typeof window === "undefined") return;
|
|
216
|
+
window.Sibu = { ...index_exports };
|
|
75
217
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
218
|
+
function umdWrapper(name, factory) {
|
|
219
|
+
const factoryStr = factory.toString();
|
|
220
|
+
return `(function (root, factory) {
|
|
221
|
+
if (typeof define === 'function' && define.amd) {
|
|
222
|
+
// AMD (RequireJS)
|
|
223
|
+
define([], factory);
|
|
224
|
+
} else if (typeof module === 'object' && module.exports) {
|
|
225
|
+
// CommonJS (Node.js)
|
|
226
|
+
module.exports = factory();
|
|
227
|
+
} else {
|
|
228
|
+
// Browser globals
|
|
229
|
+
root.${name} = factory();
|
|
230
|
+
}
|
|
231
|
+
}(typeof self !== 'undefined' ? self : this, ${factoryStr}));`;
|
|
78
232
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
233
|
+
var cdnUrls = {
|
|
234
|
+
/**
|
|
235
|
+
* Generate an unpkg CDN URL.
|
|
236
|
+
* @param version - Package version (defaults to 'latest')
|
|
237
|
+
*/
|
|
238
|
+
unpkg: (version) => `https://unpkg.com/${PACKAGE_NAME}@${version || "latest"}/dist/cdn.global.js`,
|
|
239
|
+
/**
|
|
240
|
+
* Generate a jsDelivr CDN URL.
|
|
241
|
+
* @param version - Package version (defaults to 'latest')
|
|
242
|
+
*/
|
|
243
|
+
jsdelivr: (version) => `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@${version || "latest"}/dist/cdn.global.js`,
|
|
244
|
+
/**
|
|
245
|
+
* Generate a Skypack CDN URL (ESM-native).
|
|
246
|
+
* @param version - Package version (defaults to 'latest')
|
|
247
|
+
*/
|
|
248
|
+
skypack: (version) => `https://cdn.skypack.dev/${PACKAGE_NAME}@${version || "latest"}`,
|
|
249
|
+
/**
|
|
250
|
+
* Generate a complete HTML script tag for including SibuJS from a CDN.
|
|
251
|
+
*
|
|
252
|
+
* @param provider - CDN provider to use (defaults to 'jsdelivr')
|
|
253
|
+
* @param version - Package version (defaults to 'latest')
|
|
254
|
+
* @returns An HTML script tag string
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* cdnUrls.scriptTag('jsdelivr', '1.0.0')
|
|
259
|
+
* // => '<script src="https://cdn.jsdelivr.net/npm/sibujs@1.0.0/dist/cdn.global.js"></script>'
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
scriptTag: (provider = "jsdelivr", version) => {
|
|
263
|
+
const url = cdnUrls[provider](version);
|
|
264
|
+
if (provider === "skypack") {
|
|
265
|
+
return `<script type="module">
|
|
266
|
+
import * as Sibu from '${url}';
|
|
267
|
+
window.Sibu = Sibu;
|
|
268
|
+
</script>`;
|
|
269
|
+
}
|
|
270
|
+
return `<script src="${url}"></script>`;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
function generateImportMap(baseUrl) {
|
|
274
|
+
const base = baseUrl || `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@latest`;
|
|
275
|
+
const imports = {
|
|
276
|
+
// Main package entry
|
|
277
|
+
[PACKAGE_NAME]: `${base}/dist/index.js`
|
|
278
|
+
};
|
|
279
|
+
for (const entry of SUBPATH_ENTRIES) {
|
|
280
|
+
imports[`${PACKAGE_NAME}/${entry}`] = `${base}/dist/${entry}.js`;
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
imports,
|
|
284
|
+
/**
|
|
285
|
+
* Serialize the import map to a JSON string.
|
|
286
|
+
*/
|
|
287
|
+
toJSON() {
|
|
288
|
+
return JSON.stringify({ imports }, null, 2);
|
|
289
|
+
},
|
|
290
|
+
/**
|
|
291
|
+
* Generate a complete `<script type="importmap">` tag.
|
|
292
|
+
*/
|
|
293
|
+
toScriptTag() {
|
|
294
|
+
return `<script type="importmap">
|
|
295
|
+
${JSON.stringify({ imports }, null, 2)}
|
|
296
|
+
</script>`;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/build/sourceEdit.ts
|
|
302
|
+
function isLineBreak(c) {
|
|
303
|
+
return c === 10 || c === 13 || c === 8232 || c === 8233;
|
|
304
|
+
}
|
|
305
|
+
function lineBreaksIn(code, start, end) {
|
|
306
|
+
let out = "";
|
|
307
|
+
for (let i = start; i < end; i++) {
|
|
308
|
+
if (isLineBreak(code.charCodeAt(i))) out += code[i];
|
|
309
|
+
}
|
|
310
|
+
return out;
|
|
311
|
+
}
|
|
312
|
+
var LINE_SEPARATOR = String.fromCharCode(8232);
|
|
313
|
+
var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
|
|
314
|
+
function jsStringLiteral(value) {
|
|
315
|
+
const json = JSON.stringify(value);
|
|
316
|
+
if (!json.includes(LINE_SEPARATOR) && !json.includes(PARAGRAPH_SEPARATOR)) return json;
|
|
317
|
+
const bs = "\\";
|
|
318
|
+
return json.split(LINE_SEPARATOR).join(`${bs}u2028`).split(PARAGRAPH_SEPARATOR).join(`${bs}u2029`);
|
|
319
|
+
}
|
|
320
|
+
function normalizeEdits(edits) {
|
|
321
|
+
const sorted = edits.map((edit, order) => ({ edit, order })).sort(
|
|
322
|
+
(a, b) => a.edit.start - b.edit.start || a.edit.end - a.edit.start - (b.edit.end - b.edit.start) || a.order - b.order
|
|
323
|
+
).map((x) => x.edit);
|
|
324
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
325
|
+
if (sorted[i].start < sorted[i - 1].end) return null;
|
|
326
|
+
}
|
|
327
|
+
return sorted;
|
|
328
|
+
}
|
|
329
|
+
function applyEdits(code, edits, append = "") {
|
|
330
|
+
let out = "";
|
|
331
|
+
let pos = 0;
|
|
332
|
+
for (const edit of edits) {
|
|
333
|
+
out += code.slice(pos, edit.start) + edit.text;
|
|
334
|
+
pos = edit.end;
|
|
335
|
+
}
|
|
336
|
+
return out + code.slice(pos) + append;
|
|
337
|
+
}
|
|
338
|
+
var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
339
|
+
function vlq(value) {
|
|
340
|
+
let v = value < 0 ? -value << 1 | 1 : value << 1;
|
|
341
|
+
let out = "";
|
|
342
|
+
do {
|
|
343
|
+
let digit = v & 31;
|
|
344
|
+
v >>>= 5;
|
|
345
|
+
if (v > 0) digit |= 32;
|
|
346
|
+
out += BASE64[digit];
|
|
347
|
+
} while (v > 0);
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
function isWordStart(c) {
|
|
351
|
+
return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127 || c >= 48 && c <= 57;
|
|
352
|
+
}
|
|
353
|
+
function editsSourceMap(code, edits, append, source) {
|
|
354
|
+
const lineStarts = [0];
|
|
355
|
+
for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) lineStarts.push(i + 1);
|
|
356
|
+
const origLine = (offset) => {
|
|
357
|
+
let lo = 0;
|
|
358
|
+
let hi = lineStarts.length - 1;
|
|
359
|
+
while (lo < hi) {
|
|
360
|
+
const mid = lo + hi + 1 >> 1;
|
|
361
|
+
if (lineStarts[mid] <= offset) lo = mid;
|
|
362
|
+
else hi = mid - 1;
|
|
363
|
+
}
|
|
364
|
+
return lo;
|
|
365
|
+
};
|
|
366
|
+
const lines = [[]];
|
|
367
|
+
let col = 0;
|
|
368
|
+
const mark = (offset) => {
|
|
95
369
|
const line = lines[lines.length - 1];
|
|
96
370
|
const last = line[line.length - 1];
|
|
97
371
|
if (last && last[0] === col) return;
|
|
@@ -725,285 +999,77 @@ function importsSibu(code) {
|
|
|
725
999
|
return !!scan?.imports.some((d) => isSibuSource(d.source));
|
|
726
1000
|
}
|
|
727
1001
|
|
|
728
|
-
// src/build/
|
|
729
|
-
var
|
|
730
|
-
"div",
|
|
731
|
-
"span",
|
|
732
|
-
"section",
|
|
733
|
-
"article",
|
|
734
|
-
"header",
|
|
735
|
-
"footer",
|
|
736
|
-
"nav",
|
|
737
|
-
"main",
|
|
738
|
-
"aside",
|
|
739
|
-
"p",
|
|
740
|
-
"h1",
|
|
741
|
-
"h2",
|
|
742
|
-
"h3",
|
|
743
|
-
"h4",
|
|
744
|
-
"h5",
|
|
745
|
-
"h6",
|
|
746
|
-
"blockquote",
|
|
747
|
-
"li",
|
|
748
|
-
"ol",
|
|
749
|
-
"ul",
|
|
750
|
-
"a",
|
|
751
|
-
"b",
|
|
752
|
-
"em",
|
|
753
|
-
"i",
|
|
754
|
-
"strong",
|
|
755
|
-
"small",
|
|
756
|
-
"code",
|
|
757
|
-
"mark",
|
|
758
|
-
"img",
|
|
759
|
-
"br",
|
|
760
|
-
"hr",
|
|
761
|
-
"input",
|
|
762
|
-
"button",
|
|
763
|
-
"form",
|
|
764
|
-
"label",
|
|
765
|
-
"details",
|
|
766
|
-
"summary",
|
|
767
|
-
"dialog"
|
|
768
|
-
]);
|
|
1002
|
+
// src/build/templateCompiler.ts
|
|
1003
|
+
var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
769
1004
|
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
1005
|
+
"area",
|
|
1006
|
+
"base",
|
|
770
1007
|
"br",
|
|
1008
|
+
"col",
|
|
1009
|
+
"embed",
|
|
771
1010
|
"hr",
|
|
772
1011
|
"img",
|
|
773
1012
|
"input",
|
|
774
|
-
"meta",
|
|
775
1013
|
"link",
|
|
776
|
-
"
|
|
777
|
-
"base",
|
|
778
|
-
"col",
|
|
779
|
-
"embed",
|
|
1014
|
+
"meta",
|
|
780
1015
|
"param",
|
|
781
1016
|
"source",
|
|
782
1017
|
"track",
|
|
783
1018
|
"wbr"
|
|
784
1019
|
]);
|
|
785
|
-
var
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1020
|
+
var SVG_TAGS = /* @__PURE__ */ new Set([
|
|
1021
|
+
"svg",
|
|
1022
|
+
"circle",
|
|
1023
|
+
"ellipse",
|
|
1024
|
+
"g",
|
|
1025
|
+
"line",
|
|
1026
|
+
"path",
|
|
1027
|
+
"polygon",
|
|
1028
|
+
"polyline",
|
|
1029
|
+
"rect",
|
|
1030
|
+
"text",
|
|
1031
|
+
"tspan",
|
|
1032
|
+
"defs",
|
|
1033
|
+
"clipPath",
|
|
1034
|
+
"mask",
|
|
1035
|
+
"pattern",
|
|
1036
|
+
"linearGradient",
|
|
1037
|
+
"radialGradient",
|
|
1038
|
+
"stop",
|
|
1039
|
+
"use",
|
|
1040
|
+
"symbol",
|
|
1041
|
+
"marker"
|
|
1042
|
+
]);
|
|
1043
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1044
|
+
var RuntimeParseError = class extends Error {
|
|
1045
|
+
};
|
|
1046
|
+
function parseTemplate(strings) {
|
|
1047
|
+
const exprCount = strings.length - 1;
|
|
1048
|
+
let template = strings[0];
|
|
1049
|
+
for (let i = 0; i < exprCount; i++) {
|
|
1050
|
+
template += `\0${i}\0${strings[i + 1]}`;
|
|
792
1051
|
}
|
|
793
|
-
|
|
794
|
-
|
|
1052
|
+
let pos = 0;
|
|
1053
|
+
const len = template.length;
|
|
1054
|
+
function skipWs() {
|
|
1055
|
+
while (pos < len && (template[pos] === " " || template[pos] === " " || template[pos] === "\n" || template[pos] === "\r"))
|
|
1056
|
+
pos++;
|
|
795
1057
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) return null;
|
|
812
|
-
const lower = name.toLowerCase();
|
|
813
|
-
if (SPECIAL_PROPS.has(name) || lower === "style" || lower === "srcset") return null;
|
|
814
|
-
if (isEventHandlerAttr(name) || isUrlAttribute(name) || isHtmlContentAttribute(name)) return null;
|
|
815
|
-
if (lit.kind === "keyword") {
|
|
816
|
-
if (lit.value === "null" || lit.value === "undefined") return "";
|
|
817
|
-
const on = lit.value === "true";
|
|
818
|
-
if (lower.startsWith("aria-")) return `${lower}="${on}"`;
|
|
819
|
-
if (NON_REFLECTING_BOOLEANS.has(lower)) return null;
|
|
820
|
-
return on ? `${lower}=""` : "";
|
|
821
|
-
}
|
|
822
|
-
if (!parserSafeText(lit.value)) return null;
|
|
823
|
-
return `${lower}="${escapeAttr(lit.value)}"`;
|
|
824
|
-
}
|
|
825
|
-
function parseCall(code, list, i) {
|
|
826
|
-
const at = (k2) => list[k2];
|
|
827
|
-
const isPunct = (k2, v) => {
|
|
828
|
-
const t = at(k2);
|
|
829
|
-
return !!t && t.type === "punct" && t.value === v;
|
|
830
|
-
};
|
|
831
|
-
let k = i + 1;
|
|
832
|
-
const result = { end: 0, cls: null, id: null, attrs: [], text: null };
|
|
833
|
-
let nodesText = null;
|
|
834
|
-
if (isPunct(k, "{")) {
|
|
835
|
-
k++;
|
|
836
|
-
const seen = /* @__PURE__ */ new Set();
|
|
837
|
-
while (!isPunct(k, "}")) {
|
|
838
|
-
const keyTok = at(k);
|
|
839
|
-
let key = null;
|
|
840
|
-
if (keyTok?.type === "ident") key = keyTok.value;
|
|
841
|
-
else if (keyTok?.type === "string") key = stringTokenValue(code, keyTok);
|
|
842
|
-
if (key === null || !isPunct(k + 1, ":")) return null;
|
|
843
|
-
if (seen.has(key.toLowerCase())) return null;
|
|
844
|
-
seen.add(key.toLowerCase());
|
|
845
|
-
const lit = readLiteral(code, at(k + 2));
|
|
846
|
-
if (lit === null) return null;
|
|
847
|
-
k += 3;
|
|
848
|
-
if (isPunct(k, ",")) k++;
|
|
849
|
-
else if (!isPunct(k, "}")) return null;
|
|
850
|
-
if (key === "class" || key === "id") {
|
|
851
|
-
if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
|
|
852
|
-
if (key === "class") result.cls = lit.value;
|
|
853
|
-
else result.id = lit.value;
|
|
854
|
-
} else if (key === "nodes") {
|
|
855
|
-
if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
|
|
856
|
-
nodesText = lit.value;
|
|
857
|
-
} else {
|
|
858
|
-
const html = attributeHtml(key, lit);
|
|
859
|
-
if (html === null) return null;
|
|
860
|
-
if (html) result.attrs.push(html);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
k++;
|
|
864
|
-
if (isPunct(k, ",")) {
|
|
865
|
-
const second = readLiteral(code, at(k + 1));
|
|
866
|
-
if (second === null) {
|
|
867
|
-
if (!isPunct(k + 1, ")")) return null;
|
|
868
|
-
k++;
|
|
869
|
-
} else {
|
|
870
|
-
if (second.kind !== "string" || !parserSafeText(second.value)) return null;
|
|
871
|
-
result.text = second.value;
|
|
872
|
-
k += 2;
|
|
873
|
-
if (isPunct(k, ",")) k++;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
if (result.text === null) result.text = nodesText;
|
|
877
|
-
} else {
|
|
878
|
-
const first = readLiteral(code, at(k));
|
|
879
|
-
if (first === null || first.kind !== "string" || !parserSafeText(first.value)) return null;
|
|
880
|
-
k++;
|
|
881
|
-
if (isPunct(k, ",") && at(k + 1)?.type === "string") {
|
|
882
|
-
const second = readLiteral(code, at(k + 1));
|
|
883
|
-
if (second === null || second.kind !== "string" || !parserSafeText(second.value)) return null;
|
|
884
|
-
result.cls = first.value;
|
|
885
|
-
result.text = second.value;
|
|
886
|
-
k += 2;
|
|
887
|
-
} else {
|
|
888
|
-
result.text = first.value;
|
|
889
|
-
}
|
|
890
|
-
if (isPunct(k, ",")) k++;
|
|
891
|
-
}
|
|
892
|
-
if (!isPunct(k, ")")) return null;
|
|
893
|
-
result.end = at(k).end;
|
|
894
|
-
return result;
|
|
895
|
-
}
|
|
896
|
-
function toHtml(tag, call) {
|
|
897
|
-
const attrs = [];
|
|
898
|
-
if (call.cls !== null) attrs.push(`class="${escapeAttr(call.cls)}"`);
|
|
899
|
-
if (call.id !== null) attrs.push(`id="${escapeAttr(call.id)}"`);
|
|
900
|
-
attrs.push(...call.attrs);
|
|
901
|
-
const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
902
|
-
if (VOID_ELEMENTS.has(tag)) {
|
|
903
|
-
if (call.text) return null;
|
|
904
|
-
return `<${tag}${attrStr} />`;
|
|
905
|
-
}
|
|
906
|
-
return `<${tag}${attrStr}>${escapeHtml(call.text ?? "")}</${tag}>`;
|
|
907
|
-
}
|
|
908
|
-
function analyzeStaticTemplates(code) {
|
|
909
|
-
const none = { hasStaticPatterns: false, patterns: [] };
|
|
910
|
-
if (!code.includes("sibujs")) return none;
|
|
911
|
-
const scan = scanModule(code);
|
|
912
|
-
if (!scan) return none;
|
|
913
|
-
const factories = /* @__PURE__ */ new Map();
|
|
914
|
-
for (const decl of scan.imports) {
|
|
915
|
-
if (decl.source !== "sibujs") continue;
|
|
916
|
-
for (const b of decl.bindings) if (STATIC_TAGS.has(b.imported)) factories.set(b.local, b.imported);
|
|
917
|
-
}
|
|
918
|
-
if (factories.size === 0) return none;
|
|
919
|
-
const uses = bindingUses(code, scan, new Set(factories.keys()));
|
|
920
|
-
const unsafe = new Set(uses.shadowed);
|
|
921
|
-
for (const ref of uses.tagged) unsafe.add(ref.tok.value);
|
|
922
|
-
const patterns = [];
|
|
923
|
-
for (const { tok, list, index } of uses.calls) {
|
|
924
|
-
if (unsafe.has(tok.value)) continue;
|
|
925
|
-
const tag = factories.get(tok.value);
|
|
926
|
-
const call = parseCall(code, list, index + 1);
|
|
927
|
-
if (!call) continue;
|
|
928
|
-
const templateHtml = toHtml(tag, call);
|
|
929
|
-
if (templateHtml === null) continue;
|
|
930
|
-
const start = list[index].start;
|
|
931
|
-
patterns.push({ original: code.slice(start, call.end), tag, templateHtml, start, end: call.end });
|
|
932
|
-
}
|
|
933
|
-
return { hasStaticPatterns: patterns.length > 0, patterns };
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// src/build/templateCompiler.ts
|
|
937
|
-
var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
938
|
-
var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
939
|
-
"area",
|
|
940
|
-
"base",
|
|
941
|
-
"br",
|
|
942
|
-
"col",
|
|
943
|
-
"embed",
|
|
944
|
-
"hr",
|
|
945
|
-
"img",
|
|
946
|
-
"input",
|
|
947
|
-
"link",
|
|
948
|
-
"meta",
|
|
949
|
-
"param",
|
|
950
|
-
"source",
|
|
951
|
-
"track",
|
|
952
|
-
"wbr"
|
|
953
|
-
]);
|
|
954
|
-
var SVG_TAGS = /* @__PURE__ */ new Set([
|
|
955
|
-
"svg",
|
|
956
|
-
"circle",
|
|
957
|
-
"ellipse",
|
|
958
|
-
"g",
|
|
959
|
-
"line",
|
|
960
|
-
"path",
|
|
961
|
-
"polygon",
|
|
962
|
-
"polyline",
|
|
963
|
-
"rect",
|
|
964
|
-
"text",
|
|
965
|
-
"tspan",
|
|
966
|
-
"defs",
|
|
967
|
-
"clipPath",
|
|
968
|
-
"mask",
|
|
969
|
-
"pattern",
|
|
970
|
-
"linearGradient",
|
|
971
|
-
"radialGradient",
|
|
972
|
-
"stop",
|
|
973
|
-
"use",
|
|
974
|
-
"symbol",
|
|
975
|
-
"marker"
|
|
976
|
-
]);
|
|
977
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
978
|
-
var RuntimeParseError = class extends Error {
|
|
979
|
-
};
|
|
980
|
-
function parseTemplate(strings) {
|
|
981
|
-
const exprCount = strings.length - 1;
|
|
982
|
-
let template = strings[0];
|
|
983
|
-
for (let i = 0; i < exprCount; i++) {
|
|
984
|
-
template += `\0${i}\0${strings[i + 1]}`;
|
|
985
|
-
}
|
|
986
|
-
let pos = 0;
|
|
987
|
-
const len = template.length;
|
|
988
|
-
function skipWs() {
|
|
989
|
-
while (pos < len && (template[pos] === " " || template[pos] === " " || template[pos] === "\n" || template[pos] === "\r"))
|
|
990
|
-
pos++;
|
|
991
|
-
}
|
|
992
|
-
function tryExprIdx() {
|
|
993
|
-
if (template.charCodeAt(pos) !== 0) return -1;
|
|
994
|
-
const start = pos;
|
|
995
|
-
pos++;
|
|
996
|
-
let idx = 0;
|
|
997
|
-
while (pos < len && template.charCodeAt(pos) !== 0) {
|
|
998
|
-
idx = idx * 10 + (template.charCodeAt(pos) - 48);
|
|
999
|
-
pos++;
|
|
1000
|
-
}
|
|
1001
|
-
if (pos < len) pos++;
|
|
1002
|
-
if (idx < 0 || idx >= exprCount) {
|
|
1003
|
-
pos = start;
|
|
1004
|
-
return -1;
|
|
1005
|
-
}
|
|
1006
|
-
return idx;
|
|
1058
|
+
function tryExprIdx() {
|
|
1059
|
+
if (template.charCodeAt(pos) !== 0) return -1;
|
|
1060
|
+
const start = pos;
|
|
1061
|
+
pos++;
|
|
1062
|
+
let idx = 0;
|
|
1063
|
+
while (pos < len && template.charCodeAt(pos) !== 0) {
|
|
1064
|
+
idx = idx * 10 + (template.charCodeAt(pos) - 48);
|
|
1065
|
+
pos++;
|
|
1066
|
+
}
|
|
1067
|
+
if (pos < len) pos++;
|
|
1068
|
+
if (idx < 0 || idx >= exprCount) {
|
|
1069
|
+
pos = start;
|
|
1070
|
+
return -1;
|
|
1071
|
+
}
|
|
1072
|
+
return idx;
|
|
1007
1073
|
}
|
|
1008
1074
|
function readTagName() {
|
|
1009
1075
|
const start = pos;
|
|
@@ -1143,7 +1209,7 @@ function parseTemplate(strings) {
|
|
|
1143
1209
|
const tag = readTagName();
|
|
1144
1210
|
const attrs = parseAttrs();
|
|
1145
1211
|
skipWs();
|
|
1146
|
-
const isVoid =
|
|
1212
|
+
const isVoid = VOID_ELEMENTS.has(tag);
|
|
1147
1213
|
const isSelfClosing = template[pos] === "/";
|
|
1148
1214
|
if (isSelfClosing) pos++;
|
|
1149
1215
|
if (pos < len) pos++;
|
|
@@ -1433,847 +1499,693 @@ ${trailer}
|
|
|
1433
1499
|
return plan;
|
|
1434
1500
|
}
|
|
1435
1501
|
|
|
1436
|
-
// src/build/
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
1441
|
-
return patterns.some((pattern) => {
|
|
1442
|
-
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
1443
|
-
return new RegExp(regexStr).test(normalized);
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
function devPrologueEdit(code) {
|
|
1447
|
-
let at = 0;
|
|
1448
|
-
if (code.startsWith("#!")) {
|
|
1449
|
-
const nl = code.indexOf("\n");
|
|
1450
|
-
if (nl === -1) return null;
|
|
1451
|
-
at = nl + 1;
|
|
1452
|
-
}
|
|
1502
|
+
// src/build/compileTemplates.ts
|
|
1503
|
+
function compileHtmlTemplates(code) {
|
|
1504
|
+
const plan = planHtmlTemplates(code);
|
|
1505
|
+
const edits = plan.compiledCount > 0 ? normalizeEdits(plan.edits) : null;
|
|
1453
1506
|
return {
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1507
|
+
code: edits ? applyEdits(code, edits, plan.append) : null,
|
|
1508
|
+
usedTags: plan.usedTags,
|
|
1509
|
+
usesSvg: plan.usesSvg,
|
|
1510
|
+
compiledCount: edits ? plan.compiledCount : 0,
|
|
1511
|
+
skippedCount: plan.skippedCount
|
|
1457
1512
|
};
|
|
1458
1513
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1514
|
+
|
|
1515
|
+
// src/build/declarations.ts
|
|
1516
|
+
var RECOMMENDED_OPTIONS = {
|
|
1517
|
+
target: "ES2020",
|
|
1518
|
+
module: "ESNext",
|
|
1519
|
+
moduleResolution: "bundler",
|
|
1520
|
+
strict: true,
|
|
1521
|
+
esModuleInterop: true,
|
|
1522
|
+
skipLibCheck: true,
|
|
1523
|
+
declaration: true,
|
|
1524
|
+
declarationMap: true,
|
|
1525
|
+
sourceMap: true,
|
|
1526
|
+
jsx: "preserve",
|
|
1527
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
1528
|
+
isolatedModules: true,
|
|
1529
|
+
resolveJsonModule: true,
|
|
1530
|
+
allowSyntheticDefaultImports: true,
|
|
1531
|
+
forceConsistentCasingInFileNames: true,
|
|
1532
|
+
noUncheckedIndexedAccess: true,
|
|
1533
|
+
noEmit: false
|
|
1534
|
+
};
|
|
1535
|
+
var REQUIRED_OPTIONS = {
|
|
1536
|
+
strict: {
|
|
1537
|
+
value: true,
|
|
1538
|
+
reason: "SibuJS relies on strict type checking for safe reactive state handling."
|
|
1539
|
+
},
|
|
1540
|
+
esModuleInterop: {
|
|
1541
|
+
value: true,
|
|
1542
|
+
reason: "Required for correct ESM interop with SibuJS module exports."
|
|
1543
|
+
},
|
|
1544
|
+
moduleResolution: {
|
|
1545
|
+
value: ["node", "bundler", "node16", "nodenext"],
|
|
1546
|
+
reason: "SibuJS requires a module resolution strategy that supports package.json exports."
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
var SUGGESTED_OPTIONS = {
|
|
1550
|
+
declarationMap: {
|
|
1551
|
+
value: true,
|
|
1552
|
+
reason: 'Enables "Go to Definition" to navigate to SibuJS source files in your IDE.'
|
|
1553
|
+
},
|
|
1554
|
+
sourceMap: {
|
|
1555
|
+
value: true,
|
|
1556
|
+
reason: "Enables source map debugging for SibuJS components."
|
|
1557
|
+
},
|
|
1558
|
+
noUncheckedIndexedAccess: {
|
|
1559
|
+
value: true,
|
|
1560
|
+
reason: "Improves type safety when accessing reactive state objects."
|
|
1561
|
+
},
|
|
1562
|
+
isolatedModules: {
|
|
1563
|
+
value: true,
|
|
1564
|
+
reason: "Required for compatibility with esbuild/swc transpilers used by Vite."
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
function generateTsConfig(options) {
|
|
1568
|
+
const { target = "ES2020", outDir = "dist", declarationMap = true, paths } = options || {};
|
|
1569
|
+
const compilerOptions = {
|
|
1570
|
+
...RECOMMENDED_OPTIONS,
|
|
1571
|
+
target,
|
|
1572
|
+
outDir,
|
|
1573
|
+
declarationMap
|
|
1574
|
+
};
|
|
1575
|
+
if (paths && Object.keys(paths).length > 0) {
|
|
1576
|
+
compilerOptions.baseUrl = ".";
|
|
1577
|
+
compilerOptions.paths = {
|
|
1578
|
+
// Default sibujs alias
|
|
1579
|
+
sibujs: ["node_modules/sibujs/dist/index.d.ts"],
|
|
1580
|
+
"sibujs/*": ["node_modules/sibujs/dist/*"],
|
|
1581
|
+
// User-provided paths
|
|
1582
|
+
...paths
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1494
1585
|
return {
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
isDevMode = resolveDevMode(devMode, env);
|
|
1499
|
-
return {
|
|
1500
|
-
// Optimize dependency pre-bundling for sibujs
|
|
1501
|
-
optimizeDeps: {
|
|
1502
|
-
include: ["sibujs"]
|
|
1503
|
-
},
|
|
1504
|
-
// Ensure sibujs is treated correctly for SSR
|
|
1505
|
-
ssr: {
|
|
1506
|
-
noExternal: ["sibujs"]
|
|
1507
|
-
},
|
|
1508
|
-
// Define global constants for dead code elimination
|
|
1509
|
-
define: {
|
|
1510
|
-
__SIBU_DEV__: JSON.stringify(isDevMode),
|
|
1511
|
-
__SIBU_HMR__: JSON.stringify(hmr)
|
|
1512
|
-
},
|
|
1513
|
-
// Enable source maps in dev
|
|
1514
|
-
build: {
|
|
1515
|
-
sourcemap: isDevMode
|
|
1516
|
-
}
|
|
1517
|
-
};
|
|
1518
|
-
},
|
|
1519
|
-
configResolved(config) {
|
|
1520
|
-
isDevMode = resolveDevMode(devMode, config);
|
|
1521
|
-
},
|
|
1522
|
-
transform(code, id) {
|
|
1523
|
-
if (!matchesPattern(id, include) || matchesPattern(id, exclude)) {
|
|
1524
|
-
return null;
|
|
1525
|
-
}
|
|
1526
|
-
if (!code.includes("sibujs")) return null;
|
|
1527
|
-
if (!scanModule(code)) return null;
|
|
1528
|
-
const edits = [];
|
|
1529
|
-
let append = "";
|
|
1530
|
-
if (isDevMode && importsSibu(code)) {
|
|
1531
|
-
const prologue = devPrologueEdit(code);
|
|
1532
|
-
if (prologue) edits.push(prologue);
|
|
1533
|
-
}
|
|
1534
|
-
if (pureAnnotations) {
|
|
1535
|
-
edits.push(...pureAnnotationEdits(code));
|
|
1536
|
-
}
|
|
1537
|
-
const shouldCompile = compileTemplates ?? !isDevMode;
|
|
1538
|
-
if (shouldCompile) {
|
|
1539
|
-
const plan = planHtmlTemplates(code);
|
|
1540
|
-
if (plan.compiledCount > 0) {
|
|
1541
|
-
edits.push(...plan.edits);
|
|
1542
|
-
append += plan.append;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
if (staticOptimize) {
|
|
1546
|
-
const optimized = staticOptimizationEdits(code);
|
|
1547
|
-
if (optimized) {
|
|
1548
|
-
edits.push(...optimized.edits);
|
|
1549
|
-
append += optimized.append;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
if (edits.length === 0 && append === "") return null;
|
|
1553
|
-
const sorted = normalizeEdits(edits);
|
|
1554
|
-
if (!sorted) return null;
|
|
1555
|
-
const transformed = applyEdits(code, sorted, append);
|
|
1556
|
-
if (transformed === code) return null;
|
|
1557
|
-
return {
|
|
1558
|
-
code: transformed,
|
|
1559
|
-
map: editsSourceMap(code, sorted, append, id)
|
|
1560
|
-
};
|
|
1561
|
-
},
|
|
1562
|
-
handleHotUpdate(ctx) {
|
|
1563
|
-
if (!hmr) return;
|
|
1564
|
-
const { file } = ctx;
|
|
1565
|
-
if (matchesPattern(file, include) && !matchesPattern(file, exclude)) {
|
|
1566
|
-
if (isDevMode) {
|
|
1567
|
-
console.log(`[sibu-vite-plugin] HMR update: ${file}`);
|
|
1568
|
-
}
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1586
|
+
compilerOptions,
|
|
1587
|
+
include: ["src/**/*.ts", "src/**/*.tsx"],
|
|
1588
|
+
exclude: ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
1572
1589
|
};
|
|
1573
1590
|
}
|
|
1574
|
-
function
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
},
|
|
1590
|
-
// Rollup-specific options
|
|
1591
|
-
rollupOptions: {
|
|
1592
|
-
input: ssr ? entry : void 0,
|
|
1593
|
-
output: {
|
|
1594
|
-
// Ensure consistent chunk naming
|
|
1595
|
-
chunkFileNames: "chunks/[name]-[hash].js",
|
|
1596
|
-
// Preserve pure annotations
|
|
1597
|
-
generatedCode: {
|
|
1598
|
-
constBindings: true
|
|
1599
|
-
}
|
|
1600
|
-
},
|
|
1601
|
-
// Tree-shaking configuration
|
|
1602
|
-
treeshake: {
|
|
1603
|
-
moduleSideEffects: false,
|
|
1604
|
-
propertyReadSideEffects: false,
|
|
1605
|
-
annotations: true
|
|
1606
|
-
}
|
|
1591
|
+
function validateTsConfig(config) {
|
|
1592
|
+
const warnings = [];
|
|
1593
|
+
const suggestions = [];
|
|
1594
|
+
const compilerOptions = config.compilerOptions || {};
|
|
1595
|
+
for (const [option, requirement] of Object.entries(REQUIRED_OPTIONS)) {
|
|
1596
|
+
const currentValue = compilerOptions[option];
|
|
1597
|
+
if (Array.isArray(requirement.value)) {
|
|
1598
|
+
if (currentValue === void 0) {
|
|
1599
|
+
warnings.push(
|
|
1600
|
+
`Missing required option "${option}". ${requirement.reason} Recommended value: one of ${requirement.value.map((v) => `"${v}"`).join(", ")}.`
|
|
1601
|
+
);
|
|
1602
|
+
} else if (!requirement.value.includes(currentValue)) {
|
|
1603
|
+
warnings.push(
|
|
1604
|
+
`Option "${option}" is set to "${currentValue}" which may not be compatible with SibuJS. ${requirement.reason} Recommended: one of ${requirement.value.map((v) => `"${v}"`).join(", ")}.`
|
|
1605
|
+
);
|
|
1607
1606
|
}
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
ssr: {
|
|
1618
|
-
noExternal: ["sibujs"],
|
|
1619
|
-
target: "node"
|
|
1620
|
-
},
|
|
1621
|
-
build: {
|
|
1622
|
-
outDir,
|
|
1623
|
-
target: "node18",
|
|
1624
|
-
ssr: true,
|
|
1625
|
-
rollupOptions: {
|
|
1626
|
-
input: entry,
|
|
1627
|
-
output: {
|
|
1628
|
-
format: "esm"
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1607
|
+
} else {
|
|
1608
|
+
if (currentValue === void 0) {
|
|
1609
|
+
warnings.push(
|
|
1610
|
+
`Missing required option "${option}". ${requirement.reason} Recommended value: ${JSON.stringify(requirement.value)}.`
|
|
1611
|
+
);
|
|
1612
|
+
} else if (currentValue !== requirement.value) {
|
|
1613
|
+
warnings.push(
|
|
1614
|
+
`Option "${option}" is set to ${JSON.stringify(currentValue)} but SibuJS recommends ${JSON.stringify(requirement.value)}. ${requirement.reason}`
|
|
1615
|
+
);
|
|
1631
1616
|
}
|
|
1632
|
-
} : {},
|
|
1633
|
-
// Optimize dependency handling
|
|
1634
|
-
optimizeDeps: {
|
|
1635
|
-
include: ["sibujs"],
|
|
1636
|
-
// Force pre-bundling of sibujs for faster dev startup
|
|
1637
|
-
force: false
|
|
1638
|
-
},
|
|
1639
|
-
// Environment variable handling
|
|
1640
|
-
define: {
|
|
1641
|
-
__SIBU_SSR__: JSON.stringify(ssr)
|
|
1642
1617
|
}
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
for (const key of Object.keys(source)) {
|
|
1649
|
-
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
|
|
1650
|
-
result[key] = deepMerge(target[key], source[key]);
|
|
1651
|
-
} else {
|
|
1652
|
-
result[key] = source[key];
|
|
1618
|
+
}
|
|
1619
|
+
for (const [option, suggestion] of Object.entries(SUGGESTED_OPTIONS)) {
|
|
1620
|
+
const currentValue = compilerOptions[option];
|
|
1621
|
+
if (currentValue === void 0 || currentValue !== suggestion.value) {
|
|
1622
|
+
suggestions.push(`Consider setting "${option}" to ${JSON.stringify(suggestion.value)}. ${suggestion.reason}`);
|
|
1653
1623
|
}
|
|
1654
1624
|
}
|
|
1655
|
-
|
|
1625
|
+
const target = typeof compilerOptions.target === "string" ? compilerOptions.target.toUpperCase() : void 0;
|
|
1626
|
+
if (target) {
|
|
1627
|
+
const es2020Plus = ["ES2020", "ES2021", "ES2022", "ES2023", "ES2024", "ESNEXT"];
|
|
1628
|
+
if (!es2020Plus.includes(target)) {
|
|
1629
|
+
warnings.push(
|
|
1630
|
+
`Target "${target}" may not support all features used by SibuJS. Recommended: "ES2020" or newer. SibuJS uses optional chaining, nullish coalescing, and other ES2020+ features.`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
const module = typeof compilerOptions.module === "string" ? compilerOptions.module.toLowerCase() : void 0;
|
|
1635
|
+
if (module && !["esnext", "es2020", "es2022", "node16", "nodenext"].includes(module)) {
|
|
1636
|
+
suggestions.push(
|
|
1637
|
+
`Module "${compilerOptions.module}" may not provide optimal results with SibuJS. Consider using "ESNext" for best tree-shaking and bundle optimization.`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
const lib = compilerOptions.lib;
|
|
1641
|
+
if (Array.isArray(lib)) {
|
|
1642
|
+
const hasDom = lib.some((l) => l.toUpperCase() === "DOM" || l.toUpperCase() === "DOM.ITERABLE");
|
|
1643
|
+
if (!hasDom) {
|
|
1644
|
+
suggestions.push(
|
|
1645
|
+
'The "lib" array does not include "DOM". SibuJS requires DOM types for component rendering. Add "DOM" and "DOM.Iterable" to your lib array.'
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
if (Array.isArray(config.include)) {
|
|
1650
|
+
const includesTs = config.include.some(
|
|
1651
|
+
(pattern) => pattern.includes(".ts") || pattern.includes("*")
|
|
1652
|
+
);
|
|
1653
|
+
if (!includesTs) {
|
|
1654
|
+
suggestions.push(
|
|
1655
|
+
'Your "include" patterns may not capture TypeScript files. Ensure patterns like "src/**/*.ts" are included.'
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
return {
|
|
1660
|
+
valid: warnings.length === 0,
|
|
1661
|
+
warnings,
|
|
1662
|
+
suggestions
|
|
1663
|
+
};
|
|
1656
1664
|
}
|
|
1657
1665
|
|
|
1658
|
-
// src/build/
|
|
1659
|
-
function
|
|
1660
|
-
return
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
}
|
|
1672
|
-
compiler.hooks?.compilation?.tap("SibuWebpackPlugin", (compilation) => {
|
|
1673
|
-
const comp = compilation;
|
|
1674
|
-
if (comp.hooks?.optimizeModules) {
|
|
1675
|
-
comp.hooks.optimizeModules.tap("SibuWebpackPlugin", () => {
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
});
|
|
1679
|
-
compiler.hooks?.afterResolvers?.tap("SibuWebpackPlugin", () => {
|
|
1680
|
-
if (!compiler.options.resolve) {
|
|
1681
|
-
compiler.options.resolve = {};
|
|
1682
|
-
}
|
|
1683
|
-
if (!compiler.options.resolve.mainFields) {
|
|
1684
|
-
compiler.options.resolve.mainFields = ["module", "main"];
|
|
1685
|
-
}
|
|
1686
|
-
if (!compiler.options.resolve.mainFields.includes("module")) {
|
|
1687
|
-
compiler.options.resolve.mainFields.unshift("module");
|
|
1666
|
+
// src/build/ide.ts
|
|
1667
|
+
function getComponentMetadata() {
|
|
1668
|
+
return [
|
|
1669
|
+
// ── Reactive Primitives ──────────────────────────────────────────
|
|
1670
|
+
{
|
|
1671
|
+
name: "signal",
|
|
1672
|
+
description: "Creates a reactive signal that holds a value. Returns a [getter, setter] tuple. The getter registers reactive dependencies when called.",
|
|
1673
|
+
props: [
|
|
1674
|
+
{
|
|
1675
|
+
name: "initial",
|
|
1676
|
+
type: "T",
|
|
1677
|
+
required: true,
|
|
1678
|
+
description: "The initial value of the state"
|
|
1688
1679
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1680
|
+
]
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
name: "effect",
|
|
1684
|
+
description: "Runs a side effect function immediately and re-runs it whenever any reactive dependency changes. Returns a cleanup function.",
|
|
1685
|
+
props: [
|
|
1686
|
+
{
|
|
1687
|
+
name: "effectFn",
|
|
1688
|
+
type: "() => void",
|
|
1689
|
+
required: true,
|
|
1690
|
+
description: "The effect function to run. It is tracked for reactive dependencies."
|
|
1699
1691
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1692
|
+
]
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
name: "derived",
|
|
1696
|
+
description: "Creates a derived reactive signal whose value updates automatically when dependencies change. Returns a getter function.",
|
|
1697
|
+
props: [
|
|
1698
|
+
{
|
|
1699
|
+
name: "getter",
|
|
1700
|
+
type: "() => T",
|
|
1701
|
+
required: true,
|
|
1702
|
+
description: "Function returning the computed value based on reactive signals"
|
|
1707
1703
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1704
|
+
]
|
|
1705
|
+
},
|
|
1706
|
+
{
|
|
1707
|
+
name: "ref",
|
|
1708
|
+
description: "Creates a mutable reference object that persists across renders. Updating a ref does NOT trigger re-renders.",
|
|
1709
|
+
props: [
|
|
1710
|
+
{
|
|
1711
|
+
name: "initial",
|
|
1712
|
+
type: "T",
|
|
1713
|
+
required: false,
|
|
1714
|
+
default: "undefined",
|
|
1715
|
+
description: "Optional initial value for the ref"
|
|
1715
1716
|
}
|
|
1716
|
-
|
|
1717
|
-
}
|
|
1718
|
-
};
|
|
1719
|
-
}
|
|
1720
|
-
function resolveWebpackDevMode(options) {
|
|
1721
|
-
const mode = options.mode;
|
|
1722
|
-
if (mode === "development") return true;
|
|
1723
|
-
if (mode === "production" || mode === void 0) return false;
|
|
1724
|
-
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
1725
|
-
}
|
|
1726
|
-
function createPureAnnotationsLoader() {
|
|
1727
|
-
return function sibuPureAnnotationsLoader(source) {
|
|
1728
|
-
return addPureAnnotations(source);
|
|
1729
|
-
};
|
|
1730
|
-
}
|
|
1731
|
-
function createWebpackConfig(options = {}) {
|
|
1732
|
-
const { entry = "./src/index.ts", outputPath = "dist", mode = "production" } = options;
|
|
1733
|
-
const isDev = mode === "development";
|
|
1734
|
-
return {
|
|
1735
|
-
mode,
|
|
1736
|
-
entry,
|
|
1737
|
-
output: {
|
|
1738
|
-
path: outputPath,
|
|
1739
|
-
filename: isDev ? "[name].js" : "[name].[contenthash:8].js",
|
|
1740
|
-
chunkFilename: isDev ? "[name].chunk.js" : "[name].[contenthash:8].chunk.js",
|
|
1741
|
-
clean: true,
|
|
1742
|
-
// Use ESM output
|
|
1743
|
-
module: true,
|
|
1744
|
-
library: {
|
|
1745
|
-
type: "module"
|
|
1746
|
-
}
|
|
1717
|
+
]
|
|
1747
1718
|
},
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1719
|
+
{
|
|
1720
|
+
name: "watch",
|
|
1721
|
+
description: "Watches a reactive getter and calls a callback with (newValue, oldValue) when the watched value changes. Returns a teardown function.",
|
|
1722
|
+
props: [
|
|
1723
|
+
{
|
|
1724
|
+
name: "getter",
|
|
1725
|
+
type: "() => T",
|
|
1726
|
+
required: true,
|
|
1727
|
+
description: "Function that returns the value to watch"
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
name: "callback",
|
|
1731
|
+
type: "(value: T, prev: T | undefined) => void",
|
|
1732
|
+
required: true,
|
|
1733
|
+
description: "Function called when the watched value changes"
|
|
1734
|
+
}
|
|
1735
|
+
]
|
|
1751
1736
|
},
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1737
|
+
{
|
|
1738
|
+
name: "store",
|
|
1739
|
+
description: "Creates a global store with reactive properties and subscription support. Returns a [store, actions] tuple.",
|
|
1740
|
+
props: [
|
|
1741
|
+
{
|
|
1742
|
+
name: "initialState",
|
|
1743
|
+
type: "T extends object",
|
|
1744
|
+
required: true,
|
|
1745
|
+
description: "Initial state object for the store"
|
|
1746
|
+
}
|
|
1747
|
+
]
|
|
1756
1748
|
},
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1749
|
+
// ── Lifecycle ────────────────────────────────────────────────────
|
|
1750
|
+
{
|
|
1751
|
+
name: "onMount",
|
|
1752
|
+
description: "Runs a callback once the component element has been inserted into the DOM. Uses queueMicrotask for deferred execution.",
|
|
1753
|
+
props: [
|
|
1760
1754
|
{
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
module: "esnext",
|
|
1772
|
-
moduleResolution: "node",
|
|
1773
|
-
target: "es2020"
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
]
|
|
1755
|
+
name: "callback",
|
|
1756
|
+
type: "() => void | CleanupFn",
|
|
1757
|
+
required: true,
|
|
1758
|
+
description: "Function to run after mount. May return a cleanup function."
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
name: "element",
|
|
1762
|
+
type: "HTMLElement",
|
|
1763
|
+
required: false,
|
|
1764
|
+
description: "Optional element to observe; if provided, waits until it is connected."
|
|
1778
1765
|
}
|
|
1779
1766
|
]
|
|
1780
1767
|
},
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
sibu: {
|
|
1797
|
-
test: /[\\/]node_modules[\\/]sibujs[\\/]/,
|
|
1798
|
-
name: "sibu",
|
|
1799
|
-
chunks: "all",
|
|
1800
|
-
priority: 20
|
|
1801
|
-
},
|
|
1802
|
-
// Separate other vendor code
|
|
1803
|
-
vendor: {
|
|
1804
|
-
test: /[\\/]node_modules[\\/]/,
|
|
1805
|
-
name: "vendor",
|
|
1806
|
-
chunks: "all",
|
|
1807
|
-
priority: 10
|
|
1808
|
-
}
|
|
1768
|
+
{
|
|
1769
|
+
name: "onUnmount",
|
|
1770
|
+
description: "Runs a callback when the given element is removed from the DOM. Uses MutationObserver to watch for disconnection.",
|
|
1771
|
+
props: [
|
|
1772
|
+
{
|
|
1773
|
+
name: "callback",
|
|
1774
|
+
type: "() => void",
|
|
1775
|
+
required: true,
|
|
1776
|
+
description: "Function to run on unmount"
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
name: "element",
|
|
1780
|
+
type: "HTMLElement",
|
|
1781
|
+
required: true,
|
|
1782
|
+
description: "The element to watch for removal"
|
|
1809
1783
|
}
|
|
1810
|
-
|
|
1784
|
+
]
|
|
1811
1785
|
},
|
|
1812
|
-
//
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1786
|
+
// ── Rendering ────────────────────────────────────────────────────
|
|
1787
|
+
{
|
|
1788
|
+
name: "mount",
|
|
1789
|
+
description: "Mounts a root component into a DOM element. Returns an object with the mounted node and an unmount function.",
|
|
1790
|
+
props: [
|
|
1791
|
+
{
|
|
1792
|
+
name: "component",
|
|
1793
|
+
type: "(() => HTMLElement) | HTMLElement | Node",
|
|
1794
|
+
required: true,
|
|
1795
|
+
description: "Function returning HTMLElement, or an HTMLElement directly"
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
name: "container",
|
|
1799
|
+
type: "HTMLElement",
|
|
1800
|
+
required: true,
|
|
1801
|
+
description: "Target DOM element to append the result"
|
|
1802
|
+
}
|
|
1803
|
+
]
|
|
1826
1804
|
},
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1805
|
+
{
|
|
1806
|
+
name: "each",
|
|
1807
|
+
description: "Renders a list of nodes efficiently with key-based diffing and LIS-based move minimization.",
|
|
1808
|
+
props: [
|
|
1809
|
+
{
|
|
1810
|
+
name: "getArray",
|
|
1811
|
+
type: "() => T[]",
|
|
1812
|
+
required: true,
|
|
1813
|
+
description: "A reactive getter returning an array"
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
name: "render",
|
|
1817
|
+
type: "(item: T, index: number) => NodeChild",
|
|
1818
|
+
required: true,
|
|
1819
|
+
description: "A function that returns a NodeChild for each item"
|
|
1820
|
+
},
|
|
1821
|
+
{
|
|
1822
|
+
name: "options",
|
|
1823
|
+
type: "{ key: (item: T) => string | number }",
|
|
1824
|
+
required: true,
|
|
1825
|
+
description: "A key function for unique identity of items"
|
|
1826
|
+
}
|
|
1827
|
+
]
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
name: "lazy",
|
|
1831
|
+
description: "Enables code-splitting by deferring the import of a component until it is first rendered.",
|
|
1832
|
+
props: [
|
|
1833
|
+
{
|
|
1834
|
+
name: "importFn",
|
|
1835
|
+
type: "() => Promise<{ default: Component }>",
|
|
1836
|
+
required: true,
|
|
1837
|
+
description: "Dynamic import function returning { default: Component }"
|
|
1838
|
+
}
|
|
1839
|
+
]
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
name: "Suspense",
|
|
1843
|
+
description: "Provides a fallback UI while lazy or async nodes are loading.",
|
|
1844
|
+
props: [
|
|
1845
|
+
{
|
|
1846
|
+
name: "nodes",
|
|
1847
|
+
type: "() => HTMLElement",
|
|
1848
|
+
required: true,
|
|
1849
|
+
description: "Function that returns the async/lazy component"
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
name: "fallback",
|
|
1853
|
+
type: "() => HTMLElement",
|
|
1854
|
+
required: true,
|
|
1855
|
+
description: "Function that returns the loading UI"
|
|
1856
|
+
}
|
|
1857
|
+
]
|
|
1858
|
+
},
|
|
1859
|
+
// ── Context ──────────────────────────────────────────────────────
|
|
1860
|
+
{
|
|
1861
|
+
name: "context",
|
|
1862
|
+
description: "Creates a context for dependency injection across component trees without prop drilling. Returns a Context object with provide, use, get, and set methods.",
|
|
1863
|
+
props: [
|
|
1864
|
+
{
|
|
1865
|
+
name: "defaultValue",
|
|
1866
|
+
type: "T",
|
|
1867
|
+
required: true,
|
|
1868
|
+
description: "The fallback value when no provider is found"
|
|
1869
|
+
}
|
|
1870
|
+
]
|
|
1871
|
+
},
|
|
1872
|
+
// ── Dynamic Components ───────────────────────────────────────────
|
|
1873
|
+
{
|
|
1874
|
+
name: "DynamicComponent",
|
|
1875
|
+
description: "Reactively switches between components based on a reactive getter returning a component name or function.",
|
|
1876
|
+
props: [
|
|
1877
|
+
{
|
|
1878
|
+
name: "is",
|
|
1879
|
+
type: "() => string | Component",
|
|
1880
|
+
required: true,
|
|
1881
|
+
description: "Reactive getter returning component name (string) or component function"
|
|
1882
|
+
}
|
|
1883
|
+
]
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "registerComponent",
|
|
1887
|
+
description: "Registers a component by name for dynamic resolution via DynamicComponent or resolveComponent.",
|
|
1888
|
+
props: [
|
|
1889
|
+
{
|
|
1890
|
+
name: "name",
|
|
1891
|
+
type: "string",
|
|
1892
|
+
required: true,
|
|
1893
|
+
description: "Unique component identifier"
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
name: "component",
|
|
1897
|
+
type: "() => HTMLElement",
|
|
1898
|
+
required: true,
|
|
1899
|
+
description: "The component function"
|
|
1900
|
+
}
|
|
1901
|
+
]
|
|
1902
|
+
},
|
|
1903
|
+
// ── HTML Tag Factories ───────────────────────────────────────────
|
|
1904
|
+
{
|
|
1905
|
+
name: "div",
|
|
1906
|
+
description: "Creates a reactive <div> element. Accepts TagProps including class, style, on, nodes, and arbitrary attributes.",
|
|
1907
|
+
props: [
|
|
1908
|
+
{
|
|
1909
|
+
name: "props",
|
|
1910
|
+
type: "TagProps",
|
|
1911
|
+
required: false,
|
|
1912
|
+
default: "{}",
|
|
1913
|
+
description: "Props object with id, class, style, ref, nodes, on, and other attributes"
|
|
1914
|
+
}
|
|
1915
|
+
],
|
|
1916
|
+
events: [
|
|
1917
|
+
{
|
|
1918
|
+
name: "click",
|
|
1919
|
+
description: "Fires when the element is clicked"
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
name: "input",
|
|
1923
|
+
description: "Fires when user input is received"
|
|
1924
|
+
}
|
|
1925
|
+
]
|
|
1833
1926
|
}
|
|
1834
|
-
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// src/build/cdn.ts
|
|
1838
|
-
var PACKAGE_NAME = "sibujs";
|
|
1839
|
-
var SUBPATH_ENTRIES = [
|
|
1840
|
-
"data",
|
|
1841
|
-
"browser",
|
|
1842
|
-
"patterns",
|
|
1843
|
-
"motion",
|
|
1844
|
-
"ui",
|
|
1845
|
-
"widgets",
|
|
1846
|
-
"ssr",
|
|
1847
|
-
"devtools",
|
|
1848
|
-
"performance",
|
|
1849
|
-
"ecosystem",
|
|
1850
|
-
"plugins",
|
|
1851
|
-
"build",
|
|
1852
|
-
"testing",
|
|
1853
|
-
"extras"
|
|
1854
|
-
];
|
|
1855
|
-
function registerGlobal() {
|
|
1856
|
-
if (typeof window === "undefined") return;
|
|
1857
|
-
window.Sibu = { ...index_exports };
|
|
1858
|
-
}
|
|
1859
|
-
function umdWrapper(name, factory) {
|
|
1860
|
-
const factoryStr = factory.toString();
|
|
1861
|
-
return `(function (root, factory) {
|
|
1862
|
-
if (typeof define === 'function' && define.amd) {
|
|
1863
|
-
// AMD (RequireJS)
|
|
1864
|
-
define([], factory);
|
|
1865
|
-
} else if (typeof module === 'object' && module.exports) {
|
|
1866
|
-
// CommonJS (Node.js)
|
|
1867
|
-
module.exports = factory();
|
|
1868
|
-
} else {
|
|
1869
|
-
// Browser globals
|
|
1870
|
-
root.${name} = factory();
|
|
1871
|
-
}
|
|
1872
|
-
}(typeof self !== 'undefined' ? self : this, ${factoryStr}));`;
|
|
1927
|
+
];
|
|
1873
1928
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1929
|
+
function generateVSCodeSnippets() {
|
|
1930
|
+
return {
|
|
1931
|
+
"SibuJS Component": {
|
|
1932
|
+
prefix: "sibu-component",
|
|
1933
|
+
body: [
|
|
1934
|
+
"import { div } from 'sibujs';",
|
|
1935
|
+
"",
|
|
1936
|
+
"export function ${1:ComponentName}(): HTMLElement {",
|
|
1937
|
+
" return div({",
|
|
1938
|
+
" class: '${2:component-class}',",
|
|
1939
|
+
" nodes: [",
|
|
1940
|
+
" ${3:// nodes here}",
|
|
1941
|
+
" ],",
|
|
1942
|
+
" });",
|
|
1943
|
+
"}"
|
|
1944
|
+
],
|
|
1945
|
+
description: "Create a basic SibuJS component"
|
|
1946
|
+
},
|
|
1947
|
+
"SibuJS Component with State": {
|
|
1948
|
+
prefix: "sibu-component-state",
|
|
1949
|
+
body: [
|
|
1950
|
+
"import { div, span } from 'sibujs';",
|
|
1951
|
+
"import { signal } from 'sibujs';",
|
|
1952
|
+
"",
|
|
1953
|
+
"export function ${1:ComponentName}(): HTMLElement {",
|
|
1954
|
+
" const [${2:value}, ${3:setValue}] = signal(${4:initialValue});",
|
|
1955
|
+
"",
|
|
1956
|
+
" return div({",
|
|
1957
|
+
" class: '${5:component-class}',",
|
|
1958
|
+
" nodes: [",
|
|
1959
|
+
" () => span({ nodes: String(${2:value}()) }),",
|
|
1960
|
+
" ],",
|
|
1961
|
+
" });",
|
|
1962
|
+
"}"
|
|
1963
|
+
],
|
|
1964
|
+
description: "Create a SibuJS component with reactive state"
|
|
1965
|
+
},
|
|
1966
|
+
"SibuJS signal": {
|
|
1967
|
+
prefix: "sibu-state",
|
|
1968
|
+
body: ["const [${1:value}, ${2:setValue}] = signal(${3:initialValue});"],
|
|
1969
|
+
description: "Create a reactive state with signal"
|
|
1970
|
+
},
|
|
1971
|
+
"SibuJS effect": {
|
|
1972
|
+
prefix: "sibu-effect",
|
|
1973
|
+
body: ["const cleanup = effect(() => {", " ${1:// effect logic}", "});"],
|
|
1974
|
+
description: "Create a reactive side effect with effect"
|
|
1975
|
+
},
|
|
1976
|
+
"SibuJS derived": {
|
|
1977
|
+
prefix: "sibu-computed",
|
|
1978
|
+
body: ["const ${1:computed} = derived(() => {", " return ${2:// derived value};", "});"],
|
|
1979
|
+
description: "Create a derived reactive value with derived"
|
|
1980
|
+
},
|
|
1981
|
+
"SibuJS watch": {
|
|
1982
|
+
prefix: "sibu-watch",
|
|
1983
|
+
body: [
|
|
1984
|
+
"const teardown = watch(",
|
|
1985
|
+
" () => ${1:watchedValue}(),",
|
|
1986
|
+
" (newVal, oldVal) => {",
|
|
1987
|
+
" ${2:// handle change}",
|
|
1988
|
+
" }",
|
|
1989
|
+
");"
|
|
1990
|
+
],
|
|
1991
|
+
description: "Watch a reactive value for changes"
|
|
1992
|
+
},
|
|
1993
|
+
"SibuJS each": {
|
|
1994
|
+
prefix: "sibu-each",
|
|
1995
|
+
body: [
|
|
1996
|
+
"each(",
|
|
1997
|
+
" () => ${1:items}(),",
|
|
1998
|
+
" (item) => {",
|
|
1999
|
+
" return ${2:div({ nodes: () => String(item()) })};",
|
|
2000
|
+
" },",
|
|
2001
|
+
" { key: (item) => ${3:item.id} }",
|
|
2002
|
+
")"
|
|
2003
|
+
],
|
|
2004
|
+
description: "Create a reactive list with each()"
|
|
2005
|
+
},
|
|
2006
|
+
"SibuJS Context": {
|
|
2007
|
+
prefix: "sibu-context",
|
|
2008
|
+
body: [
|
|
2009
|
+
"const ${1:MyContext} = context(${2:defaultValue});",
|
|
2010
|
+
"",
|
|
2011
|
+
"// In provider component:",
|
|
2012
|
+
"// ${1:MyContext}.provide(value);",
|
|
2013
|
+
"",
|
|
2014
|
+
"// In consumer component:",
|
|
2015
|
+
"// const ${3:value} = ${1:MyContext}.use();"
|
|
2016
|
+
],
|
|
2017
|
+
description: "Create a context for dependency injection"
|
|
2018
|
+
},
|
|
2019
|
+
"SibuJS store": {
|
|
2020
|
+
prefix: "sibu-store",
|
|
2021
|
+
body: [
|
|
2022
|
+
"const [${1:store}, { setState: ${2:setStore}, reset, subscribe }] = store({",
|
|
2023
|
+
" ${3:key}: ${4:value},",
|
|
2024
|
+
"});"
|
|
2025
|
+
],
|
|
2026
|
+
description: "Create a reactive global store"
|
|
2027
|
+
},
|
|
2028
|
+
"SibuJS Lazy Component": {
|
|
2029
|
+
prefix: "sibu-lazy",
|
|
2030
|
+
body: ["const ${1:LazyComponent} = lazy(() => import('./${2:ComponentPath}'));"],
|
|
2031
|
+
description: "Create a lazy-loaded component"
|
|
2032
|
+
},
|
|
2033
|
+
"SibuJS Suspense": {
|
|
2034
|
+
prefix: "sibu-suspense",
|
|
2035
|
+
body: [
|
|
2036
|
+
"Suspense({",
|
|
2037
|
+
" nodes: () => ${1:LazyComponent}(),",
|
|
2038
|
+
" fallback: () => div({ nodes: '${2:Loading...}' }),",
|
|
2039
|
+
"})"
|
|
2040
|
+
],
|
|
2041
|
+
description: "Wrap a lazy component with a loading fallback"
|
|
2042
|
+
},
|
|
2043
|
+
"SibuJS onMount": {
|
|
2044
|
+
prefix: "sibu-mount",
|
|
2045
|
+
body: ["onMount(() => {", " ${1:// runs after component enters the DOM}", "});"],
|
|
2046
|
+
description: "Schedule a callback to run after mount"
|
|
2047
|
+
},
|
|
2048
|
+
"SibuJS onUnmount": {
|
|
2049
|
+
prefix: "sibu-unmount",
|
|
2050
|
+
body: ["onUnmount(() => {", " ${1:// cleanup when removed from DOM}", "}, ${2:element});"],
|
|
2051
|
+
description: "Schedule a callback to run on unmount"
|
|
2052
|
+
},
|
|
2053
|
+
"SibuJS Event Handler": {
|
|
2054
|
+
prefix: "sibu-event",
|
|
2055
|
+
body: [
|
|
2056
|
+
"div({",
|
|
2057
|
+
" on: {",
|
|
2058
|
+
" ${1:click}: (e) => {",
|
|
2059
|
+
" ${2:// handle event}",
|
|
2060
|
+
" },",
|
|
2061
|
+
" },",
|
|
2062
|
+
" nodes: '${3:Click me}',",
|
|
2063
|
+
"})"
|
|
2064
|
+
],
|
|
2065
|
+
description: "Create an element with event handlers"
|
|
2066
|
+
},
|
|
2067
|
+
"SibuJS Form": {
|
|
2068
|
+
prefix: "sibu-form",
|
|
2069
|
+
body: [
|
|
2070
|
+
"const form = form({",
|
|
2071
|
+
" ${1:fieldName}: {",
|
|
2072
|
+
" initial: '${2:}',",
|
|
2073
|
+
" validators: [",
|
|
2074
|
+
" (value) => !value ? '${3:Required}' : null,",
|
|
2075
|
+
" ],",
|
|
2076
|
+
" },",
|
|
2077
|
+
"});"
|
|
2078
|
+
],
|
|
2079
|
+
description: "Create a reactive form with validation"
|
|
1910
2080
|
}
|
|
1911
|
-
return `<script src="${url}"></script>`;
|
|
1912
|
-
}
|
|
1913
|
-
};
|
|
1914
|
-
function generateImportMap(baseUrl) {
|
|
1915
|
-
const base = baseUrl || `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@latest`;
|
|
1916
|
-
const imports = {
|
|
1917
|
-
// Main package entry
|
|
1918
|
-
[PACKAGE_NAME]: `${base}/dist/index.js`
|
|
1919
2081
|
};
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
}
|
|
2082
|
+
}
|
|
2083
|
+
function generateLanguageConfig() {
|
|
1923
2084
|
return {
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
*/
|
|
1928
|
-
toJSON() {
|
|
1929
|
-
return JSON.stringify({ imports }, null, 2);
|
|
2085
|
+
comments: {
|
|
2086
|
+
lineComment: "//",
|
|
2087
|
+
blockComment: ["/*", "*/"]
|
|
1930
2088
|
},
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
2089
|
+
brackets: [
|
|
2090
|
+
["{", "}"],
|
|
2091
|
+
["[", "]"],
|
|
2092
|
+
["(", ")"],
|
|
2093
|
+
["<", ">"]
|
|
2094
|
+
],
|
|
2095
|
+
autoClosingPairs: [
|
|
2096
|
+
{ open: "{", close: "}" },
|
|
2097
|
+
{ open: "[", close: "]" },
|
|
2098
|
+
{ open: "(", close: ")" },
|
|
2099
|
+
{ open: "<", close: ">" },
|
|
2100
|
+
{ open: "'", close: "'" },
|
|
2101
|
+
{ open: '"', close: '"' },
|
|
2102
|
+
{ open: "`", close: "`" }
|
|
2103
|
+
]
|
|
1939
2104
|
};
|
|
1940
2105
|
}
|
|
1941
|
-
|
|
1942
|
-
// src/build/declarations.ts
|
|
1943
|
-
var RECOMMENDED_OPTIONS = {
|
|
1944
|
-
target: "ES2020",
|
|
1945
|
-
module: "ESNext",
|
|
1946
|
-
moduleResolution: "bundler",
|
|
1947
|
-
strict: true,
|
|
1948
|
-
esModuleInterop: true,
|
|
1949
|
-
skipLibCheck: true,
|
|
1950
|
-
declaration: true,
|
|
1951
|
-
declarationMap: true,
|
|
1952
|
-
sourceMap: true,
|
|
1953
|
-
jsx: "preserve",
|
|
1954
|
-
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
1955
|
-
isolatedModules: true,
|
|
1956
|
-
resolveJsonModule: true,
|
|
1957
|
-
allowSyntheticDefaultImports: true,
|
|
1958
|
-
forceConsistentCasingInFileNames: true,
|
|
1959
|
-
noUncheckedIndexedAccess: true,
|
|
1960
|
-
noEmit: false
|
|
1961
|
-
};
|
|
1962
|
-
var REQUIRED_OPTIONS = {
|
|
1963
|
-
strict: {
|
|
1964
|
-
value: true,
|
|
1965
|
-
reason: "SibuJS relies on strict type checking for safe reactive state handling."
|
|
1966
|
-
},
|
|
1967
|
-
esModuleInterop: {
|
|
1968
|
-
value: true,
|
|
1969
|
-
reason: "Required for correct ESM interop with SibuJS module exports."
|
|
1970
|
-
},
|
|
1971
|
-
moduleResolution: {
|
|
1972
|
-
value: ["node", "bundler", "node16", "nodenext"],
|
|
1973
|
-
reason: "SibuJS requires a module resolution strategy that supports package.json exports."
|
|
1974
|
-
}
|
|
1975
|
-
};
|
|
1976
|
-
var SUGGESTED_OPTIONS = {
|
|
1977
|
-
declarationMap: {
|
|
1978
|
-
value: true,
|
|
1979
|
-
reason: 'Enables "Go to Definition" to navigate to SibuJS source files in your IDE.'
|
|
1980
|
-
},
|
|
1981
|
-
sourceMap: {
|
|
1982
|
-
value: true,
|
|
1983
|
-
reason: "Enables source map debugging for SibuJS components."
|
|
1984
|
-
},
|
|
1985
|
-
noUncheckedIndexedAccess: {
|
|
1986
|
-
value: true,
|
|
1987
|
-
reason: "Improves type safety when accessing reactive state objects."
|
|
1988
|
-
},
|
|
1989
|
-
isolatedModules: {
|
|
1990
|
-
value: true,
|
|
1991
|
-
reason: "Required for compatibility with esbuild/swc transpilers used by Vite."
|
|
1992
|
-
}
|
|
1993
|
-
};
|
|
1994
|
-
function generateTsConfig(options) {
|
|
1995
|
-
const { target = "ES2020", outDir = "dist", declarationMap = true, paths } = options || {};
|
|
1996
|
-
const compilerOptions = {
|
|
1997
|
-
...RECOMMENDED_OPTIONS,
|
|
1998
|
-
target,
|
|
1999
|
-
outDir,
|
|
2000
|
-
declarationMap
|
|
2001
|
-
};
|
|
2002
|
-
if (paths && Object.keys(paths).length > 0) {
|
|
2003
|
-
compilerOptions.baseUrl = ".";
|
|
2004
|
-
compilerOptions.paths = {
|
|
2005
|
-
// Default sibujs alias
|
|
2006
|
-
sibujs: ["node_modules/sibujs/dist/index.d.ts"],
|
|
2007
|
-
"sibujs/*": ["node_modules/sibujs/dist/*"],
|
|
2008
|
-
// User-provided paths
|
|
2009
|
-
...paths
|
|
2010
|
-
};
|
|
2011
|
-
}
|
|
2106
|
+
function generateTypeStubs() {
|
|
2012
2107
|
return {
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2108
|
+
signal: ["declare function signal<T>(initial: T): [() => T, (next: T | ((prev: T) => T)) => void];"].join("\n"),
|
|
2109
|
+
effect: ["declare function effect(effectFn: () => void): () => void;"].join("\n"),
|
|
2110
|
+
derived: ["declare function derived<T>(getter: () => T): () => T;"].join("\n"),
|
|
2111
|
+
ref: [
|
|
2112
|
+
"interface Ref<T> { current: T; }",
|
|
2113
|
+
"declare function ref<T>(initial: T): Ref<T>;",
|
|
2114
|
+
"declare function ref<T = undefined>(): Ref<T | undefined>;"
|
|
2115
|
+
].join("\n"),
|
|
2116
|
+
watch: [
|
|
2117
|
+
"declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefined) => void): () => void;"
|
|
2118
|
+
].join("\n"),
|
|
2119
|
+
store: [
|
|
2120
|
+
"interface StoreActions<T> {",
|
|
2121
|
+
" setState: (patch: Partial<T> | ((state: T) => T)) => void;",
|
|
2122
|
+
" reset: () => void;",
|
|
2123
|
+
" subscribe: (callback: (state: T) => void) => () => void;",
|
|
2124
|
+
" subscribeKey: <K extends keyof T>(key: K, callback: (value: T[K], prev: T[K]) => void) => () => void;",
|
|
2125
|
+
" getSnapshot: () => T;",
|
|
2126
|
+
"}",
|
|
2127
|
+
"declare function store<T extends object>(initialState: T): [{ readonly [K in keyof T]: T[K] }, StoreActions<T>];"
|
|
2128
|
+
].join("\n"),
|
|
2129
|
+
mount: [
|
|
2130
|
+
"declare function mount(component: (() => HTMLElement) | HTMLElement | Node, container: HTMLElement | null): { node: Node; unmount: () => void };"
|
|
2131
|
+
].join("\n"),
|
|
2132
|
+
each: [
|
|
2133
|
+
"declare function each<T>(getArray: () => T[], render: (item: T, index: number) => NodeChild, options: { key: (item: T) => string | number }): Comment;"
|
|
2134
|
+
].join("\n"),
|
|
2135
|
+
onMount: ["declare function onMount(callback: () => void | (() => void), element?: HTMLElement): void;"].join("\n"),
|
|
2136
|
+
onUnmount: ["declare function onUnmount(callback: () => void, element: HTMLElement): void;"].join("\n"),
|
|
2137
|
+
context: [
|
|
2138
|
+
"interface Context<T> {",
|
|
2139
|
+
" provide(value: T): void;",
|
|
2140
|
+
" use(): () => T;",
|
|
2141
|
+
" get(): T;",
|
|
2142
|
+
" set(value: T): void;",
|
|
2143
|
+
"}",
|
|
2144
|
+
"declare function context<T>(defaultValue: T): Context<T>;"
|
|
2145
|
+
].join("\n"),
|
|
2146
|
+
lazy: ["declare function lazy(importFn: () => Promise<{ default: () => HTMLElement }>): () => HTMLElement;"].join(
|
|
2147
|
+
"\n"
|
|
2148
|
+
),
|
|
2149
|
+
Suspense: [
|
|
2150
|
+
"interface SuspenseProps { nodes: () => HTMLElement; fallback: () => HTMLElement; }",
|
|
2151
|
+
"declare function Suspense(props: SuspenseProps): HTMLElement;"
|
|
2152
|
+
].join("\n"),
|
|
2153
|
+
tagFactory: [
|
|
2154
|
+
"interface TagProps {",
|
|
2155
|
+
" id?: string;",
|
|
2156
|
+
" class?: string | (() => string) | Record<string, boolean | (() => boolean)>;",
|
|
2157
|
+
" style?: Record<string, string | number | (() => string | number)> | string | (() => string);",
|
|
2158
|
+
" ref?: { current: unknown };",
|
|
2159
|
+
" nodes?: NodeChildren;",
|
|
2160
|
+
" on?: Record<string, (ev: Event) => void>;",
|
|
2161
|
+
" [attr: string]: unknown;",
|
|
2162
|
+
"}",
|
|
2163
|
+
"declare function tagFactory(tag: string, ns?: string): (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;"
|
|
2164
|
+
].join("\n"),
|
|
2165
|
+
DynamicComponent: ["declare function DynamicComponent(is: () => string | (() => HTMLElement)): HTMLElement;"].join(
|
|
2166
|
+
"\n"
|
|
2167
|
+
),
|
|
2168
|
+
registerComponent: ["declare function registerComponent(name: string, component: () => HTMLElement): void;"].join(
|
|
2169
|
+
"\n"
|
|
2170
|
+
)
|
|
2016
2171
|
};
|
|
2017
2172
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
for (
|
|
2023
|
-
|
|
2024
|
-
if (Array.isArray(requirement.value)) {
|
|
2025
|
-
if (currentValue === void 0) {
|
|
2026
|
-
warnings.push(
|
|
2027
|
-
`Missing required option "${option}". ${requirement.reason} Recommended value: one of ${requirement.value.map((v) => `"${v}"`).join(", ")}.`
|
|
2028
|
-
);
|
|
2029
|
-
} else if (!requirement.value.includes(currentValue)) {
|
|
2030
|
-
warnings.push(
|
|
2031
|
-
`Option "${option}" is set to "${currentValue}" which may not be compatible with SibuJS. ${requirement.reason} Recommended: one of ${requirement.value.map((v) => `"${v}"`).join(", ")}.`
|
|
2032
|
-
);
|
|
2033
|
-
}
|
|
2034
|
-
} else {
|
|
2035
|
-
if (currentValue === void 0) {
|
|
2036
|
-
warnings.push(
|
|
2037
|
-
`Missing required option "${option}". ${requirement.reason} Recommended value: ${JSON.stringify(requirement.value)}.`
|
|
2038
|
-
);
|
|
2039
|
-
} else if (currentValue !== requirement.value) {
|
|
2040
|
-
warnings.push(
|
|
2041
|
-
`Option "${option}" is set to ${JSON.stringify(currentValue)} but SibuJS recommends ${JSON.stringify(requirement.value)}. ${requirement.reason}`
|
|
2042
|
-
);
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
for (const [option, suggestion] of Object.entries(SUGGESTED_OPTIONS)) {
|
|
2047
|
-
const currentValue = compilerOptions[option];
|
|
2048
|
-
if (currentValue === void 0 || currentValue !== suggestion.value) {
|
|
2049
|
-
suggestions.push(`Consider setting "${option}" to ${JSON.stringify(suggestion.value)}. ${suggestion.reason}`);
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
const target = typeof compilerOptions.target === "string" ? compilerOptions.target.toUpperCase() : void 0;
|
|
2053
|
-
if (target) {
|
|
2054
|
-
const es2020Plus = ["ES2020", "ES2021", "ES2022", "ES2023", "ES2024", "ESNEXT"];
|
|
2055
|
-
if (!es2020Plus.includes(target)) {
|
|
2056
|
-
warnings.push(
|
|
2057
|
-
`Target "${target}" may not support all features used by SibuJS. Recommended: "ES2020" or newer. SibuJS uses optional chaining, nullish coalescing, and other ES2020+ features.`
|
|
2058
|
-
);
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
const module = typeof compilerOptions.module === "string" ? compilerOptions.module.toLowerCase() : void 0;
|
|
2062
|
-
if (module && !["esnext", "es2020", "es2022", "node16", "nodenext"].includes(module)) {
|
|
2063
|
-
suggestions.push(
|
|
2064
|
-
`Module "${compilerOptions.module}" may not provide optimal results with SibuJS. Consider using "ESNext" for best tree-shaking and bundle optimization.`
|
|
2065
|
-
);
|
|
2066
|
-
}
|
|
2067
|
-
const lib = compilerOptions.lib;
|
|
2068
|
-
if (Array.isArray(lib)) {
|
|
2069
|
-
const hasDom = lib.some((l) => l.toUpperCase() === "DOM" || l.toUpperCase() === "DOM.ITERABLE");
|
|
2070
|
-
if (!hasDom) {
|
|
2071
|
-
suggestions.push(
|
|
2072
|
-
'The "lib" array does not include "DOM". SibuJS requires DOM types for component rendering. Add "DOM" and "DOM.Iterable" to your lib array.'
|
|
2073
|
-
);
|
|
2074
|
-
}
|
|
2173
|
+
|
|
2174
|
+
// src/build/linting.ts
|
|
2175
|
+
function getLineNumber(source, index) {
|
|
2176
|
+
let line = 1;
|
|
2177
|
+
for (let i = 0; i < index && i < source.length; i++) {
|
|
2178
|
+
if (source[i] === "\n") line++;
|
|
2075
2179
|
}
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
);
|
|
2084
|
-
}
|
|
2180
|
+
return line;
|
|
2181
|
+
}
|
|
2182
|
+
function getColumnNumber(source, index) {
|
|
2183
|
+
let col = 1;
|
|
2184
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
2185
|
+
if (source[i] === "\n") break;
|
|
2186
|
+
col++;
|
|
2085
2187
|
}
|
|
2086
|
-
return
|
|
2087
|
-
valid: warnings.length === 0,
|
|
2088
|
-
warnings,
|
|
2089
|
-
suggestions
|
|
2090
|
-
};
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
// src/build/analyzer.ts
|
|
2094
|
-
var moduleSizes = {
|
|
2095
|
-
// Core modules
|
|
2096
|
-
"core/html": 1800,
|
|
2097
|
-
"core/mount": 120,
|
|
2098
|
-
"core/each": 450,
|
|
2099
|
-
"core/slots": 200,
|
|
2100
|
-
"core/fragment": 180,
|
|
2101
|
-
"core/catch": 280,
|
|
2102
|
-
"core/portal": 350,
|
|
2103
|
-
"core/directives": 520,
|
|
2104
|
-
"core/dynamic": 380,
|
|
2105
|
-
"core/head": 300,
|
|
2106
|
-
"core/ssr": 1200,
|
|
2107
|
-
"core/customElement": 650,
|
|
2108
|
-
"core/scopedStyle": 480,
|
|
2109
|
-
"core/domRecycler": 420,
|
|
2110
|
-
"core/bundleOptimize": 200,
|
|
2111
|
-
"core/compiled": 350,
|
|
2112
|
-
"core/normalize": 280,
|
|
2113
|
-
"core/reactiveAttr": 320,
|
|
2114
|
-
"core/componentProps": 400,
|
|
2115
|
-
"core/contracts": 350,
|
|
2116
|
-
"core/worker": 550,
|
|
2117
|
-
"core/wasm": 480,
|
|
2118
|
-
"core/concurrent": 600,
|
|
2119
|
-
"core/microfrontend": 700,
|
|
2120
|
-
"core/chunkLoader": 450,
|
|
2121
|
-
"core/versioning": 300,
|
|
2122
|
-
"core/ecosystem": 250,
|
|
2123
|
-
// Signals
|
|
2124
|
-
"core/signal": 280,
|
|
2125
|
-
"core/effect": 350,
|
|
2126
|
-
"core/derived": 250,
|
|
2127
|
-
"core/watch": 300,
|
|
2128
|
-
"core/store": 380,
|
|
2129
|
-
"core/ref": 150,
|
|
2130
|
-
"core/array": 420,
|
|
2131
|
-
"core/deepSignal": 500,
|
|
2132
|
-
"core/lifecycle": 300,
|
|
2133
|
-
"core/context": 350,
|
|
2134
|
-
"core/persist": 400,
|
|
2135
|
-
"core/hoc": 280,
|
|
2136
|
-
"core/transition": 600,
|
|
2137
|
-
"core/form": 750,
|
|
2138
|
-
"core/globalStore": 450,
|
|
2139
|
-
"core/machine": 550,
|
|
2140
|
-
"core/optimistic": 380,
|
|
2141
|
-
"core/timeTravel": 500,
|
|
2142
|
-
"core/scheduler": 350,
|
|
2143
|
-
"core/plugin": 300,
|
|
2144
|
-
"core/virtualList": 800,
|
|
2145
|
-
"core/intersection": 350,
|
|
2146
|
-
"core/inputMask": 450,
|
|
2147
|
-
"core/a11y": 500,
|
|
2148
|
-
"core/debug": 400,
|
|
2149
|
-
"core/serviceWorker": 350,
|
|
2150
|
-
"core/composable": 280,
|
|
2151
|
-
// Reactivity
|
|
2152
|
-
"reactivity/signal": 100,
|
|
2153
|
-
"reactivity/track": 250,
|
|
2154
|
-
"reactivity/batch": 200,
|
|
2155
|
-
"reactivity/bindAttribute": 300,
|
|
2156
|
-
"reactivity/bindChildNode": 250,
|
|
2157
|
-
"reactivity/bindTextNode": 200,
|
|
2158
|
-
// Components
|
|
2159
|
-
"components/ErrorBoundary": 350,
|
|
2160
|
-
"components/Loading": 250,
|
|
2161
|
-
// Plugins
|
|
2162
|
-
"plugins/i18n": 600,
|
|
2163
|
-
"plugins/router": 900,
|
|
2164
|
-
"plugins/routerSSR": 500,
|
|
2165
|
-
// Build utilities
|
|
2166
|
-
"build/vite": 800,
|
|
2167
|
-
"build/webpack": 750,
|
|
2168
|
-
"build/cdn": 400,
|
|
2169
|
-
"build/declarations": 500,
|
|
2170
|
-
"build/analyzer": 600,
|
|
2171
|
-
// Testing
|
|
2172
|
-
"testing/index": 700,
|
|
2173
|
-
"testing/e2e": 500
|
|
2174
|
-
};
|
|
2175
|
-
function formatBytes(bytes) {
|
|
2176
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
2177
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
2178
|
-
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
2179
|
-
}
|
|
2180
|
-
function analyzeBundle(modules) {
|
|
2181
|
-
const sizes = {};
|
|
2182
|
-
let totalSize = 0;
|
|
2183
|
-
for (const [name, source] of Object.entries(modules)) {
|
|
2184
|
-
let byteLength;
|
|
2185
|
-
if (typeof TextEncoder !== "undefined") {
|
|
2186
|
-
byteLength = new TextEncoder().encode(source).length;
|
|
2187
|
-
} else {
|
|
2188
|
-
byteLength = source.length;
|
|
2189
|
-
}
|
|
2190
|
-
sizes[name] = byteLength;
|
|
2191
|
-
totalSize += byteLength;
|
|
2192
|
-
}
|
|
2193
|
-
const sorted = Object.entries(sizes).map(([name, size]) => ({
|
|
2194
|
-
name,
|
|
2195
|
-
size,
|
|
2196
|
-
percentage: totalSize > 0 ? size / totalSize * 100 : 0
|
|
2197
|
-
})).sort((a, b) => b.size - a.size);
|
|
2198
|
-
return {
|
|
2199
|
-
totalSize,
|
|
2200
|
-
moduleSizes: sizes,
|
|
2201
|
-
sorted,
|
|
2202
|
-
report() {
|
|
2203
|
-
const lines = [
|
|
2204
|
-
"=== SibuJS Bundle Analysis ===",
|
|
2205
|
-
"",
|
|
2206
|
-
`Total size: ${formatBytes(totalSize)}`,
|
|
2207
|
-
`Modules: ${sorted.length}`,
|
|
2208
|
-
"",
|
|
2209
|
-
"Module breakdown:",
|
|
2210
|
-
"-".repeat(60)
|
|
2211
|
-
];
|
|
2212
|
-
for (const entry of sorted) {
|
|
2213
|
-
const sizeStr = formatBytes(entry.size).padStart(10);
|
|
2214
|
-
const pctStr = `${entry.percentage.toFixed(1)}%`.padStart(6);
|
|
2215
|
-
const bar = generateBar(entry.percentage, 20);
|
|
2216
|
-
lines.push(` ${sizeStr} ${pctStr} ${bar} ${entry.name}`);
|
|
2217
|
-
}
|
|
2218
|
-
lines.push("-".repeat(60));
|
|
2219
|
-
return lines.join("\n");
|
|
2220
|
-
}
|
|
2221
|
-
};
|
|
2222
|
-
}
|
|
2223
|
-
function generateBar(percentage, width) {
|
|
2224
|
-
const filled = Math.round(percentage / 100 * width);
|
|
2225
|
-
const empty = width - filled;
|
|
2226
|
-
return `[${"#".repeat(filled)}${".".repeat(empty)}]`;
|
|
2227
|
-
}
|
|
2228
|
-
function estimateImportSize(imports) {
|
|
2229
|
-
const breakdown = {};
|
|
2230
|
-
let estimated = 0;
|
|
2231
|
-
const implicitDeps = ["reactivity/signal", "reactivity/track"];
|
|
2232
|
-
const allImports = [.../* @__PURE__ */ new Set([...implicitDeps, ...imports])];
|
|
2233
|
-
for (const importPath of allImports) {
|
|
2234
|
-
const size = moduleSizes[importPath];
|
|
2235
|
-
if (size !== void 0) {
|
|
2236
|
-
breakdown[importPath] = size;
|
|
2237
|
-
estimated += size;
|
|
2238
|
-
} else {
|
|
2239
|
-
const avgSize = 350;
|
|
2240
|
-
breakdown[importPath] = avgSize;
|
|
2241
|
-
estimated += avgSize;
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
const sortedEntries = Object.entries(breakdown).sort(([, a], [, b]) => b - a);
|
|
2245
|
-
const lines = [`Estimated bundle size: ${formatBytes(estimated)} (minified + gzipped)`, ""];
|
|
2246
|
-
for (const [modulePath, size] of sortedEntries) {
|
|
2247
|
-
const known = moduleSizes[modulePath] !== void 0;
|
|
2248
|
-
lines.push(` ${modulePath}: ${formatBytes(size)}${known ? "" : " (estimated)"}`);
|
|
2249
|
-
}
|
|
2250
|
-
if (imports.length > 0) {
|
|
2251
|
-
lines.push("");
|
|
2252
|
-
lines.push("Note: Actual sizes may vary based on tree-shaking and build configuration.");
|
|
2253
|
-
lines.push("Implicit dependencies (reactivity/signal, reactivity/track) are included.");
|
|
2254
|
-
}
|
|
2255
|
-
return {
|
|
2256
|
-
estimated,
|
|
2257
|
-
breakdown,
|
|
2258
|
-
formatted: lines.join("\n")
|
|
2259
|
-
};
|
|
2260
|
-
}
|
|
2261
|
-
|
|
2262
|
-
// src/build/linting.ts
|
|
2263
|
-
function getLineNumber(source, index) {
|
|
2264
|
-
let line = 1;
|
|
2265
|
-
for (let i = 0; i < index && i < source.length; i++) {
|
|
2266
|
-
if (source[i] === "\n") line++;
|
|
2267
|
-
}
|
|
2268
|
-
return line;
|
|
2269
|
-
}
|
|
2270
|
-
function getColumnNumber(source, index) {
|
|
2271
|
-
let col = 1;
|
|
2272
|
-
for (let i = index - 1; i >= 0; i--) {
|
|
2273
|
-
if (source[i] === "\n") break;
|
|
2274
|
-
col++;
|
|
2275
|
-
}
|
|
2276
|
-
return col;
|
|
2188
|
+
return col;
|
|
2277
2189
|
}
|
|
2278
2190
|
var lintRules = {
|
|
2279
2191
|
/** Detect signal inside loops or conditionals */
|
|
@@ -2610,636 +2522,1075 @@ function generateEslintConfig(options) {
|
|
|
2610
2522
|
};
|
|
2611
2523
|
}
|
|
2612
2524
|
|
|
2613
|
-
// src/build/
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2525
|
+
// src/build/prerender.ts
|
|
2526
|
+
var PRERENDER_CHILD_ENV = "SIBU_PRERENDER_CHILD";
|
|
2527
|
+
var MODULE_SCRIPT = /<script\b[^>]*\btype\s*=\s*["']module["'][^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/i;
|
|
2528
|
+
var MODULE_SCRIPT_SRC_FIRST = /<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*\btype\s*=\s*["']module["'][^>]*>/i;
|
|
2529
|
+
function findEntryInHtml(html) {
|
|
2530
|
+
const m = MODULE_SCRIPT.exec(html) ?? MODULE_SCRIPT_SRC_FIRST.exec(html);
|
|
2531
|
+
return m ? m[1] : null;
|
|
2532
|
+
}
|
|
2533
|
+
function outputFileFor(route) {
|
|
2534
|
+
if (route.output) {
|
|
2535
|
+
const output = route.output;
|
|
2536
|
+
if (/^[A-Za-z]:/.test(output) || output.includes("\0")) {
|
|
2537
|
+
throw new Error(`[sibu prerender] unsafe output "${output}": must be a path relative to the output dir`);
|
|
2538
|
+
}
|
|
2539
|
+
const segments2 = output.split(/[\\/]+/).filter(Boolean);
|
|
2540
|
+
if (segments2.length === 0 || segments2.some((s) => s === "." || s === "..")) {
|
|
2541
|
+
throw new Error(`[sibu prerender] unsafe output "${output}": "." and ".." segments are not allowed`);
|
|
2542
|
+
}
|
|
2543
|
+
return segments2.join("/");
|
|
2544
|
+
}
|
|
2545
|
+
const pathname = route.path.split(/[?#]/)[0];
|
|
2546
|
+
if (!pathname.startsWith("/")) throw new Error(`[sibu prerender] route "${route.path}" must start with "/"`);
|
|
2547
|
+
const segments = pathname.split("/").filter(Boolean).map((raw) => {
|
|
2548
|
+
let decoded;
|
|
2549
|
+
try {
|
|
2550
|
+
decoded = decodeURIComponent(raw);
|
|
2551
|
+
} catch {
|
|
2552
|
+
throw new Error(
|
|
2553
|
+
`[sibu prerender] route "${route.path}" has malformed percent-encoding in "${raw}"; encode a literal % as %25.`
|
|
2554
|
+
);
|
|
2555
|
+
}
|
|
2556
|
+
if (decoded === "." || decoded === ".." || /[\\/\0]/.test(decoded)) {
|
|
2557
|
+
throw new Error(
|
|
2558
|
+
`[sibu prerender] unsafe route "${route.path}": segment "${raw}" decodes to "${decoded}", which could leave the output dir (".", "..", or a path separator).`
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
return decoded;
|
|
2562
|
+
});
|
|
2563
|
+
return [...segments, "index.html"].join("/");
|
|
2564
|
+
}
|
|
2565
|
+
function resolveInsideOutDir(path, outDir, file) {
|
|
2566
|
+
const root = path.resolve(outDir);
|
|
2567
|
+
const target = path.resolve(root, file);
|
|
2568
|
+
const rel = path.relative(root, target);
|
|
2569
|
+
const escapes = rel === "" || rel === ".." || /^\.\.[\\/]/.test(rel) || path.isAbsolute(rel);
|
|
2570
|
+
if (escapes) throw new Error(`[sibu prerender] refusing to write "${file}": it resolves outside ${root}`);
|
|
2571
|
+
return target;
|
|
2572
|
+
}
|
|
2573
|
+
async function prerender(options, ctx) {
|
|
2574
|
+
const fs = await import("fs/promises");
|
|
2575
|
+
const path = await import("path");
|
|
2576
|
+
const { pathToFileURL } = await import("url");
|
|
2577
|
+
const { createRequire } = await import("module");
|
|
2578
|
+
const routes = options.routes.map((r) => typeof r === "string" ? { path: r } : r);
|
|
2579
|
+
if (routes.length === 0) return [];
|
|
2580
|
+
const planned = routes.map((route) => {
|
|
2581
|
+
const file = outputFileFor(route);
|
|
2582
|
+
resolveInsideOutDir(path, ctx.outDir, file);
|
|
2583
|
+
return { route, file };
|
|
2584
|
+
});
|
|
2585
|
+
const origin = (options.origin ?? "http://localhost").replace(/\/+$/, "");
|
|
2586
|
+
const timeout = options.timeout ?? 15e3;
|
|
2587
|
+
const settle = options.settle ?? 50;
|
|
2588
|
+
const templatePath = path.join(ctx.outDir, "index.html");
|
|
2589
|
+
const template = await fs.readFile(templatePath, "utf8");
|
|
2590
|
+
let entry = options.entry;
|
|
2591
|
+
if (!entry) {
|
|
2592
|
+
const sourceHtml = await fs.readFile(path.join(ctx.root, "index.html"), "utf8");
|
|
2593
|
+
entry = findEntryInHtml(sourceHtml) ?? void 0;
|
|
2594
|
+
if (!entry) {
|
|
2595
|
+
throw new Error("[sibu prerender] could not find the client entry in index.html; pass `prerender.entry`.");
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
const entryAbs = path.resolve(ctx.root, entry.replace(/^\//, ""));
|
|
2599
|
+
const requireFromApp = createRequire(path.join(ctx.root, "package.json"));
|
|
2600
|
+
let jsdomPath;
|
|
2601
|
+
try {
|
|
2602
|
+
jsdomPath = requireFromApp.resolve("jsdom");
|
|
2603
|
+
} catch {
|
|
2604
|
+
throw new Error("[sibu prerender] needs jsdom to render routes. Install it: npm i -D jsdom");
|
|
2605
|
+
}
|
|
2606
|
+
const tmpDir = path.join(ctx.outDir, ".sibu-prerender");
|
|
2607
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
2608
|
+
const viteBuild = await loadViteBuild(requireFromApp, fs, path, pathToFileURL);
|
|
2609
|
+
const prev = process.env[PRERENDER_CHILD_ENV];
|
|
2610
|
+
process.env[PRERENDER_CHILD_ENV] = "1";
|
|
2611
|
+
try {
|
|
2612
|
+
await viteBuild({
|
|
2613
|
+
configFile: ctx.configFile,
|
|
2614
|
+
root: ctx.root,
|
|
2615
|
+
mode: ctx.mode,
|
|
2616
|
+
logLevel: "warn",
|
|
2617
|
+
build: {
|
|
2618
|
+
ssr: entryAbs,
|
|
2619
|
+
outDir: tmpDir,
|
|
2620
|
+
emptyOutDir: true,
|
|
2621
|
+
copyPublicDir: false,
|
|
2622
|
+
minify: false,
|
|
2623
|
+
sourcemap: false,
|
|
2624
|
+
rollupOptions: { output: { format: "es", entryFileNames: "entry.mjs" } }
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
} finally {
|
|
2628
|
+
if (prev === void 0) delete process.env[PRERENDER_CHILD_ENV];
|
|
2629
|
+
else process.env[PRERENDER_CHILD_ENV] = prev;
|
|
2630
|
+
}
|
|
2631
|
+
await fs.writeFile(path.join(tmpDir, "package.json"), '{ "type": "module" }\n', "utf8");
|
|
2632
|
+
const workerFile = path.join(tmpDir, "render-worker.mjs");
|
|
2633
|
+
await fs.writeFile(workerFile, WORKER_SOURCE, "utf8");
|
|
2634
|
+
const entryUrl = pathToFileURL(path.join(tmpDir, "entry.mjs")).href;
|
|
2635
|
+
const jsdomUrl = pathToFileURL(jsdomPath).href;
|
|
2636
|
+
if (options.shell !== false) {
|
|
2637
|
+
await fs.writeFile(path.join(ctx.outDir, options.shell ?? "200.html"), template, "utf8");
|
|
2638
|
+
}
|
|
2639
|
+
const results = [];
|
|
2640
|
+
const queue = [...planned];
|
|
2641
|
+
const concurrency = Math.max(1, options.concurrency ?? 4);
|
|
2642
|
+
try {
|
|
2643
|
+
await Promise.all(
|
|
2644
|
+
Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
2645
|
+
for (let job = queue.shift(); job; job = queue.shift()) {
|
|
2646
|
+
const { route, file } = job;
|
|
2647
|
+
const started = Date.now();
|
|
2648
|
+
const html = await renderInWorker(workerFile, {
|
|
2649
|
+
entryUrl,
|
|
2650
|
+
jsdomUrl,
|
|
2651
|
+
html: template,
|
|
2652
|
+
url: origin + route.path,
|
|
2653
|
+
route: route.path,
|
|
2654
|
+
timeout,
|
|
2655
|
+
settle
|
|
2656
|
+
});
|
|
2657
|
+
const target = resolveInsideOutDir(path, ctx.outDir, file);
|
|
2658
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
2659
|
+
await fs.writeFile(target, html, "utf8");
|
|
2660
|
+
results.push({ route: route.path, file, ms: Date.now() - started });
|
|
2730
2661
|
}
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
{
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
{
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
{
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2662
|
+
})
|
|
2663
|
+
);
|
|
2664
|
+
} finally {
|
|
2665
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
2666
|
+
}
|
|
2667
|
+
return results;
|
|
2668
|
+
}
|
|
2669
|
+
function esmEntryOf(entry) {
|
|
2670
|
+
if (typeof entry === "string") return entry;
|
|
2671
|
+
if (entry === null || typeof entry !== "object") return void 0;
|
|
2672
|
+
const conditions = entry;
|
|
2673
|
+
for (const key of ["import", "module-sync", "default"]) {
|
|
2674
|
+
const found = esmEntryOf(conditions[key]);
|
|
2675
|
+
if (found) return found;
|
|
2676
|
+
}
|
|
2677
|
+
return void 0;
|
|
2678
|
+
}
|
|
2679
|
+
async function loadViteBuild(requireFromApp, fs, path, pathToFileURL) {
|
|
2680
|
+
let resolved;
|
|
2681
|
+
try {
|
|
2682
|
+
resolved = requireFromApp.resolve("vite");
|
|
2683
|
+
} catch {
|
|
2684
|
+
throw new Error("[sibu prerender] needs Vite to build the entry for Node, and could not resolve it from the app.");
|
|
2685
|
+
}
|
|
2686
|
+
let url = pathToFileURL(resolved).href;
|
|
2687
|
+
try {
|
|
2688
|
+
const pkgPath = requireFromApp.resolve("vite/package.json");
|
|
2689
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
|
|
2690
|
+
const entry = esmEntryOf(pkg.exports?.["."]);
|
|
2691
|
+
if (entry) url = pathToFileURL(path.join(path.dirname(pkgPath), entry)).href;
|
|
2692
|
+
} catch {
|
|
2693
|
+
}
|
|
2694
|
+
const mod = await import(url);
|
|
2695
|
+
const build = mod.build ?? mod.default?.build;
|
|
2696
|
+
if (typeof build !== "function") {
|
|
2697
|
+
throw new Error(`[sibu prerender] the Vite module at ${url} has no build() function.`);
|
|
2698
|
+
}
|
|
2699
|
+
return build;
|
|
2700
|
+
}
|
|
2701
|
+
async function renderInWorker(workerFile, input) {
|
|
2702
|
+
const { Worker } = await import("worker_threads");
|
|
2703
|
+
return new Promise((resolve, reject) => {
|
|
2704
|
+
const worker = new Worker(workerFile, { workerData: input });
|
|
2705
|
+
let done = false;
|
|
2706
|
+
const finish = (fn) => {
|
|
2707
|
+
if (done) return;
|
|
2708
|
+
done = true;
|
|
2709
|
+
fn();
|
|
2710
|
+
void worker.terminate();
|
|
2711
|
+
};
|
|
2712
|
+
const timer = setTimeout(
|
|
2713
|
+
() => finish(() => reject(new Error(`[sibu prerender] ${input.route}: worker did not respond`))),
|
|
2714
|
+
input.timeout + 5e3
|
|
2715
|
+
);
|
|
2716
|
+
worker.once("message", (msg) => {
|
|
2717
|
+
clearTimeout(timer);
|
|
2718
|
+
if (typeof msg.html === "string") finish(() => resolve(msg.html));
|
|
2719
|
+
else finish(() => reject(new Error(`[sibu prerender] ${input.route}: ${msg.error}`)));
|
|
2720
|
+
});
|
|
2721
|
+
worker.once("error", (err) => {
|
|
2722
|
+
clearTimeout(timer);
|
|
2723
|
+
finish(() => reject(new Error(`[sibu prerender] ${input.route}: ${err instanceof Error ? err.stack : err}`)));
|
|
2724
|
+
});
|
|
2725
|
+
worker.once("exit", (code) => {
|
|
2726
|
+
clearTimeout(timer);
|
|
2727
|
+
finish(() => reject(new Error(`[sibu prerender] ${input.route}: worker exited (${code}) before rendering`)));
|
|
2728
|
+
});
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
var WORKER_SOURCE = `
|
|
2732
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
2733
|
+
|
|
2734
|
+
const { entryUrl, jsdomUrl, html, url, route, timeout, settle } = workerData;
|
|
2735
|
+
const fail = (err) => parentPort.postMessage({ error: err instanceof Error ? (err.stack || err.message) : String(err) });
|
|
2736
|
+
|
|
2737
|
+
try {
|
|
2738
|
+
const jsdom = await import(jsdomUrl);
|
|
2739
|
+
const JSDOM = jsdom.JSDOM ?? jsdom.default.JSDOM;
|
|
2740
|
+
const dom = new JSDOM(html, { url, pretendToBeVisual: true });
|
|
2741
|
+
const win = dom.window;
|
|
2742
|
+
// Read before the app runs: the router may replace it with the route title.
|
|
2743
|
+
const templateTitle = win.document.title;
|
|
2744
|
+
|
|
2745
|
+
// Register the document as THE global DOM. Node already has a few of these
|
|
2746
|
+
// names (Event, EventTarget, \u2026); the DOM's own must win, or an event the app
|
|
2747
|
+
// builds could not be dispatched on a DOM node.
|
|
2748
|
+
const FORCE = new Set(["Event", "EventTarget", "CustomEvent", "MessageEvent", "DOMException"]);
|
|
2749
|
+
for (const key of Object.getOwnPropertyNames(win)) {
|
|
2750
|
+
if (key in globalThis && !FORCE.has(key)) continue;
|
|
2751
|
+
try { globalThis[key] = win[key]; } catch {}
|
|
2752
|
+
}
|
|
2753
|
+
globalThis.window = win;
|
|
2754
|
+
globalThis.self = win;
|
|
2755
|
+
globalThis.document = win.document;
|
|
2756
|
+
|
|
2757
|
+
let lastActivity = Date.now();
|
|
2758
|
+
const pending = new Set();
|
|
2759
|
+
const errors = [];
|
|
2760
|
+
const track = (p) => {
|
|
2761
|
+
const tracked = Promise.resolve(p).then(() => {}, () => {});
|
|
2762
|
+
pending.add(tracked);
|
|
2763
|
+
tracked.finally(() => { pending.delete(tracked); lastActivity = Date.now(); });
|
|
2764
|
+
};
|
|
2765
|
+
|
|
2766
|
+
// Hold serialization while \`p\` is pending, WITHOUT handling it: the caller
|
|
2767
|
+
// receives a promise that settles exactly as \`p\` does, so a rejection the
|
|
2768
|
+
// app handles behaves normally and one it ignores still reaches the
|
|
2769
|
+
// unhandledRejection listener below (attaching a handler to \`p\` itself
|
|
2770
|
+
// would silently mark it handled).
|
|
2771
|
+
const observe = (p) => {
|
|
2772
|
+
let release;
|
|
2773
|
+
const tracked = new Promise((r) => { release = r; });
|
|
2774
|
+
pending.add(tracked);
|
|
2775
|
+
tracked.finally(() => { pending.delete(tracked); lastActivity = Date.now(); });
|
|
2776
|
+
return Promise.resolve(p).then(
|
|
2777
|
+
(value) => { release(); return value; },
|
|
2778
|
+
(error) => { release(); throw error; },
|
|
2779
|
+
);
|
|
2780
|
+
};
|
|
2781
|
+
|
|
2782
|
+
// Every fetch holds serialization until it settles; relative URLs resolve
|
|
2783
|
+
// against the route's URL, as they would in the browser.
|
|
2784
|
+
const nodeFetch = globalThis.fetch;
|
|
2785
|
+
if (typeof nodeFetch === "function") {
|
|
2786
|
+
const wrapped = (input, init) => {
|
|
2787
|
+
const target = typeof input === "string" || input instanceof URL ? new URL(String(input), url) : input;
|
|
2788
|
+
return observe(nodeFetch(target, init));
|
|
2789
|
+
};
|
|
2790
|
+
globalThis.fetch = wrapped;
|
|
2791
|
+
win.fetch = wrapped;
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
// A fetch settles when the HEADERS arrive; the data the page renders comes
|
|
2795
|
+
// from a body reader that may still be waiting for the rest of the response.
|
|
2796
|
+
// Each reader call is held like the fetch itself. Patched on the prototype \u2014
|
|
2797
|
+
// this thread exists only to render one route \u2014 so clones, and Responses the
|
|
2798
|
+
// app builds itself, are covered, and \`this\` / brand checks are the native
|
|
2799
|
+
// ones. Bodies nobody reads are never touched, so an unused or long-lived
|
|
2800
|
+
// body does not hold the page. Reading \`response.body\` directly as a stream
|
|
2801
|
+
// is NOT held (a stream may never end); wrap such a read in prerenderWait().
|
|
2802
|
+
const ResponseCtor = globalThis.Response;
|
|
2803
|
+
if (typeof ResponseCtor === "function") {
|
|
2804
|
+
const proto = ResponseCtor.prototype;
|
|
2805
|
+
for (const name of ["arrayBuffer", "blob", "bytes", "formData", "json", "text"]) {
|
|
2806
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, name);
|
|
2807
|
+
const original = descriptor?.value;
|
|
2808
|
+
if (typeof original !== "function") continue;
|
|
2809
|
+
Object.defineProperty(proto, name, {
|
|
2810
|
+
...descriptor,
|
|
2811
|
+
value: {
|
|
2812
|
+
// A method-shorthand keeps the native name and is not constructible.
|
|
2813
|
+
[name](...args) {
|
|
2814
|
+
return observe(Reflect.apply(original, this, args));
|
|
2815
|
+
},
|
|
2816
|
+
}[name],
|
|
2817
|
+
});
|
|
2873
2818
|
}
|
|
2874
|
-
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
globalThis[Symbol.for("sibujs.prerender.v1")] = { route, wait: track };
|
|
2822
|
+
|
|
2823
|
+
new win.MutationObserver(() => { lastActivity = Date.now(); }).observe(win.document, {
|
|
2824
|
+
subtree: true, childList: true, attributes: true, characterData: true,
|
|
2825
|
+
});
|
|
2826
|
+
win.addEventListener("error", (e) => errors.push(e.error ?? e.message));
|
|
2827
|
+
process.on("unhandledRejection", (reason) => errors.push(reason));
|
|
2828
|
+
|
|
2829
|
+
await import(entryUrl);
|
|
2830
|
+
|
|
2831
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2832
|
+
const started = Date.now();
|
|
2833
|
+
for (;;) {
|
|
2834
|
+
if (errors.length > 0) throw errors[0];
|
|
2835
|
+
if (Date.now() - started > timeout) {
|
|
2836
|
+
throw new Error("did not settle within " + timeout + " ms (" + pending.size + " pending request(s) or prerenderWait() promise(s)). " +
|
|
2837
|
+
"Await the data, skip it with isPrerendering(), or drop the route from prerender.routes.");
|
|
2838
|
+
}
|
|
2839
|
+
if (pending.size > 0) await Promise.race([Promise.all([...pending]), sleep(settle)]);
|
|
2840
|
+
await sleep(settle);
|
|
2841
|
+
if (pending.size === 0 && Date.now() - lastActivity >= settle) break;
|
|
2842
|
+
}
|
|
2843
|
+
if (errors.length > 0) throw errors[0];
|
|
2844
|
+
|
|
2845
|
+
// The template title travels with the page, so the client router can give it
|
|
2846
|
+
// back to a route that sets no title of its own.
|
|
2847
|
+
const titleEl = win.document.head.querySelector("title");
|
|
2848
|
+
if (titleEl && !titleEl.hasAttribute("data-sibu-base-title")) titleEl.setAttribute("data-sibu-base-title", templateTitle);
|
|
2849
|
+
|
|
2850
|
+
parentPort.postMessage({ html: dom.serialize() });
|
|
2851
|
+
} catch (err) {
|
|
2852
|
+
fail(err);
|
|
2875
2853
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
" return div({",
|
|
2904
|
-
" class: '${5:component-class}',",
|
|
2905
|
-
" nodes: [",
|
|
2906
|
-
" () => span({ nodes: String(${2:value}()) }),",
|
|
2907
|
-
" ],",
|
|
2908
|
-
" });",
|
|
2909
|
-
"}"
|
|
2910
|
-
],
|
|
2911
|
-
description: "Create a SibuJS component with reactive state"
|
|
2912
|
-
},
|
|
2913
|
-
"SibuJS signal": {
|
|
2914
|
-
prefix: "sibu-state",
|
|
2915
|
-
body: ["const [${1:value}, ${2:setValue}] = signal(${3:initialValue});"],
|
|
2916
|
-
description: "Create a reactive state with signal"
|
|
2917
|
-
},
|
|
2918
|
-
"SibuJS effect": {
|
|
2919
|
-
prefix: "sibu-effect",
|
|
2920
|
-
body: ["const cleanup = effect(() => {", " ${1:// effect logic}", "});"],
|
|
2921
|
-
description: "Create a reactive side effect with effect"
|
|
2922
|
-
},
|
|
2923
|
-
"SibuJS derived": {
|
|
2924
|
-
prefix: "sibu-computed",
|
|
2925
|
-
body: ["const ${1:computed} = derived(() => {", " return ${2:// derived value};", "});"],
|
|
2926
|
-
description: "Create a derived reactive value with derived"
|
|
2927
|
-
},
|
|
2928
|
-
"SibuJS watch": {
|
|
2929
|
-
prefix: "sibu-watch",
|
|
2930
|
-
body: [
|
|
2931
|
-
"const teardown = watch(",
|
|
2932
|
-
" () => ${1:watchedValue}(),",
|
|
2933
|
-
" (newVal, oldVal) => {",
|
|
2934
|
-
" ${2:// handle change}",
|
|
2935
|
-
" }",
|
|
2936
|
-
");"
|
|
2937
|
-
],
|
|
2938
|
-
description: "Watch a reactive value for changes"
|
|
2939
|
-
},
|
|
2940
|
-
"SibuJS each": {
|
|
2941
|
-
prefix: "sibu-each",
|
|
2942
|
-
body: [
|
|
2943
|
-
"each(",
|
|
2944
|
-
" () => ${1:items}(),",
|
|
2945
|
-
" (item) => {",
|
|
2946
|
-
" return ${2:div({ nodes: () => String(item()) })};",
|
|
2947
|
-
" },",
|
|
2948
|
-
" { key: (item) => ${3:item.id} }",
|
|
2949
|
-
")"
|
|
2950
|
-
],
|
|
2951
|
-
description: "Create a reactive list with each()"
|
|
2952
|
-
},
|
|
2953
|
-
"SibuJS Context": {
|
|
2954
|
-
prefix: "sibu-context",
|
|
2955
|
-
body: [
|
|
2956
|
-
"const ${1:MyContext} = context(${2:defaultValue});",
|
|
2957
|
-
"",
|
|
2958
|
-
"// In provider component:",
|
|
2959
|
-
"// ${1:MyContext}.provide(value);",
|
|
2960
|
-
"",
|
|
2961
|
-
"// In consumer component:",
|
|
2962
|
-
"// const ${3:value} = ${1:MyContext}.use();"
|
|
2963
|
-
],
|
|
2964
|
-
description: "Create a context for dependency injection"
|
|
2965
|
-
},
|
|
2966
|
-
"SibuJS store": {
|
|
2967
|
-
prefix: "sibu-store",
|
|
2968
|
-
body: [
|
|
2969
|
-
"const [${1:store}, { setState: ${2:setStore}, reset, subscribe }] = store({",
|
|
2970
|
-
" ${3:key}: ${4:value},",
|
|
2971
|
-
"});"
|
|
2972
|
-
],
|
|
2973
|
-
description: "Create a reactive global store"
|
|
2974
|
-
},
|
|
2975
|
-
"SibuJS Lazy Component": {
|
|
2976
|
-
prefix: "sibu-lazy",
|
|
2977
|
-
body: ["const ${1:LazyComponent} = lazy(() => import('./${2:ComponentPath}'));"],
|
|
2978
|
-
description: "Create a lazy-loaded component"
|
|
2979
|
-
},
|
|
2980
|
-
"SibuJS Suspense": {
|
|
2981
|
-
prefix: "sibu-suspense",
|
|
2982
|
-
body: [
|
|
2983
|
-
"Suspense({",
|
|
2984
|
-
" nodes: () => ${1:LazyComponent}(),",
|
|
2985
|
-
" fallback: () => div({ nodes: '${2:Loading...}' }),",
|
|
2986
|
-
"})"
|
|
2987
|
-
],
|
|
2988
|
-
description: "Wrap a lazy component with a loading fallback"
|
|
2989
|
-
},
|
|
2990
|
-
"SibuJS onMount": {
|
|
2991
|
-
prefix: "sibu-mount",
|
|
2992
|
-
body: ["onMount(() => {", " ${1:// runs after component enters the DOM}", "});"],
|
|
2993
|
-
description: "Schedule a callback to run after mount"
|
|
2994
|
-
},
|
|
2995
|
-
"SibuJS onUnmount": {
|
|
2996
|
-
prefix: "sibu-unmount",
|
|
2997
|
-
body: ["onUnmount(() => {", " ${1:// cleanup when removed from DOM}", "}, ${2:element});"],
|
|
2998
|
-
description: "Schedule a callback to run on unmount"
|
|
2999
|
-
},
|
|
3000
|
-
"SibuJS Event Handler": {
|
|
3001
|
-
prefix: "sibu-event",
|
|
3002
|
-
body: [
|
|
3003
|
-
"div({",
|
|
3004
|
-
" on: {",
|
|
3005
|
-
" ${1:click}: (e) => {",
|
|
3006
|
-
" ${2:// handle event}",
|
|
3007
|
-
" },",
|
|
3008
|
-
" },",
|
|
3009
|
-
" nodes: '${3:Click me}',",
|
|
3010
|
-
"})"
|
|
3011
|
-
],
|
|
3012
|
-
description: "Create an element with event handlers"
|
|
3013
|
-
},
|
|
3014
|
-
"SibuJS Form": {
|
|
3015
|
-
prefix: "sibu-form",
|
|
3016
|
-
body: [
|
|
3017
|
-
"const form = form({",
|
|
3018
|
-
" ${1:fieldName}: {",
|
|
3019
|
-
" initial: '${2:}',",
|
|
3020
|
-
" validators: [",
|
|
3021
|
-
" (value) => !value ? '${3:Required}' : null,",
|
|
3022
|
-
" ],",
|
|
3023
|
-
" },",
|
|
3024
|
-
"});"
|
|
3025
|
-
],
|
|
3026
|
-
description: "Create a reactive form with validation"
|
|
2854
|
+
`;
|
|
2855
|
+
|
|
2856
|
+
// src/build/routeSplitting.ts
|
|
2857
|
+
function fileToRoute(relativePath) {
|
|
2858
|
+
const basename = relativePath.split("/").pop() || "";
|
|
2859
|
+
if (basename.startsWith("_")) return null;
|
|
2860
|
+
let route = relativePath.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
2861
|
+
route = route.replace(/\/index$/, "").replace(/^index$/, "");
|
|
2862
|
+
route = route.replace(/\[\.\.\.(\w+)\]/g, "*");
|
|
2863
|
+
route = route.replace(/\[(\w+)\]/g, ":$1");
|
|
2864
|
+
return `/${route}`;
|
|
2865
|
+
}
|
|
2866
|
+
async function scanRoutes(routesDir, exclude, fs) {
|
|
2867
|
+
const entries = await fs.readdir(routesDir, { withFileTypes: true, recursive: true });
|
|
2868
|
+
const files = [];
|
|
2869
|
+
for (const entry of entries) {
|
|
2870
|
+
if (!entry.isFile()) continue;
|
|
2871
|
+
if (!/\.(ts|tsx|js|jsx)$/.test(entry.name)) continue;
|
|
2872
|
+
const dir = (entry.parentPath || entry.path || routesDir).replace(/\\/g, "/");
|
|
2873
|
+
const routesDirNorm = routesDir.replace(/\\/g, "/");
|
|
2874
|
+
const relative = `${dir}/${entry.name}`.replace(`${routesDirNorm}/`, "").replace(/\\/g, "/");
|
|
2875
|
+
const excluded = exclude.some((pattern) => {
|
|
2876
|
+
const regex = pattern.replace(/\*/g, ".*");
|
|
2877
|
+
return new RegExp(regex).test(relative);
|
|
2878
|
+
});
|
|
2879
|
+
if (!excluded) {
|
|
2880
|
+
files.push(relative);
|
|
3027
2881
|
}
|
|
3028
|
-
}
|
|
2882
|
+
}
|
|
2883
|
+
return files.sort();
|
|
3029
2884
|
}
|
|
3030
|
-
function
|
|
2885
|
+
function buildRouteEntries(files, chunkPrefix) {
|
|
2886
|
+
const entries = [];
|
|
2887
|
+
for (const file of files) {
|
|
2888
|
+
const path = fileToRoute(file);
|
|
2889
|
+
if (path === null) continue;
|
|
2890
|
+
const chunkName = `${chunkPrefix}${file.replace(/\.(ts|tsx|js|jsx)$/, "").replace(/[/\\[\].]/g, "-").replace(/^-+|-+$/g, "").toLowerCase()}`;
|
|
2891
|
+
entries.push({
|
|
2892
|
+
path,
|
|
2893
|
+
importPath: file,
|
|
2894
|
+
chunkName,
|
|
2895
|
+
isDynamic: path.includes(":") || path.includes("*")
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
return entries.sort((a, b) => {
|
|
2899
|
+
if (a.path.includes("*") && !b.path.includes("*")) return 1;
|
|
2900
|
+
if (!a.path.includes("*") && b.path.includes("*")) return -1;
|
|
2901
|
+
if (a.isDynamic && !b.isDynamic) return 1;
|
|
2902
|
+
if (!a.isDynamic && b.isDynamic) return -1;
|
|
2903
|
+
return a.path.localeCompare(b.path);
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2906
|
+
function generateRouteModule(entries, routesDir) {
|
|
2907
|
+
const imports = entries.map(
|
|
2908
|
+
(e, i) => `const route${i} = () => import(/* webpackChunkName: "${e.chunkName}" */ "${routesDir}/${e.importPath}");`
|
|
2909
|
+
).join("\n");
|
|
2910
|
+
const routes = entries.map((e, i) => ` { path: "${e.path}", component: lazy(route${i}) }`).join(",\n");
|
|
2911
|
+
return `import { lazy } from "sibujs";
|
|
2912
|
+
|
|
2913
|
+
${imports}
|
|
2914
|
+
|
|
2915
|
+
export const routes = [
|
|
2916
|
+
${routes}
|
|
2917
|
+
];
|
|
2918
|
+
`;
|
|
2919
|
+
}
|
|
2920
|
+
var VIRTUAL_ID = "virtual:sibu-routes";
|
|
2921
|
+
var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
|
|
2922
|
+
function sibuRouteSplitting(options = {}) {
|
|
2923
|
+
const { routesDir = "src/routes", exclude = [], chunkPrefix = "route-" } = options;
|
|
2924
|
+
let projectRoot = "";
|
|
3031
2925
|
return {
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
2926
|
+
name: "sibu-route-splitting",
|
|
2927
|
+
enforce: "pre",
|
|
2928
|
+
resolveId(id) {
|
|
2929
|
+
if (id === VIRTUAL_ID) {
|
|
2930
|
+
return RESOLVED_VIRTUAL_ID;
|
|
2931
|
+
}
|
|
2932
|
+
return void 0;
|
|
3035
2933
|
},
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
{
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
2934
|
+
async load(id) {
|
|
2935
|
+
if (id !== RESOLVED_VIRTUAL_ID) return void 0;
|
|
2936
|
+
const { readdir } = await import("fs/promises");
|
|
2937
|
+
const { resolve } = await import("path");
|
|
2938
|
+
if (!projectRoot) {
|
|
2939
|
+
projectRoot = process.cwd();
|
|
2940
|
+
}
|
|
2941
|
+
const fullRoutesDir = resolve(projectRoot, routesDir);
|
|
2942
|
+
try {
|
|
2943
|
+
const files = await scanRoutes(fullRoutesDir, exclude, {
|
|
2944
|
+
readdir
|
|
2945
|
+
});
|
|
2946
|
+
const entries = buildRouteEntries(files, chunkPrefix);
|
|
2947
|
+
return generateRouteModule(entries, routesDir.startsWith("/") ? routesDir : `./${routesDir}`);
|
|
2948
|
+
} catch {
|
|
2949
|
+
return "export const routes = [];\n";
|
|
2950
|
+
}
|
|
2951
|
+
},
|
|
2952
|
+
handleHotUpdate(ctx) {
|
|
2953
|
+
const normalizedRoutesDir = routesDir.replace(/\\/g, "/");
|
|
2954
|
+
if (ctx.file.replace(/\\/g, "/").includes(normalizedRoutesDir)) {
|
|
2955
|
+
const mod = ctx.server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID);
|
|
2956
|
+
if (mod) {
|
|
2957
|
+
ctx.server.moduleGraph.invalidateModule(mod);
|
|
2958
|
+
ctx.server.ws.send({ type: "full-reload" });
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
3051
2962
|
};
|
|
3052
2963
|
}
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
2964
|
+
|
|
2965
|
+
// src/build/staticAnalysis.ts
|
|
2966
|
+
var STATIC_TAGS = /* @__PURE__ */ new Set([
|
|
2967
|
+
"div",
|
|
2968
|
+
"span",
|
|
2969
|
+
"section",
|
|
2970
|
+
"article",
|
|
2971
|
+
"header",
|
|
2972
|
+
"footer",
|
|
2973
|
+
"nav",
|
|
2974
|
+
"main",
|
|
2975
|
+
"aside",
|
|
2976
|
+
"p",
|
|
2977
|
+
"h1",
|
|
2978
|
+
"h2",
|
|
2979
|
+
"h3",
|
|
2980
|
+
"h4",
|
|
2981
|
+
"h5",
|
|
2982
|
+
"h6",
|
|
2983
|
+
"blockquote",
|
|
2984
|
+
"li",
|
|
2985
|
+
"ol",
|
|
2986
|
+
"ul",
|
|
2987
|
+
"a",
|
|
2988
|
+
"b",
|
|
2989
|
+
"em",
|
|
2990
|
+
"i",
|
|
2991
|
+
"strong",
|
|
2992
|
+
"small",
|
|
2993
|
+
"code",
|
|
2994
|
+
"mark",
|
|
2995
|
+
"img",
|
|
2996
|
+
"br",
|
|
2997
|
+
"hr",
|
|
2998
|
+
"input",
|
|
2999
|
+
"button",
|
|
3000
|
+
"form",
|
|
3001
|
+
"label",
|
|
3002
|
+
"details",
|
|
3003
|
+
"summary",
|
|
3004
|
+
"dialog"
|
|
3005
|
+
]);
|
|
3006
|
+
var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
3007
|
+
"br",
|
|
3008
|
+
"hr",
|
|
3009
|
+
"img",
|
|
3010
|
+
"input",
|
|
3011
|
+
"meta",
|
|
3012
|
+
"link",
|
|
3013
|
+
"area",
|
|
3014
|
+
"base",
|
|
3015
|
+
"col",
|
|
3016
|
+
"embed",
|
|
3017
|
+
"param",
|
|
3018
|
+
"source",
|
|
3019
|
+
"track",
|
|
3020
|
+
"wbr"
|
|
3021
|
+
]);
|
|
3022
|
+
var SPECIAL_PROPS = /* @__PURE__ */ new Set(["style", "ref", "on", "onElement"]);
|
|
3023
|
+
var NON_REFLECTING_BOOLEANS = /* @__PURE__ */ new Set(["checked", "selected"]);
|
|
3024
|
+
function readLiteral(code, tok) {
|
|
3025
|
+
if (!tok) return null;
|
|
3026
|
+
if (tok.type === "string") {
|
|
3027
|
+
const value = stringTokenValue(code, tok);
|
|
3028
|
+
return value === null ? null : { kind: "string", value };
|
|
3029
|
+
}
|
|
3030
|
+
if (tok.type === "number" && /^(0|[1-9]\d*)(\.\d+)?$/.test(tok.value)) {
|
|
3031
|
+
return { kind: "number", value: String(Number(tok.value)) };
|
|
3032
|
+
}
|
|
3033
|
+
if (tok.type === "ident" && ["true", "false", "null", "undefined"].includes(tok.value)) {
|
|
3034
|
+
return { kind: "keyword", value: tok.value };
|
|
3035
|
+
}
|
|
3036
|
+
return null;
|
|
3037
|
+
}
|
|
3038
|
+
function parserSafeText(s) {
|
|
3039
|
+
return !s.includes("\r") && !s.includes("\0");
|
|
3040
|
+
}
|
|
3041
|
+
function escapeHtml(str) {
|
|
3042
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3043
|
+
}
|
|
3044
|
+
function escapeAttr(str) {
|
|
3045
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
3046
|
+
}
|
|
3047
|
+
function attributeHtml(name, lit) {
|
|
3048
|
+
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) return null;
|
|
3049
|
+
const lower = name.toLowerCase();
|
|
3050
|
+
if (SPECIAL_PROPS.has(name) || lower === "style" || lower === "srcset") return null;
|
|
3051
|
+
if (isEventHandlerAttr(name) || isUrlAttribute(name) || isHtmlContentAttribute(name)) return null;
|
|
3052
|
+
if (lit.kind === "keyword") {
|
|
3053
|
+
if (lit.value === "null" || lit.value === "undefined") return "";
|
|
3054
|
+
const on = lit.value === "true";
|
|
3055
|
+
if (lower.startsWith("aria-")) return `${lower}="${on}"`;
|
|
3056
|
+
if (NON_REFLECTING_BOOLEANS.has(lower)) return null;
|
|
3057
|
+
return on ? `${lower}=""` : "";
|
|
3058
|
+
}
|
|
3059
|
+
if (!parserSafeText(lit.value)) return null;
|
|
3060
|
+
return `${lower}="${escapeAttr(lit.value)}"`;
|
|
3061
|
+
}
|
|
3062
|
+
function parseCall(code, list, i) {
|
|
3063
|
+
const at = (k2) => list[k2];
|
|
3064
|
+
const isPunct = (k2, v) => {
|
|
3065
|
+
const t = at(k2);
|
|
3066
|
+
return !!t && t.type === "punct" && t.value === v;
|
|
3118
3067
|
};
|
|
3068
|
+
let k = i + 1;
|
|
3069
|
+
const result = { end: 0, cls: null, id: null, attrs: [], text: null };
|
|
3070
|
+
let nodesText = null;
|
|
3071
|
+
if (isPunct(k, "{")) {
|
|
3072
|
+
k++;
|
|
3073
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3074
|
+
while (!isPunct(k, "}")) {
|
|
3075
|
+
const keyTok = at(k);
|
|
3076
|
+
let key = null;
|
|
3077
|
+
if (keyTok?.type === "ident") key = keyTok.value;
|
|
3078
|
+
else if (keyTok?.type === "string") key = stringTokenValue(code, keyTok);
|
|
3079
|
+
if (key === null || !isPunct(k + 1, ":")) return null;
|
|
3080
|
+
if (seen.has(key.toLowerCase())) return null;
|
|
3081
|
+
seen.add(key.toLowerCase());
|
|
3082
|
+
const lit = readLiteral(code, at(k + 2));
|
|
3083
|
+
if (lit === null) return null;
|
|
3084
|
+
k += 3;
|
|
3085
|
+
if (isPunct(k, ",")) k++;
|
|
3086
|
+
else if (!isPunct(k, "}")) return null;
|
|
3087
|
+
if (key === "class" || key === "id") {
|
|
3088
|
+
if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
|
|
3089
|
+
if (key === "class") result.cls = lit.value;
|
|
3090
|
+
else result.id = lit.value;
|
|
3091
|
+
} else if (key === "nodes") {
|
|
3092
|
+
if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
|
|
3093
|
+
nodesText = lit.value;
|
|
3094
|
+
} else {
|
|
3095
|
+
const html = attributeHtml(key, lit);
|
|
3096
|
+
if (html === null) return null;
|
|
3097
|
+
if (html) result.attrs.push(html);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
k++;
|
|
3101
|
+
if (isPunct(k, ",")) {
|
|
3102
|
+
const second = readLiteral(code, at(k + 1));
|
|
3103
|
+
if (second === null) {
|
|
3104
|
+
if (!isPunct(k + 1, ")")) return null;
|
|
3105
|
+
k++;
|
|
3106
|
+
} else {
|
|
3107
|
+
if (second.kind !== "string" || !parserSafeText(second.value)) return null;
|
|
3108
|
+
result.text = second.value;
|
|
3109
|
+
k += 2;
|
|
3110
|
+
if (isPunct(k, ",")) k++;
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
if (result.text === null) result.text = nodesText;
|
|
3114
|
+
} else {
|
|
3115
|
+
const first = readLiteral(code, at(k));
|
|
3116
|
+
if (first === null || first.kind !== "string" || !parserSafeText(first.value)) return null;
|
|
3117
|
+
k++;
|
|
3118
|
+
if (isPunct(k, ",") && at(k + 1)?.type === "string") {
|
|
3119
|
+
const second = readLiteral(code, at(k + 1));
|
|
3120
|
+
if (second === null || second.kind !== "string" || !parserSafeText(second.value)) return null;
|
|
3121
|
+
result.cls = first.value;
|
|
3122
|
+
result.text = second.value;
|
|
3123
|
+
k += 2;
|
|
3124
|
+
} else {
|
|
3125
|
+
result.text = first.value;
|
|
3126
|
+
}
|
|
3127
|
+
if (isPunct(k, ",")) k++;
|
|
3128
|
+
}
|
|
3129
|
+
if (!isPunct(k, ")")) return null;
|
|
3130
|
+
result.end = at(k).end;
|
|
3131
|
+
return result;
|
|
3132
|
+
}
|
|
3133
|
+
function toHtml(tag, call) {
|
|
3134
|
+
const attrs = [];
|
|
3135
|
+
if (call.cls !== null) attrs.push(`class="${escapeAttr(call.cls)}"`);
|
|
3136
|
+
if (call.id !== null) attrs.push(`id="${escapeAttr(call.id)}"`);
|
|
3137
|
+
attrs.push(...call.attrs);
|
|
3138
|
+
const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
3139
|
+
if (VOID_ELEMENTS2.has(tag)) {
|
|
3140
|
+
if (call.text) return null;
|
|
3141
|
+
return `<${tag}${attrStr} />`;
|
|
3142
|
+
}
|
|
3143
|
+
return `<${tag}${attrStr}>${escapeHtml(call.text ?? "")}</${tag}>`;
|
|
3144
|
+
}
|
|
3145
|
+
function analyzeStaticTemplates(code) {
|
|
3146
|
+
const none = { hasStaticPatterns: false, patterns: [] };
|
|
3147
|
+
if (!code.includes("sibujs")) return none;
|
|
3148
|
+
const scan = scanModule(code);
|
|
3149
|
+
if (!scan) return none;
|
|
3150
|
+
const factories = /* @__PURE__ */ new Map();
|
|
3151
|
+
for (const decl of scan.imports) {
|
|
3152
|
+
if (decl.source !== "sibujs") continue;
|
|
3153
|
+
for (const b of decl.bindings) if (STATIC_TAGS.has(b.imported)) factories.set(b.local, b.imported);
|
|
3154
|
+
}
|
|
3155
|
+
if (factories.size === 0) return none;
|
|
3156
|
+
const uses = bindingUses(code, scan, new Set(factories.keys()));
|
|
3157
|
+
const unsafe = new Set(uses.shadowed);
|
|
3158
|
+
for (const ref of uses.tagged) unsafe.add(ref.tok.value);
|
|
3159
|
+
const patterns = [];
|
|
3160
|
+
for (const { tok, list, index } of uses.calls) {
|
|
3161
|
+
if (unsafe.has(tok.value)) continue;
|
|
3162
|
+
const tag = factories.get(tok.value);
|
|
3163
|
+
const call = parseCall(code, list, index + 1);
|
|
3164
|
+
if (!call) continue;
|
|
3165
|
+
const templateHtml = toHtml(tag, call);
|
|
3166
|
+
if (templateHtml === null) continue;
|
|
3167
|
+
const start = list[index].start;
|
|
3168
|
+
patterns.push({ original: code.slice(start, call.end), tag, templateHtml, start, end: call.end });
|
|
3169
|
+
}
|
|
3170
|
+
return { hasStaticPatterns: patterns.length > 0, patterns };
|
|
3119
3171
|
}
|
|
3120
3172
|
|
|
3121
|
-
// src/build/
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3173
|
+
// src/build/vite.ts
|
|
3174
|
+
var DEFAULT_INCLUDE = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"];
|
|
3175
|
+
var DEFAULT_EXCLUDE = ["node_modules/**", "dist/**", "**/*.test.*", "**/*.spec.*"];
|
|
3176
|
+
function matchesPattern(filePath, patterns) {
|
|
3177
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
3178
|
+
return patterns.some((pattern) => {
|
|
3179
|
+
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
3180
|
+
return new RegExp(regexStr).test(normalized);
|
|
3181
|
+
});
|
|
3182
|
+
}
|
|
3183
|
+
function devPrologueEdit(code) {
|
|
3184
|
+
let at = 0;
|
|
3185
|
+
if (code.startsWith("#!")) {
|
|
3186
|
+
const nl = code.indexOf("\n");
|
|
3187
|
+
if (nl === -1) return null;
|
|
3188
|
+
at = nl + 1;
|
|
3189
|
+
}
|
|
3125
3190
|
return {
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
compiledCount: edits ? plan.compiledCount : 0,
|
|
3130
|
-
skippedCount: plan.skippedCount
|
|
3191
|
+
start: at,
|
|
3192
|
+
end: at,
|
|
3193
|
+
text: '/* SibuJS Dev Mode */ if (typeof globalThis !== "undefined") { globalThis.__SIBU_DEV__ = true; } '
|
|
3131
3194
|
};
|
|
3132
3195
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
const
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3196
|
+
function staticOptimizationEdits(code) {
|
|
3197
|
+
const analysis = analyzeStaticTemplates(code);
|
|
3198
|
+
if (!analysis.hasStaticPatterns) return null;
|
|
3199
|
+
const P = uniquePrefix(code);
|
|
3200
|
+
const edits = analysis.patterns.map((pattern) => ({
|
|
3201
|
+
start: pattern.start,
|
|
3202
|
+
end: pattern.end,
|
|
3203
|
+
// A call spanning several lines keeps its line breaks.
|
|
3204
|
+
text: `${P}static(${jsStringLiteral(pattern.templateHtml)}${lineBreaksIn(code, pattern.start, pattern.end)})`
|
|
3205
|
+
}));
|
|
3206
|
+
const append = `
|
|
3207
|
+
import { staticTemplate as ${P}staticTemplate } from "sibujs/performance";
|
|
3208
|
+
function ${P}static(markup) {
|
|
3209
|
+
return document.adoptNode(${P}staticTemplate(markup));
|
|
3143
3210
|
}
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3211
|
+
`;
|
|
3212
|
+
return { edits, append };
|
|
3213
|
+
}
|
|
3214
|
+
function resolveDevMode(explicit, info) {
|
|
3215
|
+
if (explicit !== void 0) return explicit;
|
|
3216
|
+
if (info?.command === "build") return info.mode === "development";
|
|
3217
|
+
if (info?.command === "serve") return true;
|
|
3218
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
3219
|
+
}
|
|
3220
|
+
function sibuVitePlugin(options = {}) {
|
|
3221
|
+
const {
|
|
3222
|
+
hmr = true,
|
|
3223
|
+
pureAnnotations = true,
|
|
3224
|
+
include = DEFAULT_INCLUDE,
|
|
3225
|
+
exclude = DEFAULT_EXCLUDE,
|
|
3226
|
+
devMode,
|
|
3227
|
+
staticOptimize = false,
|
|
3228
|
+
compileTemplates,
|
|
3229
|
+
prerender: prerenderOptions
|
|
3230
|
+
} = options;
|
|
3231
|
+
let prerenderTarget = null;
|
|
3232
|
+
let isDevMode = resolveDevMode(devMode, void 0);
|
|
3233
|
+
return {
|
|
3234
|
+
name: "sibu-vite-plugin",
|
|
3235
|
+
enforce: "pre",
|
|
3236
|
+
config(_userConfig, env) {
|
|
3237
|
+
isDevMode = resolveDevMode(devMode, env);
|
|
3238
|
+
return {
|
|
3239
|
+
// Optimize dependency pre-bundling for sibujs
|
|
3240
|
+
optimizeDeps: {
|
|
3241
|
+
include: ["sibujs"]
|
|
3242
|
+
},
|
|
3243
|
+
// Ensure sibujs is treated correctly for SSR
|
|
3244
|
+
ssr: {
|
|
3245
|
+
noExternal: ["sibujs"]
|
|
3246
|
+
},
|
|
3247
|
+
// Define global constants for dead code elimination
|
|
3248
|
+
define: {
|
|
3249
|
+
__SIBU_DEV__: JSON.stringify(isDevMode),
|
|
3250
|
+
__SIBU_HMR__: JSON.stringify(hmr)
|
|
3251
|
+
},
|
|
3252
|
+
// Enable source maps in dev
|
|
3253
|
+
build: {
|
|
3254
|
+
sourcemap: isDevMode
|
|
3255
|
+
}
|
|
3256
|
+
};
|
|
3257
|
+
},
|
|
3258
|
+
configResolved(config) {
|
|
3259
|
+
isDevMode = resolveDevMode(devMode, config);
|
|
3260
|
+
if (prerenderOptions && config.command === "build" && !config.build?.ssr && typeof process !== "undefined" && !process.env?.[PRERENDER_CHILD_ENV] && config.root) {
|
|
3261
|
+
const outDir = config.build?.outDir ?? "dist";
|
|
3262
|
+
const absolute = /^([a-zA-Z]:)?[\\/]/.test(outDir);
|
|
3263
|
+
prerenderTarget = {
|
|
3264
|
+
root: config.root,
|
|
3265
|
+
outDir: absolute ? outDir : `${config.root.replace(/[\\/]+$/, "")}/${outDir}`,
|
|
3266
|
+
configFile: config.configFile,
|
|
3267
|
+
mode: config.mode
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
async closeBundle() {
|
|
3272
|
+
if (!prerenderOptions || !prerenderTarget) return;
|
|
3273
|
+
const target = prerenderTarget;
|
|
3274
|
+
prerenderTarget = null;
|
|
3275
|
+
const results = await prerender(prerenderOptions, target);
|
|
3276
|
+
for (const r of results) console.log(`[sibu prerender] ${r.route} \u2192 ${r.file} (${r.ms} ms)`);
|
|
3277
|
+
},
|
|
3278
|
+
transform(code, id) {
|
|
3279
|
+
if (!matchesPattern(id, include) || matchesPattern(id, exclude)) {
|
|
3280
|
+
return null;
|
|
3281
|
+
}
|
|
3282
|
+
if (!code.includes("sibujs")) return null;
|
|
3283
|
+
if (!scanModule(code)) return null;
|
|
3284
|
+
const edits = [];
|
|
3285
|
+
let append = "";
|
|
3286
|
+
if (isDevMode && importsSibu(code)) {
|
|
3287
|
+
const prologue = devPrologueEdit(code);
|
|
3288
|
+
if (prologue) edits.push(prologue);
|
|
3289
|
+
}
|
|
3290
|
+
if (pureAnnotations) {
|
|
3291
|
+
edits.push(...pureAnnotationEdits(code));
|
|
3292
|
+
}
|
|
3293
|
+
const shouldCompile = compileTemplates ?? !isDevMode;
|
|
3294
|
+
if (shouldCompile) {
|
|
3295
|
+
const plan = planHtmlTemplates(code);
|
|
3296
|
+
if (plan.compiledCount > 0) {
|
|
3297
|
+
edits.push(...plan.edits);
|
|
3298
|
+
append += plan.append;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
if (staticOptimize) {
|
|
3302
|
+
const optimized = staticOptimizationEdits(code);
|
|
3303
|
+
if (optimized) {
|
|
3304
|
+
edits.push(...optimized.edits);
|
|
3305
|
+
append += optimized.append;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
if (edits.length === 0 && append === "") return null;
|
|
3309
|
+
const sorted = normalizeEdits(edits);
|
|
3310
|
+
if (!sorted) return null;
|
|
3311
|
+
const transformed = applyEdits(code, sorted, append);
|
|
3312
|
+
if (transformed === code) return null;
|
|
3313
|
+
return {
|
|
3314
|
+
code: transformed,
|
|
3315
|
+
map: editsSourceMap(code, sorted, append, id)
|
|
3316
|
+
};
|
|
3317
|
+
},
|
|
3318
|
+
handleHotUpdate(ctx) {
|
|
3319
|
+
if (!hmr) return;
|
|
3320
|
+
const { file } = ctx;
|
|
3321
|
+
if (matchesPattern(file, include) && !matchesPattern(file, exclude)) {
|
|
3322
|
+
if (isDevMode) {
|
|
3323
|
+
console.log(`[sibu-vite-plugin] HMR update: ${file}`);
|
|
3324
|
+
}
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
function createViteConfig(options = {}) {
|
|
3331
|
+
const { entry = "src/main.ts", outDir = "dist", ssr = false, overrides = {} } = options;
|
|
3332
|
+
const baseConfig = {
|
|
3333
|
+
// Use the SibuJS Vite plugin
|
|
3334
|
+
plugins: [sibuVitePlugin()],
|
|
3335
|
+
// Build configuration
|
|
3336
|
+
build: {
|
|
3337
|
+
outDir,
|
|
3338
|
+
target: "es2020",
|
|
3339
|
+
minify: "esbuild",
|
|
3340
|
+
sourcemap: true,
|
|
3341
|
+
// Library mode configuration when building a library
|
|
3342
|
+
lib: ssr ? void 0 : {
|
|
3343
|
+
entry,
|
|
3344
|
+
formats: ["es", "cjs"]
|
|
3345
|
+
},
|
|
3346
|
+
// Rollup-specific options
|
|
3347
|
+
rollupOptions: {
|
|
3348
|
+
input: ssr ? entry : void 0,
|
|
3349
|
+
output: {
|
|
3350
|
+
// Ensure consistent chunk naming
|
|
3351
|
+
chunkFileNames: "chunks/[name]-[hash].js",
|
|
3352
|
+
// Preserve pure annotations
|
|
3353
|
+
generatedCode: {
|
|
3354
|
+
constBindings: true
|
|
3355
|
+
}
|
|
3356
|
+
},
|
|
3357
|
+
// Tree-shaking configuration
|
|
3358
|
+
treeshake: {
|
|
3359
|
+
moduleSideEffects: false,
|
|
3360
|
+
propertyReadSideEffects: false,
|
|
3361
|
+
annotations: true
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
},
|
|
3365
|
+
// Resolve configuration
|
|
3366
|
+
resolve: {
|
|
3367
|
+
// Prefer ESM versions of packages
|
|
3368
|
+
mainFields: ["module", "jsnext:main", "jsnext", "main"],
|
|
3369
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
|
|
3370
|
+
},
|
|
3371
|
+
// SSR configuration
|
|
3372
|
+
...ssr ? {
|
|
3373
|
+
ssr: {
|
|
3374
|
+
noExternal: ["sibujs"],
|
|
3375
|
+
target: "node"
|
|
3376
|
+
},
|
|
3377
|
+
build: {
|
|
3378
|
+
outDir,
|
|
3379
|
+
target: "node18",
|
|
3380
|
+
ssr: true,
|
|
3381
|
+
rollupOptions: {
|
|
3382
|
+
input: entry,
|
|
3383
|
+
output: {
|
|
3384
|
+
format: "esm"
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
} : {},
|
|
3389
|
+
// Optimize dependency handling
|
|
3390
|
+
optimizeDeps: {
|
|
3391
|
+
include: ["sibujs"],
|
|
3392
|
+
// Force pre-bundling of sibujs for faster dev startup
|
|
3393
|
+
force: false
|
|
3394
|
+
},
|
|
3395
|
+
// Environment variable handling
|
|
3396
|
+
define: {
|
|
3397
|
+
__SIBU_SSR__: JSON.stringify(ssr)
|
|
3159
3398
|
}
|
|
3160
|
-
}
|
|
3161
|
-
return
|
|
3399
|
+
};
|
|
3400
|
+
return deepMerge(baseConfig, overrides);
|
|
3162
3401
|
}
|
|
3163
|
-
function
|
|
3164
|
-
const
|
|
3165
|
-
for (const
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
importPath: file,
|
|
3172
|
-
chunkName,
|
|
3173
|
-
isDynamic: path.includes(":") || path.includes("*")
|
|
3174
|
-
});
|
|
3402
|
+
function deepMerge(target, source) {
|
|
3403
|
+
const result = { ...target };
|
|
3404
|
+
for (const key of Object.keys(source)) {
|
|
3405
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
|
|
3406
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
3407
|
+
} else {
|
|
3408
|
+
result[key] = source[key];
|
|
3409
|
+
}
|
|
3175
3410
|
}
|
|
3176
|
-
return
|
|
3177
|
-
if (a.path.includes("*") && !b.path.includes("*")) return 1;
|
|
3178
|
-
if (!a.path.includes("*") && b.path.includes("*")) return -1;
|
|
3179
|
-
if (a.isDynamic && !b.isDynamic) return 1;
|
|
3180
|
-
if (!a.isDynamic && b.isDynamic) return -1;
|
|
3181
|
-
return a.path.localeCompare(b.path);
|
|
3182
|
-
});
|
|
3411
|
+
return result;
|
|
3183
3412
|
}
|
|
3184
|
-
function generateRouteModule(entries, routesDir) {
|
|
3185
|
-
const imports = entries.map(
|
|
3186
|
-
(e, i) => `const route${i} = () => import(/* webpackChunkName: "${e.chunkName}" */ "${routesDir}/${e.importPath}");`
|
|
3187
|
-
).join("\n");
|
|
3188
|
-
const routes = entries.map((e, i) => ` { path: "${e.path}", component: lazy(route${i}) }`).join(",\n");
|
|
3189
|
-
return `import { lazy } from "sibujs";
|
|
3190
|
-
|
|
3191
|
-
${imports}
|
|
3192
3413
|
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
`;
|
|
3414
|
+
// src/build/webpack.ts
|
|
3415
|
+
function addPureAnnotations(source) {
|
|
3416
|
+
return injectPureAnnotations(source);
|
|
3197
3417
|
}
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
function sibuRouteSplitting(options = {}) {
|
|
3201
|
-
const { routesDir = "src/routes", exclude = [], chunkPrefix = "route-" } = options;
|
|
3202
|
-
let projectRoot = "";
|
|
3418
|
+
function sibuWebpackPlugin(options = {}) {
|
|
3419
|
+
const { devMode } = options;
|
|
3203
3420
|
return {
|
|
3204
|
-
name: "
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
return void 0;
|
|
3211
|
-
},
|
|
3212
|
-
async load(id) {
|
|
3213
|
-
if (id !== RESOLVED_VIRTUAL_ID) return void 0;
|
|
3214
|
-
const { readdir } = await import("fs/promises");
|
|
3215
|
-
const { resolve } = await import("path");
|
|
3216
|
-
if (!projectRoot) {
|
|
3217
|
-
projectRoot = process.cwd();
|
|
3421
|
+
name: "SibuWebpackPlugin",
|
|
3422
|
+
apply(compiler) {
|
|
3423
|
+
if (options.pureAnnotations === true) {
|
|
3424
|
+
console.warn(
|
|
3425
|
+
'[SibuWebpackPlugin] `pureAnnotations` is not applied by the plugin: webpack needs a loader file. Create sibu-pure-loader.cjs containing `module.exports = require("sibujs/build").createPureAnnotationsLoader();` and add a module rule `{ test: /\\.[cm]?[jt]sx?$/, exclude: /node_modules/, use: "./sibu-pure-loader.cjs" }`.'
|
|
3426
|
+
);
|
|
3218
3427
|
}
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3428
|
+
compiler.hooks?.compilation?.tap("SibuWebpackPlugin", (compilation) => {
|
|
3429
|
+
const comp = compilation;
|
|
3430
|
+
if (comp.hooks?.optimizeModules) {
|
|
3431
|
+
comp.hooks.optimizeModules.tap("SibuWebpackPlugin", () => {
|
|
3432
|
+
});
|
|
3433
|
+
}
|
|
3434
|
+
});
|
|
3435
|
+
compiler.hooks?.afterResolvers?.tap("SibuWebpackPlugin", () => {
|
|
3436
|
+
if (!compiler.options.resolve) {
|
|
3437
|
+
compiler.options.resolve = {};
|
|
3438
|
+
}
|
|
3439
|
+
if (!compiler.options.resolve.mainFields) {
|
|
3440
|
+
compiler.options.resolve.mainFields = ["module", "main"];
|
|
3441
|
+
}
|
|
3442
|
+
if (!compiler.options.resolve.mainFields.includes("module")) {
|
|
3443
|
+
compiler.options.resolve.mainFields.unshift("module");
|
|
3444
|
+
}
|
|
3445
|
+
});
|
|
3446
|
+
const tapDoneLogger = () => compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
|
|
3447
|
+
const statsObj = stats;
|
|
3448
|
+
const info = statsObj?.toJson?.({ modules: false, chunks: false });
|
|
3449
|
+
if (info) {
|
|
3450
|
+
console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
|
|
3451
|
+
const warnings = info.warnings;
|
|
3452
|
+
if (warnings?.length) {
|
|
3453
|
+
console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
});
|
|
3457
|
+
if (devMode === true) tapDoneLogger();
|
|
3458
|
+
compiler.hooks?.environment?.tap("SibuWebpackPlugin", () => {
|
|
3459
|
+
const isDevMode = devMode ?? resolveWebpackDevMode(compiler.options);
|
|
3460
|
+
if (devMode === void 0 && isDevMode) tapDoneLogger();
|
|
3461
|
+
if (!compiler.options.plugins) {
|
|
3462
|
+
compiler.options.plugins = [];
|
|
3463
|
+
}
|
|
3464
|
+
const defines = {
|
|
3465
|
+
__SIBU_DEV__: JSON.stringify(isDevMode)
|
|
3466
|
+
};
|
|
3467
|
+
compiler.__sibuDefines = defines;
|
|
3468
|
+
const DefinePlugin = compiler.webpack?.DefinePlugin;
|
|
3469
|
+
if (DefinePlugin) {
|
|
3470
|
+
new DefinePlugin(defines).apply(compiler);
|
|
3471
|
+
}
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
function resolveWebpackDevMode(options) {
|
|
3477
|
+
const mode = options.mode;
|
|
3478
|
+
if (mode === "development") return true;
|
|
3479
|
+
if (mode === "production" || mode === void 0) return false;
|
|
3480
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
3481
|
+
}
|
|
3482
|
+
function createPureAnnotationsLoader() {
|
|
3483
|
+
return function sibuPureAnnotationsLoader(source) {
|
|
3484
|
+
return addPureAnnotations(source);
|
|
3485
|
+
};
|
|
3486
|
+
}
|
|
3487
|
+
function createWebpackConfig(options = {}) {
|
|
3488
|
+
const { entry = "./src/index.ts", outputPath = "dist", mode = "production" } = options;
|
|
3489
|
+
const isDev = mode === "development";
|
|
3490
|
+
return {
|
|
3491
|
+
mode,
|
|
3492
|
+
entry,
|
|
3493
|
+
output: {
|
|
3494
|
+
path: outputPath,
|
|
3495
|
+
filename: isDev ? "[name].js" : "[name].[contenthash:8].js",
|
|
3496
|
+
chunkFilename: isDev ? "[name].chunk.js" : "[name].[contenthash:8].chunk.js",
|
|
3497
|
+
clean: true,
|
|
3498
|
+
// Use ESM output
|
|
3499
|
+
module: true,
|
|
3500
|
+
library: {
|
|
3501
|
+
type: "module"
|
|
3228
3502
|
}
|
|
3229
3503
|
},
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3504
|
+
// Enable experiments for ESM output
|
|
3505
|
+
experiments: {
|
|
3506
|
+
outputModule: true
|
|
3507
|
+
},
|
|
3508
|
+
resolve: {
|
|
3509
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
|
|
3510
|
+
mainFields: ["module", "main"],
|
|
3511
|
+
alias: {}
|
|
3512
|
+
},
|
|
3513
|
+
module: {
|
|
3514
|
+
rules: [
|
|
3515
|
+
// TypeScript/JavaScript handling
|
|
3516
|
+
{
|
|
3517
|
+
test: /\.[jt]sx?$/,
|
|
3518
|
+
exclude: /node_modules/,
|
|
3519
|
+
use: [
|
|
3520
|
+
{
|
|
3521
|
+
// Users should configure their preferred TS loader
|
|
3522
|
+
// (ts-loader, babel-loader, esbuild-loader, swc-loader)
|
|
3523
|
+
loader: "ts-loader",
|
|
3524
|
+
options: {
|
|
3525
|
+
transpileOnly: true,
|
|
3526
|
+
compilerOptions: {
|
|
3527
|
+
module: "esnext",
|
|
3528
|
+
moduleResolution: "node",
|
|
3529
|
+
target: "es2020"
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
]
|
|
3534
|
+
}
|
|
3535
|
+
]
|
|
3536
|
+
},
|
|
3537
|
+
plugins: [
|
|
3538
|
+
// SibuJS plugin for optimizations
|
|
3539
|
+
sibuWebpackPlugin({ devMode: isDev })
|
|
3540
|
+
],
|
|
3541
|
+
optimization: {
|
|
3542
|
+
minimize: !isDev,
|
|
3543
|
+
// Enable tree-shaking
|
|
3544
|
+
usedExports: true,
|
|
3545
|
+
sideEffects: true,
|
|
3546
|
+
// Split chunks for better caching
|
|
3547
|
+
splitChunks: isDev ? false : {
|
|
3548
|
+
chunks: "all",
|
|
3549
|
+
cacheGroups: {
|
|
3550
|
+
// Separate sibujs framework code into its own chunk (the package
|
|
3551
|
+
// directory is `sibujs`; matching `sibu` never selected anything)
|
|
3552
|
+
sibu: {
|
|
3553
|
+
test: /[\\/]node_modules[\\/]sibujs[\\/]/,
|
|
3554
|
+
name: "sibu",
|
|
3555
|
+
chunks: "all",
|
|
3556
|
+
priority: 20
|
|
3557
|
+
},
|
|
3558
|
+
// Separate other vendor code
|
|
3559
|
+
vendor: {
|
|
3560
|
+
test: /[\\/]node_modules[\\/]/,
|
|
3561
|
+
name: "vendor",
|
|
3562
|
+
chunks: "all",
|
|
3563
|
+
priority: 10
|
|
3564
|
+
}
|
|
3237
3565
|
}
|
|
3238
3566
|
}
|
|
3567
|
+
},
|
|
3568
|
+
// Source maps
|
|
3569
|
+
devtool: isDev ? "eval-cheap-module-source-map" : "source-map",
|
|
3570
|
+
// Dev server configuration
|
|
3571
|
+
devServer: isDev ? {
|
|
3572
|
+
hot: true,
|
|
3573
|
+
open: true,
|
|
3574
|
+
port: 3e3,
|
|
3575
|
+
historyApiFallback: true
|
|
3576
|
+
} : void 0,
|
|
3577
|
+
// Performance hints
|
|
3578
|
+
performance: {
|
|
3579
|
+
hints: isDev ? false : "warning",
|
|
3580
|
+
maxEntrypointSize: 25e4,
|
|
3581
|
+
maxAssetSize: 25e4
|
|
3582
|
+
},
|
|
3583
|
+
// Cache for faster rebuilds
|
|
3584
|
+
cache: {
|
|
3585
|
+
type: "filesystem",
|
|
3586
|
+
buildDependencies: {
|
|
3587
|
+
config: []
|
|
3588
|
+
}
|
|
3239
3589
|
}
|
|
3240
3590
|
};
|
|
3241
3591
|
}
|
|
3242
3592
|
export {
|
|
3593
|
+
PRERENDER_CHILD_ENV,
|
|
3243
3594
|
analyzeBundle,
|
|
3244
3595
|
analyzeStaticTemplates,
|
|
3245
3596
|
buildRouteEntries,
|
|
@@ -3248,8 +3599,10 @@ export {
|
|
|
3248
3599
|
createPureAnnotationsLoader,
|
|
3249
3600
|
createViteConfig,
|
|
3250
3601
|
createWebpackConfig,
|
|
3602
|
+
esmEntryOf,
|
|
3251
3603
|
estimateImportSize,
|
|
3252
3604
|
fileToRoute,
|
|
3605
|
+
findEntryInHtml,
|
|
3253
3606
|
generateEslintConfig,
|
|
3254
3607
|
generateImportMap,
|
|
3255
3608
|
generateLanguageConfig,
|
|
@@ -3260,7 +3613,10 @@ export {
|
|
|
3260
3613
|
lintRules,
|
|
3261
3614
|
lintSource,
|
|
3262
3615
|
moduleSizes,
|
|
3616
|
+
outputFileFor,
|
|
3617
|
+
prerender,
|
|
3263
3618
|
registerGlobal,
|
|
3619
|
+
resolveInsideOutDir,
|
|
3264
3620
|
sibuRouteSplitting,
|
|
3265
3621
|
sibuVitePlugin,
|
|
3266
3622
|
sibuWebpackPlugin,
|