round-core 0.0.7 → 0.0.8

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.
Files changed (75) hide show
  1. package/README.md +21 -0
  2. package/dist/index.d.ts +341 -341
  3. package/dist/vite-plugin.js +52 -3
  4. package/package.json +7 -3
  5. package/.github/workflows/benchmarks.yml +0 -44
  6. package/Round.png +0 -0
  7. package/benchmarks/apps/react/index.html +0 -9
  8. package/benchmarks/apps/react/main.jsx +0 -25
  9. package/benchmarks/apps/react/vite.config.js +0 -12
  10. package/benchmarks/apps/round/index.html +0 -11
  11. package/benchmarks/apps/round/main.jsx +0 -22
  12. package/benchmarks/apps/round/vite.config.js +0 -15
  13. package/benchmarks/bun.lock +0 -497
  14. package/benchmarks/dist-bench/react/assets/index-9KGqIPOU.js +0 -8
  15. package/benchmarks/dist-bench/react/index.html +0 -10
  16. package/benchmarks/dist-bench/round/assets/index-CBBIRhox.js +0 -52
  17. package/benchmarks/dist-bench/round/index.html +0 -8
  18. package/benchmarks/package.json +0 -22
  19. package/benchmarks/scripts/measure-build.js +0 -64
  20. package/benchmarks/tests/runtime.bench.js +0 -51
  21. package/benchmarks/vitest.config.js +0 -8
  22. package/bun.lock +0 -425
  23. package/cli.js +0 -2
  24. package/extension/.vscodeignore +0 -5
  25. package/extension/LICENSE +0 -21
  26. package/extension/cgmanifest.json +0 -45
  27. package/extension/extension.js +0 -163
  28. package/extension/images/round-config-dark.svg +0 -10
  29. package/extension/images/round-config-light.svg +0 -10
  30. package/extension/images/round-dark.svg +0 -10
  31. package/extension/images/round-light.svg +0 -10
  32. package/extension/javascript-language-configuration.json +0 -241
  33. package/extension/package-lock.json +0 -97
  34. package/extension/package.json +0 -119
  35. package/extension/package.nls.json +0 -4
  36. package/extension/round-0.1.0.vsix +0 -0
  37. package/extension/round-lsp/package-lock.json +0 -185
  38. package/extension/round-lsp/package.json +0 -21
  39. package/extension/round-lsp/src/round-transformer-lsp.js +0 -248
  40. package/extension/round-lsp/src/server.js +0 -396
  41. package/extension/snippets/javascript.code-snippets +0 -266
  42. package/extension/snippets/round.code-snippets +0 -109
  43. package/extension/syntaxes/JavaScript.tmLanguage.json +0 -6001
  44. package/extension/syntaxes/JavaScriptReact.tmLanguage.json +0 -6066
  45. package/extension/syntaxes/Readme.md +0 -12
  46. package/extension/syntaxes/Regular Expressions (JavaScript).tmLanguage +0 -237
  47. package/extension/syntaxes/Round.tmLanguage.json +0 -290
  48. package/extension/syntaxes/RoundInject.tmLanguage.json +0 -20
  49. package/extension/tags-language-configuration.json +0 -152
  50. package/extension/temp_astro/package-lock.json +0 -912
  51. package/extension/temp_astro/package.json +0 -16
  52. package/extension/types/round-core.d.ts +0 -326
  53. package/index.js +0 -2
  54. package/logo.svg +0 -10
  55. package/src/cli.js +0 -608
  56. package/src/compiler/index.js +0 -2
  57. package/src/compiler/transformer.js +0 -443
  58. package/src/compiler/vite-plugin.js +0 -472
  59. package/src/index.d.ts +0 -341
  60. package/src/index.js +0 -45
  61. package/src/runtime/context.js +0 -101
  62. package/src/runtime/dom.js +0 -403
  63. package/src/runtime/error-boundary.js +0 -48
  64. package/src/runtime/error-reporter.js +0 -13
  65. package/src/runtime/error-store.js +0 -85
  66. package/src/runtime/errors.js +0 -152
  67. package/src/runtime/lifecycle.js +0 -142
  68. package/src/runtime/markdown.js +0 -72
  69. package/src/runtime/router.js +0 -468
  70. package/src/runtime/signals.js +0 -548
  71. package/src/runtime/store.js +0 -215
  72. package/src/runtime/suspense.js +0 -128
  73. package/vite.config.build.js +0 -48
  74. package/vite.config.js +0 -10
  75. package/vitest.config.js +0 -8
