round-core 0.0.7 → 0.0.9

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 (76) hide show
  1. package/README.md +62 -41
  2. package/dist/index.d.ts +341 -341
  3. package/dist/index.js +211 -192
  4. package/dist/vite-plugin.js +52 -3
  5. package/package.json +7 -4
  6. package/.github/workflows/benchmarks.yml +0 -44
  7. package/Round.png +0 -0
  8. package/benchmarks/apps/react/index.html +0 -9
  9. package/benchmarks/apps/react/main.jsx +0 -25
  10. package/benchmarks/apps/react/vite.config.js +0 -12
  11. package/benchmarks/apps/round/index.html +0 -11
  12. package/benchmarks/apps/round/main.jsx +0 -22
  13. package/benchmarks/apps/round/vite.config.js +0 -15
  14. package/benchmarks/bun.lock +0 -497
  15. package/benchmarks/dist-bench/react/assets/index-9KGqIPOU.js +0 -8
  16. package/benchmarks/dist-bench/react/index.html +0 -10
  17. package/benchmarks/dist-bench/round/assets/index-CBBIRhox.js +0 -52
  18. package/benchmarks/dist-bench/round/index.html +0 -8
  19. package/benchmarks/package.json +0 -22
  20. package/benchmarks/scripts/measure-build.js +0 -64
  21. package/benchmarks/tests/runtime.bench.js +0 -51
  22. package/benchmarks/vitest.config.js +0 -8
  23. package/bun.lock +0 -425
  24. package/cli.js +0 -2
  25. package/extension/.vscodeignore +0 -5
  26. package/extension/LICENSE +0 -21
  27. package/extension/cgmanifest.json +0 -45
  28. package/extension/extension.js +0 -163
  29. package/extension/images/round-config-dark.svg +0 -10
  30. package/extension/images/round-config-light.svg +0 -10
  31. package/extension/images/round-dark.svg +0 -10
  32. package/extension/images/round-light.svg +0 -10
  33. package/extension/javascript-language-configuration.json +0 -241
  34. package/extension/package-lock.json +0 -97
  35. package/extension/package.json +0 -119
  36. package/extension/package.nls.json +0 -4
  37. package/extension/round-0.1.0.vsix +0 -0
  38. package/extension/round-lsp/package-lock.json +0 -185
  39. package/extension/round-lsp/package.json +0 -21
  40. package/extension/round-lsp/src/round-transformer-lsp.js +0 -248
  41. package/extension/round-lsp/src/server.js +0 -396
  42. package/extension/snippets/javascript.code-snippets +0 -266
  43. package/extension/snippets/round.code-snippets +0 -109
  44. package/extension/syntaxes/JavaScript.tmLanguage.json +0 -6001
  45. package/extension/syntaxes/JavaScriptReact.tmLanguage.json +0 -6066
  46. package/extension/syntaxes/Readme.md +0 -12
  47. package/extension/syntaxes/Regular Expressions (JavaScript).tmLanguage +0 -237
  48. package/extension/syntaxes/Round.tmLanguage.json +0 -290
  49. package/extension/syntaxes/RoundInject.tmLanguage.json +0 -20
  50. package/extension/tags-language-configuration.json +0 -152
  51. package/extension/temp_astro/package-lock.json +0 -912
  52. package/extension/temp_astro/package.json +0 -16
  53. package/extension/types/round-core.d.ts +0 -326
  54. package/index.js +0 -2
  55. package/logo.svg +0 -10
  56. package/src/cli.js +0 -608
  57. package/src/compiler/index.js +0 -2
  58. package/src/compiler/transformer.js +0 -443
  59. package/src/compiler/vite-plugin.js +0 -472
  60. package/src/index.d.ts +0 -341
  61. package/src/index.js +0 -45
  62. package/src/runtime/context.js +0 -101
  63. package/src/runtime/dom.js +0 -403
  64. package/src/runtime/error-boundary.js +0 -48
  65. package/src/runtime/error-reporter.js +0 -13
  66. package/src/runtime/error-store.js +0 -85
  67. package/src/runtime/errors.js +0 -152
  68. package/src/runtime/lifecycle.js +0 -142
  69. package/src/runtime/markdown.js +0 -72
  70. package/src/runtime/router.js +0 -468
  71. package/src/runtime/signals.js +0 -548
  72. package/src/runtime/store.js +0 -215
  73. package/src/runtime/suspense.js +0 -128
  74. package/vite.config.build.js +0 -48
  75. package/vite.config.js +0 -10
  76. 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
- }