@stacksjs/browser-extension 0.70.88 → 0.70.91
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/build.d.ts +16 -0
- package/dist/build.js +106 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +16 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +6 -0
- package/dist/manifest.d.ts +45 -0
- package/dist/manifest.js +60 -0
- package/dist/package.d.ts +15 -0
- package/dist/package.js +21 -0
- package/dist/sanitize.d.ts +11 -0
- package/dist/sanitize.js +7 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +0 -0
- package/package.json +1 -1
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BuildOptions, ExtensionConfig, ExtensionTarget } from './types';
|
|
2
|
+
/** Resolve the output directory for a target. */
|
|
3
|
+
export declare function resolveOutdir(config: ExtensionConfig, target: ExtensionTarget, override?: string): string;
|
|
4
|
+
/**`) + extra `assets` copied in
|
|
5
|
+
* - stx pages → `<name>.html` (+ companion `<name>.js`), sanitized for the
|
|
6
|
+
* extension CSP, with stx dev chunks removed
|
|
7
|
+
* - content + background scripts → classic IIFE bundles
|
|
8
|
+
* - declarativeNetRequest rulesets compiled from their `source` modules
|
|
9
|
+
* - `manifest.json` generated per target
|
|
10
|
+
* - the app's `hooks.postBuild` run last
|
|
11
|
+
*
|
|
12
|
+
* Replaces the hand-rolled `build-extension.ts` every extension used to carry.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildExtension(config: ExtensionConfig, options: BuildOptions): Promise<{ outdir: string, target: ExtensionTarget }>;
|
|
15
|
+
/** Build every configured target. */
|
|
16
|
+
export declare function buildAllTargets(config: ExtensionConfig, options: Omit<BuildOptions, 'target'>): Promise<void>;
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Glob } from "bun";
|
|
2
|
+
import { cp, mkdir, rm } from "node:fs/promises";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { contentScriptOut, generateManifest } from "./manifest";
|
|
6
|
+
import { sanitizeExtensionHtml } from "./sanitize";
|
|
7
|
+
export function resolveOutdir(config, target, override) {
|
|
8
|
+
if (override)
|
|
9
|
+
return override;
|
|
10
|
+
if (typeof config.outdir === "string")
|
|
11
|
+
return config.outdir;
|
|
12
|
+
if (config.outdir?.[target])
|
|
13
|
+
return config.outdir[target];
|
|
14
|
+
return target === "firefox" ? "dist-firefox" : "dist";
|
|
15
|
+
}
|
|
16
|
+
function normalizePage(page) {
|
|
17
|
+
return typeof page === "string" ? { template: page } : page;
|
|
18
|
+
}
|
|
19
|
+
function pageEntries(pages) {
|
|
20
|
+
if (!pages)
|
|
21
|
+
return [];
|
|
22
|
+
const out = [];
|
|
23
|
+
if (pages.popup)
|
|
24
|
+
out.push({ name: "popup", page: normalizePage(pages.popup) });
|
|
25
|
+
if (pages.options)
|
|
26
|
+
out.push({ name: "options", page: normalizePage(pages.options) });
|
|
27
|
+
for (const [name, page] of Object.entries(pages.extra ?? {}))
|
|
28
|
+
out.push({ name, page: normalizePage(page) });
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
async function buildScript(entry, out, outdir, cwd, minify) {
|
|
32
|
+
const result = await Bun.build({
|
|
33
|
+
entrypoints: [resolve(cwd, entry)],
|
|
34
|
+
target: "browser",
|
|
35
|
+
format: "iife",
|
|
36
|
+
splitting: !1,
|
|
37
|
+
minify
|
|
38
|
+
});
|
|
39
|
+
if (!result.success)
|
|
40
|
+
throw Error(`[browser-extension] failed to build ${entry}: ${result.logs.join(`
|
|
41
|
+
`)}`);
|
|
42
|
+
const output = result.outputs[0];
|
|
43
|
+
if (!output)
|
|
44
|
+
throw Error(`[browser-extension] build produced no output for ${entry}`);
|
|
45
|
+
await Bun.write(join(outdir, out), await output.text());
|
|
46
|
+
}
|
|
47
|
+
export async function buildExtension(config, options) {
|
|
48
|
+
const target = options.target ?? "chrome", cwd = options.cwd ?? process.cwd(), outdir = resolve(cwd, resolveOutdir(config, target, options.outdir)), minify = options.minify ?? !0;
|
|
49
|
+
await rm(outdir, { recursive: !0, force: !0 });
|
|
50
|
+
await mkdir(join(outdir, "rules"), { recursive: !0 });
|
|
51
|
+
await mkdir(join(outdir, "icons"), { recursive: !0 });
|
|
52
|
+
if (config.public) {
|
|
53
|
+
const pub = resolve(cwd, config.public);
|
|
54
|
+
if (existsSync(pub))
|
|
55
|
+
await cp(pub, outdir, { recursive: !0 });
|
|
56
|
+
}
|
|
57
|
+
for (const [dest, src] of Object.entries(config.assets ?? {})) {
|
|
58
|
+
const s = resolve(cwd, src);
|
|
59
|
+
if (existsSync(s))
|
|
60
|
+
await Bun.write(join(outdir, dest), Bun.file(s));
|
|
61
|
+
}
|
|
62
|
+
const pages = pageEntries(config.pages);
|
|
63
|
+
await Promise.all(pages.filter((p) => p.page.script).map((p) => buildScript(p.page.script, `${p.name}.js`, outdir, cwd, minify)));
|
|
64
|
+
if (pages.length) {
|
|
65
|
+
const { default: stxPlugin } = await import("bun-plugin-stx"), result = await Bun.build({
|
|
66
|
+
entrypoints: pages.map((p) => resolve(cwd, p.page.template)),
|
|
67
|
+
outdir,
|
|
68
|
+
minify,
|
|
69
|
+
naming: { entry: "[name].html" },
|
|
70
|
+
plugins: [stxPlugin()]
|
|
71
|
+
});
|
|
72
|
+
if (!result.success)
|
|
73
|
+
throw Error(`[browser-extension] failed to build pages: ${result.logs.join(`
|
|
74
|
+
`)}`);
|
|
75
|
+
for (const { name, page } of pages) {
|
|
76
|
+
const file = join(outdir, `${name}.html`);
|
|
77
|
+
if (!existsSync(file))
|
|
78
|
+
continue;
|
|
79
|
+
const own = page.script ? [`${name}.js`] : [];
|
|
80
|
+
await Bun.write(file, sanitizeExtensionHtml(await Bun.file(file).text(), own));
|
|
81
|
+
}
|
|
82
|
+
for await (const chunk of new Glob("chunk-*.js").scan(outdir))
|
|
83
|
+
await rm(join(outdir, chunk), { force: !0 });
|
|
84
|
+
}
|
|
85
|
+
const scripts = [];
|
|
86
|
+
if (config.background)
|
|
87
|
+
scripts.push({ entry: config.background, out: "background.js" });
|
|
88
|
+
for (const cs of config.content ?? [])
|
|
89
|
+
scripts.push({ entry: cs.entry, out: contentScriptOut(cs.entry, cs.out) });
|
|
90
|
+
await Promise.all(scripts.map((s) => buildScript(s.entry, s.out, outdir, cwd, minify)));
|
|
91
|
+
for (const rule of config.rules ?? []) {
|
|
92
|
+
if (!rule.source)
|
|
93
|
+
continue;
|
|
94
|
+
const mod = await import(resolve(cwd, rule.source)), build = mod.default ?? mod.buildRules ?? mod.rules ?? Object.values(mod).find((v) => typeof v === "function") ?? Object.values(mod).find((v) => Array.isArray(v)), data = typeof build === "function" ? await build() : build;
|
|
95
|
+
await Bun.write(join(outdir, rule.path ?? `rules/${rule.id}.json`), `${JSON.stringify(data, null, 2)}
|
|
96
|
+
`);
|
|
97
|
+
}
|
|
98
|
+
await Bun.write(join(outdir, "manifest.json"), `${JSON.stringify(generateManifest(config, { version: options.version, target }), null, 2)}
|
|
99
|
+
`);
|
|
100
|
+
await config.hooks?.postBuild?.({ config, target, outdir, version: options.version, cwd });
|
|
101
|
+
return { outdir, target };
|
|
102
|
+
}
|
|
103
|
+
export async function buildAllTargets(config, options) {
|
|
104
|
+
for (const target of config.targets ?? ["chrome", "firefox"])
|
|
105
|
+
await buildExtension(config, { ...options, target });
|
|
106
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExtensionConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Identity helper for authoring `config/extension.ts` with full type-checking
|
|
4
|
+
* and editor completion:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* // config/extension.ts
|
|
8
|
+
* import { defineExtension } from '@stacksjs/browser-extension'
|
|
9
|
+
* export default defineExtension({ name: 'My Extension', description: '…', … })
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function defineExtension(config: ExtensionConfig): ExtensionConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Load the project's extension config from `config/extension.ts` (default
|
|
15
|
+
* export). Returns null when the project has no extension config.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadExtensionConfig(cwd?: string): Promise<ExtensionConfig | null>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
export function defineExtension(config) {
|
|
4
|
+
return config;
|
|
5
|
+
}
|
|
6
|
+
export async function loadExtensionConfig(cwd = process.cwd()) {
|
|
7
|
+
for (const rel of ["config/extension.ts", "extension.config.ts"]) {
|
|
8
|
+
const path = resolve(cwd, rel);
|
|
9
|
+
if (!existsSync(path))
|
|
10
|
+
continue;
|
|
11
|
+
const mod = await import(path), config = mod.default ?? mod.extension ?? mod.config;
|
|
12
|
+
if (config)
|
|
13
|
+
return config;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @stacksjs/browser-extension
|
|
3
|
+
*
|
|
4
|
+
* Build MV3 browser extensions (Chrome + Firefox) from a single
|
|
5
|
+
* `ExtensionConfig` — manifest generation, content/background script bundling,
|
|
6
|
+
* stx pages, declarativeNetRequest rulesets, and store-ready packaging — with
|
|
7
|
+
* none of the per-project boilerplate.
|
|
8
|
+
*/
|
|
9
|
+
export * from './build';
|
|
10
|
+
export * from './config';
|
|
11
|
+
export * from './manifest';
|
|
12
|
+
export * from './package';
|
|
13
|
+
export * from './sanitize';
|
|
14
|
+
export * from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ExtensionConfig, ExtensionTarget } from './types';
|
|
2
|
+
/** Output filename for a content script (explicit `out`, else entry basename). */
|
|
3
|
+
export declare function contentScriptOut(entry: string, out?: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Generate an MV3 manifest for a target from the extension config. Chrome uses
|
|
6
|
+
* a `service_worker` background + `minimum_chrome_version`; Firefox uses a
|
|
7
|
+
* `scripts` event page + `browser_specific_settings.gecko` (required by AMO).
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateManifest(config: ExtensionConfig, opts: { version: string, target?: ExtensionTarget }): GeneratedManifest;
|
|
10
|
+
/** MV3 manifest shape (loose — the two targets diverge on `background`). */
|
|
11
|
+
export declare interface GeneratedManifest {
|
|
12
|
+
manifest_version: 3
|
|
13
|
+
name: string
|
|
14
|
+
description: string
|
|
15
|
+
version: string
|
|
16
|
+
minimum_chrome_version?: string
|
|
17
|
+
action?: { default_title?: string, default_popup?: string }
|
|
18
|
+
options_page?: string
|
|
19
|
+
background?: { service_worker: string, type?: 'module' } | { scripts: string[], type?: 'module' }
|
|
20
|
+
browser_specific_settings?: {
|
|
21
|
+
gecko: {
|
|
22
|
+
id: string
|
|
23
|
+
strict_min_version: string
|
|
24
|
+
data_collection_permissions: { required: ['none'] }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
permissions?: string[]
|
|
28
|
+
optional_permissions?: string[]
|
|
29
|
+
host_permissions?: string[]
|
|
30
|
+
icons?: Record<string, string>
|
|
31
|
+
content_scripts?: Array<{
|
|
32
|
+
matches: string[]
|
|
33
|
+
js: string[]
|
|
34
|
+
css?: string[]
|
|
35
|
+
run_at?: string
|
|
36
|
+
world?: string
|
|
37
|
+
all_frames?: boolean
|
|
38
|
+
match_about_blank?: boolean
|
|
39
|
+
exclude_matches?: string[]
|
|
40
|
+
}>
|
|
41
|
+
declarative_net_request?: { rule_resources: Array<{ id: string, enabled: boolean, path: string }> }
|
|
42
|
+
content_security_policy?: { extension_pages: string }
|
|
43
|
+
web_accessible_resources?: Array<{ resources: string[], matches: string[] }>
|
|
44
|
+
[key: string]: unknown
|
|
45
|
+
}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
export function contentScriptOut(entry, out) {
|
|
3
|
+
return out ?? basename(entry).replace(/\.[cm]?tsx?$/, ".js");
|
|
4
|
+
}
|
|
5
|
+
export function generateManifest(config, opts) {
|
|
6
|
+
const isFirefox = (opts.target ?? "chrome") === "firefox", m = config.manifest ?? {}, manifest = {
|
|
7
|
+
manifest_version: 3,
|
|
8
|
+
name: config.name,
|
|
9
|
+
description: config.description,
|
|
10
|
+
version: opts.version
|
|
11
|
+
};
|
|
12
|
+
if (!isFirefox && m.minimumChromeVersion)
|
|
13
|
+
manifest.minimum_chrome_version = m.minimumChromeVersion;
|
|
14
|
+
if (config.pages?.popup)
|
|
15
|
+
manifest.action = { default_title: config.name, default_popup: "popup.html" };
|
|
16
|
+
if (config.pages?.options)
|
|
17
|
+
manifest.options_page = "options.html";
|
|
18
|
+
if (config.background)
|
|
19
|
+
manifest.background = isFirefox ? { scripts: ["background.js"], type: "module" } : { service_worker: "background.js", type: "module" };
|
|
20
|
+
if (isFirefox && config.geckoId)
|
|
21
|
+
manifest.browser_specific_settings = {
|
|
22
|
+
gecko: {
|
|
23
|
+
id: config.geckoId,
|
|
24
|
+
strict_min_version: m.firefoxMinVersion ?? "128.0",
|
|
25
|
+
data_collection_permissions: { required: ["none"] }
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
if (m.permissions?.length)
|
|
29
|
+
manifest.permissions = m.permissions;
|
|
30
|
+
if (m.optionalPermissions?.length)
|
|
31
|
+
manifest.optional_permissions = m.optionalPermissions;
|
|
32
|
+
if (m.hostPermissions?.length)
|
|
33
|
+
manifest.host_permissions = m.hostPermissions;
|
|
34
|
+
if (config.icons)
|
|
35
|
+
manifest.icons = Object.fromEntries(Object.entries(config.icons).map(([size, path]) => [String(size), path]));
|
|
36
|
+
if (config.content?.length)
|
|
37
|
+
manifest.content_scripts = config.content.map((cs) => ({
|
|
38
|
+
matches: cs.matches,
|
|
39
|
+
js: [contentScriptOut(cs.entry, cs.out)],
|
|
40
|
+
...cs.runAt ? { run_at: cs.runAt } : {},
|
|
41
|
+
...cs.world ? { world: cs.world } : {},
|
|
42
|
+
...cs.allFrames ? { all_frames: !0 } : {},
|
|
43
|
+
...cs.matchAboutBlank ? { match_about_blank: !0 } : {},
|
|
44
|
+
...cs.excludeMatches ? { exclude_matches: cs.excludeMatches } : {}
|
|
45
|
+
}));
|
|
46
|
+
if (config.rules?.length)
|
|
47
|
+
manifest.declarative_net_request = {
|
|
48
|
+
rule_resources: config.rules.map((r) => ({
|
|
49
|
+
id: r.id,
|
|
50
|
+
enabled: r.enabled ?? !0,
|
|
51
|
+
path: r.path ?? `rules/${r.id}.json`
|
|
52
|
+
}))
|
|
53
|
+
};
|
|
54
|
+
manifest.content_security_policy = {
|
|
55
|
+
extension_pages: m.contentSecurityPolicy ?? "script-src 'self'; object-src 'self'"
|
|
56
|
+
};
|
|
57
|
+
if (m.webAccessibleResources?.length)
|
|
58
|
+
manifest.web_accessible_resources = m.webAccessibleResources;
|
|
59
|
+
return { ...manifest, ...m.extra ?? {} };
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExtensionConfig, ExtensionTarget } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Package a built extension into a store-ready `.zip` (one per target). Builds
|
|
4
|
+
* first unless `build: false`. Uses the system `zip` (available on CI runners
|
|
5
|
+
* and dev machines) so the archive matches what the Chrome/Firefox stores
|
|
6
|
+
* expect (files at the archive root, no wrapping directory).
|
|
7
|
+
*/
|
|
8
|
+
export declare function packageExtension(config: ExtensionConfig, options: PackageOptions): Promise<string>;
|
|
9
|
+
export declare interface PackageOptions {
|
|
10
|
+
target?: ExtensionTarget
|
|
11
|
+
version: string
|
|
12
|
+
cwd?: string
|
|
13
|
+
outfile?: string
|
|
14
|
+
build?: boolean
|
|
15
|
+
}
|
package/dist/package.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { buildExtension, resolveOutdir } from "./build";
|
|
5
|
+
function slug(name) {
|
|
6
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
7
|
+
}
|
|
8
|
+
export async function packageExtension(config, options) {
|
|
9
|
+
const target = options.target ?? "chrome", cwd = options.cwd ?? process.cwd();
|
|
10
|
+
if (options.build !== !1)
|
|
11
|
+
await buildExtension(config, { target, version: options.version, cwd });
|
|
12
|
+
const outdir = resolve(cwd, resolveOutdir(config, target));
|
|
13
|
+
if (!existsSync(outdir))
|
|
14
|
+
throw Error(`[browser-extension] nothing to package: ${outdir} does not exist`);
|
|
15
|
+
const suffix = target === "firefox" ? "-firefox" : "", outfile = resolve(cwd, options.outfile ?? `${slug(config.name)}-${options.version}${suffix}.zip`);
|
|
16
|
+
await mkdir(dirname(outfile), { recursive: !0 });
|
|
17
|
+
const proc = Bun.spawn(["zip", "-r", "-q", outfile, "."], { cwd: outdir, stdout: "pipe", stderr: "pipe" }), code = await proc.exited;
|
|
18
|
+
if (code !== 0)
|
|
19
|
+
throw Error(`[browser-extension] zip failed (${code}): ${await new Response(proc.stderr).text()}`);
|
|
20
|
+
return outfile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize an stx-built page for use as an extension page.
|
|
3
|
+
*
|
|
4
|
+
* MV3 extension pages run under a strict CSP (`script-src 'self'`) — no inline
|
|
5
|
+
* `<script>` or `<style>`, and asset URLs must be relative (the page loads from
|
|
6
|
+
* `chrome-extension://…/<name>.html`, not a web root). stx emits dev niceties
|
|
7
|
+
* (SEO meta, an inline style block, an inline hydration script) and absolute
|
|
8
|
+
* `/asset` paths, so strip/rewrite them, keeping only the page's own compiled
|
|
9
|
+
* script(s).
|
|
10
|
+
*/
|
|
11
|
+
export declare function sanitizeExtensionHtml(html: string, ownScripts: string[]): string;
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function sanitizeExtensionHtml(html, ownScripts) {
|
|
2
|
+
const keep = ownScripts.map((s) => `(?![^>]*src="/?${s.replace(".", "\\.")}")`).join("");
|
|
3
|
+
let out = html.replace(/\n?<!-- stx SEO Tags -->[\s\S]*?(?=\n\s*<meta charset=)/, "").replace(/\n?\s*<style\b[\s\S]*?<\/style>/g, "").replace(new RegExp(`\\n?\\s*<script\\b${keep}[\\s\\S]*?<\\/script>`, "g"), "").replaceAll('href="/styles.css"', 'href="styles.css"').replaceAll('href="/icons/', 'href="icons/');
|
|
4
|
+
for (const s of ownScripts)
|
|
5
|
+
out = out.replaceAll(`src="/${s}"`, `src="${s}"`);
|
|
6
|
+
return out;
|
|
7
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** A content script mapped into the manifest's `content_scripts`. */
|
|
2
|
+
export declare interface ContentScript {
|
|
3
|
+
entry: string
|
|
4
|
+
out?: string
|
|
5
|
+
matches: string[]
|
|
6
|
+
runAt?: 'document_start' | 'document_end' | 'document_idle'
|
|
7
|
+
world?: 'ISOLATED' | 'MAIN'
|
|
8
|
+
allFrames?: boolean
|
|
9
|
+
matchAboutBlank?: boolean
|
|
10
|
+
excludeMatches?: string[]
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A declarativeNetRequest static ruleset. Either point `path` at a pre-built
|
|
14
|
+
* JSON file, or give `source` — a module whose default export is a function (or
|
|
15
|
+
* value) producing the rules array, which the build compiles to
|
|
16
|
+
* `rules/<id>.json`.
|
|
17
|
+
*/
|
|
18
|
+
export declare interface RuleResource {
|
|
19
|
+
id: string
|
|
20
|
+
path?: string
|
|
21
|
+
enabled?: boolean
|
|
22
|
+
source?: string
|
|
23
|
+
}
|
|
24
|
+
/** An extension page: an stx template + an optional companion script. */
|
|
25
|
+
export declare interface ExtensionPage {
|
|
26
|
+
template: string
|
|
27
|
+
script?: string
|
|
28
|
+
}
|
|
29
|
+
/** stx pages bundled to HTML (popup, options, …). A bare string = template only. */
|
|
30
|
+
export declare interface ExtensionPages {
|
|
31
|
+
popup?: string | ExtensionPage
|
|
32
|
+
options?: string | ExtensionPage
|
|
33
|
+
extra?: Record<string, string | ExtensionPage>
|
|
34
|
+
}
|
|
35
|
+
/** Overrides merged verbatim into the generated manifest, per target. */
|
|
36
|
+
export declare interface ManifestOverrides {
|
|
37
|
+
permissions?: string[]
|
|
38
|
+
hostPermissions?: string[]
|
|
39
|
+
optionalPermissions?: string[]
|
|
40
|
+
minimumChromeVersion?: string
|
|
41
|
+
firefoxMinVersion?: string
|
|
42
|
+
contentSecurityPolicy?: string
|
|
43
|
+
webAccessibleResources?: Array<{ resources: string[], matches: string[] }>
|
|
44
|
+
extra?: Record<string, unknown>
|
|
45
|
+
}
|
|
46
|
+
/** Context passed to build hooks. */
|
|
47
|
+
export declare interface BuildContext {
|
|
48
|
+
config: ExtensionConfig
|
|
49
|
+
target: ExtensionTarget
|
|
50
|
+
outdir: string
|
|
51
|
+
version: string
|
|
52
|
+
cwd: string
|
|
53
|
+
}
|
|
54
|
+
export declare interface ExtensionConfig {
|
|
55
|
+
name: string
|
|
56
|
+
description: string
|
|
57
|
+
geckoId?: string
|
|
58
|
+
targets?: ExtensionTarget[]
|
|
59
|
+
background?: string
|
|
60
|
+
content?: ContentScript[]
|
|
61
|
+
pages?: ExtensionPages
|
|
62
|
+
icons?: Record<number, string>
|
|
63
|
+
public?: string
|
|
64
|
+
assets?: Record<string, string>
|
|
65
|
+
rules?: RuleResource[]
|
|
66
|
+
manifest?: ManifestOverrides
|
|
67
|
+
outdir?: Partial<Record<ExtensionTarget, string>> | string
|
|
68
|
+
hooks?: {
|
|
69
|
+
postBuild?: (ctx: BuildContext) => void | Promise<void>
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export declare interface BuildOptions {
|
|
73
|
+
target?: ExtensionTarget
|
|
74
|
+
version: string
|
|
75
|
+
outdir?: string
|
|
76
|
+
minify?: boolean
|
|
77
|
+
cwd?: string
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Config-driven MV3 browser-extension types.
|
|
81
|
+
*
|
|
82
|
+
* A consuming app declares one `ExtensionConfig` (typically in
|
|
83
|
+
* `config/extension.ts` via `defineExtension`) and the framework derives the
|
|
84
|
+
* manifest, build graph, and packaging from it — no hand-written manifest.json
|
|
85
|
+
* or per-project build script.
|
|
86
|
+
*/
|
|
87
|
+
export type ExtensionTarget = 'chrome' | 'firefox';
|
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/browser-extension",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.91",
|
|
5
5
|
"description": "Build MV3 browser extensions (Chrome + Firefox) the Stacks way — manifest, content/background scripts, DNR rules, packaging, all config-driven.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|