@@ -1,443 +0,0 @@
1
- // Transformer for .round files
2
- // Handles custom syntax like:
3
- // {if(cond){ ... }} -> {cond ? (...) : null}
4
- // {for(item in list){ ... }} -> {list.map(item => (...))}
5
-
6
- export function transform(code) {
7
- // Process "if" blocks first, then "for" blocks (or vice versa, order matters if nested)
8
-
9
- // Helper to find balanced block starting at index
10
- function parseBlock(str, startIndex) {
11
- let open = 0;
12
- let startBlockIndex = -1;
13
- let endBlockIndex = -1;
14
-
15
- let inSingle = false;
16
- let inDouble = false;
17
- let inTemplate = false;
18
- let inCommentLine = false;
19
- let inCommentMulti = false;
20
-
21
- for (let i = startIndex; i < str.length; i++) {
22
- const ch = str[i];
23
- const prev = i > 0 ? str[i - 1] : '';
24
- const next = i < str.length - 1 ? str[i + 1] : '';
25
-
26
- // Handle strings and comments
27
- if (inCommentLine) {
28
- if (ch === '\n' || ch === '\r') inCommentLine = false;
29
- continue;
30
- }
31
- if (inCommentMulti) {
32
- if (ch === '*' && next === '/') {
33
- inCommentMulti = false;
34
- i++;
35
- }
36
- continue;
37
- }
38
- if (inTemplate) {
39
- if (ch === '`' && prev !== '\\') inTemplate = false;
40
- continue;
41
- }
42
- if (inSingle) {
43
- if (ch === '\'' && prev !== '\\') inSingle = false;
44
- continue;
45
- }
46
- if (inDouble) {
47
- if (ch === '"' && prev !== '\\') inDouble = false;
48
- continue;
49
- }
50
-
51
- // Check for start of strings/comments
52
- if (ch === '/' && next === '/') {
53
- inCommentLine = true;
54
- i++;
55
- continue;
56
- }
57
- if (ch === '/' && next === '*') {
58
- inCommentMulti = true;
59
- i++;
60
- continue;
61
- }
62
- if (ch === '`') {
63
- inTemplate = true;
64
- continue;
65
- }
66
- if (ch === '\'') {
67
- inSingle = true;
68
- continue;
69
- }
70
- if (ch === '"') {
71
- inDouble = true;
72
- continue;
73
- }
74
-
75
- if (ch === '{') {
76
- if (open === 0) startBlockIndex = i;
77
- open++;
78
- } else if (ch === '}') {
79
- open--;
80
- if (open === 0) {
81
- endBlockIndex = i;
82
- return { start: startBlockIndex, end: endBlockIndex };
83
- }
84
- }
85
- }
86
- return null;
87
- }
88
-
89
- let result = code;
90
-
91
- function consumeWhitespace(str, i) {
92
- while (i < str.length && /\s/.test(str[i])) i++;
93
- return i;
94
- }
95
-
96
- function parseIfChain(str, ifIndex) {
97
- const head = str.slice(ifIndex);
98
- const m = head.match(/^if\s*\((.*?)\)\s*\{/);
99
- if (!m) return null;
100
-
101
- let i = ifIndex;
102
- const cases = [];
103
- let elseContent = null;
104
-
105
- while (true) {
106
- const cur = str.slice(i);
107
- const mm = cur.match(/^if\s*\((.*?)\)\s*\{/);
108
- if (!mm) return null;
109
- let cond = mm[1];
110
-
111
- // Allow {if(signal){...}} where signal is a simple identifier/member path.
112
- // For those cases, auto-unwrap signal-like values by calling them.
113
- // Examples supported:
114
- // - if(flags.showCounter){...}
115
- // - if(user.loggedIn){...}
116
- // Complex expressions are left untouched.
117
- const trimmedCond = String(cond).trim();
118
- const isSimplePath = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(trimmedCond);
119
- if (isSimplePath && !trimmedCond.endsWith(')')) {
120
- cond = `((typeof (${trimmedCond}) === 'function' && typeof (${trimmedCond}).peek === 'function' && ('value' in (${trimmedCond}))) ? (${trimmedCond})() : (${trimmedCond}))`;
121
- }
122
- const blockStart = i + mm[0].length - 1;
123
- const block = parseBlock(str, blockStart);
124
- if (!block) return null;
125
-
126
- const content = str.substring(block.start + 1, block.end);
127
- cases.push({ cond, content });
128
-
129
- i = block.end + 1;
130
- i = consumeWhitespace(str, i);
131
-
132
- if (!str.startsWith('else', i)) {
133
- break;
134
- }
135
-
136
- i += 4;
137
- i = consumeWhitespace(str, i);
138
-
139
- if (str.startsWith('if', i)) {
140
- continue;
141
- }
142
-
143
- if (str[i] !== '{') return null;
144
- const elseBlock = parseBlock(str, i);
145
- if (!elseBlock) return null;
146
- elseContent = str.substring(elseBlock.start + 1, elseBlock.end);
147
- i = elseBlock.end + 1;
148
- break;
149
- }
150
-
151
- const end = i;
152
-
153
- let expr = '';
154
- for (let idx = 0; idx < cases.length; idx++) {
155
- const c = cases[idx];
156
- const body = `<Fragment>${c.content}</Fragment>`;
157
- if (idx === 0) {
158
- expr = `(${c.cond}) ? (${body}) : `;
159
- } else {
160
- expr += `(${c.cond}) ? (${body}) : `;
161
- }
162
- }
163
- if (elseContent !== null) {
164
- expr += `(<Fragment>${elseContent}</Fragment>)`;
165
- } else {
166
- expr += 'null';
167
- }
168
-
169
- const replacement = `(() => ${expr})`;
170
- return { start: ifIndex, end, replacement };
171
- }
172
-
173
- function parseIfStatement(str, ifIndex) {
174
- if (!str.startsWith('if', ifIndex)) return null;
175
- const chain = parseIfChain(str, ifIndex);
176
- if (!chain) return null;
177
- return {
178
- start: chain.start,
179
- end: chain.end,
180
- replacement: `{${chain.replacement}}`
181
- };
182
- }
183
-
184
- function parseIfExpression(str, exprStart) {
185
- if (str[exprStart] !== '{') return null;
186
-
187
- let i = consumeWhitespace(str, exprStart + 1);
188
- if (!str.startsWith('if', i)) return null;
189
-
190
- const outer = parseBlock(str, exprStart);
191
- if (!outer) return null;
192
-
193
- const chain = parseIfChain(str, i);
194
- if (!chain) return null;
195
-
196
- return {
197
- start: exprStart,
198
- end: outer.end + 1,
199
- replacement: `{${chain.replacement}}`
200
- };
201
- }
202
-
203
- let prev = null;
204
- while (prev !== result) {
205
- prev = result;
206
-
207
- while (true) {
208
- const match = result.match(/\{\s*if\s*\(/);
209
- if (!match) break;
210
- const matchIndex = match.index;
211
-
212
- const parsed = parseIfExpression(result, matchIndex);
213
- if (!parsed) {
214
- console.warn('Unbalanced IF expression found, skipping transformation.');
215
- break;
216
- }
217
-
218
- const before = result.substring(0, parsed.start);
219
- const after = result.substring(parsed.end);
220
- result = before + parsed.replacement + after;
221
- }
222
-
223
- while (true) {
224
- const match = result.match(/(^|[\n\r])\s*if\s*\(/m);
225
- if (!match) break;
226
- const ifIndex = match.index + match[0].lastIndexOf('if');
227
-
228
- const parsed = parseIfStatement(result, ifIndex);
229
- if (!parsed) break;
230
-
231
- const before = result.substring(0, parsed.start);
232
- const after = result.substring(parsed.end);
233
- result = before + parsed.replacement + after;
234
- }
235
-
236
- while (true) {
237
- const match = result.match(/\{\s*for\s*\((.*?)\s+in\s+(.*?)\)\s*\{/);
238
- if (!match) break;
239
-
240
- const item = match[1];
241
- const list = match[2];
242
- const exprStart = match.index;
243
-
244
- const outer = parseBlock(result, exprStart);
245
- if (!outer) break;
246
-
247
- let i = consumeWhitespace(result, exprStart + 1);
248
- const head = result.slice(i);
249
- const mm = head.match(/^for\s*\((.*?)\s+in\s+(.*?)\)\s*\{/);
250
- if (!mm) break;
251
- const forStart = i;
252
- const blockStart = forStart + mm[0].length - 1;
253
- const block = parseBlock(result, blockStart);
254
- if (!block) break;
255
-
256
- const content = result.substring(block.start + 1, block.end);
257
- const replacement = `{${list}.map(${item} => (<Fragment>${content}</Fragment>))}`;
258
-
259
- const before = result.substring(0, exprStart);
260
- const after = result.substring(outer.end + 1);
261
-
262
- result = before + replacement + after;
263
- }
264
-
265
- while (true) {
266
- const match = result.match(/(^|[\n\r])\s*for\s*\((.*?)\s+in\s+(.*?)\)\s*\{/m);
267
- if (!match) break;
268
-
269
- const exprStart = match.index + match[0].lastIndexOf('for');
270
- const item = match[2];
271
- const list = match[3];
272
-
273
- const forHead = result.slice(exprStart);
274
- const mm = forHead.match(/^for\s*\((.*?)\s+in\s+(.*?)\)\s*\{/);
275
- if (!mm) break;
276
-
277
- const blockStart = exprStart + mm[0].length - 1;
278
- const block = parseBlock(result, blockStart);
279
- if (!block) break;
280
-
281
- const content = result.substring(block.start + 1, block.end);
282
- const replacement = `{${list}.map(${item} => (<Fragment>${content}</Fragment>))}`;
283
-
284
- const before = result.substring(0, exprStart);
285
- const after = result.substring(block.end + 1);
286
- result = before + replacement + after;
287
- }
288
- }
289
-
290
- function findJsxTagEnd(str, startIndex) {
291
- let inSingle = false;
292
- let inDouble = false;
293
- let inTemplate = false;
294
- let braceDepth = 0;
295
-
296
- for (let i = startIndex; i < str.length; i++) {
297
- const ch = str[i];
298
- const prevCh = i > 0 ? str[i - 1] : '';
299
-
300
- if (!inDouble && !inTemplate && ch === '\'' && prevCh !== '\\') inSingle = !inSingle;
301
- else if (!inSingle && !inTemplate && ch === '"' && prevCh !== '\\') inDouble = !inDouble;
302
- else if (!inSingle && !inDouble && ch === '`' && prevCh !== '\\') inTemplate = !inTemplate;
303
-
304
- if (inSingle || inDouble || inTemplate) continue;
305
-
306
- if (ch === '{') braceDepth++;
307
- else if (ch === '}') braceDepth = Math.max(0, braceDepth - 1);
308
- else if (ch === '>' && braceDepth === 0) return i;
309
- }
310
- return -1;
311
- }
312
-
313
- function transformSuspenseBlocks(str) {
314
- let out = str;
315
- let cursor = 0;
316
- while (true) {
317
- const openIndex = out.indexOf('<Suspense', cursor);
318
- if (openIndex === -1) break;
319
- const openEnd = findJsxTagEnd(out, openIndex);
320
- if (openEnd === -1) break;
321
-
322
- const openTagText = out.slice(openIndex, openEnd + 1);
323
- if (/\/>\s*$/.test(openTagText)) {
324
- cursor = openEnd + 1;
325
- continue;
326
- }
327
-
328
- let depth = 1;
329
- let i = openEnd + 1;
330
- let closeStart = -1;
331
- while (i < out.length) {
332
- const nextOpen = out.indexOf('<Suspense', i);
333
- const nextClose = out.indexOf('</Suspense>', i);
334
- if (nextClose === -1) break;
335
- if (nextOpen !== -1 && nextOpen < nextClose) {
336
- const innerOpenEnd = findJsxTagEnd(out, nextOpen);
337
- if (innerOpenEnd === -1) break;
338
- const innerOpenText = out.slice(nextOpen, innerOpenEnd + 1);
339
- if (!/\/>\s*$/.test(innerOpenText)) depth++;
340
- i = innerOpenEnd + 1;
341
- continue;
342
- }
343
-
344
- depth--;
345
- if (depth === 0) {
346
- closeStart = nextClose;
347
- break;
348
- }
349
- i = nextClose + '</Suspense>'.length;
350
- }
351
- if (closeStart === -1) break;
352
-
353
- const inner = out.slice(openEnd + 1, closeStart);
354
- const innerTrim = inner.trim();
355
- if (innerTrim.startsWith('{() =>')) {
356
- cursor = closeStart + '</Suspense>'.length;
357
- continue;
358
- }
359
-
360
- const wrapped = `{() => (<Fragment>${inner}</Fragment>)}`;
361
- out = out.slice(0, openEnd + 1) + wrapped + out.slice(closeStart);
362
- cursor = closeStart + wrapped.length + '</Suspense>'.length;
363
- }
364
- return out;
365
- }
366
-
367
- function transformProviderBlocks(str) {
368
- let out = str;
369
- let cursor = 0;
370
- while (true) {
371
- const dot = out.indexOf('.Provider', cursor);
372
- if (dot === -1) break;
373
- const lt = out.lastIndexOf('<', dot);
374
- if (lt === -1) break;
375
- const openEnd = findJsxTagEnd(out, lt);
376
- if (openEnd === -1) break;
377
-
378
- const openTagText = out.slice(lt, openEnd + 1);
379
- if (/\/>\s*$/.test(openTagText)) {
380
- cursor = openEnd + 1;
381
- continue;
382
- }
383
-
384
- const m = openTagText.match(/^<\s*([A-Za-z_$][\w$]*\.Provider)\b/);
385
- if (!m) {
386
- cursor = openEnd + 1;
387
- continue;
388
- }
389
- const tagName = m[1];
390
- const closeTag = `</${tagName}>`;
391
-
392
- let depth = 1;
393
- let i = openEnd + 1;
394
- let closeStart = -1;
395
- while (i < out.length) {
396
- const nextOpen = out.indexOf(`<${tagName}`, i);
397
- const nextClose = out.indexOf(closeTag, i);
398
- if (nextClose === -1) break;
399
- if (nextOpen !== -1 && nextOpen < nextClose) {
400
- const innerOpenEnd = findJsxTagEnd(out, nextOpen);
401
- if (innerOpenEnd === -1) break;
402
- const innerOpenText = out.slice(nextOpen, innerOpenEnd + 1);
403
- if (!/\/>\s*$/.test(innerOpenText)) depth++;
404
- i = innerOpenEnd + 1;
405
- continue;
406
- }
407
-
408
- depth--;
409
- if (depth === 0) {
410
- closeStart = nextClose;
411
- break;
412
- }
413
- i = nextClose + closeTag.length;
414
- }
415
- if (closeStart === -1) break;
416
-
417
- const inner = out.slice(openEnd + 1, closeStart);
418
- const innerTrim = inner.trim();
419
- if (innerTrim.startsWith('{() =>')) {
420
- cursor = closeStart + closeTag.length;
421
- continue;
422
- }
423
-
424
- const wrapped = `{() => (<Fragment>${inner}</Fragment>)}`;
425
- out = out.slice(0, openEnd + 1) + wrapped + out.slice(closeStart);
426
- cursor = closeStart + wrapped.length + closeTag.length;
427
- }
428
- return out;
429
- }
430
-
431
- result = transformSuspenseBlocks(result);
432
- result = transformProviderBlocks(result);
433
-
434
- // Make `signal()` reactive in JSX by passing a function to the runtime.
435
- // `{count()}` -> `{() => count()}``
436
- // `value={count()}` -> `value={() => count()}``
437
- // This is intentionally limited to zero-arg identifier calls.
438
- result = result
439
- .replace(/\{\s*([A-Za-z_$][\w$]*)\s*\(\s*\)\s*\}/g, '{() => $1()}')
440
- .replace(/=\{\s*([A-Za-z_$][\w$]*)\s*\(\s*\)\s*\}/g, '={() => $1()}');
441
-
442
- return result;
443
- }