@emberkit/core 0.1.2-alpha.0

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 (62) hide show
  1. package/LICENSE +199 -0
  2. package/dist/boundaries/error-boundary.js +70 -0
  3. package/dist/boundaries/errors.js +72 -0
  4. package/dist/boundaries/index.js +3 -0
  5. package/dist/boundaries/loading-boundary.js +106 -0
  6. package/dist/cache/index.js +213 -0
  7. package/dist/compiler/compiler.js +44 -0
  8. package/dist/compiler/helpers/attributes.js +35 -0
  9. package/dist/compiler/helpers/utils.js +31 -0
  10. package/dist/compiler/index.js +4 -0
  11. package/dist/compiler/types.js +3 -0
  12. package/dist/context/index.js +51 -0
  13. package/dist/context/types.js +1 -0
  14. package/dist/dev-server/index.js +121 -0
  15. package/dist/forms/index.js +164 -0
  16. package/dist/forms/mutations.js +258 -0
  17. package/dist/hmr/client.js +84 -0
  18. package/dist/hmr/index.js +2 -0
  19. package/dist/hmr/types.js +133 -0
  20. package/dist/hydration/helpers/analyzer.js +94 -0
  21. package/dist/hydration/helpers/hydration.js +129 -0
  22. package/dist/hydration/index.js +3 -0
  23. package/dist/hydration/types.js +18 -0
  24. package/dist/image/index.js +34 -0
  25. package/dist/image/processor.js +143 -0
  26. package/dist/index.js +16 -0
  27. package/dist/jsx-dev-runtime.js +7 -0
  28. package/dist/jsx-runtime.js +7 -0
  29. package/dist/loader/helpers/loader.js +61 -0
  30. package/dist/loader/index.js +2 -0
  31. package/dist/loader/types.js +14 -0
  32. package/dist/markdown/index.js +365 -0
  33. package/dist/mdx/index.js +156 -0
  34. package/dist/mdx/loader.js +6 -0
  35. package/dist/meta/head-registry.js +15 -0
  36. package/dist/meta/head.js +100 -0
  37. package/dist/meta/index.js +210 -0
  38. package/dist/navigation/helpers/navigation.js +53 -0
  39. package/dist/navigation/helpers/useNavigate.js +10 -0
  40. package/dist/navigation/index.js +3 -0
  41. package/dist/navigation/types.js +2 -0
  42. package/dist/plugin/index.js +74 -0
  43. package/dist/router/helpers/path.js +74 -0
  44. package/dist/router/helpers/route.js +109 -0
  45. package/dist/router/index.js +110 -0
  46. package/dist/router/types.js +5 -0
  47. package/dist/runtime/helpers/element.js +52 -0
  48. package/dist/runtime/helpers/render.js +121 -0
  49. package/dist/runtime/index.js +132 -0
  50. package/dist/runtime/types.js +1 -0
  51. package/dist/signals/helpers/core.js +96 -0
  52. package/dist/signals/helpers/utils.js +22 -0
  53. package/dist/signals/index.js +3 -0
  54. package/dist/signals/types.js +1 -0
  55. package/dist/ssg/index.js +119 -0
  56. package/dist/ssr/helpers/render-html.js +55 -0
  57. package/dist/ssr/helpers/ssr.js +90 -0
  58. package/dist/ssr/index.js +3 -0
  59. package/dist/ssr/types.js +24 -0
  60. package/dist/vite-plugin/index.js +650 -0
  61. package/dist/vite-plugin/types.js +13 -0
  62. package/package.json +66 -0
