@ogygia/core 0.11.5 → 0.12.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/Region.svelte +28 -21
- package/dist/Region.svelte.d.ts +0 -2
- package/dist/compiler/bom.js +1 -1
- package/dist/compiler/browser.d.ts +1 -1
- package/dist/compiler/browser.js +1 -1
- package/dist/compiler/ctx.d.ts +1 -1
- package/dist/compiler/ctx.js +3 -3
- package/dist/compiler/{macros/dedent.d.ts → dedent.d.ts} +12 -1
- package/dist/compiler/dedent.js +43 -0
- package/dist/compiler/driver.d.ts +9 -6
- package/dist/compiler/driver.js +16 -24
- package/dist/compiler/index.d.ts +2 -2
- package/dist/compiler/index.js +2 -2
- package/dist/compiler/link/caps.js +59 -9
- package/dist/compiler/link/island-deps.js +225 -22
- package/dist/compiler/link/location-map.d.ts +10 -0
- package/dist/compiler/link/location-map.js +53 -0
- package/dist/compiler/link/manifest.js +5 -1
- package/dist/compiler/link/server-manifest.js +43 -7
- package/dist/compiler/link/transport.js +39 -7
- package/dist/compiler/macros/source.js +2 -2
- package/dist/compiler/region/transform.d.ts +13 -4
- package/dist/compiler/region/transform.js +15 -6
- package/dist/hooks.d.ts +16 -0
- package/dist/hooks.js +4 -2
- package/dist/insights/page-insights.js +2 -2
- package/dist/runtime/beacon.js +1 -1
- package/dist/runtime/boot-link.js +4 -1
- package/dist/runtime/core.js +3 -2
- package/dist/runtime/entry-locations.d.ts +10 -3
- package/dist/runtime/entry-locations.js +132 -21
- package/dist/runtime/morph.d.ts +7 -0
- package/dist/runtime/morph.js +21 -15
- package/dist/runtime/ownership.d.ts +10 -1
- package/dist/runtime/ownership.js +19 -1
- package/dist/runtime/reconcile.d.ts +5 -2
- package/dist/runtime/reconcile.js +52 -16
- package/dist/runtime/region-endpoint-url.js +1 -0
- package/dist/runtime/router-nav.js +15 -0
- package/dist/runtime/router.js +3 -0
- package/dist/runtime/slots.d.ts +8 -2
- package/dist/server/document-tail.d.ts +3 -2
- package/dist/server/document-tail.js +4 -3
- package/dist/server/entry-location.js +11 -8
- package/dist/server/props-wire.js +2 -2
- package/dist/server/signing-key.d.ts +10 -2
- package/dist/server/signing-key.js +49 -15
- package/dist/vite/index.js +5 -4
- package/dist/vite/kit-dirs.js +1 -1
- package/package.json +1 -1
- package/dist/compiler/link/entry-shim.d.ts +0 -4
- package/dist/compiler/link/entry-shim.js +0 -29
- package/dist/compiler/macros/dedent.js +0 -36
package/dist/runtime/router.js
CHANGED
|
@@ -2,6 +2,7 @@ import { kit_hydrates_page } from "./kit-boot.js";
|
|
|
2
2
|
import { slots } from "./slots.js";
|
|
3
3
|
import { publish_nav } from "./nav-handle.js";
|
|
4
4
|
import { nearest_attr } from "./nearest-attr.js";
|
|
5
|
+
import { record_server_body } from "./ownership.js";
|
|
5
6
|
// src/runtime/router.ts
|
|
6
7
|
/**
|
|
7
8
|
* Minimal SPA router for ogygia (Astro's ClientRouter equivalent).
|
|
@@ -300,6 +301,8 @@ var SpaRouter = class {
|
|
|
300
301
|
this.#started = true;
|
|
301
302
|
this.doc_key = document_key(new URL(location.href));
|
|
302
303
|
this.current_url = new URL(location.href);
|
|
304
|
+
const body = document.body;
|
|
305
|
+
if (body) record_server_body(body, body, Array.from(body.childNodes));
|
|
303
306
|
document.addEventListener("click", (event) => {
|
|
304
307
|
const anchor = anchor_of(event);
|
|
305
308
|
const url = this.#should_intercept(event, anchor);
|
package/dist/runtime/slots.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { regions_in_shadow } from "./connected.js";
|
|
|
9
9
|
import { yield_task } from "./schedule.js";
|
|
10
10
|
import { beacon_nav } from "./beacon.js";
|
|
11
11
|
import { load_slot } from "./load-scheduler.js";
|
|
12
|
-
import { is_self_owned, walk_enters } from "./ownership.js";
|
|
12
|
+
import { is_self_owned, record_server_body, server_body_attributes, server_body_child, walk_enters } from "./ownership.js";
|
|
13
13
|
import { document_key, jump_to_hash, push_state, replace_state } from "./router.js";
|
|
14
14
|
import { resolve_address } from "./interaction.js";
|
|
15
15
|
import { sync_attributes } from "./morph.js";
|
|
@@ -53,9 +53,11 @@ export type FormOps = {
|
|
|
53
53
|
/** Wake a cold island when interaction lands inside it; returns a disarm fn. */
|
|
54
54
|
export type ArmFn = (el: HTMLElement, fire: () => void) => void | (() => void);
|
|
55
55
|
/** `target: 'walk'` = the hydration repair (the server sequence Svelte's walk binds); default = a live
|
|
56
|
-
* morph toward a new answer. Same ownership rules either way (runtime/ownership.ts).
|
|
56
|
+
* morph toward a new answer. Same ownership rules either way (runtime/ownership.ts). `stays`: a
|
|
57
|
+
* child of `parent` the morph steps over (the page's own, beside the render's). */
|
|
57
58
|
export type MorphFn = (parent: Element, nodes: ArrayLike<Node>, options?: {
|
|
58
59
|
target?: "live" | "walk";
|
|
60
|
+
stays?: (node: Node) => boolean;
|
|
59
61
|
}) => void;
|
|
60
62
|
export type WireOps = {
|
|
61
63
|
/** THE hub key (`OgygiaRef`) — every transportable kind crosses under it. */
|
|
@@ -149,6 +151,10 @@ export type BootLink = {
|
|
|
149
151
|
/** DOM ownership (./ownership.ts): which children Svelte's walk enters, and self-owned elements */
|
|
150
152
|
walk_enters: typeof walk_enters;
|
|
151
153
|
is_self_owned: typeof is_self_owned;
|
|
154
|
+
/** `<body>`'s two owners (./ownership.ts): the router's record of what the server sent it */
|
|
155
|
+
record_server_body: typeof record_server_body;
|
|
156
|
+
server_body_attributes: typeof server_body_attributes;
|
|
157
|
+
server_body_child: typeof server_body_child;
|
|
152
158
|
};
|
|
153
159
|
export type RouterLink = {
|
|
154
160
|
document_key: typeof document_key;
|
|
@@ -41,9 +41,10 @@ export declare function modulepreload_tag(href: string): string;
|
|
|
41
41
|
* module's imports after downloading and parsing it; hinted here, they download ALONGSIDE the
|
|
42
42
|
* runtime instead of one round trip after it. Normal priority — the runtime cannot run without
|
|
43
43
|
* them. `data-ogygia-runtime-dep` keeps them attached to the script when the handle moves it
|
|
44
|
-
* (head-presence.ts `runtime_first`).
|
|
44
|
+
* (head-presence.ts `runtime_first`). `locations`: the build's location map, which the runtime reads
|
|
45
|
+
* for a load that arrives with an identity alone (runtime/entry-locations.ts).
|
|
45
46
|
*/
|
|
46
|
-
export declare function runtime_bootstrap_tags(src: string, deps: readonly string[]): string;
|
|
47
|
+
export declare function runtime_bootstrap_tags(src: string, deps: readonly string[], locations?: string | null): string;
|
|
47
48
|
/**
|
|
48
49
|
* The island graph (island-graph.ts) as one inert JSON script: entry → the chunks its code needs.
|
|
49
50
|
* The runtime turns an entry's list into modulepreload links when that island wakes. Empty map →
|
|
@@ -33,10 +33,11 @@ function modulepreload_tag(href) {
|
|
|
33
33
|
* module's imports after downloading and parsing it; hinted here, they download ALONGSIDE the
|
|
34
34
|
* runtime instead of one round trip after it. Normal priority — the runtime cannot run without
|
|
35
35
|
* them. `data-ogygia-runtime-dep` keeps them attached to the script when the handle moves it
|
|
36
|
-
* (head-presence.ts `runtime_first`).
|
|
36
|
+
* (head-presence.ts `runtime_first`). `locations`: the build's location map, which the runtime reads
|
|
37
|
+
* for a load that arrives with an identity alone (runtime/entry-locations.ts).
|
|
37
38
|
*/
|
|
38
|
-
function runtime_bootstrap_tags(src, deps) {
|
|
39
|
-
let html = "<script type=\"module\" data-ogygia-runtime src=\"" + src + "\"><\/script>";
|
|
39
|
+
function runtime_bootstrap_tags(src, deps, locations = null) {
|
|
40
|
+
let html = "<script type=\"module\" data-ogygia-runtime" + (locations ? " data-og-locations=\"" + locations + "\"" : "") + " src=\"" + src + "\"><\/script>";
|
|
40
41
|
for (const dep of deps) html += "<link rel=\"modulepreload\" href=\"" + dep + "\" data-ogygia-runtime-dep>";
|
|
41
42
|
return html;
|
|
42
43
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { runtime_bootstrap_tags } from "./document-tail.js";
|
|
2
2
|
import runtime_url from "virtual:ogygia/runtime-url";
|
|
3
|
-
import { entryLocation, islandDeps } from "virtual:ogygia/island-deps";
|
|
3
|
+
import { entryLocation, islandDeps, locationMap } from "virtual:ogygia/island-deps";
|
|
4
4
|
// src/server/entry-location.ts
|
|
5
5
|
/**
|
|
6
6
|
* ENTRY IDENTITY → LOCATION, on the server. A hydrate island's entry and the runtime each have an
|
|
7
|
-
* IDENTITY — the stable URL the compiler bakes (`/<appDir>/
|
|
7
|
+
* IDENTITY — the stable URL the compiler bakes (`/<appDir>/ogygia/og-region.<iid>.js`, the
|
|
8
8
|
* runtime's stable name) and every map keys by — and a LOCATION: the content-hashed file the build
|
|
9
|
-
* emitted
|
|
10
|
-
*
|
|
11
|
-
* bundle, so the location is looked up at render
|
|
9
|
+
* emitted under `immutable/`, the one a browser loads. A hashed name cannot go stale; the stable name
|
|
10
|
+
* lives outside `immutable/` (compiler/region/transform.ts `STABLE_NAME_DIR`), so a host revalidates
|
|
11
|
+
* it. The server bundle is built before the client bundle, so the location is looked up at render
|
|
12
|
+
* time through the build handoff (`entryLocation`).
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* In dev the identity is the location (the dev server serves the module at it). No location in a
|
|
15
|
+
* build (a handoff from before hashing, an island from another app): a region writes no `src`, and the
|
|
16
|
+
* browser finds the current file through the build's location maps (compiler/link/location-map.ts).
|
|
15
17
|
*/
|
|
16
18
|
/** The file an entry is loaded from: its content-hashed location, else the identity itself. */
|
|
17
19
|
function entry_src(identity) {
|
|
@@ -24,6 +26,7 @@ function entry_src(identity) {
|
|
|
24
26
|
*/
|
|
25
27
|
function runtime_bootstrap(resolve = (url) => url) {
|
|
26
28
|
if (!runtime_url) return "";
|
|
27
|
-
|
|
29
|
+
const map = locationMap();
|
|
30
|
+
return runtime_bootstrap_tags(resolve(entry_src(runtime_url)), islandDeps(runtime_url).map(resolve), map ? resolve(map) : null);
|
|
28
31
|
}
|
|
29
32
|
export { entry_src, runtime_bootstrap };
|
|
@@ -153,8 +153,8 @@ function plan_props_wire(value, entry) {
|
|
|
153
153
|
culprit: () => culprit === void 0 ? culprit = json_culprit(value) : culprit
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
|
-
const LIVE_ENTRY_MARK = "/
|
|
157
|
-
const LIVE_ENTRY_RE = /^\/[^"\s]+\/
|
|
156
|
+
const LIVE_ENTRY_MARK = "/ogygia/og-region.";
|
|
157
|
+
const LIVE_ENTRY_RE = /^\/[^"\s]+\/ogygia\/og-region\.[0-9a-f]+\.js$/;
|
|
158
158
|
function live_entries_in(text) {
|
|
159
159
|
const out = [];
|
|
160
160
|
let at = text.indexOf(LIVE_ENTRY_MARK);
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
export interface SigningKey {
|
|
2
2
|
secret: string;
|
|
3
|
-
/** env
|
|
3
|
+
/** the app's or the env's: survives redeploys and is shared by every deployment that sets the same value */
|
|
4
4
|
stable: boolean;
|
|
5
5
|
/** 8-char key fingerprint (`&k=` on capabilities, `x-ogygia-key` on refusals) */
|
|
6
6
|
fp: string;
|
|
7
|
+
/** where the secret came from: `handle({ secret })`, `OGYGIA_SECRET`, or this build's random key */
|
|
8
|
+
source: "option" | "env" | "build";
|
|
7
9
|
}
|
|
8
10
|
export declare function signing_key(): SigningKey;
|
|
11
|
+
/**
|
|
12
|
+
* Record the app's `handle({ secret })`. A blank one counts as none (an app can pass a value that may
|
|
13
|
+
* be unset). Once a key is decided it never changes in the process: capabilities minted under the
|
|
14
|
+
* first would stop verifying, so a different secret arriving later is an error (thrown in dev).
|
|
15
|
+
*/
|
|
16
|
+
export declare function configure_signing_secret(secret: string | undefined): void;
|
|
9
17
|
export declare function key_fingerprint(secret: string): string;
|
|
10
|
-
/** Tests: forget the decided key. */
|
|
18
|
+
/** Tests: forget the decided key and the app's secret. */
|
|
11
19
|
export declare function reset_signing_key(): void;
|
|
@@ -5,7 +5,12 @@ import { building } from "$app/env";
|
|
|
5
5
|
/**
|
|
6
6
|
* THE region signing key, decided once at first use (the first mint or verify), not at module eval:
|
|
7
7
|
* a host that fills `process.env` after the server's modules load (an adapter loading a `.env` late)
|
|
8
|
-
* still counts. The
|
|
8
|
+
* still counts. The app's `handle({ secret })` wins, then the runtime's `OGYGIA_SECRET`, else this
|
|
9
|
+
* build's random key.
|
|
10
|
+
*
|
|
11
|
+
* Process-wide on `globalThis`, not module state (like the islands endpoint, server/endpoint.ts):
|
|
12
|
+
* one server can load this file as two copies (pnpm gives a package one folder per set of peer
|
|
13
|
+
* versions), and a copy that never saw the handle's secret would mint with the build's key.
|
|
9
14
|
*
|
|
10
15
|
* A capability carries its minter's key FINGERPRINT (`&k=`): an HMAC of a constant under the key —
|
|
11
16
|
* the same on every instance sharing the key, and it reveals nothing of the key. A deployment that
|
|
@@ -13,18 +18,47 @@ import { building } from "$app/env";
|
|
|
13
18
|
* instead of an opaque 403: the page and its islands endpoint came from different deployments (a CDN
|
|
14
19
|
* routing them to two backends, HTML cached across a key change).
|
|
15
20
|
*/
|
|
16
|
-
|
|
21
|
+
const SLOT = Symbol.for("ogygia.signing-key");
|
|
22
|
+
const SOURCE_NAME = {
|
|
23
|
+
option: "handle({ secret })",
|
|
24
|
+
env: "OGYGIA_SECRET",
|
|
25
|
+
build: "this build's random key"
|
|
26
|
+
};
|
|
27
|
+
function slot() {
|
|
28
|
+
const g = globalThis;
|
|
29
|
+
return g[SLOT] ??= {};
|
|
30
|
+
}
|
|
17
31
|
function signing_key() {
|
|
18
|
-
|
|
32
|
+
const s = slot();
|
|
33
|
+
if (s.key) return s.key;
|
|
19
34
|
const from_env = typeof process !== "undefined" && process.env?.OGYGIA_SECRET || "";
|
|
20
|
-
const secret = from_env
|
|
21
|
-
key = {
|
|
35
|
+
const [secret, source] = s.option ? [s.option, "option"] : from_env ? [from_env, "env"] : [buildKey, "build"];
|
|
36
|
+
s.key = {
|
|
22
37
|
secret,
|
|
23
|
-
stable:
|
|
24
|
-
fp: key_fingerprint(secret)
|
|
38
|
+
stable: source !== "build",
|
|
39
|
+
fp: key_fingerprint(secret),
|
|
40
|
+
source
|
|
25
41
|
};
|
|
26
|
-
if (!key.stable && !import.meta.env.DEV) warn_build_key();
|
|
27
|
-
return key;
|
|
42
|
+
if (!s.key.stable && !import.meta.env.DEV) warn_build_key();
|
|
43
|
+
return s.key;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Record the app's `handle({ secret })`. A blank one counts as none (an app can pass a value that may
|
|
47
|
+
* be unset). Once a key is decided it never changes in the process: capabilities minted under the
|
|
48
|
+
* first would stop verifying, so a different secret arriving later is an error (thrown in dev).
|
|
49
|
+
*/
|
|
50
|
+
function configure_signing_secret(secret) {
|
|
51
|
+
if (!secret?.trim()) return;
|
|
52
|
+
const s = slot();
|
|
53
|
+
const decided = s.key?.secret ?? s.option;
|
|
54
|
+
if (decided === void 0 || decided === secret) {
|
|
55
|
+
s.option = secret;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const from = s.key ? `${SOURCE_NAME[s.key.source]}, decided by an earlier mint or verify` : "another handle({ secret })";
|
|
59
|
+
const message = `[ogygia] handle({ secret }) names key ${key_fingerprint(secret)}, but this server already signs with key ${key_fingerprint(decided)} (from ${from}). The first key stays: pass one secret, before anything renders.`;
|
|
60
|
+
if (import.meta.env.DEV) throw new Error(message);
|
|
61
|
+
console.error(message);
|
|
28
62
|
}
|
|
29
63
|
function key_fingerprint(secret) {
|
|
30
64
|
return sign(secret, "ogygia:key-fp").slice(0, 8);
|
|
@@ -36,12 +70,12 @@ function warn_build_key() {
|
|
|
36
70
|
const K = Symbol.for("ogygia.build-key-warned");
|
|
37
71
|
if (g[K]) return;
|
|
38
72
|
g[K] = true;
|
|
39
|
-
if (building) console.warn("[ogygia] prerendered page mints region capabilities signed with a per-build random key — they stop verifying after your next deploy while the static HTML lives on. Set a stable OGYGIA_SECRET env var so prerendered holes survive redeploys.");
|
|
40
|
-
else if (buildHadSecret) console.error("[ogygia] OGYGIA_SECRET was set when this app was built but is not set where it runs: region capabilities are signed with this build's random key, so every hole minted by another deployment (another backend behind the same CDN, the previous deploy) is refused (403, x-ogygia-deny: foreign-key). Set OGYGIA_SECRET in the server's runtime environment.");
|
|
41
|
-
else console.warn("[ogygia] OGYGIA_SECRET is not set: region capabilities are signed with this build's random key, so a page cached across a deploy, or served by another deployment than its islands endpoint, gets its holes refused (403, x-ogygia-deny: foreign-key). Set a stable OGYGIA_SECRET.");
|
|
73
|
+
if (building) console.warn("[ogygia] prerendered page mints region capabilities signed with a per-build random key — they stop verifying after your next deploy while the static HTML lives on. Set a stable OGYGIA_SECRET env var (or pass it to handle({ secret })) so prerendered holes survive redeploys.");
|
|
74
|
+
else if (buildHadSecret) console.error("[ogygia] OGYGIA_SECRET was set when this app was built but is not set where it runs: region capabilities are signed with this build's random key, so every hole minted by another deployment (another backend behind the same CDN, the previous deploy) is refused (403, x-ogygia-deny: foreign-key). Set OGYGIA_SECRET in the server's runtime environment, or pass it to handle({ secret }) (e.g. from $env/static/private).");
|
|
75
|
+
else console.warn("[ogygia] OGYGIA_SECRET is not set: region capabilities are signed with this build's random key, so a page cached across a deploy, or served by another deployment than its islands endpoint, gets its holes refused (403, x-ogygia-deny: foreign-key). Set a stable OGYGIA_SECRET, or pass one to handle({ secret }).");
|
|
42
76
|
}
|
|
43
|
-
/** Tests: forget the decided key. */
|
|
77
|
+
/** Tests: forget the decided key and the app's secret. */
|
|
44
78
|
function reset_signing_key() {
|
|
45
|
-
|
|
79
|
+
delete globalThis[SLOT];
|
|
46
80
|
}
|
|
47
|
-
export { key_fingerprint, reset_signing_key, signing_key };
|
|
81
|
+
export { configure_signing_secret, key_fingerprint, reset_signing_key, signing_key };
|
package/dist/vite/index.js
CHANGED
|
@@ -701,10 +701,10 @@ function ogygia(options = {}) {
|
|
|
701
701
|
},
|
|
702
702
|
generateBundle() {
|
|
703
703
|
if (!is_build || server_leg_of(this)) return;
|
|
704
|
-
for (const
|
|
704
|
+
for (const file of compiler.location_maps((ref) => this.getFileName(ref))) this.emitFile({
|
|
705
705
|
type: "asset",
|
|
706
|
-
fileName:
|
|
707
|
-
source:
|
|
706
|
+
fileName: file.fileName,
|
|
707
|
+
source: file.source
|
|
708
708
|
});
|
|
709
709
|
},
|
|
710
710
|
closeBundle: {
|
|
@@ -805,7 +805,8 @@ function ogygia(options = {}) {
|
|
|
805
805
|
content_css,
|
|
806
806
|
css_inline,
|
|
807
807
|
fn_manifest: Object.fromEntries(compiler.dollar_hoists),
|
|
808
|
-
entries: entries.locations
|
|
808
|
+
entries: entries.locations,
|
|
809
|
+
locations: compiler.location_map_url
|
|
809
810
|
});
|
|
810
811
|
emit_island_deps_handoff(root, json, kit_dirs(root).out_dir);
|
|
811
812
|
}
|
package/dist/vite/kit-dirs.js
CHANGED
|
@@ -8,7 +8,7 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
* `src/routes`) and `outDir` (default `.svelte-kit`). ogygia scans the routes tree (csr worlds, the
|
|
9
9
|
* client-build keepalive, route options) and writes/reads its island-deps handoff under Kit's output
|
|
10
10
|
* dir — every one of those must follow the app's configuration, not the defaults (an app building
|
|
11
|
-
* two route trees from one source, `
|
|
11
|
+
* two route trees from one source, `CATALOG_MODE=v2 → src/routes-v2` + `.svelte-kit-v2`, had its
|
|
12
12
|
* all-csr=false tree read as "no routes": Kit skipped the client build, the runtime chunk was never
|
|
13
13
|
* emitted, every island 404'd).
|
|
14
14
|
*
|
package/package.json
CHANGED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/** An island entry's shim: every export of its hashed file (a component entry has a default). */
|
|
2
|
-
export declare function island_shim_source(shim_file: string, entry_file: string): string;
|
|
3
|
-
/** The runtime's shim: the hashed runtime, run (it has no exports; loading it boots). */
|
|
4
|
-
export declare function runtime_shim_source(shim_file: string, runtime_file: string): string;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { path } from "../host.js";
|
|
2
|
-
// src/compiler/link/entry-shim.ts
|
|
3
|
-
/**
|
|
4
|
-
* THE STABLE-NAME SHIMS. An island entry and the runtime are content-hashed files; each one's old
|
|
5
|
-
* stable name is ALSO written, as a shim of the hashed file, so that a page cached before content
|
|
6
|
-
* hashing (whose islands import their stable names), and a load whose location failed (the runtime
|
|
7
|
-
* re-fetches the stable name fresh), still reach the CURRENT build.
|
|
8
|
-
*
|
|
9
|
-
* A shim is written as a plain ASSET at the bundle's end, once every hashed name is final — never as
|
|
10
|
-
* a bundled chunk. Its name never changes, so the bundler has nothing to hash, and a chunk would make
|
|
11
|
-
* the island's module the target of two entries: the bundler would split its code behind two facades
|
|
12
|
-
* and every island load would cost two requests. As an asset, the island has one entry, its location
|
|
13
|
-
* IS its code, and the shim is a static re-export by relative path.
|
|
14
|
-
*/
|
|
15
|
-
/** The specifier from the shim's file to the hashed file (both output-relative paths). */
|
|
16
|
-
function relative_specifier(from_file, to_file) {
|
|
17
|
-
const rel = path.posix.relative(path.posix.dirname(from_file), to_file);
|
|
18
|
-
return rel.startsWith(".") ? rel : "./" + rel;
|
|
19
|
-
}
|
|
20
|
-
/** An island entry's shim: every export of its hashed file (a component entry has a default). */
|
|
21
|
-
function island_shim_source(shim_file, entry_file) {
|
|
22
|
-
const spec = JSON.stringify(relative_specifier(shim_file, entry_file));
|
|
23
|
-
return `export * from ${spec};\nexport { default } from ${spec};\n`;
|
|
24
|
-
}
|
|
25
|
-
/** The runtime's shim: the hashed runtime, run (it has no exports; loading it boots). */
|
|
26
|
-
function runtime_shim_source(shim_file, runtime_file) {
|
|
27
|
-
return `import ${JSON.stringify(relative_specifier(shim_file, runtime_file))};\n`;
|
|
28
|
-
}
|
|
29
|
-
export { island_shim_source, runtime_shim_source };
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// src/compiler/macros/dedent.ts
|
|
2
|
-
const CRLF_G = /\r\n?/g;
|
|
3
|
-
const LEADING_INDENT_RE = /^[\t ]*/;
|
|
4
|
-
/**
|
|
5
|
-
* Strip the common leading indentation from a block of source, and trim leading/trailing blank
|
|
6
|
-
* lines. Source embedded in a call (`import.meta.og.code(\`…\`, …)`) is indented to match where the
|
|
7
|
-
* call sits; that indentation is an artifact of the surrounding code, not part of the snippet. This
|
|
8
|
-
* normalizes it so the rendered `<pre>` shows the snippet's OWN logical indentation, flush-left.
|
|
9
|
-
*
|
|
10
|
-
* Blank lines (whitespace-only) don't count toward the common indent; tabs and spaces are treated by
|
|
11
|
-
* their literal column contribution to the prefix (the common prefix is computed on raw characters,
|
|
12
|
-
* so mixed tabs/spaces still de-indent correctly as long as the leading run is consistent — which it
|
|
13
|
-
* is for machine-indented source).
|
|
14
|
-
*/
|
|
15
|
-
function dedent(text) {
|
|
16
|
-
const lines = text.replace(CRLF_G, "\n").split("\n");
|
|
17
|
-
while (lines.length && lines[0].trim() === "") lines.shift();
|
|
18
|
-
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19
|
-
if (!lines.length) return "";
|
|
20
|
-
let common = null;
|
|
21
|
-
for (const line of lines) {
|
|
22
|
-
if (line.trim() === "") continue;
|
|
23
|
-
const lead = LEADING_INDENT_RE.exec(line)[0];
|
|
24
|
-
if (common === null) common = lead;
|
|
25
|
-
else {
|
|
26
|
-
let i = 0;
|
|
27
|
-
const max = Math.min(common.length, lead.length);
|
|
28
|
-
while (i < max && common[i] === lead[i]) i++;
|
|
29
|
-
common = common.slice(0, i);
|
|
30
|
-
}
|
|
31
|
-
if (common === "") break;
|
|
32
|
-
}
|
|
33
|
-
const n = common?.length ?? 0;
|
|
34
|
-
return lines.map((line) => line.trim() === "" ? "" : line.slice(n)).join("\n");
|
|
35
|
-
}
|
|
36
|
-
export { dedent };
|