extrojs 0.3.0 → 0.3.1
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.
|
@@ -30,6 +30,12 @@ export interface AssetOptions {
|
|
|
30
30
|
export interface Artifacts {
|
|
31
31
|
manifest: ManifestV3;
|
|
32
32
|
html: Partial<Record<RoutableSurface, string>>;
|
|
33
|
+
/**
|
|
34
|
+
* The dev probe (`extro-dev.js`) the dev shells load to reveal the
|
|
35
|
+
* offline screen when the dev server is unreachable. Absent in prod
|
|
36
|
+
* builds and when there are no HTML surfaces to probe for.
|
|
37
|
+
*/
|
|
38
|
+
devProbe?: string;
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* Pure projection from inputs to artifact strings: no filesystem access (the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { generateManifest } from "./manifest.js";
|
|
2
|
-
import { generateHTML } from "./generators/html.js";
|
|
2
|
+
import { DEV_PROBE_FILE, generateDevProbe, generateHTML, } from "./generators/html.js";
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Public API
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
@@ -11,14 +11,18 @@ import { generateHTML } from "./generators/html.js";
|
|
|
11
11
|
*/
|
|
12
12
|
export function composeArtifacts(opts) {
|
|
13
13
|
const manifest = generateManifest(opts);
|
|
14
|
+
const surfaces = Object.keys(opts.tree.surfaces);
|
|
14
15
|
const html = {};
|
|
15
|
-
for (const surface of
|
|
16
|
+
for (const surface of surfaces) {
|
|
16
17
|
html[surface] = generateHTML({
|
|
17
18
|
surface,
|
|
18
19
|
dev: opts.dev ? { port: opts.dev.port } : undefined,
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
+
const devProbe = opts.dev && surfaces.length > 0
|
|
23
|
+
? generateDevProbe({ port: opts.dev.port })
|
|
24
|
+
: undefined;
|
|
25
|
+
return { manifest, html, devProbe };
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* Canonical emission of Extro's static outputs (manifest + per-surface HTML).
|
|
@@ -30,8 +34,11 @@ export function composeArtifacts(opts) {
|
|
|
30
34
|
* `viteBuild` already wrote them to disk).
|
|
31
35
|
*/
|
|
32
36
|
export async function emitAssets(opts, emit) {
|
|
33
|
-
const { manifest, html } = composeArtifacts(opts);
|
|
37
|
+
const { manifest, html, devProbe } = composeArtifacts(opts);
|
|
34
38
|
await emit("manifest.json", JSON.stringify(manifest, null, 2));
|
|
39
|
+
if (devProbe) {
|
|
40
|
+
await emit(DEV_PROBE_FILE, devProbe);
|
|
41
|
+
}
|
|
35
42
|
for (const [surface, body] of Object.entries(html)) {
|
|
36
43
|
if (!body)
|
|
37
44
|
continue;
|
|
@@ -4,13 +4,19 @@ interface GenerateHTMLOptions {
|
|
|
4
4
|
port: number;
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* The dev probe script emitted next to the HTML shells in the dev output
|
|
9
|
+
* dir. Referenced by the shells and reserved against Public assets, so the
|
|
10
|
+
* name lives here once.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEV_PROBE_FILE = "extro-dev.js";
|
|
7
13
|
/**
|
|
8
14
|
* @describe Generates the HTML shell for a surface. In dev mode, the shell
|
|
9
15
|
* points at the Vite dev server (with @vite/client for HMR) instead of the
|
|
10
|
-
* built bundle, and
|
|
11
|
-
* inside #root.
|
|
12
|
-
* the
|
|
13
|
-
*
|
|
16
|
+
* built bundle, and carries a hidden brand-styled "dev server offline"
|
|
17
|
+
* screen inside #root. The probe script (generateDevProbe) reveals it only
|
|
18
|
+
* when the dev server is unreachable; on a normal start it never paints,
|
|
19
|
+
* and React's createRoot replaces #root's children on mount.
|
|
14
20
|
*/
|
|
15
21
|
export declare function generateHTML({ surface, dev }: GenerateHTMLOptions): string;
|
|
16
22
|
export interface DevScreen {
|
|
@@ -23,17 +29,36 @@ export interface DevScreen {
|
|
|
23
29
|
* (popup), the screen sizes to content with a fixed minimum.
|
|
24
30
|
*/
|
|
25
31
|
fill?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* When true, the screen ships with the `hidden` attribute and stays out
|
|
34
|
+
* of the first paint until something removes it (the dev probe, for the
|
|
35
|
+
* offline screen).
|
|
36
|
+
*/
|
|
37
|
+
hidden?: boolean;
|
|
26
38
|
}
|
|
27
39
|
/**
|
|
28
40
|
* @describe Renders a brand-styled "developer screen" card. Used as the
|
|
29
41
|
* pre-render inside #root for offline-dev-server fallback today, intended
|
|
30
42
|
* for reuse in other build-time / runtime dev panels.
|
|
31
43
|
*/
|
|
32
|
-
export declare const renderDevScreen: ({ title, body, fill }: DevScreen) => string;
|
|
44
|
+
export declare const renderDevScreen: ({ title, body, fill, hidden, }: DevScreen) => string;
|
|
33
45
|
/**
|
|
34
46
|
* @describe Global styles for any `.extro-dev-screen` rendered in dev. Body
|
|
35
|
-
* is painted dark only while a screen is in the DOM (via `:has()`),
|
|
36
|
-
* user's app
|
|
47
|
+
* is painted dark only while a visible screen is in the DOM (via `:has()`),
|
|
48
|
+
* so the user's app keeps default styling both before React mounts (screen
|
|
49
|
+
* still hidden) and after (screen wiped from #root).
|
|
37
50
|
*/
|
|
38
51
|
export declare const devScreenStyles: () => string;
|
|
52
|
+
/**
|
|
53
|
+
* @describe Generates the dev probe (`extro-dev.js`), emitted into the dev
|
|
54
|
+
* output dir next to the HTML shells. MV3 CSP bans inline scripts but allows
|
|
55
|
+
* 'self', so the probe loads from the extension origin and runs even when
|
|
56
|
+
* the Vite dev server is down. It reveals the hidden offline screen only
|
|
57
|
+
* when the @vite/client script fails to fetch, the one unambiguous signal
|
|
58
|
+
* that the server is unreachable; a failing user module while the server is
|
|
59
|
+
* up stays Vite's error overlay's problem.
|
|
60
|
+
*/
|
|
61
|
+
export declare const generateDevProbe: ({ port }: {
|
|
62
|
+
port: number;
|
|
63
|
+
}) => string;
|
|
39
64
|
export {};
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dev probe script emitted next to the HTML shells in the dev output
|
|
3
|
+
* dir. Referenced by the shells and reserved against Public assets, so the
|
|
4
|
+
* name lives here once.
|
|
5
|
+
*/
|
|
6
|
+
export const DEV_PROBE_FILE = "extro-dev.js";
|
|
1
7
|
/**
|
|
2
8
|
* @describe Generates the HTML shell for a surface. In dev mode, the shell
|
|
3
9
|
* points at the Vite dev server (with @vite/client for HMR) instead of the
|
|
4
|
-
* built bundle, and
|
|
5
|
-
* inside #root.
|
|
6
|
-
* the
|
|
7
|
-
*
|
|
10
|
+
* built bundle, and carries a hidden brand-styled "dev server offline"
|
|
11
|
+
* screen inside #root. The probe script (generateDevProbe) reveals it only
|
|
12
|
+
* when the dev server is unreachable; on a normal start it never paints,
|
|
13
|
+
* and React's createRoot replaces #root's children on mount.
|
|
8
14
|
*/
|
|
9
15
|
export function generateHTML({ surface, dev }) {
|
|
10
16
|
const title = surface.charAt(0).toUpperCase() + surface.slice(1);
|
|
11
17
|
const scripts = dev
|
|
12
18
|
? `
|
|
19
|
+
<script src="./${DEV_PROBE_FILE}"></script>
|
|
13
20
|
<script type="module" src="http://localhost:${dev.port}/@vite/client"></script>
|
|
14
21
|
<script type="module" src="http://localhost:${dev.port}/@id/@vitejs/plugin-react/preamble"></script>
|
|
15
22
|
<script type="module" src="http://localhost:${dev.port}/@id/virtual:extro/runtime/${surface}"></script>
|
|
@@ -25,6 +32,10 @@ export function generateHTML({ surface, dev }) {
|
|
|
25
32
|
// Popups size to content (no viewport to fill); everything else
|
|
26
33
|
// (options/sidepanel/tab) gets a full-viewport centered layout.
|
|
27
34
|
fill: surface !== "popup",
|
|
35
|
+
// Hidden until the probe proves the dev server is down, so a normal
|
|
36
|
+
// start never paints it (the screen used to flash for the moment
|
|
37
|
+
// between document load and React mount).
|
|
38
|
+
hidden: true,
|
|
28
39
|
})
|
|
29
40
|
: "";
|
|
30
41
|
return `
|
|
@@ -46,10 +57,10 @@ export function generateHTML({ surface, dev }) {
|
|
|
46
57
|
* pre-render inside #root for offline-dev-server fallback today, intended
|
|
47
58
|
* for reuse in other build-time / runtime dev panels.
|
|
48
59
|
*/
|
|
49
|
-
export const renderDevScreen = ({ title, body, fill = false }) => {
|
|
60
|
+
export const renderDevScreen = ({ title, body, fill = false, hidden = false, }) => {
|
|
50
61
|
const cls = `extro-dev-screen${fill ? " extro-dev-screen--fill" : ""}`;
|
|
51
62
|
return `
|
|
52
|
-
<div class="${cls}">
|
|
63
|
+
<div class="${cls}"${hidden ? " hidden" : ""}>
|
|
53
64
|
<div class="extro-dev-screen__card">
|
|
54
65
|
<span class="extro-dev-screen__tag">EXTRO</span>
|
|
55
66
|
<h1>${title}</h1>
|
|
@@ -60,13 +71,14 @@ export const renderDevScreen = ({ title, body, fill = false }) => {
|
|
|
60
71
|
};
|
|
61
72
|
/**
|
|
62
73
|
* @describe Global styles for any `.extro-dev-screen` rendered in dev. Body
|
|
63
|
-
* is painted dark only while a screen is in the DOM (via `:has()`),
|
|
64
|
-
* user's app
|
|
74
|
+
* is painted dark only while a visible screen is in the DOM (via `:has()`),
|
|
75
|
+
* so the user's app keeps default styling both before React mounts (screen
|
|
76
|
+
* still hidden) and after (screen wiped from #root).
|
|
65
77
|
*/
|
|
66
78
|
export const devScreenStyles = () => `
|
|
67
79
|
<style>
|
|
68
80
|
body { margin: 0; }
|
|
69
|
-
body:has(.extro-dev-screen) { background: #0a0a0a; }
|
|
81
|
+
body:has(.extro-dev-screen:not([hidden])) { background: #0a0a0a; }
|
|
70
82
|
.extro-dev-screen {
|
|
71
83
|
box-sizing: border-box;
|
|
72
84
|
min-width: 360px;
|
|
@@ -77,7 +89,10 @@ export const devScreenStyles = () => `
|
|
|
77
89
|
align-items: center;
|
|
78
90
|
justify-content: center;
|
|
79
91
|
opacity: 0;
|
|
80
|
-
animation: extro-dev-screen-in 0.18s
|
|
92
|
+
animation: extro-dev-screen-in 0.18s forwards;
|
|
93
|
+
}
|
|
94
|
+
.extro-dev-screen[hidden] {
|
|
95
|
+
display: none;
|
|
81
96
|
}
|
|
82
97
|
.extro-dev-screen--fill {
|
|
83
98
|
min-height: 100vh;
|
|
@@ -125,3 +140,28 @@ export const devScreenStyles = () => `
|
|
|
125
140
|
@keyframes extro-dev-screen-in { to { opacity: 1; } }
|
|
126
141
|
</style>
|
|
127
142
|
`.trim();
|
|
143
|
+
/**
|
|
144
|
+
* @describe Generates the dev probe (`extro-dev.js`), emitted into the dev
|
|
145
|
+
* output dir next to the HTML shells. MV3 CSP bans inline scripts but allows
|
|
146
|
+
* 'self', so the probe loads from the extension origin and runs even when
|
|
147
|
+
* the Vite dev server is down. It reveals the hidden offline screen only
|
|
148
|
+
* when the @vite/client script fails to fetch, the one unambiguous signal
|
|
149
|
+
* that the server is unreachable; a failing user module while the server is
|
|
150
|
+
* up stays Vite's error overlay's problem.
|
|
151
|
+
*/
|
|
152
|
+
export const generateDevProbe = ({ port }) => `
|
|
153
|
+
// Generated by Extro. Reveals the "dev server isn't running" screen when
|
|
154
|
+
// the Vite dev server can't be reached. Script fetch errors don't bubble,
|
|
155
|
+
// hence the capture-phase listener; this classic script runs before the
|
|
156
|
+
// module scripts below it can fail.
|
|
157
|
+
window.addEventListener(
|
|
158
|
+
"error",
|
|
159
|
+
(event) => {
|
|
160
|
+
const el = event.target;
|
|
161
|
+
if (!(el instanceof HTMLScriptElement)) return;
|
|
162
|
+
if (el.src !== "http://localhost:${port}/@vite/client") return;
|
|
163
|
+
document.querySelector(".extro-dev-screen")?.removeAttribute("hidden");
|
|
164
|
+
},
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
`.trimStart();
|
package/dist/plugin/public.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { DEV_PROBE_FILE } from "./generators/html.js";
|
|
3
4
|
/**
|
|
4
5
|
* @file public.ts
|
|
5
6
|
* @description Discovers Public assets (static files under the project-root
|
|
@@ -31,6 +32,11 @@ function walk(dir, base) {
|
|
|
31
32
|
function isReservedName(name, tree) {
|
|
32
33
|
if (name === "manifest.json")
|
|
33
34
|
return true;
|
|
35
|
+
// Dev-only output, but reserved in all modes so the partition (and the
|
|
36
|
+
// web_accessible_resources list derived from it) doesn't depend on the
|
|
37
|
+
// build mode.
|
|
38
|
+
if (name === DEV_PROBE_FILE)
|
|
39
|
+
return true;
|
|
34
40
|
if (name === "icons" || name.startsWith("icons/"))
|
|
35
41
|
return true;
|
|
36
42
|
for (const surface of Object.keys(tree.surfaces)) {
|
package/dist/router/link.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"
|
|
|
15
15
|
export declare const Link: ({ href, replace, onClick, ...rest }: LinkProps) => import("react").DetailedReactHTMLElement<{
|
|
16
16
|
content?: string | undefined | undefined;
|
|
17
17
|
title?: string | undefined | undefined;
|
|
18
|
+
hidden?: boolean | undefined | undefined;
|
|
18
19
|
type?: string | undefined | undefined;
|
|
19
20
|
slot?: string | undefined | undefined;
|
|
20
21
|
style?: import("react").CSSProperties | undefined;
|
|
@@ -39,7 +40,6 @@ export declare const Link: ({ href, replace, onClick, ...rest }: LinkProps) => i
|
|
|
39
40
|
dir?: string | undefined | undefined;
|
|
40
41
|
draggable?: (boolean | "true" | "false") | undefined;
|
|
41
42
|
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
|
|
42
|
-
hidden?: boolean | undefined | undefined;
|
|
43
43
|
id?: string | undefined | undefined;
|
|
44
44
|
lang?: string | undefined | undefined;
|
|
45
45
|
nonce?: string | undefined | undefined;
|
package/package.json
CHANGED