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,468 +0,0 @@
1
- import { signal, effect } from './signals.js';
2
- import { createElement } from './dom.js';
3
- import { createContext, readContext } from './context.js';
4
-
5
- const hasWindow = typeof window !== 'undefined' && typeof document !== 'undefined';
6
-
7
- const ROUTING_TRAILING_SLASH = (typeof __ROUND_ROUTING_TRAILING_SLASH__ !== 'undefined')
8
- ? Boolean(__ROUND_ROUTING_TRAILING_SLASH__)
9
- : true;
10
-
11
- const currentPath = signal(hasWindow ? window.location.pathname : '/');
12
- let listenerInitialized = false;
13
-
14
- let lastPathEvaluated = null;
15
- let hasMatchForPath = false;
16
-
17
- const pathHasMatch = signal(false);
18
- const pathEvalReady = signal(true);
19
-
20
- let defaultNotFoundComponent = null;
21
- let autoNotFoundMounted = false;
22
- let userProvidedNotFound = false;
23
-
24
- const RoutingContext = createContext('');
25
-
26
- function ensureListener() {
27
- if (!hasWindow || listenerInitialized) return;
28
- listenerInitialized = true;
29
-
30
- mountAutoNotFound();
31
-
32
- window.addEventListener('popstate', () => {
33
- currentPath(window.location.pathname);
34
- });
35
- }
36
-
37
- export function getPathname() {
38
- return normalizePathname(currentPath());
39
- }
40
-
41
- export function usePathname() {
42
- return () => normalizePathname(currentPath());
43
- }
44
-
45
- export function getLocation() {
46
- if (!hasWindow) {
47
- return { pathname: normalizePathname('/'), search: '', hash: '' };
48
- }
49
- return {
50
- pathname: normalizePathname(window.location.pathname),
51
- search: window.location.search ?? '',
52
- hash: window.location.hash ?? ''
53
- };
54
- }
55
-
56
- export function useLocation() {
57
- return () => {
58
- const pathname = normalizePathname(currentPath());
59
- if (!hasWindow) return { pathname, search: '', hash: '' };
60
- return { pathname, search: window.location.search ?? '', hash: window.location.hash ?? '' };
61
- };
62
- }
63
-
64
- export function getRouteReady() {
65
- const pathname = normalizePathname(currentPath());
66
- return Boolean(pathEvalReady()) && lastPathEvaluated === pathname;
67
- }
68
-
69
- export function useRouteReady() {
70
- return () => {
71
- const pathname = normalizePathname(currentPath());
72
- return Boolean(pathEvalReady()) && lastPathEvaluated === pathname;
73
- };
74
- }
75
-
76
- export function getIsNotFound() {
77
- const pathname = normalizePathname(currentPath());
78
- if (pathname === '/') return false;
79
- if (!(Boolean(pathEvalReady()) && lastPathEvaluated === pathname)) return false;
80
- return !Boolean(pathHasMatch());
81
- }
82
-
83
- export function useIsNotFound() {
84
- return () => {
85
- const pathname = normalizePathname(currentPath());
86
- if (pathname === '/') return false;
87
- if (!(Boolean(pathEvalReady()) && lastPathEvaluated === pathname)) return false;
88
- return !Boolean(pathHasMatch());
89
- };
90
- }
91
-
92
- function mountAutoNotFound() {
93
- if (!hasWindow || autoNotFoundMounted) return;
94
- autoNotFoundMounted = true;
95
-
96
- const host = document.getElementById('app') ?? document.body;
97
- const root = document.createElement('div');
98
- root.setAttribute('data-round-auto-notfound', '1');
99
- host.appendChild(root);
100
-
101
- const view = createElement('span', { style: { display: 'contents' } }, () => {
102
- if (userProvidedNotFound) return null;
103
-
104
- const pathname = normalizePathname(currentPath());
105
- const ready = pathEvalReady();
106
- const hasMatch = pathHasMatch();
107
-
108
- if (!ready) return null;
109
- if (lastPathEvaluated !== pathname) return null;
110
- if (hasMatch) return null;
111
-
112
- // Skip absolute 404 overlay for the root path if no match found,
113
- // allowing the base app to render its non-routed content.
114
- if (pathname === '/') return null;
115
-
116
- const Comp = defaultNotFoundComponent;
117
- if (typeof Comp === 'function') {
118
- return createElement(Comp, { pathname });
119
- }
120
-
121
- return createElement('div', { style: { padding: '16px' } },
122
- createElement('h1', null, '404'),
123
- createElement('p', null, 'Page not found: ', pathname)
124
- );
125
- });
126
-
127
- root.appendChild(view);
128
- }
129
-
130
- /**
131
- * Navigate to a different path programmatically.
132
- * @param {string} to The destination URL or path.
133
- * @param {object} [options] Navigation options (e.g., { replace: true }).
134
- */
135
- export function navigate(to, options = {}) {
136
- if (!hasWindow) return;
137
- ensureListener();
138
-
139
- const normalizedTo = normalizeTo(to);
140
- const replace = Boolean(options.replace);
141
- if (replace) window.history.replaceState({}, '', normalizedTo);
142
- else window.history.pushState({}, '', normalizedTo);
143
-
144
- currentPath(window.location.pathname);
145
- }
146
-
147
- function applyHead({ title, meta, links, icon, favicon }) {
148
- if (!hasWindow) return;
149
-
150
- if (typeof title === 'string') {
151
- document.title = title;
152
- }
153
-
154
- document.querySelectorAll('[data-round-head="1"]').forEach((n) => n.remove());
155
-
156
- const iconHref = icon ?? favicon;
157
- if (typeof iconHref === 'string' && iconHref.length) {
158
- const el = document.createElement('link');
159
- el.setAttribute('data-round-head', '1');
160
- el.setAttribute('rel', 'icon');
161
- el.setAttribute('href', iconHref);
162
- document.head.appendChild(el);
163
- }
164
-
165
- if (Array.isArray(links)) {
166
- links.forEach((l) => {
167
- if (!l || typeof l !== 'object') return;
168
- const el = document.createElement('link');
169
- el.setAttribute('data-round-head', '1');
170
- Object.entries(l).forEach(([k, v]) => {
171
- if (v === null || v === undefined) return;
172
- el.setAttribute(k, String(v));
173
- });
174
- document.head.appendChild(el);
175
- });
176
- }
177
-
178
- if (Array.isArray(meta)) {
179
- meta.forEach((entry) => {
180
- if (!entry) return;
181
- const el = document.createElement('meta');
182
- el.setAttribute('data-round-head', '1');
183
-
184
- if (Array.isArray(entry) && entry.length >= 2) {
185
- const [name, content] = entry;
186
- if (typeof name === 'string') el.setAttribute('name', name);
187
- el.setAttribute('content', String(content ?? ''));
188
- } else if (typeof entry === 'object') {
189
- Object.entries(entry).forEach(([k, v]) => {
190
- if (v === null || v === undefined) return;
191
- el.setAttribute(k, String(v));
192
- });
193
- } else {
194
- return;
195
- }
196
-
197
- document.head.appendChild(el);
198
- });
199
- } else if (meta && typeof meta === 'object') {
200
- Object.entries(meta).forEach(([name, content]) => {
201
- if (typeof name !== 'string') return;
202
- const el = document.createElement('meta');
203
- el.setAttribute('data-round-head', '1');
204
- el.setAttribute('name', name);
205
- el.setAttribute('content', String(content ?? ''));
206
- document.head.appendChild(el);
207
- });
208
- }
209
- }
210
-
211
- export function startHead(_head) {
212
- return _head;
213
- }
214
-
215
- function splitUrl(url) {
216
- const str = String(url ?? '');
217
- const hashIdx = str.indexOf('#');
218
- const queryIdx = str.indexOf('?');
219
- const cutIdx = (hashIdx === -1)
220
- ? queryIdx
221
- : (queryIdx === -1 ? hashIdx : Math.min(hashIdx, queryIdx));
222
-
223
- if (cutIdx === -1) return { path: str, suffix: '' };
224
- return { path: str.slice(0, cutIdx), suffix: str.slice(cutIdx) };
225
- }
226
-
227
- function normalizePathname(p) {
228
- let pathname = String(p ?? '/');
229
- if (!pathname.startsWith('/')) pathname = '/' + pathname;
230
- if (pathname.length > 1) {
231
- if (ROUTING_TRAILING_SLASH) {
232
- if (!pathname.endsWith('/')) pathname += '/';
233
- } else {
234
- if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
235
- }
236
- }
237
- return pathname;
238
- }
239
-
240
- function normalizeTo(to) {
241
- const { path, suffix } = splitUrl(to);
242
- if (!path.startsWith('/')) return String(to ?? '');
243
- return normalizePathname(path) + suffix;
244
- }
245
-
246
- function matchRoute(route, pathname, exact = true) {
247
- const r = normalizePathname(route);
248
- const p = normalizePathname(pathname);
249
- if (exact) return r === p;
250
- // Prefix match: either exactly the same, or p starts with r plus a slash
251
- return p === r || p.startsWith(r.endsWith('/') ? r : r + '/');
252
- }
253
-
254
- function beginPathEvaluation(pathname) {
255
- if (pathname !== lastPathEvaluated) {
256
- lastPathEvaluated = pathname;
257
- hasMatchForPath = false;
258
- pathHasMatch(false);
259
-
260
- pathEvalReady(false);
261
- setTimeout(() => {
262
- if (lastPathEvaluated !== pathname) return;
263
- pathEvalReady(true);
264
- }, 0);
265
- }
266
- }
267
-
268
- export function setNotFound(Component) {
269
- defaultNotFoundComponent = Component;
270
- }
271
-
272
- /**
273
- * Define a route that renders its children when the path matches.
274
- * @param {object} props Route properties.
275
- * @param {string} [props.route='/'] The path to match.
276
- * @param {boolean} [props.exact] Whether to use exact matching.
277
- * @param {string} [props.title] Page title to set when active.
278
- * @param {string} [props.description] Meta description to set when active.
279
- * @param {any} [props.children] Content to render.
280
- */
281
- export function Route(props = {}) {
282
- ensureListener();
283
-
284
- return createElement('span', { style: { display: 'contents' } }, () => {
285
- const parentPath = readContext(RoutingContext) || '';
286
- const pathname = normalizePathname(currentPath());
287
- beginPathEvaluation(pathname);
288
-
289
- const routeProp = props.route ?? '/';
290
- if (typeof routeProp === 'string' && !routeProp.startsWith('/')) {
291
- throw new Error(`Invalid route: "${routeProp}". All routes must start with a forward slash "/". (Nested under: "${parentPath || 'root'}")`);
292
- }
293
-
294
- let fullRoute = '';
295
- if (parentPath && parentPath !== '/') {
296
- const cleanParent = parentPath.endsWith('/') ? parentPath.slice(0, -1) : parentPath;
297
- const cleanChild = routeProp.startsWith('/') ? routeProp : '/' + routeProp;
298
-
299
- if (cleanChild.startsWith(cleanParent + '/') || cleanChild === cleanParent) {
300
- fullRoute = normalizePathname(cleanChild);
301
- } else {
302
- fullRoute = normalizePathname(cleanParent + cleanChild);
303
- }
304
- } else {
305
- fullRoute = normalizePathname(routeProp);
306
- }
307
-
308
- const isRoot = fullRoute === '/';
309
- const exact = props.exact !== undefined ? Boolean(props.exact) : isRoot;
310
-
311
- // For nested routing, we match as a prefix so parents stay rendered while children are active
312
- if (!matchRoute(fullRoute, pathname, exact)) return null;
313
-
314
- // If it's an exact match of the FULL segments, mark as matched for 404 purposes
315
- if (matchRoute(fullRoute, pathname, true)) {
316
- hasMatchForPath = true;
317
- pathHasMatch(true);
318
- }
319
-
320
- const mergedHead = (props.head && typeof props.head === 'object') ? props.head : {};
321
- const meta = props.description
322
- ? ([{ name: 'description', content: String(props.description) }].concat(mergedHead.meta ?? props.meta ?? []))
323
- : (mergedHead.meta ?? props.meta);
324
- const links = mergedHead.links ?? props.links;
325
- const title = mergedHead.title ?? props.title;
326
- const icon = mergedHead.icon ?? props.icon;
327
- const favicon = mergedHead.favicon ?? props.favicon;
328
-
329
- applyHead({ title, meta, links, icon, favicon });
330
-
331
- // Provide the current full path to nested routes
332
- return createElement(RoutingContext.Provider, { value: fullRoute }, props.children);
333
- });
334
- }
335
-
336
- /**
337
- * An alias for Route, typically used for top-level pages.
338
- * @param {object} props Page properties (same as Route).
339
- */
340
- export function Page(props = {}) {
341
- ensureListener();
342
-
343
- return createElement('span', { style: { display: 'contents' } }, () => {
344
- const parentPath = readContext(RoutingContext) || '';
345
- const pathname = normalizePathname(currentPath());
346
- beginPathEvaluation(pathname);
347
-
348
- const routeProp = props.route ?? '/';
349
- if (typeof routeProp === 'string' && !routeProp.startsWith('/')) {
350
- throw new Error(`Invalid route: "${routeProp}". All routes must start with a forward slash "/". (Nested under: "${parentPath || 'root'}")`);
351
- }
352
-
353
- let fullRoute = '';
354
- if (parentPath && parentPath !== '/') {
355
- const cleanParent = parentPath.endsWith('/') ? parentPath.slice(0, -1) : parentPath;
356
- const cleanChild = routeProp.startsWith('/') ? routeProp : '/' + routeProp;
357
-
358
- if (cleanChild.startsWith(cleanParent + '/') || cleanChild === cleanParent) {
359
- fullRoute = normalizePathname(cleanChild);
360
- } else {
361
- fullRoute = normalizePathname(cleanParent + cleanChild);
362
- }
363
- } else {
364
- fullRoute = normalizePathname(routeProp);
365
- }
366
-
367
- const isRoot = fullRoute === '/';
368
- const exact = props.exact !== undefined ? Boolean(props.exact) : isRoot;
369
-
370
- if (!matchRoute(fullRoute, pathname, exact)) return null;
371
-
372
- if (matchRoute(fullRoute, pathname, true)) {
373
- hasMatchForPath = true;
374
- pathHasMatch(true);
375
- }
376
-
377
- const mergedHead = (props.head && typeof props.head === 'object') ? props.head : {};
378
- const meta = props.description
379
- ? ([{ name: 'description', content: String(props.description) }].concat(mergedHead.meta ?? props.meta ?? []))
380
- : (mergedHead.meta ?? props.meta);
381
- const links = mergedHead.links ?? props.links;
382
- const title = mergedHead.title ?? props.title;
383
- const icon = mergedHead.icon ?? props.icon;
384
- const favicon = mergedHead.favicon ?? props.favicon;
385
-
386
- applyHead({ title, meta, links, icon, favicon });
387
-
388
- return createElement(RoutingContext.Provider, { value: fullRoute }, props.children);
389
- });
390
- }
391
-
392
- /**
393
- * Define a fallback component or content for when no routes match.
394
- */
395
- export function NotFound(props = {}) {
396
- ensureListener();
397
-
398
- userProvidedNotFound = true;
399
-
400
- return createElement('span', { style: { display: 'contents' } }, () => {
401
- const pathname = normalizePathname(currentPath());
402
- beginPathEvaluation(pathname);
403
-
404
- const ready = pathEvalReady();
405
- const hasMatch = pathHasMatch();
406
- if (!ready) return null;
407
- if (lastPathEvaluated !== pathname) return null;
408
-
409
- if (hasMatch) return null;
410
- if (pathname === '/') return null;
411
-
412
- const Comp = props.component ?? defaultNotFoundComponent;
413
- if (typeof Comp === 'function') {
414
- return createElement(Comp, { pathname });
415
- }
416
-
417
- if (props.children !== undefined) return props.children;
418
-
419
- return createElement('div', { style: { padding: '16px' } },
420
- createElement('h1', null, '404'),
421
- createElement('p', null, 'Page not found: ', pathname)
422
- );
423
- });
424
- }
425
-
426
- /**
427
- * A standard link component that performs SPA navigation.
428
- * @param {object} props Link properties.
429
- * @param {string} [props.href] The destination path.
430
- * @param {boolean} [props.spa=true] Use SPA navigation (prevents reload).
431
- * @param {any} [props.children] Link content.
432
- */
433
- export function Link(props = {}) {
434
- ensureListener();
435
-
436
- const rawHref = props.href ?? props.to ?? '#';
437
- const href = spaNormalizeHref(rawHref);
438
-
439
- const spa = props.spa !== undefined ? Boolean(props.spa) : true;
440
- const reload = Boolean(props.reload);
441
-
442
- const onClick = (e) => {
443
- if (typeof props.onClick === 'function') props.onClick(e);
444
- if (e.defaultPrevented) return;
445
-
446
- // Classic navigation: allow the browser to reload.
447
- if (!spa || reload) return;
448
-
449
- if (e.button !== 0) return;
450
- if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
451
-
452
- e.preventDefault();
453
- navigate(href);
454
- };
455
-
456
- const { children, to, ...rest } = props;
457
- const normalizedChildren = Array.isArray(children)
458
- ? children
459
- : (children === undefined || children === null ? [] : [children]);
460
-
461
- return createElement('a', { ...rest, href, onClick }, ...normalizedChildren);
462
- }
463
-
464
- function spaNormalizeHref(href) {
465
- const str = String(href ?? '#');
466
- if (!str.startsWith('/')) return str;
467
- return normalizeTo(str);
468
- }