round-core 0.1.2 → 0.1.4
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/README.md +68 -18
- package/package.json +13 -8
- package/src/cli.js +609 -0
- package/src/compiler/index.js +2 -0
- package/src/compiler/transformer.js +610 -0
- package/src/compiler/vite-plugin.d.ts +8 -0
- package/src/compiler/vite-plugin.js +472 -0
- package/{dist → src}/index.d.ts +86 -19
- package/src/index.js +34 -0
- package/src/runtime/context-shared.js +63 -0
- package/src/runtime/context.js +70 -0
- package/src/runtime/dom.js +402 -0
- package/src/runtime/error-boundary.js +48 -0
- package/src/runtime/error-reporter.js +21 -0
- package/src/runtime/error-store.js +85 -0
- package/src/runtime/errors.js +152 -0
- package/src/runtime/lifecycle.js +140 -0
- package/src/runtime/router.js +475 -0
- package/src/runtime/signals.js +577 -0
- package/src/runtime/store.js +215 -0
- package/src/runtime/suspense.js +128 -0
- package/dist/cli.js +0 -607
- package/dist/index.js +0 -2071
- package/dist/vite-plugin.js +0 -883
package/dist/vite-plugin.js
DELETED
|
@@ -1,883 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
function transform(code, initialDepth = 0) {
|
|
6
|
-
let result = "";
|
|
7
|
-
let i = 0;
|
|
8
|
-
let jsxDepth = initialDepth;
|
|
9
|
-
function parseBlock(str, startIndex) {
|
|
10
|
-
let open = 0;
|
|
11
|
-
let startBlockIndex = -1;
|
|
12
|
-
let inSingle2 = false, inDouble2 = false, inTemplate2 = false;
|
|
13
|
-
let inCommentLine2 = false, inCommentMulti2 = false;
|
|
14
|
-
for (let j = startIndex; j < str.length; j++) {
|
|
15
|
-
const ch = str[j];
|
|
16
|
-
const prev = j > 0 ? str[j - 1] : "";
|
|
17
|
-
const next = j < str.length - 1 ? str[j + 1] : "";
|
|
18
|
-
if (inCommentLine2) {
|
|
19
|
-
if (ch === "\n" || ch === "\r") inCommentLine2 = false;
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
if (inCommentMulti2) {
|
|
23
|
-
if (ch === "*" && next === "/") {
|
|
24
|
-
inCommentMulti2 = false;
|
|
25
|
-
j++;
|
|
26
|
-
}
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (inTemplate2) {
|
|
30
|
-
if (ch === "`" && prev !== "\\") inTemplate2 = false;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (inSingle2) {
|
|
34
|
-
if (ch === "'" && prev !== "\\") inSingle2 = false;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (inDouble2) {
|
|
38
|
-
if (ch === '"' && prev !== "\\") inDouble2 = false;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (ch === "/" && next === "/") {
|
|
42
|
-
inCommentLine2 = true;
|
|
43
|
-
j++;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (ch === "/" && next === "*") {
|
|
47
|
-
inCommentMulti2 = true;
|
|
48
|
-
j++;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (ch === "`") {
|
|
52
|
-
inTemplate2 = true;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (ch === "'") {
|
|
56
|
-
inSingle2 = true;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (ch === '"') {
|
|
60
|
-
inDouble2 = true;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (ch === "{") {
|
|
64
|
-
if (open === 0) startBlockIndex = j;
|
|
65
|
-
open++;
|
|
66
|
-
} else if (ch === "}") {
|
|
67
|
-
open--;
|
|
68
|
-
if (open === 0) {
|
|
69
|
-
return { start: startBlockIndex, end: j };
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
__name(parseBlock, "parseBlock");
|
|
76
|
-
function consumeWhitespace(str, idx) {
|
|
77
|
-
while (idx < str.length && /\s/.test(str[idx])) idx++;
|
|
78
|
-
return idx;
|
|
79
|
-
}
|
|
80
|
-
__name(consumeWhitespace, "consumeWhitespace");
|
|
81
|
-
function extractCondition(str, startIndex) {
|
|
82
|
-
if (str[startIndex] !== "(") return null;
|
|
83
|
-
let depth = 1;
|
|
84
|
-
let j = startIndex + 1;
|
|
85
|
-
let inSingle2 = false, inDouble2 = false, inTemplate2 = false;
|
|
86
|
-
while (j < str.length && depth > 0) {
|
|
87
|
-
const ch = str[j], prev = str[j - 1] || "";
|
|
88
|
-
if (!inDouble2 && !inTemplate2 && ch === "'" && prev !== "\\") inSingle2 = !inSingle2;
|
|
89
|
-
else if (!inSingle2 && !inTemplate2 && ch === '"' && prev !== "\\") inDouble2 = !inDouble2;
|
|
90
|
-
else if (!inSingle2 && !inDouble2 && ch === "`" && prev !== "\\") inTemplate2 = !inTemplate2;
|
|
91
|
-
if (!inSingle2 && !inDouble2 && !inTemplate2) {
|
|
92
|
-
if (ch === "(") depth++;
|
|
93
|
-
else if (ch === ")") depth--;
|
|
94
|
-
}
|
|
95
|
-
j++;
|
|
96
|
-
}
|
|
97
|
-
if (depth !== 0) return null;
|
|
98
|
-
return { cond: str.substring(startIndex + 1, j - 1), end: j };
|
|
99
|
-
}
|
|
100
|
-
__name(extractCondition, "extractCondition");
|
|
101
|
-
function handleIf(currI, isBare = false) {
|
|
102
|
-
let startPtr = currI;
|
|
103
|
-
if (!isBare) {
|
|
104
|
-
startPtr = consumeWhitespace(code, currI + 1);
|
|
105
|
-
}
|
|
106
|
-
if (!code.startsWith("if", startPtr)) return null;
|
|
107
|
-
let ptr = startPtr + 2;
|
|
108
|
-
ptr = consumeWhitespace(code, ptr);
|
|
109
|
-
if (code[ptr] !== "(") return null;
|
|
110
|
-
const cases = [];
|
|
111
|
-
let elseContent = null;
|
|
112
|
-
let currentPtr = ptr;
|
|
113
|
-
let first = true;
|
|
114
|
-
while (true) {
|
|
115
|
-
if (!first) {
|
|
116
|
-
if (!code.startsWith("if", currentPtr)) break;
|
|
117
|
-
currentPtr += 2;
|
|
118
|
-
currentPtr = consumeWhitespace(code, currentPtr);
|
|
119
|
-
}
|
|
120
|
-
first = false;
|
|
121
|
-
const condRes = extractCondition(code, currentPtr);
|
|
122
|
-
if (!condRes) return null;
|
|
123
|
-
currentPtr = consumeWhitespace(code, condRes.end);
|
|
124
|
-
if (code[currentPtr] !== "{") return null;
|
|
125
|
-
const block = parseBlock(code, currentPtr);
|
|
126
|
-
if (!block) return null;
|
|
127
|
-
const rawContent = code.substring(block.start + 1, block.end);
|
|
128
|
-
const transformedContent = transform(rawContent, 1);
|
|
129
|
-
cases.push({ cond: condRes.cond, content: transformedContent });
|
|
130
|
-
currentPtr = block.end + 1;
|
|
131
|
-
currentPtr = consumeWhitespace(code, currentPtr);
|
|
132
|
-
if (code.startsWith("else", currentPtr)) {
|
|
133
|
-
currentPtr += 4;
|
|
134
|
-
currentPtr = consumeWhitespace(code, currentPtr);
|
|
135
|
-
if (code.startsWith("if", currentPtr)) {
|
|
136
|
-
continue;
|
|
137
|
-
} else if (code[currentPtr] === "{") {
|
|
138
|
-
const elseBlock = parseBlock(code, currentPtr);
|
|
139
|
-
if (!elseBlock) return null;
|
|
140
|
-
const rawElse = code.substring(elseBlock.start + 1, elseBlock.end);
|
|
141
|
-
elseContent = transform(rawElse, 1);
|
|
142
|
-
currentPtr = elseBlock.end + 1;
|
|
143
|
-
break;
|
|
144
|
-
} else {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
let endIdx = currentPtr;
|
|
152
|
-
if (!isBare) {
|
|
153
|
-
endIdx = consumeWhitespace(code, endIdx);
|
|
154
|
-
if (code[endIdx] !== "}") return null;
|
|
155
|
-
endIdx++;
|
|
156
|
-
}
|
|
157
|
-
let expr = "";
|
|
158
|
-
for (let idx = 0; idx < cases.length; idx++) {
|
|
159
|
-
const c = cases[idx];
|
|
160
|
-
let cond = c.cond.trim();
|
|
161
|
-
const isSimplePath = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(cond);
|
|
162
|
-
if (isSimplePath && !cond.endsWith(")")) {
|
|
163
|
-
cond = `((typeof (${cond}) === 'function' && typeof (${cond}).peek === 'function' && ('value' in (${cond}))) ? (${cond})() : (${cond}))`;
|
|
164
|
-
}
|
|
165
|
-
const body = `<Fragment>${c.content}</Fragment>`;
|
|
166
|
-
expr += `(${cond}) ? (${body}) : `;
|
|
167
|
-
}
|
|
168
|
-
expr += elseContent ? `(<Fragment>${elseContent}</Fragment>)` : "null";
|
|
169
|
-
return { end: endIdx, replacement: `{(() => ${expr})}` };
|
|
170
|
-
}
|
|
171
|
-
__name(handleIf, "handleIf");
|
|
172
|
-
function handleFor(currI, isBare = false) {
|
|
173
|
-
let ptr = currI;
|
|
174
|
-
if (!isBare) ptr = consumeWhitespace(code, currI + 1);
|
|
175
|
-
if (!code.startsWith("for", ptr)) return null;
|
|
176
|
-
ptr += 3;
|
|
177
|
-
ptr = consumeWhitespace(code, ptr);
|
|
178
|
-
const condRes = extractCondition(code, ptr);
|
|
179
|
-
if (!condRes) return null;
|
|
180
|
-
const forCond = condRes.cond;
|
|
181
|
-
const inMatch = forCond.match(/^\s*(\S+)\s+in\s+(.+)$/);
|
|
182
|
-
if (!inMatch) return null;
|
|
183
|
-
const item = inMatch[1].trim();
|
|
184
|
-
const list = inMatch[2].trim();
|
|
185
|
-
ptr = consumeWhitespace(code, condRes.end);
|
|
186
|
-
if (code[ptr] !== "{") return null;
|
|
187
|
-
const block = parseBlock(code, ptr);
|
|
188
|
-
if (!block) return null;
|
|
189
|
-
const rawContent = code.substring(block.start + 1, block.end);
|
|
190
|
-
const transformedContent = transform(rawContent, 1);
|
|
191
|
-
let endIdx = block.end + 1;
|
|
192
|
-
if (!isBare) {
|
|
193
|
-
endIdx = consumeWhitespace(code, endIdx);
|
|
194
|
-
if (code[endIdx] !== "}") return null;
|
|
195
|
-
endIdx++;
|
|
196
|
-
}
|
|
197
|
-
const replacement = `{(() => ${list}.map(${item} => (<Fragment>${transformedContent}</Fragment>)))}`;
|
|
198
|
-
return { end: endIdx, replacement };
|
|
199
|
-
}
|
|
200
|
-
__name(handleFor, "handleFor");
|
|
201
|
-
function handleSwitch(currI, isBare = false) {
|
|
202
|
-
let ptr = currI;
|
|
203
|
-
if (!isBare) ptr = consumeWhitespace(code, currI + 1);
|
|
204
|
-
if (!code.startsWith("switch", ptr)) return null;
|
|
205
|
-
ptr += 6;
|
|
206
|
-
ptr = consumeWhitespace(code, ptr);
|
|
207
|
-
const condRes = extractCondition(code, ptr);
|
|
208
|
-
if (!condRes) return null;
|
|
209
|
-
const cond = condRes.cond;
|
|
210
|
-
ptr = consumeWhitespace(code, condRes.end);
|
|
211
|
-
if (code[ptr] !== "{") return null;
|
|
212
|
-
const block = parseBlock(code, ptr);
|
|
213
|
-
if (!block) return null;
|
|
214
|
-
const rawContent = code.substring(block.start + 1, block.end);
|
|
215
|
-
const transformedInner = transform(rawContent, 0);
|
|
216
|
-
const finalContent = transformedInner.replace(/(case\s+.*?:|default:)([\s\S]*?)(?=case\s+.*?:|default:|$)/g, (m, label, body) => {
|
|
217
|
-
const trimmed = body.trim();
|
|
218
|
-
if (!trimmed) return m;
|
|
219
|
-
if (trimmed.startsWith("return ")) return m;
|
|
220
|
-
return `${label} return (<Fragment>${body}</Fragment>);`;
|
|
221
|
-
});
|
|
222
|
-
let endIdx = block.end + 1;
|
|
223
|
-
if (!isBare) {
|
|
224
|
-
endIdx = consumeWhitespace(code, endIdx);
|
|
225
|
-
if (code[endIdx] !== "}") return null;
|
|
226
|
-
endIdx++;
|
|
227
|
-
}
|
|
228
|
-
const replacement = `{function() { __ROUND_SWITCH_TOKEN__(${cond}) { ${finalContent} } }}`;
|
|
229
|
-
return { end: endIdx, replacement };
|
|
230
|
-
}
|
|
231
|
-
__name(handleSwitch, "handleSwitch");
|
|
232
|
-
let inSingle = false, inDouble = false, inTemplate = false;
|
|
233
|
-
let inCommentLine = false, inCommentMulti = false;
|
|
234
|
-
while (i < code.length) {
|
|
235
|
-
const ch = code[i];
|
|
236
|
-
const next = i < code.length - 1 ? code[i + 1] : "";
|
|
237
|
-
const prev = i > 0 ? code[i - 1] : "";
|
|
238
|
-
if (inCommentLine) {
|
|
239
|
-
result += ch;
|
|
240
|
-
if (ch === "\n" || ch === "\r") inCommentLine = false;
|
|
241
|
-
i++;
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
if (inCommentMulti) {
|
|
245
|
-
result += ch;
|
|
246
|
-
if (ch === "*" && next === "/") {
|
|
247
|
-
inCommentMulti = false;
|
|
248
|
-
result += "/";
|
|
249
|
-
i += 2;
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
i++;
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
if (inTemplate) {
|
|
256
|
-
result += ch;
|
|
257
|
-
if (ch === "`" && prev !== "\\") inTemplate = false;
|
|
258
|
-
i++;
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (inSingle) {
|
|
262
|
-
result += ch;
|
|
263
|
-
if (ch === "'" && prev !== "\\") inSingle = false;
|
|
264
|
-
i++;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
if (inDouble) {
|
|
268
|
-
result += ch;
|
|
269
|
-
if (ch === '"' && prev !== "\\") inDouble = false;
|
|
270
|
-
i++;
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
if (ch === "/" && next === "/") {
|
|
274
|
-
inCommentLine = true;
|
|
275
|
-
result += "//";
|
|
276
|
-
i += 2;
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
if (ch === "/" && next === "*") {
|
|
280
|
-
inCommentMulti = true;
|
|
281
|
-
result += "/*";
|
|
282
|
-
i += 2;
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
if (ch === "`") {
|
|
286
|
-
inTemplate = true;
|
|
287
|
-
result += ch;
|
|
288
|
-
i++;
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (ch === "'") {
|
|
292
|
-
inSingle = true;
|
|
293
|
-
result += ch;
|
|
294
|
-
i++;
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
if (ch === '"') {
|
|
298
|
-
inDouble = true;
|
|
299
|
-
result += ch;
|
|
300
|
-
i++;
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (ch === "<") {
|
|
304
|
-
const isTag = /[a-zA-Z0-9_$]/.test(next) || next === ">";
|
|
305
|
-
if (isTag) jsxDepth++;
|
|
306
|
-
}
|
|
307
|
-
if (ch === "<" && next === "/") {
|
|
308
|
-
if (jsxDepth > 0) jsxDepth--;
|
|
309
|
-
}
|
|
310
|
-
if (ch === "/" && next === ">") {
|
|
311
|
-
if (jsxDepth > 0) jsxDepth--;
|
|
312
|
-
}
|
|
313
|
-
if (jsxDepth > 0) {
|
|
314
|
-
let processed = false;
|
|
315
|
-
if (ch === "{") {
|
|
316
|
-
let ptr = consumeWhitespace(code, i + 1);
|
|
317
|
-
if (code.startsWith("if", ptr)) {
|
|
318
|
-
const res = handleIf(i, false);
|
|
319
|
-
if (res) {
|
|
320
|
-
result += res.replacement;
|
|
321
|
-
i = res.end;
|
|
322
|
-
processed = true;
|
|
323
|
-
}
|
|
324
|
-
} else if (code.startsWith("for", ptr)) {
|
|
325
|
-
const res = handleFor(i, false);
|
|
326
|
-
if (res) {
|
|
327
|
-
result += res.replacement;
|
|
328
|
-
i = res.end;
|
|
329
|
-
processed = true;
|
|
330
|
-
}
|
|
331
|
-
} else if (code.startsWith("switch", ptr)) {
|
|
332
|
-
const res = handleSwitch(i, false);
|
|
333
|
-
if (res) {
|
|
334
|
-
result += res.replacement;
|
|
335
|
-
i = res.end;
|
|
336
|
-
processed = true;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
} else if (ch === "i" && code.startsWith("if", i)) {
|
|
340
|
-
let ptr = consumeWhitespace(code, i + 2);
|
|
341
|
-
if (code[ptr] === "(") {
|
|
342
|
-
const res = handleIf(i, true);
|
|
343
|
-
if (res) {
|
|
344
|
-
result += res.replacement;
|
|
345
|
-
i = res.end;
|
|
346
|
-
processed = true;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
} else if (ch === "f" && code.startsWith("for", i)) {
|
|
350
|
-
let ptr = consumeWhitespace(code, i + 3);
|
|
351
|
-
if (code[ptr] === "(") {
|
|
352
|
-
const res = handleFor(i, true);
|
|
353
|
-
if (res) {
|
|
354
|
-
result += res.replacement;
|
|
355
|
-
i = res.end;
|
|
356
|
-
processed = true;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
} else if (ch === "s" && code.startsWith("switch", i)) {
|
|
360
|
-
let ptr = consumeWhitespace(code, i + 6);
|
|
361
|
-
if (code[ptr] === "(") {
|
|
362
|
-
const res = handleSwitch(i, true);
|
|
363
|
-
if (res) {
|
|
364
|
-
result += res.replacement;
|
|
365
|
-
i = res.end;
|
|
366
|
-
processed = true;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
if (processed) continue;
|
|
371
|
-
}
|
|
372
|
-
result += ch;
|
|
373
|
-
i++;
|
|
374
|
-
}
|
|
375
|
-
function findJsxTagEnd(str, startIndex) {
|
|
376
|
-
let inSingle2 = false, inDouble2 = false, inTemplate2 = false;
|
|
377
|
-
let braceDepth = 0;
|
|
378
|
-
for (let k = startIndex; k < str.length; k++) {
|
|
379
|
-
const c = str[k];
|
|
380
|
-
const p = k > 0 ? str[k - 1] : "";
|
|
381
|
-
if (!inDouble2 && !inTemplate2 && c === "'" && p !== "\\") inSingle2 = !inSingle2;
|
|
382
|
-
else if (!inSingle2 && !inTemplate2 && c === '"' && p !== "\\") inDouble2 = !inDouble2;
|
|
383
|
-
else if (!inSingle2 && !inDouble2 && c === "`" && p !== "\\") inTemplate2 = !inTemplate2;
|
|
384
|
-
if (inSingle2 || inDouble2 || inTemplate2) continue;
|
|
385
|
-
if (c === "{") braceDepth++;
|
|
386
|
-
else if (c === "}") braceDepth = Math.max(0, braceDepth - 1);
|
|
387
|
-
else if (c === ">" && braceDepth === 0) return k;
|
|
388
|
-
}
|
|
389
|
-
return -1;
|
|
390
|
-
}
|
|
391
|
-
__name(findJsxTagEnd, "findJsxTagEnd");
|
|
392
|
-
function transformSuspenseBlocks(str) {
|
|
393
|
-
let out = str;
|
|
394
|
-
let cursor = 0;
|
|
395
|
-
while (true) {
|
|
396
|
-
const openIndex = out.indexOf("<Suspense", cursor);
|
|
397
|
-
if (openIndex === -1) break;
|
|
398
|
-
const openEnd = findJsxTagEnd(out, openIndex);
|
|
399
|
-
if (openEnd === -1) break;
|
|
400
|
-
const openTagText = out.slice(openIndex, openEnd + 1);
|
|
401
|
-
if (/\/>\s*$/.test(openTagText)) {
|
|
402
|
-
cursor = openEnd + 1;
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
let depth = 1, k = openEnd + 1, closeStart = -1;
|
|
406
|
-
while (k < out.length) {
|
|
407
|
-
if (out.slice(k).startsWith("<Suspense")) {
|
|
408
|
-
depth++;
|
|
409
|
-
k += 9;
|
|
410
|
-
} else if (out.slice(k).startsWith("</Suspense>")) {
|
|
411
|
-
depth--;
|
|
412
|
-
if (depth === 0) {
|
|
413
|
-
closeStart = k;
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
k += 11;
|
|
417
|
-
} else k++;
|
|
418
|
-
}
|
|
419
|
-
if (closeStart === -1) break;
|
|
420
|
-
const inner = out.slice(openEnd + 1, closeStart);
|
|
421
|
-
const wrapped = `{(() => (<Fragment>${inner}</Fragment>))}`;
|
|
422
|
-
out = out.slice(0, openEnd + 1) + wrapped + out.slice(closeStart);
|
|
423
|
-
cursor = closeStart + wrapped.length + 11;
|
|
424
|
-
}
|
|
425
|
-
return out;
|
|
426
|
-
}
|
|
427
|
-
__name(transformSuspenseBlocks, "transformSuspenseBlocks");
|
|
428
|
-
function transformProviderBlocks(str) {
|
|
429
|
-
let out = str;
|
|
430
|
-
let cursor = 0;
|
|
431
|
-
while (true) {
|
|
432
|
-
const dot = out.indexOf(".Provider", cursor);
|
|
433
|
-
if (dot === -1) break;
|
|
434
|
-
const lt = out.lastIndexOf("<", dot);
|
|
435
|
-
if (lt === -1) break;
|
|
436
|
-
const openEnd = findJsxTagEnd(out, lt);
|
|
437
|
-
if (openEnd === -1) break;
|
|
438
|
-
const tagText = out.slice(lt, openEnd + 1);
|
|
439
|
-
if (/\/>\s*$/.test(tagText)) {
|
|
440
|
-
cursor = openEnd + 1;
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
const m = tagText.match(/^<\s*([A-Za-z_$][\w$]*\.Provider)\b/);
|
|
444
|
-
if (!m) {
|
|
445
|
-
cursor = openEnd + 1;
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
const tagName = m[1];
|
|
449
|
-
const closeTag = `</${tagName}>`;
|
|
450
|
-
let depth = 1, k = openEnd + 1, closeStart = -1;
|
|
451
|
-
while (k < out.length) {
|
|
452
|
-
const nOpen = out.indexOf(`<${tagName}`, k);
|
|
453
|
-
const nClose = out.indexOf(closeTag, k);
|
|
454
|
-
if (nClose === -1) break;
|
|
455
|
-
if (nOpen !== -1 && nOpen < nClose) {
|
|
456
|
-
const innerEnd = findJsxTagEnd(out, nOpen);
|
|
457
|
-
if (innerEnd !== -1 && !/\/>\s*$/.test(out.slice(nOpen, innerEnd + 1))) depth++;
|
|
458
|
-
k = innerEnd + 1;
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
depth--;
|
|
462
|
-
if (depth === 0) {
|
|
463
|
-
closeStart = nClose;
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
k = nClose + closeTag.length;
|
|
467
|
-
}
|
|
468
|
-
if (closeStart === -1) break;
|
|
469
|
-
const inner = out.slice(openEnd + 1, closeStart);
|
|
470
|
-
const wrapped = `{(() => (<Fragment>${inner}</Fragment>))}`;
|
|
471
|
-
out = out.slice(0, openEnd + 1) + wrapped + out.slice(closeStart);
|
|
472
|
-
cursor = closeStart + wrapped.length + closeTag.length;
|
|
473
|
-
}
|
|
474
|
-
return out;
|
|
475
|
-
}
|
|
476
|
-
__name(transformProviderBlocks, "transformProviderBlocks");
|
|
477
|
-
result = transformSuspenseBlocks(result);
|
|
478
|
-
result = transformProviderBlocks(result);
|
|
479
|
-
result = result.replace(/\{\s*([A-Za-z_$][\w$]*)\s*\(\s*\)\s*\}/g, "{() => $1()}").replace(/=\{\s*([A-Za-z_$][\w$]*)\s*\(\s*\)\s*\}/g, "={() => $1()}");
|
|
480
|
-
return result.replace(/__ROUND_SWITCH_TOKEN__/g, "switch");
|
|
481
|
-
}
|
|
482
|
-
__name(transform, "transform");
|
|
483
|
-
function normalizePath(p) {
|
|
484
|
-
return p.replaceAll("\\", "/");
|
|
485
|
-
}
|
|
486
|
-
__name(normalizePath, "normalizePath");
|
|
487
|
-
function isMdRawRequest(id) {
|
|
488
|
-
return typeof id === "string" && id.includes(".md") && id.includes("?raw");
|
|
489
|
-
}
|
|
490
|
-
__name(isMdRawRequest, "isMdRawRequest");
|
|
491
|
-
function stripQuery(id) {
|
|
492
|
-
return id.split("?")[0];
|
|
493
|
-
}
|
|
494
|
-
__name(stripQuery, "stripQuery");
|
|
495
|
-
function escapeForJsString(s) {
|
|
496
|
-
return String(s).replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("${", "\\${");
|
|
497
|
-
}
|
|
498
|
-
__name(escapeForJsString, "escapeForJsString");
|
|
499
|
-
function resolveMaybeRelative(baseDir, p) {
|
|
500
|
-
if (!p) return null;
|
|
501
|
-
if (path.isAbsolute(p)) return p;
|
|
502
|
-
return path.resolve(baseDir, p);
|
|
503
|
-
}
|
|
504
|
-
__name(resolveMaybeRelative, "resolveMaybeRelative");
|
|
505
|
-
function inlineMarkdownInRound(code, fileAbs, addWatchFile) {
|
|
506
|
-
if (typeof code !== "string" || typeof fileAbs !== "string") return code;
|
|
507
|
-
const dir = path.dirname(fileAbs);
|
|
508
|
-
const re = /<Markdown\b([^>]*?)\bsrc\s*=\s*("([^"]+)"|'([^']+)')([^>]*)\/>/g;
|
|
509
|
-
return code.replace(re, (full, beforeAttrs, _q, dbl, sgl, afterAttrs) => {
|
|
510
|
-
const src = dbl ?? sgl;
|
|
511
|
-
if (!src || typeof src !== "string") return full;
|
|
512
|
-
if (!src.startsWith("./") && !src.startsWith("../")) return full;
|
|
513
|
-
const mdAbs = path.resolve(dir, src);
|
|
514
|
-
try {
|
|
515
|
-
const raw = fs.readFileSync(mdAbs, "utf8");
|
|
516
|
-
if (typeof addWatchFile === "function") {
|
|
517
|
-
try {
|
|
518
|
-
addWatchFile(mdAbs);
|
|
519
|
-
} catch {
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
const content = escapeForJsString(raw);
|
|
523
|
-
const rebuilt = `<Markdown${beforeAttrs}content={\`${content}\`} ${afterAttrs} />`;
|
|
524
|
-
return rebuilt.replace(/\s+\/>$/, " />");
|
|
525
|
-
} catch (e) {
|
|
526
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
527
|
-
throw new Error(`Markdown file not found: ${src} (resolved: ${mdAbs})
|
|
528
|
-
${msg}`);
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
__name(inlineMarkdownInRound, "inlineMarkdownInRound");
|
|
533
|
-
function isExcluded(fileAbsPath, excludeAbs) {
|
|
534
|
-
const file = normalizePath(fileAbsPath);
|
|
535
|
-
for (const pat of excludeAbs) {
|
|
536
|
-
const patNorm = normalizePath(pat);
|
|
537
|
-
const prefix = patNorm.endsWith("/**") ? patNorm.slice(0, -3) : patNorm;
|
|
538
|
-
if (file.startsWith(prefix)) return true;
|
|
539
|
-
}
|
|
540
|
-
return false;
|
|
541
|
-
}
|
|
542
|
-
__name(isExcluded, "isExcluded");
|
|
543
|
-
function isIncluded(fileAbsPath, includeAbs) {
|
|
544
|
-
if (!includeAbs.length) return true;
|
|
545
|
-
const file = normalizePath(fileAbsPath);
|
|
546
|
-
for (const pat of includeAbs) {
|
|
547
|
-
const patNorm = normalizePath(pat);
|
|
548
|
-
const prefix = patNorm.endsWith("/**") ? patNorm.slice(0, -3) : patNorm;
|
|
549
|
-
if (file.startsWith(prefix)) return true;
|
|
550
|
-
}
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
__name(isIncluded, "isIncluded");
|
|
554
|
-
function RoundPlugin(pluginOptions = {}) {
|
|
555
|
-
const state = {
|
|
556
|
-
rootDir: process.cwd(),
|
|
557
|
-
includeAbs: [],
|
|
558
|
-
excludeAbs: [],
|
|
559
|
-
configLoaded: false,
|
|
560
|
-
routingTrailingSlash: true,
|
|
561
|
-
configPathAbs: null,
|
|
562
|
-
configDir: null,
|
|
563
|
-
entryAbs: null,
|
|
564
|
-
entryRel: null,
|
|
565
|
-
name: "Round",
|
|
566
|
-
startHead: null,
|
|
567
|
-
startHeadHtml: null
|
|
568
|
-
};
|
|
569
|
-
let lastRuntimeErrorKey = null;
|
|
570
|
-
let lastRuntimeErrorAt = 0;
|
|
571
|
-
const runtimeImport = pluginOptions.runtimeImport ?? "round-core";
|
|
572
|
-
const restartOnConfigChange = pluginOptions.restartOnConfigChange !== void 0 ? Boolean(pluginOptions.restartOnConfigChange) : true;
|
|
573
|
-
function loadConfigOnce(rootDir) {
|
|
574
|
-
if (state.configLoaded) return;
|
|
575
|
-
state.configLoaded = true;
|
|
576
|
-
const configPath = pluginOptions.configPath ? resolveMaybeRelative(rootDir, pluginOptions.configPath) : resolveMaybeRelative(rootDir, "./round.config.json");
|
|
577
|
-
state.configPathAbs = configPath;
|
|
578
|
-
const configDir = configPath ? path.dirname(configPath) : rootDir;
|
|
579
|
-
state.configDir = configDir;
|
|
580
|
-
let config = null;
|
|
581
|
-
if (configPath && fs.existsSync(configPath)) {
|
|
582
|
-
try {
|
|
583
|
-
const raw = fs.readFileSync(configPath, "utf8");
|
|
584
|
-
config = JSON.parse(raw);
|
|
585
|
-
} catch {
|
|
586
|
-
config = null;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
const trailingSlash = config?.routing?.trailingSlash;
|
|
590
|
-
state.routingTrailingSlash = trailingSlash !== void 0 ? Boolean(trailingSlash) : true;
|
|
591
|
-
const customTags = config?.htmlTags;
|
|
592
|
-
state.customTags = Array.isArray(customTags) ? customTags : [];
|
|
593
|
-
state.name = config?.name ?? "Round";
|
|
594
|
-
const entryRel = config?.entry;
|
|
595
|
-
state.entryRel = entryRel;
|
|
596
|
-
state.entryAbs = entryRel ? resolveMaybeRelative(configDir, entryRel) : null;
|
|
597
|
-
const include = pluginOptions.include ?? config?.include ?? [];
|
|
598
|
-
const exclude = pluginOptions.exclude ?? config?.exclude ?? ["./node_modules", "./dist"];
|
|
599
|
-
const includeBase = pluginOptions.include ? rootDir : configDir;
|
|
600
|
-
const excludeBase = pluginOptions.exclude ? rootDir : configDir;
|
|
601
|
-
state.includeAbs = Array.isArray(include) ? include.map((p) => resolveMaybeRelative(includeBase, p)).filter(Boolean) : [];
|
|
602
|
-
state.excludeAbs = Array.isArray(exclude) ? exclude.map((p) => resolveMaybeRelative(excludeBase, p)).filter(Boolean) : [];
|
|
603
|
-
}
|
|
604
|
-
__name(loadConfigOnce, "loadConfigOnce");
|
|
605
|
-
function findBlock(str, startIndex) {
|
|
606
|
-
let open = 0;
|
|
607
|
-
let inSingle = false;
|
|
608
|
-
let inDouble = false;
|
|
609
|
-
let inTemplate = false;
|
|
610
|
-
let start = -1;
|
|
611
|
-
for (let i = startIndex; i < str.length; i++) {
|
|
612
|
-
const ch = str[i];
|
|
613
|
-
const prev = i > 0 ? str[i - 1] : "";
|
|
614
|
-
if (!inDouble && !inTemplate && ch === "'" && prev !== "\\") inSingle = !inSingle;
|
|
615
|
-
else if (!inSingle && !inTemplate && ch === '"' && prev !== "\\") inDouble = !inDouble;
|
|
616
|
-
else if (!inSingle && !inDouble && ch === "`" && prev !== "\\") inTemplate = !inTemplate;
|
|
617
|
-
if (inSingle || inDouble || inTemplate) continue;
|
|
618
|
-
if (ch === "{") {
|
|
619
|
-
if (open === 0) start = i;
|
|
620
|
-
open++;
|
|
621
|
-
} else if (ch === "}") {
|
|
622
|
-
open--;
|
|
623
|
-
if (open === 0 && start !== -1) {
|
|
624
|
-
return { start, end: i };
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return null;
|
|
629
|
-
}
|
|
630
|
-
__name(findBlock, "findBlock");
|
|
631
|
-
function parseStartHeadCallArgument(str, fromIndex) {
|
|
632
|
-
const idx = str.indexOf("startHead", fromIndex);
|
|
633
|
-
if (idx === -1) return null;
|
|
634
|
-
const callIdx = str.indexOf("(", idx);
|
|
635
|
-
if (callIdx === -1) return null;
|
|
636
|
-
let i = callIdx;
|
|
637
|
-
let paren = 0;
|
|
638
|
-
let inSingle = false;
|
|
639
|
-
let inDouble = false;
|
|
640
|
-
let inTemplate = false;
|
|
641
|
-
for (; i < str.length; i++) {
|
|
642
|
-
const ch = str[i];
|
|
643
|
-
const prev = i > 0 ? str[i - 1] : "";
|
|
644
|
-
if (!inDouble && !inTemplate && ch === "'" && prev !== "\\") inSingle = !inSingle;
|
|
645
|
-
else if (!inSingle && !inTemplate && ch === '"' && prev !== "\\") inDouble = !inDouble;
|
|
646
|
-
else if (!inSingle && !inDouble && ch === "`" && prev !== "\\") inTemplate = !inTemplate;
|
|
647
|
-
if (inSingle || inDouble || inTemplate) continue;
|
|
648
|
-
if (ch === "(") paren++;
|
|
649
|
-
else if (ch === ")") {
|
|
650
|
-
paren--;
|
|
651
|
-
if (paren === 0) {
|
|
652
|
-
const inner = str.slice(callIdx + 1, i).trim();
|
|
653
|
-
return { arg: inner, start: idx, end: i + 1 };
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
__name(parseStartHeadCallArgument, "parseStartHeadCallArgument");
|
|
660
|
-
function parseStartHeadInDefaultExport(code) {
|
|
661
|
-
const m = code.match(/export\s+default\s+function\b/);
|
|
662
|
-
const hasAnyCall = /\bstartHead\s*\(/.test(code);
|
|
663
|
-
if (!m || typeof m.index !== "number") return { headExpr: null, hasAny: hasAnyCall };
|
|
664
|
-
const fnStart = m.index;
|
|
665
|
-
const braceIdx = code.indexOf("{", fnStart);
|
|
666
|
-
if (braceIdx === -1) return { headExpr: null, hasAny: hasAnyCall };
|
|
667
|
-
const block = findBlock(code, braceIdx);
|
|
668
|
-
if (!block) return { headExpr: null, hasAny: hasAnyCall };
|
|
669
|
-
const body = code.slice(block.start + 1, block.end);
|
|
670
|
-
const call = parseStartHeadCallArgument(body, 0);
|
|
671
|
-
return { headExpr: call ? call.arg : null, hasAny: hasAnyCall, hasOutside: hasAnyCall && !call };
|
|
672
|
-
}
|
|
673
|
-
__name(parseStartHeadInDefaultExport, "parseStartHeadInDefaultExport");
|
|
674
|
-
function headToHtml(head) {
|
|
675
|
-
if (!head || typeof head !== "object") return "";
|
|
676
|
-
let out = "";
|
|
677
|
-
if (typeof head.title === "string") {
|
|
678
|
-
out += `
|
|
679
|
-
<title>${head.title}</title>`;
|
|
680
|
-
}
|
|
681
|
-
const meta = head.meta;
|
|
682
|
-
const links = head.links;
|
|
683
|
-
const renderAttrs = /* @__PURE__ */ __name((attrs) => {
|
|
684
|
-
if (!attrs || typeof attrs !== "object") return "";
|
|
685
|
-
return Object.entries(attrs).filter(([, v]) => v !== null && v !== void 0).map(([k, v]) => ` ${k}="${String(v).replaceAll('"', """)}"`).join("");
|
|
686
|
-
}, "renderAttrs");
|
|
687
|
-
if (Array.isArray(meta)) {
|
|
688
|
-
meta.forEach((m) => {
|
|
689
|
-
if (!m) return;
|
|
690
|
-
if (Array.isArray(m) && m.length >= 2) {
|
|
691
|
-
out += `
|
|
692
|
-
<meta name="${String(m[0]).replaceAll('"', """)}" content="${String(m[1] ?? "").replaceAll('"', """)}">`;
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
if (typeof m === "object") {
|
|
696
|
-
out += `
|
|
697
|
-
<meta${renderAttrs(m)}>`;
|
|
698
|
-
}
|
|
699
|
-
});
|
|
700
|
-
} else if (meta && typeof meta === "object") {
|
|
701
|
-
Object.entries(meta).forEach(([name, content]) => {
|
|
702
|
-
out += `
|
|
703
|
-
<meta name="${String(name).replaceAll('"', """)}" content="${String(content ?? "").replaceAll('"', """)}">`;
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
if (Array.isArray(links)) {
|
|
707
|
-
links.forEach((l) => {
|
|
708
|
-
if (!l || typeof l !== "object") return;
|
|
709
|
-
out += `
|
|
710
|
-
<link${renderAttrs(l)}>`;
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
if (typeof head.raw === "string" && head.raw.trim()) {
|
|
714
|
-
out += `
|
|
715
|
-
${head.raw}`;
|
|
716
|
-
}
|
|
717
|
-
return out;
|
|
718
|
-
}
|
|
719
|
-
__name(headToHtml, "headToHtml");
|
|
720
|
-
return {
|
|
721
|
-
name: "vite-plugin-round",
|
|
722
|
-
enforce: "pre",
|
|
723
|
-
transformIndexHtml(html) {
|
|
724
|
-
if (!state.startHeadHtml) return html;
|
|
725
|
-
if (!html.includes("</head>")) return html;
|
|
726
|
-
let next = html;
|
|
727
|
-
if (state.startHead && typeof state.startHead.title === "string") {
|
|
728
|
-
next = next.replace(/<title>[\s\S]*?<\/title>/i, "");
|
|
729
|
-
}
|
|
730
|
-
return next.replace("</head>", `${state.startHeadHtml}
|
|
731
|
-
</head>`);
|
|
732
|
-
},
|
|
733
|
-
config(userConfig, env) {
|
|
734
|
-
const rootDir = path.resolve(process.cwd(), userConfig.root ?? ".");
|
|
735
|
-
state.rootDir = rootDir;
|
|
736
|
-
loadConfigOnce(rootDir);
|
|
737
|
-
return {
|
|
738
|
-
define: {
|
|
739
|
-
__ROUND_ROUTING_TRAILING_SLASH__: JSON.stringify(state.routingTrailingSlash),
|
|
740
|
-
__ROUND_CUSTOM_TAGS__: JSON.stringify(state.customTags ?? [])
|
|
741
|
-
},
|
|
742
|
-
esbuild: {
|
|
743
|
-
include: /\.(round|js|jsx|ts|tsx)$/,
|
|
744
|
-
loader: "jsx",
|
|
745
|
-
jsxFactory: "createElement",
|
|
746
|
-
jsxFragment: "Fragment"
|
|
747
|
-
// NOTE: Inject the runtime import in transform() to avoid
|
|
748
|
-
},
|
|
749
|
-
// Ensure .round files are treated as JS/JSX
|
|
750
|
-
resolve: {
|
|
751
|
-
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".round"]
|
|
752
|
-
}
|
|
753
|
-
};
|
|
754
|
-
},
|
|
755
|
-
resolveId(id) {
|
|
756
|
-
return null;
|
|
757
|
-
},
|
|
758
|
-
load(id) {
|
|
759
|
-
if (!isMdRawRequest(id)) return;
|
|
760
|
-
const fileAbs = stripQuery(id);
|
|
761
|
-
try {
|
|
762
|
-
const raw = fs.readFileSync(fileAbs, "utf8");
|
|
763
|
-
this.addWatchFile(fileAbs);
|
|
764
|
-
return `export default \`${escapeForJsString(raw)}\`;`;
|
|
765
|
-
} catch {
|
|
766
|
-
this.addWatchFile(fileAbs);
|
|
767
|
-
return "export default ``;";
|
|
768
|
-
}
|
|
769
|
-
},
|
|
770
|
-
configureServer(server) {
|
|
771
|
-
loadConfigOnce(server.config.root ?? process.cwd());
|
|
772
|
-
if (state.configPathAbs) {
|
|
773
|
-
server.watcher.add(state.configPathAbs);
|
|
774
|
-
}
|
|
775
|
-
server.middlewares.use((req, res, next) => {
|
|
776
|
-
if (!req.url) return next();
|
|
777
|
-
const [urlPath] = req.url.split("?");
|
|
778
|
-
if (urlPath && urlPath.endsWith(".md")) {
|
|
779
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
780
|
-
}
|
|
781
|
-
next();
|
|
782
|
-
});
|
|
783
|
-
server.ws.on("round:runtime-error", (payload = {}) => {
|
|
784
|
-
try {
|
|
785
|
-
const message = typeof payload.message === "string" ? payload.message : "Runtime error";
|
|
786
|
-
const phase = typeof payload.phase === "string" && payload.phase ? ` (${payload.phase})` : "";
|
|
787
|
-
const component = typeof payload.component === "string" && payload.component ? ` in ${payload.component}` : "";
|
|
788
|
-
const header = `[round] Runtime error${component}${phase}: ${message}`;
|
|
789
|
-
const stack = payload.stack ? String(payload.stack) : "";
|
|
790
|
-
const key = `${header}
|
|
791
|
-
${stack}`;
|
|
792
|
-
const now = Date.now();
|
|
793
|
-
if (lastRuntimeErrorKey === key && now - lastRuntimeErrorAt < 2e3) return;
|
|
794
|
-
lastRuntimeErrorKey = key;
|
|
795
|
-
lastRuntimeErrorAt = now;
|
|
796
|
-
server.config.logger.error(header);
|
|
797
|
-
if (stack) server.config.logger.error(stack);
|
|
798
|
-
} catch {
|
|
799
|
-
server.config.logger.error("[round] Runtime error");
|
|
800
|
-
}
|
|
801
|
-
});
|
|
802
|
-
},
|
|
803
|
-
handleHotUpdate(ctx) {
|
|
804
|
-
if (state.configPathAbs && ctx.file === state.configPathAbs) {
|
|
805
|
-
if (!restartOnConfigChange) return [];
|
|
806
|
-
try {
|
|
807
|
-
if (typeof ctx.server.restart === "function") {
|
|
808
|
-
ctx.server.restart();
|
|
809
|
-
} else {
|
|
810
|
-
ctx.server.ws.send({ type: "full-reload" });
|
|
811
|
-
}
|
|
812
|
-
} catch {
|
|
813
|
-
ctx.server.ws.send({ type: "full-reload" });
|
|
814
|
-
}
|
|
815
|
-
return [];
|
|
816
|
-
}
|
|
817
|
-
},
|
|
818
|
-
configurePreviewServer(server) {
|
|
819
|
-
server.middlewares.use((req, res, next) => {
|
|
820
|
-
if (!req.url) return next();
|
|
821
|
-
const [urlPath] = req.url.split("?");
|
|
822
|
-
if (urlPath && urlPath.endsWith(".md")) {
|
|
823
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
824
|
-
}
|
|
825
|
-
next();
|
|
826
|
-
});
|
|
827
|
-
},
|
|
828
|
-
transform(code, id) {
|
|
829
|
-
if (id.endsWith(".round")) {
|
|
830
|
-
const fileAbs = path.isAbsolute(id) ? id : path.resolve(state.rootDir, id);
|
|
831
|
-
if (!isIncluded(fileAbs, state.includeAbs)) return;
|
|
832
|
-
if (isExcluded(fileAbs, state.excludeAbs)) return;
|
|
833
|
-
const isEntry = state.entryAbs && normalizePath(fileAbs) === normalizePath(state.entryAbs);
|
|
834
|
-
const parsedHead = parseStartHeadInDefaultExport(code);
|
|
835
|
-
if (parsedHead.hasAny && !isEntry) {
|
|
836
|
-
this.error(new Error(`startHead() can only be used in the entry module's export default function: ${state.entryAbs ?? "(unknown entry)"}
|
|
837
|
-
Found in: ${fileAbs}`));
|
|
838
|
-
}
|
|
839
|
-
if (isEntry && parsedHead.hasOutside) {
|
|
840
|
-
this.error(new Error(`startHead() must be called inside the entry module's export default function body (not at top-level).
|
|
841
|
-
Entry: ${fileAbs}`));
|
|
842
|
-
}
|
|
843
|
-
if (isEntry && parsedHead.headExpr) {
|
|
844
|
-
const trimmed = parsedHead.headExpr.trim();
|
|
845
|
-
if (!trimmed.startsWith("{")) {
|
|
846
|
-
this.error(new Error(`startHead(...) expects an object literal. Example: startHead({ title: 'Home' })
|
|
847
|
-
Found: ${trimmed.slice(0, 60)}...`));
|
|
848
|
-
}
|
|
849
|
-
if (/\bfunction\b|=>|\bimport\b|\brequire\b|\bprocess\b|\bglobal\b/.test(trimmed)) {
|
|
850
|
-
this.error(new Error("startHead object must be static data (no functions/imports)."));
|
|
851
|
-
}
|
|
852
|
-
let headObj = null;
|
|
853
|
-
try {
|
|
854
|
-
headObj = Function(`"use strict"; return (${trimmed});`)();
|
|
855
|
-
} catch (e) {
|
|
856
|
-
this.error(new Error(`Failed to parse startHead(...) object in ${fileAbs}: ${String(e?.message ?? e)}`));
|
|
857
|
-
}
|
|
858
|
-
state.startHead = headObj;
|
|
859
|
-
state.startHeadHtml = headToHtml(headObj);
|
|
860
|
-
}
|
|
861
|
-
let nextCode = code;
|
|
862
|
-
try {
|
|
863
|
-
nextCode = inlineMarkdownInRound(nextCode, fileAbs, (p) => this.addWatchFile(p));
|
|
864
|
-
} catch (e) {
|
|
865
|
-
this.error(e);
|
|
866
|
-
}
|
|
867
|
-
let transformedCode = transform(nextCode);
|
|
868
|
-
if (!/^\s*import\s+\{\s*createElement\s*,\s*Fragment\s*\}\s+from\s+['"][^'"]+['"];?/m.test(transformedCode)) {
|
|
869
|
-
transformedCode = `import { createElement, Fragment } from '${runtimeImport}';
|
|
870
|
-
` + transformedCode;
|
|
871
|
-
}
|
|
872
|
-
return {
|
|
873
|
-
code: transformedCode,
|
|
874
|
-
map: null
|
|
875
|
-
};
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
__name(RoundPlugin, "RoundPlugin");
|
|
881
|
-
export {
|
|
882
|
-
RoundPlugin as default
|
|
883
|
-
};
|