@@ -0,0 +1,96 @@
1
+ import { DEFAULT_EQUALS } from '../types.js';
2
+ export function createSignal(initialValue, options = {}) {
3
+ const { equals = DEFAULT_EQUALS } = options;
4
+ let value = initialValue;
5
+ function notify() {
6
+ void equals;
7
+ }
8
+ function getter() {
9
+ return value;
10
+ }
11
+ function setter(newValue) {
12
+ if (!equals(value, newValue)) {
13
+ value = newValue;
14
+ notify();
15
+ }
16
+ }
17
+ const signal = {
18
+ get value() {
19
+ return value;
20
+ },
21
+ set value(newValue) {
22
+ if (!equals(value, newValue)) {
23
+ value = newValue;
24
+ notify();
25
+ }
26
+ },
27
+ peek() {
28
+ return value;
29
+ },
30
+ [Symbol.iterator]() {
31
+ let index = 0;
32
+ const methods = [getter, setter];
33
+ return {
34
+ next: () => {
35
+ if (index < methods.length) {
36
+ return { value: methods[index++], done: false };
37
+ }
38
+ return { done: true };
39
+ },
40
+ [Symbol.iterator]() {
41
+ return this;
42
+ },
43
+ };
44
+ },
45
+ };
46
+ // Make it array-like for tuple destructuring: [getter, setter]
47
+ signal[0] = getter;
48
+ signal[1] = setter;
49
+ signal.length = 2;
50
+ return signal;
51
+ }
52
+ export function createMemo(computation, _options) {
53
+ void _options;
54
+ let value;
55
+ let isStale = true;
56
+ return {
57
+ get value() {
58
+ if (isStale) {
59
+ value = computation();
60
+ isStale = false;
61
+ }
62
+ return value;
63
+ },
64
+ peek() {
65
+ if (isStale) {
66
+ return computation();
67
+ }
68
+ return value;
69
+ },
70
+ };
71
+ }
72
+ export function createEffect(callback) {
73
+ let cleanup;
74
+ function run() {
75
+ if (cleanup) {
76
+ const fn = cleanup;
77
+ cleanup = undefined;
78
+ fn();
79
+ }
80
+ cleanup = callback();
81
+ }
82
+ run();
83
+ return () => {
84
+ if (cleanup) {
85
+ const fn = cleanup;
86
+ cleanup = undefined;
87
+ fn();
88
+ }
89
+ };
90
+ }
91
+ export function batch(fn) {
92
+ return fn();
93
+ }
94
+ export function untrack(fn) {
95
+ return fn();
96
+ }
@@ -0,0 +1,22 @@
1
+ export function isSignal(value) {
2
+ if (typeof value !== 'object' || value === null)
3
+ return false;
4
+ return 'value' in value && 'peek' in value;
5
+ }
6
+ export function isReadonlySignal(signal) {
7
+ return !('value' in signal && Object.getOwnPropertyDescriptor(signal, 'value')?.set);
8
+ }
9
+ export function getSignalValue(signal) {
10
+ return signal.value;
11
+ }
12
+ export function setSignalValue(signal, value) {
13
+ if ('value' in signal) {
14
+ const descriptor = Object.getOwnPropertyDescriptor(signal, 'value');
15
+ if (descriptor && descriptor.writable) {
16
+ signal.value = value;
17
+ }
18
+ }
19
+ }
20
+ export function readSignal(signal) {
21
+ return signal.value;
22
+ }
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './helpers/core.js';
3
+ export * from './helpers/utils.js';
@@ -0,0 +1 @@
1
+ export const DEFAULT_EQUALS = (prev, next) => prev === next;
@@ -0,0 +1,119 @@
1
+ export class SSGBuilder {
2
+ config;
3
+ manifest;
4
+ constructor(config) {
5
+ this.config = {
6
+ outDir: config.outDir,
7
+ routes: config.routes,
8
+ prerender: config.prerender ?? true,
9
+ cacheControl: config.cacheControl ?? 'public, max-age=3600',
10
+ };
11
+ this.manifest = {
12
+ pages: new Map(),
13
+ errors: new Map(),
14
+ buildTime: 0,
15
+ };
16
+ }
17
+ async build(renderFn) {
18
+ const start = Date.now();
19
+ const pages = await Promise.allSettled(this.config.routes.map(async (route) => {
20
+ const html = await renderFn(route);
21
+ const page = {
22
+ path: route,
23
+ html,
24
+ status: 200,
25
+ headers: {
26
+ 'Content-Type': 'text/html',
27
+ 'Cache-Control': this.config.cacheControl,
28
+ },
29
+ };
30
+ this.manifest.pages.set(route, html);
31
+ return page;
32
+ }));
33
+ for (let i = 0; i < pages.length; i++) {
34
+ const result = pages[i];
35
+ if (result.status === 'rejected') {
36
+ this.manifest.errors.set(this.config.routes[i], result.reason);
37
+ }
38
+ }
39
+ this.manifest.buildTime = Date.now() - start;
40
+ return this.manifest;
41
+ }
42
+ getManifest() {
43
+ return this.manifest;
44
+ }
45
+ getBuildStats() {
46
+ return {
47
+ pages: this.manifest.pages.size,
48
+ errors: this.manifest.errors.size,
49
+ time: this.manifest.buildTime,
50
+ };
51
+ }
52
+ }
53
+ export async function generateStaticPages(routes, renderFn, options = {}) {
54
+ const builder = new SSGBuilder({
55
+ outDir: options.outDir ?? './dist',
56
+ routes,
57
+ ...options,
58
+ });
59
+ return builder.build(renderFn);
60
+ }
61
+ export function createSSGManifest(pages) {
62
+ return {
63
+ pages: new Map(pages.map((p) => [p.path, p.html])),
64
+ errors: new Map(),
65
+ buildTime: 0,
66
+ };
67
+ }
68
+ export function serializeManifest(manifest) {
69
+ return JSON.stringify({
70
+ pages: Array.from(manifest.pages.entries()),
71
+ errors: Array.from(manifest.errors.entries()).map(([k, v]) => [k, v.message]),
72
+ buildTime: manifest.buildTime,
73
+ });
74
+ }
75
+ export function deserializeManifest(json) {
76
+ const data = JSON.parse(json);
77
+ return {
78
+ pages: new Map(data.pages),
79
+ errors: new Map(data.errors),
80
+ buildTime: data.buildTime,
81
+ };
82
+ }
83
+ export const STATIC_ROUTE_PATTERNS = [
84
+ '/',
85
+ '/about',
86
+ '/contact',
87
+ '/blog',
88
+ '/pricing',
89
+ ];
90
+ export function isStaticRoute(path) {
91
+ return (!path.includes('[') &&
92
+ !path.includes(':') &&
93
+ !path.startsWith('/api/') &&
94
+ !path.includes('...'));
95
+ }
96
+ export async function crawlRoutes(startPath, shouldCrawl, getLinks) {
97
+ const visited = new Set();
98
+ const queue = [startPath];
99
+ while (queue.length > 0) {
100
+ const path = queue.shift();
101
+ if (visited.has(path) || !isStaticRoute(path)) {
102
+ continue;
103
+ }
104
+ visited.add(path);
105
+ if (await shouldCrawl(path)) {
106
+ const html = await fetch(path).then((r) => r.text()).catch(() => '');
107
+ const links = getLinks(html);
108
+ for (const link of links) {
109
+ if (!visited.has(link)) {
110
+ queue.push(link);
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return [...visited];
116
+ }
117
+ export function estimateBuildTime(pages) {
118
+ return pages * 50;
119
+ }
@@ -0,0 +1,55 @@
1
+ import { renderToString } from '../../runtime/helpers/render.js';
2
+ export function renderToHTMLString(element) {
3
+ if (!element)
4
+ return '';
5
+ if (typeof element === 'string')
6
+ return element;
7
+ if (typeof element === 'number')
8
+ return String(element);
9
+ if (typeof element === 'object' && element !== null && 'type' in element) {
10
+ return renderToString(element);
11
+ }
12
+ return '';
13
+ }
14
+ export function renderChildrenToHTML(children) {
15
+ return children.map((child) => renderToHTMLString(child)).join('');
16
+ }
17
+ export function createHtmlDocument(html, options = {}) {
18
+ const { title = '', lang = 'en', doctype = '<!DOCTYPE html>', baseUrl = '', headExtra = '' } = options;
19
+ const fullHtml = doctype + '\n' +
20
+ `<html${lang ? ` lang="${lang}"` : ''}>\n` +
21
+ '<head>\n' +
22
+ `<meta charset="utf-8">\n` +
23
+ `<meta name="viewport" content="width=device-width, initial-scale=1">\n` +
24
+ (title ? `<title>${escapeHtml(title)}</title>\n` : '') +
25
+ (baseUrl ? `<base href="${baseUrl}">\n` : '') +
26
+ (headExtra ? headExtra + '\n' : '') +
27
+ '</head>\n' +
28
+ `<body>\n${html}\n</body>\n` +
29
+ '</html>';
30
+ return fullHtml;
31
+ }
32
+ export function escapeHtml(text) {
33
+ return text
34
+ .replace(/&/g, '&amp;')
35
+ .replace(/</g, '&lt;')
36
+ .replace(/>/g, '&gt;')
37
+ .replace(/"/g, '&quot;')
38
+ .replace(/'/g, '&#039;');
39
+ }
40
+ export function createMetaTags(meta) {
41
+ return Object.entries(meta)
42
+ .map(([name, content]) => `<meta name="${escapeHtml(name)}" content="${escapeHtml(content)}">`)
43
+ .join('\n');
44
+ }
45
+ export function createLinkTags(links) {
46
+ return Object.entries(links)
47
+ .map(([rel, href]) => `<link rel="${escapeHtml(rel)}" href="${escapeHtml(href)}">`)
48
+ .join('\n');
49
+ }
50
+ export function createScriptTags(scripts) {
51
+ return scripts.map((src) => `<script src="${escapeHtml(src)}"></script>`).join('\n');
52
+ }
53
+ export function createStyleTags(styles) {
54
+ return styles.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`).join('\n');
55
+ }
@@ -0,0 +1,90 @@
1
+ import { renderToHTMLString, createHtmlDocument } from './render-html.js';
2
+ import { drainHeadContent } from '../../meta/head-registry.js';
3
+ import { getStatusText } from '../types.js';
4
+ export function renderSSR(element, options = {}) {
5
+ const { doctype = '<!DOCTYPE html>', title, lang = 'en', baseUrl, headExtra } = options;
6
+ const html = renderToHTMLString(element);
7
+ const collectedHead = drainHeadContent();
8
+ const allHeadExtra = [headExtra, collectedHead].filter(Boolean).join('\n');
9
+ const docOptions = {};
10
+ if (title !== undefined)
11
+ docOptions.title = title;
12
+ if (lang !== undefined)
13
+ docOptions.lang = lang;
14
+ if (doctype !== undefined)
15
+ docOptions.doctype = doctype;
16
+ if (baseUrl !== undefined)
17
+ docOptions.baseUrl = baseUrl;
18
+ if (allHeadExtra)
19
+ docOptions.headExtra = allHeadExtra;
20
+ const fullHtml = createHtmlDocument(html, docOptions);
21
+ return {
22
+ html: fullHtml,
23
+ status: 200,
24
+ headers: new Headers({
25
+ 'Content-Type': 'text/html',
26
+ }),
27
+ };
28
+ }
29
+ export function renderSSRWithError(element, error, options = {}) {
30
+ const status = error && 'error' in error ? error.error.status : 500;
31
+ const message = error && 'error' in error ? error.error.message : 'Internal Server Error';
32
+ const errorHtml = error ? `<div class="error"><h1>Error ${status}</h1><p>${message}</p></div>` : '';
33
+ const html = renderToHTMLString(element) + errorHtml;
34
+ const collectedHead = drainHeadContent();
35
+ const allHeadExtra = [options.headExtra, collectedHead].filter(Boolean).join('\n');
36
+ const fullHtml = createHtmlDocument(html, {
37
+ title: `Error ${status} - ${getStatusText(status)}`,
38
+ ...options,
39
+ ...(allHeadExtra ? { headExtra: allHeadExtra } : {}),
40
+ });
41
+ return {
42
+ html: fullHtml,
43
+ status,
44
+ headers: new Headers({
45
+ 'Content-Type': 'text/html',
46
+ }),
47
+ };
48
+ }
49
+ export function renderSSRWithHeaders(element, headers, options = {}) {
50
+ const result = renderSSR(element, options);
51
+ for (const [key, value] of Object.entries(headers)) {
52
+ result.headers.set(key, value);
53
+ }
54
+ return result;
55
+ }
56
+ export function createStreamingRenderer() {
57
+ const chunks = [];
58
+ return {
59
+ write(html) {
60
+ chunks.push(html);
61
+ },
62
+ writeChunk(type, content) {
63
+ if (type === 'status') {
64
+ chunks.push(`<!--status:${content}-->`);
65
+ }
66
+ else if (type === 'error') {
67
+ chunks.push(`<!--error:${content}-->`);
68
+ }
69
+ else {
70
+ chunks.push(content);
71
+ }
72
+ },
73
+ end() {
74
+ return chunks.join('');
75
+ },
76
+ reset() {
77
+ chunks.length = 0;
78
+ },
79
+ };
80
+ }
81
+ export function injectScripts(html, scripts) {
82
+ if (scripts.length === 0)
83
+ return html;
84
+ const scriptTags = scripts.map((src) => `<script src="${src}"></script>`).join('');
85
+ const insertPoint = html.lastIndexOf('</body>');
86
+ if (insertPoint === -1) {
87
+ return html + '\n' + scriptTags;
88
+ }
89
+ return html.slice(0, insertPoint) + scriptTags + html.slice(insertPoint);
90
+ }
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './helpers/render-html.js';
3
+ export * from './helpers/ssr.js';
@@ -0,0 +1,24 @@
1
+ export const DEFAULT_DOCTYPE = '<!DOCTYPE html>';
2
+ export const DEFAULT_LANG = 'en';
3
+ export const STATUS_CODES = {
4
+ 200: 'OK',
5
+ 201: 'Created',
6
+ 204: 'No Content',
7
+ 301: 'Moved Permanently',
8
+ 302: 'Found',
9
+ 304: 'Not Modified',
10
+ 400: 'Bad Request',
11
+ 401: 'Unauthorized',
12
+ 403: 'Forbidden',
13
+ 404: 'Not Found',
14
+ 405: 'Method Not Allowed',
15
+ 409: 'Conflict',
16
+ 422: 'Unprocessable Entity',
17
+ 429: 'Too Many Requests',
18
+ 500: 'Internal Server Error',
19
+ 502: 'Bad Gateway',
20
+ 503: 'Service Unavailable',
21
+ };
22
+ export function getStatusText(status) {
23
+ return STATUS_CODES[status] ?? 'Unknown';
24
+ }