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,403 +0,0 @@
1
- import { effect, untrack } from './signals.js';
2
- import { runInLifecycle, createComponentInstance, mountComponent, initLifecycleRoot } from './lifecycle.js';
3
- import { reportErrorSafe } from './error-reporter.js';
4
- import { captureContext, runInContext, readContext } from './context.js';
5
- import { SuspenseContext } from './suspense.js';
6
-
7
-
8
- let isObserverInitialized = false;
9
-
10
- const warnedSignals = new Set();
11
-
12
- function isPromiseLike(v) {
13
- return v && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function';
14
- }
15
-
16
- function warnSignalDirectUsage(fn, kind) {
17
- try {
18
- if (typeof fn !== 'function') return;
19
- if (typeof fn.peek !== 'function') return;
20
- if (!('value' in fn)) return;
21
- // Using signals as dynamic children/props is a supported pattern.
22
- if (kind === 'child') return;
23
- if (typeof kind === 'string' && kind.startsWith('prop:')) return;
24
- const key = `${kind}:${fn.name ?? 'signal'}`;
25
- if (warnedSignals.has(key)) return;
26
- warnedSignals.add(key);
27
- console.warn(`[round] Prefer {signal()} (reactive) or {signal.value} (static). Direct {signal} usage is allowed but discouraged.`);
28
- } catch {
29
- }
30
- }
31
-
32
- /**
33
- * Create a DOM element or instance a component.
34
- * @param {string | Function} tag HTML tag name or Component function.
35
- * @param {object} [props] Element attributes or component props.
36
- * @param {...any} children Child nodes.
37
- * @returns {Node} The resulting DOM node.
38
- */
39
- export function createElement(tag, props = {}, ...children) {
40
- if (typeof tag === 'function') {
41
- const componentInstance = createComponentInstance();
42
- const componentName = tag?.name ?? 'Anonymous';
43
- componentInstance.name = componentName;
44
-
45
- let node = runInLifecycle(componentInstance, () => {
46
- const componentProps = { ...props, children };
47
- try {
48
- const res = untrack(() => tag(componentProps));
49
- if (isPromiseLike(res)) throw res;
50
- return res;
51
- } catch (e) {
52
- if (isPromiseLike(e)) {
53
- const suspense = readContext(SuspenseContext);
54
- if (!suspense) {
55
- throw new Error("cannot instance a lazy component outside a suspense");
56
- }
57
- throw e;
58
- }
59
- reportErrorSafe(e, { phase: 'component.render', component: componentName });
60
- return createElement('div', { style: { padding: '16px' } }, `Error in ${componentName}`);
61
- }
62
- });
63
-
64
- if (Array.isArray(node)) {
65
- const wrapper = document.createElement('span');
66
- wrapper.style.display = 'contents';
67
- node.forEach(n => appendChild(wrapper, n));
68
- node = wrapper;
69
- }
70
-
71
- if (node instanceof Node) {
72
- node._componentInstance = componentInstance;
73
- componentInstance.nodes.push(node);
74
-
75
- componentInstance.mountTimerId = setTimeout(() => {
76
- componentInstance.mountTimerId = null;
77
- mountComponent(componentInstance);
78
- }, 0);
79
- }
80
-
81
- return node;
82
- }
83
-
84
- if (typeof tag === 'string') {
85
- const isCustomElement = tag.includes('-');
86
-
87
- const isStandard = /^(a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|slot|small|source|span|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|menu|animate|animateMotion|animateTransform|circle|clipPath|defs|desc|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|foreignObject|g|image|line|linearGradient|marker|mask|metadata|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|stop|switch|symbol|text|textPath|tspan|use|view)$/.test(tag);
88
-
89
- // __ROUND_CUSTOM_TAGS__ is injected by the vite plugin from round.config.json
90
- const isCustomConfigured = typeof __ROUND_CUSTOM_TAGS__ !== 'undefined' && __ROUND_CUSTOM_TAGS__.includes(tag);
91
-
92
- if (!isCustomElement && !isStandard && !isCustomConfigured && /^[a-z]/.test(tag)) {
93
- throw new Error(`Component names must start with an uppercase letter: <${tag} />`);
94
- }
95
- }
96
-
97
- const element = document.createElement(tag);
98
-
99
- if (props) {
100
- Object.entries(props).forEach(([key, value]) => {
101
- if (key === 'bind:value' || key === 'bind:checked') {
102
- const isSignalLike = typeof value === 'function' && typeof value.peek === 'function' && ('value' in value);
103
- const isBindable = isSignalLike && value.bind === true;
104
-
105
- if (!isSignalLike) {
106
- try {
107
- console.warn('[round] bind:* expects a signal/bindable. Example: const name = bindable(\'\'); <input bind:value={name} />');
108
- } catch {
109
- }
110
- return;
111
- }
112
-
113
- if (!isBindable) {
114
- try {
115
- console.warn('[round] bind:* is intended to be used with bindable(). Plain signal() is accepted but discouraged.');
116
- } catch {
117
- }
118
- }
119
-
120
- const isValueBinding = key === 'bind:value';
121
- const isCheckedBinding = key === 'bind:checked';
122
- const el = element;
123
- const tagName = String(el.tagName ?? '').toLowerCase();
124
- const type = String(el.getAttribute?.('type') ?? '').toLowerCase();
125
-
126
- const isInput = tagName === 'input';
127
- const isTextarea = tagName === 'textarea';
128
- const isSelect = tagName === 'select';
129
-
130
- if (isCheckedBinding && !(isInput && (type === 'checkbox' || type === 'radio'))) {
131
- try {
132
- console.warn(`[round] bind:checked is only supported on <input type="checkbox|radio">. Got <${tagName}${type ? ` type=\"${type}\"` : ''}>.`);
133
- } catch {
134
- }
135
- return;
136
- }
137
-
138
- if (isValueBinding && !(isInput || isTextarea || isSelect)) {
139
- try {
140
- console.warn(`[round] bind:value is only supported on <input>, <textarea>, and <select>. Got <${tagName}>.`);
141
- } catch {
142
- }
143
- return;
144
- }
145
-
146
- const coerceFromDom = () => {
147
- if (isCheckedBinding) {
148
- if (type === 'radio') {
149
- return Boolean(el.checked);
150
- }
151
- return Boolean(el.checked);
152
- }
153
-
154
- if (isInput && type === 'number') {
155
- const raw = el.value;
156
- if (raw === '') return '';
157
- const n = Number(raw);
158
- return Number.isFinite(n) ? n : raw;
159
- }
160
-
161
- if (isSelect && el.multiple) {
162
- try {
163
- return Array.from(el.selectedOptions ?? []).map(o => o.value);
164
- } catch {
165
- return [];
166
- }
167
- }
168
-
169
- return el.value;
170
- };
171
-
172
- const writeToDom = (v) => {
173
- if (isCheckedBinding) {
174
- const b = Boolean(v);
175
- if (type === 'radio') {
176
- el.checked = b;
177
- } else {
178
- el.checked = b;
179
- }
180
- return;
181
- }
182
-
183
- if (isSelect && el.multiple) {
184
- const arr = Array.isArray(v) ? v.map(x => String(x)) : [];
185
- try {
186
- Array.from(el.options ?? []).forEach(opt => {
187
- opt.selected = arr.includes(opt.value);
188
- });
189
- } catch {
190
- }
191
- return;
192
- }
193
-
194
- el.value = v ?? '';
195
- };
196
-
197
- const warnTypeMismatch = (next) => {
198
- try {
199
- if (isCheckedBinding && typeof next !== 'boolean') {
200
- console.warn('[round] bind:checked expects a boolean signal value.');
201
- }
202
- if (isValueBinding && isSelect && el.multiple && !Array.isArray(next)) {
203
- console.warn('[round] bind:value on <select multiple> expects an array signal value.');
204
- }
205
- if (isValueBinding && isInput && type === 'number' && !(typeof next === 'number' || typeof next === 'string')) {
206
- console.warn('[round] bind:value on <input type="number"> expects number|string (empty string allowed).');
207
- }
208
- } catch {
209
- }
210
- };
211
-
212
- effect(() => {
213
- const v = value();
214
- warnTypeMismatch(v);
215
- writeToDom(v);
216
- }, { onLoad: false });
217
-
218
- const validateOn = isValueBinding && value && typeof value === 'function'
219
- ? value.__round_validateOn
220
- : null;
221
- const valueEvent = (validateOn === 'blur') ? 'blur' : (isSelect ? 'change' : 'input');
222
- const eventName = isCheckedBinding ? 'change' : valueEvent;
223
- el.addEventListener(eventName, (e) => {
224
- try {
225
- const target = e.currentTarget;
226
- if (!target) return;
227
- const next = coerceFromDom();
228
- value(next);
229
- } catch {
230
- }
231
- });
232
- return;
233
- }
234
-
235
- if (key.startsWith('on') && typeof value === 'function') {
236
- element.addEventListener(key.toLowerCase().substring(2), value);
237
- return;
238
- }
239
-
240
- if (key === 'dangerouslySetInnerHTML') {
241
- if (typeof value === 'function') {
242
- effect(() => {
243
- const v = value();
244
- if (v && typeof v === 'object' && '__html' in v) {
245
- element.innerHTML = v.__html ?? '';
246
- }
247
- }, { onLoad: false });
248
- } else if (value && typeof value === 'object' && '__html' in value) {
249
- element.innerHTML = value.__html ?? '';
250
- }
251
- return;
252
- }
253
-
254
- if (key === 'style') {
255
- if (typeof value === 'function') {
256
- effect(() => {
257
- const v = value();
258
- if (v && typeof v === 'object') {
259
- Object.assign(element.style, v);
260
- }
261
- }, { onLoad: false });
262
- return;
263
- }
264
- if (value && typeof value === 'object') {
265
- Object.assign(element.style, value);
266
- return;
267
- }
268
- }
269
-
270
- if (typeof value === 'function') {
271
- warnSignalDirectUsage(value, `prop:${key}`);
272
- effect(() => {
273
- const val = value();
274
- if (key === 'className') element.className = val;
275
- else if (key === 'value') element.value = val;
276
- else if (key === 'checked') element.checked = Boolean(val);
277
- else element.setAttribute(key, val);
278
- }, { onLoad: false });
279
- return;
280
- }
281
-
282
- if (key === 'classList') {
283
- if (value && typeof value === 'object') {
284
- Object.entries(value).forEach(([className, condition]) => {
285
- if (typeof condition === 'function') {
286
- effect(() => {
287
- element.classList.toggle(className, !!condition());
288
- }, { onLoad: false });
289
- } else {
290
- element.classList.toggle(className, !!condition);
291
- }
292
- });
293
- }
294
- return;
295
- }
296
-
297
- if (key === 'className') element.className = value;
298
- else if (key === 'value') element.value = value;
299
- else if (key === 'checked') element.checked = Boolean(value);
300
- else element.setAttribute(key, value);
301
- });
302
- }
303
-
304
- children.forEach(child => appendChild(element, child));
305
-
306
- return element;
307
- }
308
-
309
- function appendChild(parent, child) {
310
- if (child === null || child === undefined) return;
311
-
312
- if (Array.isArray(child)) {
313
- child.forEach(c => appendChild(parent, c));
314
- return;
315
- }
316
-
317
- if (typeof child === 'string' || typeof child === 'number') {
318
- parent.appendChild(document.createTextNode(child));
319
- return;
320
- }
321
-
322
- if (typeof child === 'function') {
323
- warnSignalDirectUsage(child, 'child');
324
- const placeholder = document.createTextNode('');
325
- parent.appendChild(placeholder);
326
-
327
- let currentNode = placeholder;
328
-
329
- const ctxSnapshot = captureContext();
330
-
331
- effect(() => {
332
- runInContext(ctxSnapshot, () => {
333
- let val;
334
- try {
335
- val = child();
336
- if (isPromiseLike(val)) throw val;
337
- } catch (e) {
338
- if (isPromiseLike(e)) {
339
- const suspense = readContext(SuspenseContext);
340
- if (suspense && typeof suspense.register === 'function') {
341
- suspense.register(e);
342
- return;
343
- }
344
- throw new Error("cannot instance a lazy component outside a suspense");
345
- }
346
- reportErrorSafe(e, { phase: 'child.dynamic' });
347
- val = createElement('div', { style: { padding: '16px' } }, 'Error');
348
- }
349
-
350
- if (Array.isArray(val)) {
351
- if (!(currentNode instanceof Element) || !currentNode._roundArrayWrapper) {
352
- const wrapper = document.createElement('span');
353
- wrapper.style.display = 'contents';
354
- wrapper._roundArrayWrapper = true;
355
- if (currentNode.parentNode) {
356
- currentNode.parentNode.replaceChild(wrapper, currentNode);
357
- currentNode = wrapper;
358
- }
359
- }
360
-
361
- while (currentNode.firstChild) currentNode.removeChild(currentNode.firstChild);
362
- val.forEach(v => appendChild(currentNode, v));
363
- return;
364
- }
365
-
366
- if (val instanceof Node) {
367
- if (currentNode !== val) {
368
- if (currentNode.parentNode) {
369
- currentNode.parentNode.replaceChild(val, currentNode);
370
- currentNode = val;
371
- }
372
- }
373
- }
374
- else {
375
- const textContent = (val === null || val === undefined) ? '' : val;
376
-
377
- if (currentNode instanceof Element) {
378
- const newText = document.createTextNode(textContent);
379
- if (currentNode.parentNode) {
380
- currentNode.parentNode.replaceChild(newText, currentNode);
381
- currentNode = newText;
382
- }
383
- } else {
384
- currentNode.textContent = textContent;
385
- }
386
- }
387
- });
388
- }, { onLoad: false });
389
- return;
390
- }
391
-
392
- if (child instanceof Node) {
393
- parent.appendChild(child);
394
- return;
395
- }
396
- }
397
-
398
- /**
399
- * A grouping component that returns its children without a wrapper element.
400
- */
401
- export function Fragment(props) {
402
- return props.children;
403
- }
@@ -1,48 +0,0 @@
1
- import { signal } from './signals.js';
2
- import { createElement } from './dom.js';
3
- import { reportError } from './error-store.js';
4
-
5
- export function ErrorBoundary(props = {}) {
6
- const error = signal(null);
7
-
8
- const name = props.name ?? 'ErrorBoundary';
9
- const fallback = props.fallback;
10
- const resetKey = props.resetKey;
11
-
12
- let lastResetKey = resetKey;
13
-
14
- return createElement('span', { style: { display: 'contents' } }, () => {
15
- if (resetKey !== undefined && resetKey !== lastResetKey) {
16
- lastResetKey = resetKey;
17
- if (error()) error(null);
18
- }
19
-
20
- const err = error();
21
- if (err) {
22
- if (typeof fallback === 'function') {
23
- try {
24
- return fallback({ error: err });
25
- } catch (e) {
26
- reportError(e, { phase: 'ErrorBoundary.fallback', component: name });
27
- return createElement('div', { style: { padding: '16px' } }, 'ErrorBoundary fallback crashed');
28
- }
29
- }
30
- if (fallback !== undefined) return fallback;
31
- return createElement('div', { style: { padding: '16px' } }, 'Something went wrong.');
32
- }
33
-
34
- const renderFn = (typeof props.render === 'function')
35
- ? props.render
36
- : (typeof props.children === 'function' ? props.children : null);
37
-
38
- if (typeof renderFn !== 'function') return props.children ?? null;
39
-
40
- try {
41
- return renderFn();
42
- } catch (e) {
43
- if (!error() || error() !== e) error(e);
44
- reportError(e, { phase: 'ErrorBoundary.render', component: name });
45
- return null;
46
- }
47
- });
48
- }
@@ -1,13 +0,0 @@
1
- let reporter = null;
2
-
3
- export function setErrorReporter(fn) {
4
- reporter = typeof fn === 'function' ? fn : null;
5
- }
6
-
7
- export function reportErrorSafe(error, info) {
8
- if (!reporter) return;
9
- try {
10
- reporter(error, info);
11
- } catch {
12
- }
13
- }
@@ -1,85 +0,0 @@
1
- import { signal } from './signals.js';
2
- import { setErrorReporter } from './error-reporter.js';
3
-
4
- const errors = signal([]);
5
-
6
- let lastSentKey = null;
7
- let lastSentAt = 0;
8
-
9
- let lastStoredKey = null;
10
- let lastStoredAt = 0;
11
-
12
- export function reportError(error, info = {}) {
13
- const err = error instanceof Error ? error : new Error(String(error));
14
- const stack = err.stack ? String(err.stack) : '';
15
- const message = err.message;
16
- const phase = info.phase ?? null;
17
- const component = info.component ?? null;
18
- const key = `${message}|${component ?? ''}|${phase ?? ''}|${stack}`;
19
- const now = Date.now();
20
-
21
- if (lastStoredKey === key && (now - lastStoredAt) < 1500) {
22
- return;
23
- }
24
- lastStoredKey = key;
25
- lastStoredAt = now;
26
-
27
- const entry = {
28
- error: err,
29
- message,
30
- stack,
31
- phase,
32
- component,
33
- time: now
34
- };
35
-
36
- const current = typeof errors.peek === 'function' ? errors.peek() : errors();
37
- errors([entry, ...(Array.isArray(current) ? current : [])]);
38
-
39
- try {
40
- const where = entry.component ? ` in ${entry.component}` : '';
41
- const phase = entry.phase ? ` (${entry.phase})` : '';
42
- const label = `[round] Runtime error${where}${phase}`;
43
-
44
- if (typeof console.groupCollapsed === 'function') {
45
- console.groupCollapsed(label);
46
- console.error(entry.error);
47
- if (entry.stack) console.log(entry.stack);
48
- if (info && Object.keys(info).length) console.log('info:', info);
49
- console.groupEnd();
50
- } else {
51
- console.error(label);
52
- console.error(entry.error);
53
- if (entry.stack) console.log(entry.stack);
54
- if (info && Object.keys(info).length) console.log('info:', info);
55
- }
56
- } catch {
57
- }
58
-
59
- try {
60
- if (import.meta?.hot && typeof import.meta.hot.send === 'function') {
61
- if (lastSentKey !== key || (now - lastSentAt) > 1500) {
62
- lastSentKey = key;
63
- lastSentAt = now;
64
- import.meta.hot.send('round:runtime-error', {
65
- message: entry.message,
66
- stack: entry.stack ? String(entry.stack) : '',
67
- phase: entry.phase,
68
- component: entry.component,
69
- time: entry.time
70
- });
71
- }
72
- }
73
- } catch {
74
- }
75
- }
76
-
77
- export function clearErrors() {
78
- errors([]);
79
- }
80
-
81
- export function useErrors() {
82
- return errors;
83
- }
84
-
85
- setErrorReporter(reportError);