@ternkit/tern 1.0.0 → 1.2.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.
- package/dist/goscript/index.svelte.d.ts +44 -0
- package/dist/goscript/index.svelte.js +204 -0
- package/dist/image/Image.svelte +62 -0
- package/dist/image/Image.svelte.d.ts +26 -0
- package/dist/image/types.d.ts +15 -0
- package/dist/image/types.js +1 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.js +5 -1
- package/dist/runtime/api.d.ts +18 -0
- package/dist/runtime/api.js +55 -0
- package/dist/runtime/base.d.ts +2 -0
- package/dist/runtime/base.js +10 -0
- package/dist/runtime/format.d.ts +19 -0
- package/dist/runtime/format.js +279 -0
- package/dist/runtime/i18n.svelte.d.ts +50 -0
- package/dist/runtime/i18n.svelte.js +105 -0
- package/dist/runtime/realtime.svelte.d.ts +78 -0
- package/dist/runtime/realtime.svelte.js +163 -0
- package/dist/runtime/router.svelte.js +16 -0
- package/dist/runtime/types.d.ts +6 -0
- package/dist/server/email.d.ts +18 -0
- package/dist/server/email.js +34 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +6 -0
- package/dist/server/intl.js +1 -1
- package/dist/server/polyfills.d.ts +1 -2
- package/dist/server/polyfills.js +29 -2
- package/dist/server/url.d.ts +56 -0
- package/dist/server/url.js +594 -0
- package/dist/server/web.d.ts +74 -0
- package/dist/server/web.js +395 -0
- package/dist/vite/goscript.d.ts +12 -0
- package/dist/vite/goscript.js +226 -0
- package/dist/vite/image.d.ts +4 -0
- package/dist/vite/image.js +180 -0
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/index.js +161 -4
- package/package.json +14 -3
|
@@ -3,6 +3,8 @@ import DefaultError from './DefaultError.svelte';
|
|
|
3
3
|
import Passthrough from './Passthrough.svelte';
|
|
4
4
|
import Root from './Root.svelte';
|
|
5
5
|
import { DeferredScope, readStreamed, revive } from './deferred.js';
|
|
6
|
+
import { setBase } from './base.js';
|
|
7
|
+
import { configureI18n, useLocale } from './i18n.svelte.js';
|
|
6
8
|
import { runAfter, runBefore, runOn } from './navigation.js';
|
|
7
9
|
import { plan } from './select.js';
|
|
8
10
|
import { applyPayloadToPage, page, setNavigating } from './state.svelte.js';
|
|
@@ -31,6 +33,8 @@ const RELOAD = Symbol('reload');
|
|
|
31
33
|
/** Starts the client. Called by the generated entry module. */
|
|
32
34
|
export async function start(m) {
|
|
33
35
|
manifest = m;
|
|
36
|
+
setBase(m.base ?? '');
|
|
37
|
+
configureI18n(m.i18n ?? null);
|
|
34
38
|
const el = document.getElementById('__tern_data');
|
|
35
39
|
const target = document.getElementById('tern-root');
|
|
36
40
|
if (!el || !target) {
|
|
@@ -66,6 +70,7 @@ export async function start(m) {
|
|
|
66
70
|
started = true;
|
|
67
71
|
await tick();
|
|
68
72
|
runAfter({ from: null, to: current(), type: 'enter' });
|
|
73
|
+
registerServiceWorker();
|
|
69
74
|
const saved = scrollPositions[historyIndex];
|
|
70
75
|
if (saved) {
|
|
71
76
|
scrollTo(saved.x, saved.y);
|
|
@@ -75,6 +80,15 @@ export async function start(m) {
|
|
|
75
80
|
}
|
|
76
81
|
listen();
|
|
77
82
|
}
|
|
83
|
+
function registerServiceWorker() {
|
|
84
|
+
// Not in development: a caching worker would serve stale modules.
|
|
85
|
+
if (!manifest.serviceWorker || import.meta.env?.DEV || !('serviceWorker' in navigator))
|
|
86
|
+
return;
|
|
87
|
+
const base = manifest.base ?? '';
|
|
88
|
+
navigator.serviceWorker.register(`${base}/service-worker.js`, { scope: `${base}/` }).catch((err) => {
|
|
89
|
+
console.error('Tern: registering the service worker failed', err);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
78
92
|
/** Navigates to href, which may be relative to the current URL. */
|
|
79
93
|
export async function goto(href, opts = {}) {
|
|
80
94
|
if (!started) {
|
|
@@ -329,6 +343,7 @@ async function render(p, url, state, id) {
|
|
|
329
343
|
}
|
|
330
344
|
}
|
|
331
345
|
const what = plan(manifest, p);
|
|
346
|
+
const ready = useLocale(p.locale); // messages load alongside the components
|
|
332
347
|
let components;
|
|
333
348
|
switch (what.kind) {
|
|
334
349
|
case 'not-a-page':
|
|
@@ -351,6 +366,7 @@ async function render(p, url, state, id) {
|
|
|
351
366
|
layoutCount = 0;
|
|
352
367
|
}
|
|
353
368
|
nodes = p.nodes ?? [];
|
|
369
|
+
await ready;
|
|
354
370
|
if (id !== token)
|
|
355
371
|
return true; // superseded; the newer navigation renders
|
|
356
372
|
if (p.type === 'data') {
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export interface RouteEntry {
|
|
|
23
23
|
export interface ClientManifest {
|
|
24
24
|
/** The base path from tern.json, such as '/app', or ''. */
|
|
25
25
|
base?: string;
|
|
26
|
+
/** Whether to register /service-worker.js. */
|
|
27
|
+
serviceWorker?: boolean;
|
|
28
|
+
/** Translations from src/locales, or null. */
|
|
29
|
+
i18n?: import('./i18n.svelte.js').I18nConfig | null;
|
|
26
30
|
layouts: LayoutEntry[];
|
|
27
31
|
routes: Record<string, RouteEntry>;
|
|
28
32
|
fallback: {
|
|
@@ -51,6 +55,8 @@ export interface Payload {
|
|
|
51
55
|
reused?: number[];
|
|
52
56
|
/** PUBLIC_ environment variables, on full page loads. */
|
|
53
57
|
env?: Record<string, string>;
|
|
58
|
+
/** The request's locale, when the app has src/locales. */
|
|
59
|
+
locale?: string;
|
|
54
60
|
}
|
|
55
61
|
export interface NodeUses {
|
|
56
62
|
url?: boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
/** An email component module from src/emails. */
|
|
3
|
+
export interface EmailModule {
|
|
4
|
+
default: Component<any>;
|
|
5
|
+
/** Props for the preview page under tern dev. */
|
|
6
|
+
preview?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export interface EmailResult {
|
|
9
|
+
subject: string;
|
|
10
|
+
html: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates the function Go calls to render emails: a component's markup
|
|
15
|
+
* becomes the body, its <svelte:head> the head, and its <title> the
|
|
16
|
+
* subject.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createEmailRenderer(emails: Record<string, EmailModule>): (name: string, propsJSON: string) => string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { render } from 'svelte/server';
|
|
2
|
+
const entities = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", ''': "'" };
|
|
3
|
+
function decode(s) {
|
|
4
|
+
return s.replace(/&(amp|lt|gt|quot|#39|#x27);/g, (m) => entities[m] ?? m);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Creates the function Go calls to render emails: a component's markup
|
|
8
|
+
* becomes the body, its <svelte:head> the head, and its <title> the
|
|
9
|
+
* subject.
|
|
10
|
+
*/
|
|
11
|
+
export function createEmailRenderer(emails) {
|
|
12
|
+
return (name, propsJSON) => {
|
|
13
|
+
if (name === '') {
|
|
14
|
+
// The list of emails and their preview props, for the dev preview.
|
|
15
|
+
return JSON.stringify(Object.fromEntries(Object.entries(emails).map(([n, m]) => [n, m.preview ?? {}])));
|
|
16
|
+
}
|
|
17
|
+
const mod = emails[name];
|
|
18
|
+
if (!mod)
|
|
19
|
+
return JSON.stringify({ subject: '', html: '', error: `no email named ${name} in src/emails` });
|
|
20
|
+
const props = JSON.parse(propsJSON || '{}');
|
|
21
|
+
const out = render(mod.default, { props });
|
|
22
|
+
const title = /<title>([\s\S]*?)<\/title>/.exec(out.head);
|
|
23
|
+
const subject = title ? decode(title[1].trim()) : '';
|
|
24
|
+
const head = out.head.replace(/<!--[\s\S]*?-->/g, '');
|
|
25
|
+
const body = out.body.replace(/<!--[\s\S]*?-->/g, '');
|
|
26
|
+
const html = '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">' +
|
|
27
|
+
'<meta name="color-scheme" content="light dark">' +
|
|
28
|
+
head +
|
|
29
|
+
'</head><body>' +
|
|
30
|
+
body +
|
|
31
|
+
'</body></html>';
|
|
32
|
+
return JSON.stringify({ subject, html });
|
|
33
|
+
};
|
|
34
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
|
+
import { type I18nConfig } from '../runtime/i18n.svelte.js';
|
|
2
3
|
/** The server manifest generated by `tern gen` in .tern/generated/server.js. */
|
|
3
4
|
export interface ServerManifest {
|
|
4
5
|
layouts: {
|
|
@@ -19,6 +20,8 @@ export interface ServerManifest {
|
|
|
19
20
|
component: Component<any>;
|
|
20
21
|
}[];
|
|
21
22
|
};
|
|
23
|
+
i18n?: I18nConfig | null;
|
|
24
|
+
base?: string;
|
|
22
25
|
}
|
|
23
26
|
/** The result of a server render, as JSON for the Go side. */
|
|
24
27
|
export interface RenderResult {
|
|
@@ -33,3 +36,5 @@ export interface RenderResult {
|
|
|
33
36
|
* exactly the same tree and hydration matches.
|
|
34
37
|
*/
|
|
35
38
|
export declare function createRenderer(manifest: ServerManifest): (payload: string, href: string) => string;
|
|
39
|
+
export { createEmailRenderer } from './email.js';
|
|
40
|
+
export type { EmailModule, EmailResult } from './email.js';
|
package/dist/server/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import { render } from 'svelte/server';
|
|
|
2
2
|
import DefaultError from '../runtime/DefaultError.svelte';
|
|
3
3
|
import Passthrough from '../runtime/Passthrough.svelte';
|
|
4
4
|
import Root from '../runtime/Root.svelte';
|
|
5
|
+
import { setBase } from '../runtime/base.js';
|
|
5
6
|
import { revive } from '../runtime/deferred.js';
|
|
7
|
+
import { configureI18n, useLocaleSync } from '../runtime/i18n.svelte.js';
|
|
6
8
|
import { plan } from '../runtime/select.js';
|
|
7
9
|
import { applyPayloadToPage } from '../runtime/state.svelte.js';
|
|
8
10
|
/**
|
|
@@ -12,6 +14,8 @@ import { applyPayloadToPage } from '../runtime/state.svelte.js';
|
|
|
12
14
|
*/
|
|
13
15
|
export function createRenderer(manifest) {
|
|
14
16
|
const layout = (i) => manifest.layouts[i]?.component ?? Passthrough;
|
|
17
|
+
configureI18n(manifest.i18n ?? null);
|
|
18
|
+
setBase(manifest.base ?? '');
|
|
15
19
|
return (payloadJSON, href) => {
|
|
16
20
|
const p = JSON.parse(payloadJSON);
|
|
17
21
|
// Deferred values are still loading: render their pending state.
|
|
@@ -32,9 +36,11 @@ export function createRenderer(manifest) {
|
|
|
32
36
|
case 'default-error':
|
|
33
37
|
components = [DefaultError];
|
|
34
38
|
}
|
|
39
|
+
useLocaleSync(p.locale);
|
|
35
40
|
applyPayloadToPage(p, new URL(href), {}, what.data);
|
|
36
41
|
const form = p.hasForm ? (p.form ?? null) : null;
|
|
37
42
|
const out = render(Root, { props: { components, data: what.data, form } });
|
|
38
43
|
return JSON.stringify({ head: out.head, body: out.body });
|
|
39
44
|
};
|
|
40
45
|
}
|
|
46
|
+
export { createEmailRenderer } from './email.js';
|
package/dist/server/intl.js
CHANGED
|
@@ -33,7 +33,7 @@ class NumberFormat {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
const isCurrency = o.style === 'currency';
|
|
36
|
-
const max = o.maximumFractionDigits ?? (isCurrency ? 2 : o.notation === 'compact' ? 1 : 3);
|
|
36
|
+
const max = o.maximumFractionDigits ?? (isCurrency ? 2 : o.style === 'percent' ? 0 : o.notation === 'compact' ? 1 : 3);
|
|
37
37
|
const min = o.minimumFractionDigits ?? (isCurrency ? 2 : 0);
|
|
38
38
|
let s = Math.abs(n).toFixed(Math.max(min, Math.min(max, 20)));
|
|
39
39
|
if (s.includes('.')) {
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import 'core-js/actual/url-search-params';
|
|
1
|
+
export {};
|
package/dist/server/polyfills.js
CHANGED
|
@@ -2,17 +2,44 @@
|
|
|
2
2
|
// JavaScript engine (QuickJS) that only implements the language itself.
|
|
3
3
|
// Each polyfill is skipped where the API already exists, such as in Node
|
|
4
4
|
// during development.
|
|
5
|
-
import 'core-js/actual/url';
|
|
6
|
-
import 'core-js/actual/url-search-params';
|
|
7
5
|
import { IntlShim } from './intl.js';
|
|
6
|
+
import { URL, URLSearchParams } from './url.js';
|
|
7
|
+
import { install } from './web.js';
|
|
8
8
|
const g = globalThis;
|
|
9
|
+
install(g);
|
|
9
10
|
// Some libraries read process.env.SOMETHING at runtime. Give them an empty
|
|
10
11
|
// environment instead of a ReferenceError.
|
|
11
12
|
if (typeof g.process === 'undefined') {
|
|
12
13
|
g.process = { env: { NODE_ENV: 'production' }, browser: false, version: '', versions: {} };
|
|
13
14
|
}
|
|
15
|
+
if (typeof g.URL === 'undefined') {
|
|
16
|
+
g.URL = URL;
|
|
17
|
+
g.URLSearchParams = URLSearchParams;
|
|
18
|
+
}
|
|
14
19
|
if (typeof g.Intl === 'undefined') {
|
|
15
20
|
g.Intl = IntlShim;
|
|
21
|
+
// QuickJS's own toLocaleString ignores locales: (5e6).toLocaleString()
|
|
22
|
+
// is "5000000" where browsers print "5,000,000". Route them through the
|
|
23
|
+
// shim so server-rendered text matches the browser's en-US output.
|
|
24
|
+
const NF = IntlShim.NumberFormat;
|
|
25
|
+
const DF = IntlShim.DateTimeFormat;
|
|
26
|
+
Object.defineProperty(Number.prototype, 'toLocaleString', {
|
|
27
|
+
value(locales, options) {
|
|
28
|
+
return new NF(locales, options).format(Number(this));
|
|
29
|
+
},
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true
|
|
32
|
+
});
|
|
33
|
+
const dateMethod = (defaults) => function (locales, options) {
|
|
34
|
+
return new DF(locales, { ...defaults, ...options }).format(this);
|
|
35
|
+
};
|
|
36
|
+
for (const [name, defaults] of [
|
|
37
|
+
['toLocaleString', { dateStyle: 'short', timeStyle: 'medium' }],
|
|
38
|
+
['toLocaleDateString', { dateStyle: 'short' }],
|
|
39
|
+
['toLocaleTimeString', { timeStyle: 'medium' }]
|
|
40
|
+
]) {
|
|
41
|
+
Object.defineProperty(Date.prototype, name, { value: dateMethod(defaults), configurable: true, writable: true });
|
|
42
|
+
}
|
|
16
43
|
}
|
|
17
44
|
if (typeof g.queueMicrotask !== 'function') {
|
|
18
45
|
g.queueMicrotask = (fn) => void Promise.resolve().then(fn);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare class URLSearchParams {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(init?: string | Record<string, string> | Iterable<[string, string]> | URLSearchParams);
|
|
4
|
+
/** @internal Links the params to their URL, so changes update it. */
|
|
5
|
+
static _link(params: URLSearchParams, url: URL, query: string | null): void;
|
|
6
|
+
/** @internal */
|
|
7
|
+
static _reset(params: URLSearchParams, query: string | null): void;
|
|
8
|
+
get size(): number;
|
|
9
|
+
append(name: string, value: string): void;
|
|
10
|
+
delete(name: string, value?: string): void;
|
|
11
|
+
get(name: string): string | null;
|
|
12
|
+
getAll(name: string): string[];
|
|
13
|
+
has(name: string, value?: string): boolean;
|
|
14
|
+
set(name: string, value: string): void;
|
|
15
|
+
sort(): void;
|
|
16
|
+
forEach(fn: (value: string, name: string, params: URLSearchParams) => void, thisArg?: unknown): void;
|
|
17
|
+
entries(): IterableIterator<[string, string]>;
|
|
18
|
+
keys(): IterableIterator<string>;
|
|
19
|
+
values(): IterableIterator<string>;
|
|
20
|
+
[Symbol.iterator](): IterableIterator<[string, string]>;
|
|
21
|
+
toString(): string;
|
|
22
|
+
get [Symbol.toStringTag](): string;
|
|
23
|
+
}
|
|
24
|
+
export declare class URL {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(url: string | URL, base?: string | URL);
|
|
27
|
+
static canParse(url: string, base?: string): boolean;
|
|
28
|
+
static parse(url: string, base?: string): URL | null;
|
|
29
|
+
/** @internal */
|
|
30
|
+
static _setQuery(url: URL, query: string): void;
|
|
31
|
+
get href(): string;
|
|
32
|
+
set href(v: string);
|
|
33
|
+
get origin(): string;
|
|
34
|
+
get protocol(): string;
|
|
35
|
+
set protocol(v: string);
|
|
36
|
+
get username(): string;
|
|
37
|
+
set username(v: string);
|
|
38
|
+
get password(): string;
|
|
39
|
+
set password(v: string);
|
|
40
|
+
get host(): string;
|
|
41
|
+
set host(v: string);
|
|
42
|
+
get hostname(): string;
|
|
43
|
+
set hostname(v: string);
|
|
44
|
+
get port(): string;
|
|
45
|
+
set port(v: string);
|
|
46
|
+
get pathname(): string;
|
|
47
|
+
set pathname(v: string);
|
|
48
|
+
get search(): string;
|
|
49
|
+
set search(v: string);
|
|
50
|
+
get searchParams(): URLSearchParams;
|
|
51
|
+
get hash(): string;
|
|
52
|
+
set hash(v: string);
|
|
53
|
+
toString(): string;
|
|
54
|
+
toJSON(): string;
|
|
55
|
+
get [Symbol.toStringTag](): string;
|
|
56
|
+
}
|