extrojs 0.2.0 → 0.3.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/client.d.ts +7 -15
- package/dist/commands/build.js +1 -1
- package/dist/commands/dev.js +12 -29
- package/dist/config.d.ts +2 -2
- package/dist/core/asset.d.ts +10 -0
- package/dist/core/asset.js +10 -0
- package/dist/dev-assets.d.ts +3 -3
- package/dist/dev-assets.js +18 -16
- package/dist/exports/asset.d.ts +1 -0
- package/dist/exports/asset.js +1 -0
- package/dist/exports/link.d.ts +1 -0
- package/dist/exports/link.js +1 -0
- package/dist/exports/navigation.d.ts +2 -0
- package/dist/exports/navigation.js +1 -0
- package/dist/exports/runtime.d.ts +2 -0
- package/dist/exports/runtime.js +3 -0
- package/dist/load-config.d.ts +1 -1
- package/dist/paths.d.ts +1 -1
- package/dist/plugin/app-tree.d.ts +59 -0
- package/dist/plugin/app-tree.js +214 -0
- package/dist/plugin/asset-inventory.d.ts +24 -0
- package/dist/plugin/asset-inventory.js +9 -0
- package/dist/plugin/dev-reactions.d.ts +59 -0
- package/dist/plugin/dev-reactions.js +62 -0
- package/dist/plugin/emit-assets.d.ts +50 -0
- package/dist/plugin/emit-assets.js +40 -0
- package/dist/plugin/generators/html.d.ts +39 -0
- package/dist/plugin/generators/html.js +127 -0
- package/dist/plugin/generators/icons.d.ts +15 -0
- package/dist/plugin/generators/icons.js +16 -0
- package/dist/plugin/generators/public.d.ts +17 -0
- package/dist/plugin/generators/public.js +20 -0
- package/dist/plugin/icons.d.ts +5 -0
- package/dist/plugin/icons.js +20 -0
- package/dist/plugin/index.d.ts +31 -0
- package/dist/plugin/index.js +246 -0
- package/dist/plugin/internal.d.ts +14 -0
- package/dist/plugin/internal.js +6 -0
- package/dist/plugin/manifest.d.ts +29 -0
- package/dist/plugin/manifest.js +68 -0
- package/dist/plugin/public.d.ts +21 -0
- package/dist/plugin/public.js +63 -0
- package/dist/plugin/runtimes/clients/csui-mount.js +90 -0
- package/dist/plugin/runtimes/clients/dev-bridge.js +194 -0
- package/dist/plugin/runtimes/csui-mount.d.ts +18 -0
- package/dist/plugin/runtimes/csui-mount.js +21 -0
- package/dist/plugin/runtimes/dev-bridge.d.ts +22 -0
- package/dist/plugin/runtimes/dev-bridge.js +19 -0
- package/dist/plugin/runtimes/routes-module.d.ts +20 -0
- package/dist/plugin/runtimes/routes-module.js +51 -0
- package/dist/plugin/runtimes/runtime-module.d.ts +16 -0
- package/dist/plugin/runtimes/runtime-module.js +40 -0
- package/dist/plugin/surfaces.d.ts +37 -0
- package/dist/plugin/surfaces.js +67 -0
- package/dist/plugin/types/index.d.ts +9 -0
- package/dist/plugin/types/index.js +1 -0
- package/dist/plugin/utils/read-json.d.ts +1 -0
- package/dist/plugin/utils/read-json.js +8 -0
- package/dist/react/env.d.ts +13 -0
- package/dist/react/env.js +1 -0
- package/dist/router/build-tree.d.ts +46 -0
- package/dist/router/build-tree.js +56 -0
- package/dist/router/context.d.ts +13 -0
- package/dist/router/context.js +2 -0
- package/dist/router/create-router.d.ts +10 -0
- package/dist/router/create-router.js +126 -0
- package/dist/router/defaults.d.ts +24 -0
- package/dist/router/defaults.js +25 -0
- package/dist/router/error-boundary.d.ts +23 -0
- package/dist/router/error-boundary.js +21 -0
- package/dist/router/hooks.d.ts +18 -0
- package/dist/router/hooks.js +34 -0
- package/dist/router/index.d.ts +8 -0
- package/dist/router/index.js +7 -0
- package/dist/router/link.d.ts +305 -0
- package/dist/router/link.js +30 -0
- package/dist/router/match.d.ts +14 -0
- package/dist/router/match.js +27 -0
- package/dist/router/types.d.ts +55 -0
- package/dist/router/types.js +1 -0
- package/dist/types/index.d.ts +152 -0
- package/dist/types/index.js +1 -0
- package/package.json +39 -7
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { routeManifest } from "./app-tree.js";
|
|
2
|
+
/**
|
|
3
|
+
* A changed file under `src/app/background/` dirties background; under
|
|
4
|
+
* `src/app/content/` dirties content; anything else (shared code) dirties both.
|
|
5
|
+
* This is the only place that knows the `src/app` layout, so the CLI no longer
|
|
6
|
+
* hardcodes those paths.
|
|
7
|
+
*/
|
|
8
|
+
export function classifyScriptChange(changedPath) {
|
|
9
|
+
const p = changedPath.replace(/\\/g, "/");
|
|
10
|
+
const background = p.includes("/src/app/background/");
|
|
11
|
+
const content = p.includes("/src/app/content/");
|
|
12
|
+
if (!background && !content)
|
|
13
|
+
return { background: true, content: true };
|
|
14
|
+
return { background, content };
|
|
15
|
+
}
|
|
16
|
+
/** Union two dirty states (accumulating `change` events across one build). */
|
|
17
|
+
export function mergeDirty(a, b) {
|
|
18
|
+
return {
|
|
19
|
+
background: a.background || b.background,
|
|
20
|
+
content: a.content || b.content,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the accumulated dirty state at `BUNDLE_END`. No classified change
|
|
25
|
+
* (the initial build, or an `extro dev` restart) conservatively means both,
|
|
26
|
+
* matching the broadcast-on-first-build behavior.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveFlush(dirty) {
|
|
29
|
+
if (!dirty.background && !dirty.content) {
|
|
30
|
+
return { background: true, content: true };
|
|
31
|
+
}
|
|
32
|
+
return { background: dirty.background, content: dirty.content };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Diff two AppTrees into a {@link DevReaction}. A birth short-circuits the
|
|
36
|
+
* invalidate scan: the session has to restart regardless of any route delta.
|
|
37
|
+
*/
|
|
38
|
+
export function decideTreeReaction(prev, next, routables) {
|
|
39
|
+
if (!prev.scripts.background && next.scripts.background) {
|
|
40
|
+
return { kind: "restart", surface: "background" };
|
|
41
|
+
}
|
|
42
|
+
if (!prev.scripts.content && next.scripts.content) {
|
|
43
|
+
return { kind: "restart", surface: "content" };
|
|
44
|
+
}
|
|
45
|
+
for (const surface of routables) {
|
|
46
|
+
const had = (prev.surfaces[surface]?.routes.length ?? 0) > 0;
|
|
47
|
+
const has = (next.surfaces[surface]?.routes.length ?? 0) > 0;
|
|
48
|
+
if (has && !had)
|
|
49
|
+
return { kind: "restart", surface };
|
|
50
|
+
}
|
|
51
|
+
// The Route manifest IS what the routes module emits and is fully
|
|
52
|
+
// serializable, so its stable stringify is a faithful identity: invalidation
|
|
53
|
+
// can no longer drift from the contract (ADR 0005). This is what kills the
|
|
54
|
+
// historical HMR-drift bug class.
|
|
55
|
+
const changed = [];
|
|
56
|
+
for (const surface of routables) {
|
|
57
|
+
const key = (t) => JSON.stringify(routeManifest(t, surface));
|
|
58
|
+
if (key(prev) !== key(next))
|
|
59
|
+
changed.push(surface);
|
|
60
|
+
}
|
|
61
|
+
return changed.length ? { kind: "invalidate", surfaces: changed } : { kind: "noop" };
|
|
62
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ExtroConfig, ManifestV3 } from "../types/index.js";
|
|
2
|
+
import type { AppTree } from "./app-tree.js";
|
|
3
|
+
import type { AssetInventory } from "./asset-inventory.js";
|
|
4
|
+
import type { RoutableSurface } from "./surfaces.js";
|
|
5
|
+
/**
|
|
6
|
+
* Receives one finished asset at a time. Sync (Vite's `emitFile`) and async
|
|
7
|
+
* (`fs.writeFile`) sinks both fit — callers that await the seam will see
|
|
8
|
+
* any rejection from the async path.
|
|
9
|
+
*/
|
|
10
|
+
export type EmitSink = (fileName: string, source: string) => void | Promise<void>;
|
|
11
|
+
export interface AssetOptions {
|
|
12
|
+
tree: AppTree;
|
|
13
|
+
/**
|
|
14
|
+
* The build's discovered icons + Public assets. Both call paths
|
|
15
|
+
* (`generateBundle`, `writeDevAssets`) run `discoverAssets` once at their
|
|
16
|
+
* filesystem edge and pass the result in, so composition stays pure.
|
|
17
|
+
*/
|
|
18
|
+
inventory: AssetInventory;
|
|
19
|
+
pkg: {
|
|
20
|
+
name?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
};
|
|
24
|
+
config: ExtroConfig;
|
|
25
|
+
dev?: {
|
|
26
|
+
port: number;
|
|
27
|
+
signalPort: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface Artifacts {
|
|
31
|
+
manifest: ManifestV3;
|
|
32
|
+
html: Partial<Record<RoutableSurface, string>>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pure projection from inputs to artifact strings: no filesystem access (the
|
|
36
|
+
* Asset inventory is discovered at the caller's edge and passed in). Exposed
|
|
37
|
+
* alongside `emitAssets` so tests can assert "given this tree + inventory, this
|
|
38
|
+
* is the manifest" without a sink and without staging files on disk.
|
|
39
|
+
*/
|
|
40
|
+
export declare function composeArtifacts(opts: AssetOptions): Artifacts;
|
|
41
|
+
/**
|
|
42
|
+
* Canonical emission of Extro's static outputs (manifest + per-surface HTML).
|
|
43
|
+
* Both the build (`generateBundle`) and dev (`writeDevAssets`) call paths go
|
|
44
|
+
* through here — the only difference between them is the sink.
|
|
45
|
+
*
|
|
46
|
+
* Icons live outside this seam: they are emitted once during build via
|
|
47
|
+
* Rollup's binary-asset path and are not needed in dev (the initial
|
|
48
|
+
* `viteBuild` already wrote them to disk).
|
|
49
|
+
*/
|
|
50
|
+
export declare function emitAssets(opts: AssetOptions, emit: EmitSink): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { generateManifest } from "./manifest.js";
|
|
2
|
+
import { generateHTML } from "./generators/html.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Public API
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Pure projection from inputs to artifact strings: no filesystem access (the
|
|
8
|
+
* Asset inventory is discovered at the caller's edge and passed in). Exposed
|
|
9
|
+
* alongside `emitAssets` so tests can assert "given this tree + inventory, this
|
|
10
|
+
* is the manifest" without a sink and without staging files on disk.
|
|
11
|
+
*/
|
|
12
|
+
export function composeArtifacts(opts) {
|
|
13
|
+
const manifest = generateManifest(opts);
|
|
14
|
+
const html = {};
|
|
15
|
+
for (const surface of Object.keys(opts.tree.surfaces)) {
|
|
16
|
+
html[surface] = generateHTML({
|
|
17
|
+
surface,
|
|
18
|
+
dev: opts.dev ? { port: opts.dev.port } : undefined,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return { manifest, html };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Canonical emission of Extro's static outputs (manifest + per-surface HTML).
|
|
25
|
+
* Both the build (`generateBundle`) and dev (`writeDevAssets`) call paths go
|
|
26
|
+
* through here — the only difference between them is the sink.
|
|
27
|
+
*
|
|
28
|
+
* Icons live outside this seam: they are emitted once during build via
|
|
29
|
+
* Rollup's binary-asset path and are not needed in dev (the initial
|
|
30
|
+
* `viteBuild` already wrote them to disk).
|
|
31
|
+
*/
|
|
32
|
+
export async function emitAssets(opts, emit) {
|
|
33
|
+
const { manifest, html } = composeArtifacts(opts);
|
|
34
|
+
await emit("manifest.json", JSON.stringify(manifest, null, 2));
|
|
35
|
+
for (const [surface, body] of Object.entries(html)) {
|
|
36
|
+
if (!body)
|
|
37
|
+
continue;
|
|
38
|
+
await emit(`${surface}.html`, body);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
interface GenerateHTMLOptions {
|
|
2
|
+
surface: string;
|
|
3
|
+
dev?: {
|
|
4
|
+
port: number;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* @describe Generates the HTML shell for a surface. In dev mode, the shell
|
|
9
|
+
* points at the Vite dev server (with @vite/client for HMR) instead of the
|
|
10
|
+
* built bundle, and pre-renders a brand-styled "dev server offline" screen
|
|
11
|
+
* inside #root. React's createRoot replaces #root's children on mount, so
|
|
12
|
+
* the screen vanishes on a normal start; if the dev server never comes up,
|
|
13
|
+
* the screen stays visible.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateHTML({ surface, dev }: GenerateHTMLOptions): string;
|
|
16
|
+
export interface DevScreen {
|
|
17
|
+
title: string;
|
|
18
|
+
/** HTML body content (paragraphs, code, etc.). Inserted as-is. */
|
|
19
|
+
body: string;
|
|
20
|
+
/**
|
|
21
|
+
* When true, the screen fills the viewport and centers its card. Use for
|
|
22
|
+
* surfaces with a real viewport (options tab, sidepanel). When false
|
|
23
|
+
* (popup), the screen sizes to content with a fixed minimum.
|
|
24
|
+
*/
|
|
25
|
+
fill?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @describe Renders a brand-styled "developer screen" card. Used as the
|
|
29
|
+
* pre-render inside #root for offline-dev-server fallback today, intended
|
|
30
|
+
* for reuse in other build-time / runtime dev panels.
|
|
31
|
+
*/
|
|
32
|
+
export declare const renderDevScreen: ({ title, body, fill }: DevScreen) => string;
|
|
33
|
+
/**
|
|
34
|
+
* @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()`), so the
|
|
36
|
+
* user's app reverts to default styling once React mounts.
|
|
37
|
+
*/
|
|
38
|
+
export declare const devScreenStyles: () => string;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @describe Generates the HTML shell for a surface. In dev mode, the shell
|
|
3
|
+
* points at the Vite dev server (with @vite/client for HMR) instead of the
|
|
4
|
+
* built bundle, and pre-renders a brand-styled "dev server offline" screen
|
|
5
|
+
* inside #root. React's createRoot replaces #root's children on mount, so
|
|
6
|
+
* the screen vanishes on a normal start; if the dev server never comes up,
|
|
7
|
+
* the screen stays visible.
|
|
8
|
+
*/
|
|
9
|
+
export function generateHTML({ surface, dev }) {
|
|
10
|
+
const title = surface.charAt(0).toUpperCase() + surface.slice(1);
|
|
11
|
+
const scripts = dev
|
|
12
|
+
? `
|
|
13
|
+
<script type="module" src="http://localhost:${dev.port}/@vite/client"></script>
|
|
14
|
+
<script type="module" src="http://localhost:${dev.port}/@id/@vitejs/plugin-react/preamble"></script>
|
|
15
|
+
<script type="module" src="http://localhost:${dev.port}/@id/virtual:extro/runtime/${surface}"></script>
|
|
16
|
+
`
|
|
17
|
+
: `<script type="module" src="./${surface}.js"></script>`;
|
|
18
|
+
const rootContent = dev
|
|
19
|
+
? renderDevScreen({
|
|
20
|
+
title: "Dev server isn't running",
|
|
21
|
+
body: `
|
|
22
|
+
<p>Start it with <code>extro dev</code>, then reopen this surface.</p>
|
|
23
|
+
<p>Expected at <code>http://localhost:${dev.port}</code>.</p>
|
|
24
|
+
`,
|
|
25
|
+
// Popups size to content (no viewport to fill); everything else
|
|
26
|
+
// (options/sidepanel/tab) gets a full-viewport centered layout.
|
|
27
|
+
fill: surface !== "popup",
|
|
28
|
+
})
|
|
29
|
+
: "";
|
|
30
|
+
return `
|
|
31
|
+
<!doctype html>
|
|
32
|
+
<html>
|
|
33
|
+
<head>
|
|
34
|
+
<title>${title}</title>
|
|
35
|
+
${dev ? devScreenStyles() : ""}
|
|
36
|
+
</head>
|
|
37
|
+
<body>
|
|
38
|
+
<div id="root">${rootContent}</div>
|
|
39
|
+
${scripts.trim()}
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
42
|
+
`.trim();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @describe Renders a brand-styled "developer screen" card. Used as the
|
|
46
|
+
* pre-render inside #root for offline-dev-server fallback today, intended
|
|
47
|
+
* for reuse in other build-time / runtime dev panels.
|
|
48
|
+
*/
|
|
49
|
+
export const renderDevScreen = ({ title, body, fill = false }) => {
|
|
50
|
+
const cls = `extro-dev-screen${fill ? " extro-dev-screen--fill" : ""}`;
|
|
51
|
+
return `
|
|
52
|
+
<div class="${cls}">
|
|
53
|
+
<div class="extro-dev-screen__card">
|
|
54
|
+
<span class="extro-dev-screen__tag">EXTRO</span>
|
|
55
|
+
<h1>${title}</h1>
|
|
56
|
+
${body.trim()}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
`.trim();
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* @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()`), so the
|
|
64
|
+
* user's app reverts to default styling once React mounts.
|
|
65
|
+
*/
|
|
66
|
+
export const devScreenStyles = () => `
|
|
67
|
+
<style>
|
|
68
|
+
body { margin: 0; }
|
|
69
|
+
body:has(.extro-dev-screen) { background: #0a0a0a; }
|
|
70
|
+
.extro-dev-screen {
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
min-width: 360px;
|
|
73
|
+
min-height: 240px;
|
|
74
|
+
padding: 24px 28px;
|
|
75
|
+
background: #0a0a0a;
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
opacity: 0;
|
|
80
|
+
animation: extro-dev-screen-in 0.18s 0.05s forwards;
|
|
81
|
+
}
|
|
82
|
+
.extro-dev-screen--fill {
|
|
83
|
+
min-height: 100vh;
|
|
84
|
+
}
|
|
85
|
+
.extro-dev-screen__card {
|
|
86
|
+
box-sizing: border-box;
|
|
87
|
+
width: 100%;
|
|
88
|
+
max-width: 480px;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
gap: 10px;
|
|
92
|
+
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
93
|
+
color: #e5e5e5;
|
|
94
|
+
}
|
|
95
|
+
.extro-dev-screen__tag {
|
|
96
|
+
align-self: flex-start;
|
|
97
|
+
font-size: 11px;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
letter-spacing: 0.08em;
|
|
100
|
+
padding: 3px 8px;
|
|
101
|
+
color: #0a0a0a;
|
|
102
|
+
background: #CC785C;
|
|
103
|
+
border-radius: 3px;
|
|
104
|
+
}
|
|
105
|
+
.extro-dev-screen h1 {
|
|
106
|
+
margin: 4px 0 0;
|
|
107
|
+
font-size: 15px;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
color: #fafafa;
|
|
110
|
+
}
|
|
111
|
+
.extro-dev-screen p {
|
|
112
|
+
margin: 0;
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
line-height: 1.5;
|
|
115
|
+
color: #a3a3a3;
|
|
116
|
+
}
|
|
117
|
+
.extro-dev-screen code {
|
|
118
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
119
|
+
font-size: 12px;
|
|
120
|
+
padding: 1px 6px;
|
|
121
|
+
color: #CC785C;
|
|
122
|
+
background: #1a1a1a;
|
|
123
|
+
border-radius: 3px;
|
|
124
|
+
}
|
|
125
|
+
@keyframes extro-dev-screen-in { to { opacity: 1; } }
|
|
126
|
+
</style>
|
|
127
|
+
`.trim();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PluginContextLike } from "../types/index.js";
|
|
2
|
+
interface EmitIconsOptions {
|
|
3
|
+
ctx: PluginContextLike;
|
|
4
|
+
root: string;
|
|
5
|
+
/** The recognized icon set from the Asset inventory (size -> "icons/16.png"). */
|
|
6
|
+
icons: Record<string, string> | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* @file generators/icons.ts
|
|
10
|
+
* @description Emits the recognized extension icons named by the Asset
|
|
11
|
+
* inventory. Discovery already decided which sizes exist, so this only reads
|
|
12
|
+
* bytes: what ships is exactly what `manifest.icons` references.
|
|
13
|
+
*/
|
|
14
|
+
export declare function emitIcons({ ctx, root, icons }: EmitIconsOptions): void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* @file generators/icons.ts
|
|
5
|
+
* @description Emits the recognized extension icons named by the Asset
|
|
6
|
+
* inventory. Discovery already decided which sizes exist, so this only reads
|
|
7
|
+
* bytes: what ships is exactly what `manifest.icons` references.
|
|
8
|
+
*/
|
|
9
|
+
export function emitIcons({ ctx, root, icons }) {
|
|
10
|
+
if (!icons)
|
|
11
|
+
return;
|
|
12
|
+
for (const rel of Object.values(icons)) {
|
|
13
|
+
const source = fs.readFileSync(path.join(root, rel));
|
|
14
|
+
ctx.emitFile({ type: "asset", fileName: rel, source });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PluginContextLike } from "../types/index.js";
|
|
2
|
+
import { type PublicAssets } from "../public.js";
|
|
3
|
+
interface EmitPublicAssetsOptions {
|
|
4
|
+
ctx: PluginContextLike;
|
|
5
|
+
root: string;
|
|
6
|
+
/** The partitioned Public assets from the Asset inventory. */
|
|
7
|
+
publicAssets: PublicAssets;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* @file generators/public.ts
|
|
11
|
+
* @description Emits Public assets into the build output with their original
|
|
12
|
+
* names, from the partition the Asset inventory already computed. Mirrors
|
|
13
|
+
* `emitIcons`; the collision guard skips any file that would overwrite a
|
|
14
|
+
* generated output and warns instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function emitPublicAssets({ ctx, root, publicAssets }: EmitPublicAssetsOptions): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PUBLIC_DIR } from "../public.js";
|
|
4
|
+
/**
|
|
5
|
+
* @file generators/public.ts
|
|
6
|
+
* @description Emits Public assets into the build output with their original
|
|
7
|
+
* names, from the partition the Asset inventory already computed. Mirrors
|
|
8
|
+
* `emitIcons`; the collision guard skips any file that would overwrite a
|
|
9
|
+
* generated output and warns instead.
|
|
10
|
+
*/
|
|
11
|
+
export function emitPublicAssets({ ctx, root, publicAssets }) {
|
|
12
|
+
const { files, conflicts } = publicAssets;
|
|
13
|
+
for (const conflict of conflicts) {
|
|
14
|
+
ctx.warn(`public/${conflict} collides with a generated output; skipping. Rename it to ship it.`);
|
|
15
|
+
}
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const source = fs.readFileSync(path.join(root, PUBLIC_DIR, file));
|
|
18
|
+
ctx.emitFile({ type: "asset", fileName: file, source });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* @file icons.ts
|
|
5
|
+
* @description Detects the icons for the extension.
|
|
6
|
+
*/
|
|
7
|
+
export function detectIcons(root) {
|
|
8
|
+
const iconsDir = path.join(root, "icons");
|
|
9
|
+
if (!fs.existsSync(iconsDir))
|
|
10
|
+
return undefined;
|
|
11
|
+
const sizes = ["16", "32", "48", "128"];
|
|
12
|
+
const icons = {};
|
|
13
|
+
for (const size of sizes) {
|
|
14
|
+
const file = path.join(iconsDir, `${size}.png`);
|
|
15
|
+
if (fs.existsSync(file)) {
|
|
16
|
+
icons[size] = `icons/${size}.png`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return Object.keys(icons).length ? icons : undefined;
|
|
20
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import type { ExtroConfig } from "../types/index.js";
|
|
3
|
+
interface ExtroPluginOptions {
|
|
4
|
+
root: string;
|
|
5
|
+
config?: ExtroConfig;
|
|
6
|
+
/**
|
|
7
|
+
* When true, only background/content are bundled. Manifest + HTML emission
|
|
8
|
+
* is skipped (writeDevAssets handles those separately during `extro dev`).
|
|
9
|
+
* Used by the dev build-watch sidecar.
|
|
10
|
+
*/
|
|
11
|
+
scriptsOnly?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When set, wrap the background entry in the dev bridge so a service
|
|
14
|
+
* worker exists in dev mode (even if the user didn't write one) to
|
|
15
|
+
* receive rebuild signals from the CLI's WS server and forward Vite HMR
|
|
16
|
+
* events to content scripts.
|
|
17
|
+
*/
|
|
18
|
+
devBridge?: {
|
|
19
|
+
signalPort: number;
|
|
20
|
+
vitePort: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Called from the dev-server plugin's `handleHotUpdate` with a payload
|
|
24
|
+
* shaped like Vite's own HMR `update` event. The CLI uses this to
|
|
25
|
+
* broadcast HMR over the signal WS (avoiding Vite's HMR WS, whose
|
|
26
|
+
* origin check rejects chrome-extension:// service workers).
|
|
27
|
+
*/
|
|
28
|
+
broadcastHmr?: (payload: object) => void;
|
|
29
|
+
}
|
|
30
|
+
export declare function extro(options: ExtroPluginOptions): Plugin;
|
|
31
|
+
export {};
|