@stacksjs/browser-extension 0.70.138 → 0.70.140
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/firefox-addons.d.ts +2 -0
- package/dist/firefox-addons.js +17 -2
- package/dist/safari.d.ts +2 -0
- package/dist/safari.js +53 -3
- package/package.json +1 -1
- package/safari-template/__APP_NAME__ Extension/Resources.inputs.xcfilelist +1 -0
- package/safari-template/__APP_NAME__ Extension/Resources.outputs.xcfilelist +1 -0
- package/safari-template/__APP_NAME__.xcodeproj/project.pbxproj +21 -2
package/dist/firefox-addons.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ExtensionConfig, FirefoxAddonsConfig } from './types';
|
|
2
2
|
/** Metadata accepted by AMO v5 when web-ext creates the first listed version. */
|
|
3
3
|
export declare function firefoxListingMetadata(config: ExtensionConfig, store: FirefoxAddonsConfig): Record<string, unknown> | undefined;
|
|
4
|
+
/** Resolve web-ext from PATH or from this package's declared dependency. */
|
|
5
|
+
export declare function resolveWebExtExecutable(which?: (command: string) => string | null): string | undefined;
|
|
4
6
|
/** Build and submit a Firefox extension through Mozilla's official web-ext/AMO v5 flow. */
|
|
5
7
|
export declare function publishFirefoxExtension(config: ExtensionConfig, options: FirefoxPublishOptions): Promise<FirefoxPublishResult>;
|
|
6
8
|
export declare interface FirefoxAddonsAuth {
|
package/dist/firefox-addons.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { join, resolve } from "node:path";
|
|
5
6
|
import { buildExtension, resolveOutdir } from "./build";
|
|
@@ -23,13 +24,27 @@ export function firefoxListingMetadata(config, store) {
|
|
|
23
24
|
requires_payment: store.requiresPayment ?? !1
|
|
24
25
|
};
|
|
25
26
|
}
|
|
27
|
+
export function resolveWebExtExecutable(which = Bun.which) {
|
|
28
|
+
const fromPath = which("web-ext");
|
|
29
|
+
if (fromPath)
|
|
30
|
+
return fromPath;
|
|
31
|
+
try {
|
|
32
|
+
const packagePath = createRequire(import.meta.url).resolve("web-ext/package.json"), packageJson = JSON.parse(readFileSync(packagePath, "utf8")), relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.["web-ext"];
|
|
33
|
+
if (!relativeBin)
|
|
34
|
+
return;
|
|
35
|
+
const executable = resolve(packagePath, "..", relativeBin);
|
|
36
|
+
return existsSync(executable) ? executable : void 0;
|
|
37
|
+
} catch {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
26
41
|
export async function publishFirefoxExtension(config, options) {
|
|
27
42
|
if (!config.geckoId)
|
|
28
43
|
throw Error("[browser-extension] Firefox publishing needs geckoId in config/extension.ts");
|
|
29
44
|
const store = config.firefoxAddons ?? {}, auth = resolveFirefoxAuth(options), cwd = options.cwd ?? process.cwd();
|
|
30
45
|
if (options.build !== !1)
|
|
31
46
|
await buildExtension(config, { target: "firefox", version: options.version, cwd });
|
|
32
|
-
const sourceDir = resolve(cwd, resolveOutdir(config, "firefox")), artifactsDir = resolve(cwd, store.artifactsDir ?? "web-ext-artifacts"), executable =
|
|
47
|
+
const sourceDir = resolve(cwd, resolveOutdir(config, "firefox")), artifactsDir = resolve(cwd, store.artifactsDir ?? "web-ext-artifacts"), executable = resolveWebExtExecutable();
|
|
33
48
|
if (!executable)
|
|
34
49
|
throw Error("[browser-extension] web-ext is unavailable; reinstall @stacksjs/browser-extension dependencies");
|
|
35
50
|
await mkdir(artifactsDir, { recursive: !0 });
|
package/dist/safari.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ import type { ExtensionConfig } from './types';
|
|
|
18
18
|
export declare function safariAppName(config: ExtensionConfig): string;
|
|
19
19
|
/** Directory the container app is scaffolded into. */
|
|
20
20
|
export declare function safariProjectDir(cwd?: unknown, dir?: string): string;
|
|
21
|
+
/** Upgrade a Stacks-generated project that copied Resources as a nested folder. */
|
|
22
|
+
export declare function migrateSafariResourceBuildPhase(project: string, appName: string): string;
|
|
21
23
|
/**
|
|
22
24
|
* Scaffold the macOS container app (Xcode project + SwiftUI app + appex) from
|
|
23
25
|
* the tokenized template. Idempotent: existing files are kept unless `force`.
|
package/dist/safari.js
CHANGED
|
@@ -17,8 +17,43 @@ function templateRoot() {
|
|
|
17
17
|
function fillTemplate(text, vars) {
|
|
18
18
|
return text.replaceAll("__EXT_BUNDLE_ID__", vars.extBundleId).replaceAll("__BUNDLE_ID__", vars.bundleId).replaceAll("__APP_DISPLAY_NAME__", vars.displayName).replaceAll("__APP_NAME__", vars.appName).replaceAll("__MARKETING_VERSION__", vars.version).replaceAll("__DEVELOPMENT_TEAM__", vars.teamId).replaceAll("__YEAR__", String(new Date().getFullYear()));
|
|
19
19
|
}
|
|
20
|
+
function safariResourceCopyBuildPhase(appName) {
|
|
21
|
+
return `/* Begin PBXShellScriptBuildPhase section */
|
|
22
|
+
0D0000000000000000000023 /* Copy Web Extension Resources */ = {
|
|
23
|
+
isa = PBXShellScriptBuildPhase;
|
|
24
|
+
alwaysOutOfDate = 1;
|
|
25
|
+
buildActionMask = 2147483647;
|
|
26
|
+
files = (
|
|
27
|
+
);
|
|
28
|
+
inputFileListPaths = (
|
|
29
|
+
"$(SRCROOT)/${appName} Extension/Resources.inputs.xcfilelist",
|
|
30
|
+
);
|
|
31
|
+
name = "Copy Web Extension Resources";
|
|
32
|
+
outputFileListPaths = (
|
|
33
|
+
"$(SRCROOT)/${appName} Extension/Resources.outputs.xcfilelist",
|
|
34
|
+
);
|
|
35
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
36
|
+
shellPath = /bin/sh;
|
|
37
|
+
shellScript = "set -e\\nresources_root=\\"\${SRCROOT}/${appName} Extension/Resources/\\"\\nwhile IFS= read -r source_file; do\\n relative_path=\\"\${source_file#$resources_root}\\"\\n output_file=\\"\${TARGET_BUILD_DIR}/\${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\${relative_path}\\"\\n /bin/mkdir -p \\"$(/usr/bin/dirname \\"$output_file\\")\\"\\n /bin/cp \\"$source_file\\" \\"$output_file\\"\\ndone < \\"$SCRIPT_INPUT_FILE_LIST_0\\"\\n";
|
|
38
|
+
};
|
|
39
|
+
/* End PBXShellScriptBuildPhase section */
|
|
40
|
+
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
export function migrateSafariResourceBuildPhase(project, appName) {
|
|
44
|
+
if (project.includes("SCRIPT_INPUT_FILE_LIST_0"))
|
|
45
|
+
return project;
|
|
46
|
+
if (project.includes("0D0000000000000000000023 /* Copy Web Extension Resources */"))
|
|
47
|
+
return project.replace(/\/\* Begin PBXShellScriptBuildPhase section \*\/[\s\S]*?\/\* End PBXShellScriptBuildPhase section \*\/\n/, safariResourceCopyBuildPhase(appName));
|
|
48
|
+
if (!project.includes("0C0000000000000000000023 /* Resources in Resources */"))
|
|
49
|
+
return project;
|
|
50
|
+
return project.replace(/^\s*0C0000000000000000000023 \/\* Resources in Resources \*\/.*\n/m, "").replace(/^\s*0C0000000000000000000023 \/\* Resources in Resources \*\/,\n/mg, "").replace(` 0D0000000000000000000022 /* Resources */,
|
|
51
|
+
`, ` 0D0000000000000000000022 /* Resources */,
|
|
52
|
+
0D0000000000000000000023 /* Copy Web Extension Resources */,
|
|
53
|
+
`).replace("/* Begin PBXSourcesBuildPhase section */", `${safariResourceCopyBuildPhase(appName)}/* Begin PBXSourcesBuildPhase section */`);
|
|
54
|
+
}
|
|
20
55
|
function* walk(dir, prefix = "") {
|
|
21
|
-
for (const entry of readdirSync(dir)) {
|
|
56
|
+
for (const entry of readdirSync(dir).sort()) {
|
|
22
57
|
const rel = prefix ? `${prefix}/${entry}` : entry;
|
|
23
58
|
if (statSync(join(dir, entry)).isDirectory())
|
|
24
59
|
yield* walk(join(dir, entry), rel);
|
|
@@ -92,7 +127,13 @@ export async function syncSafariResources(config, options = {}) {
|
|
|
92
127
|
const cwd = options.cwd ?? process.cwd(), outdir = resolve(cwd, options.outdir ?? resolveOutdir(config, "safari"));
|
|
93
128
|
if (!existsSync(join(outdir, "manifest.json")))
|
|
94
129
|
throw Error(`[browser-extension] ${outdir}/manifest.json is missing. Run extension:build --target safari first.`);
|
|
95
|
-
const appName = safariAppName(config),
|
|
130
|
+
const appName = safariAppName(config), projectDir = safariProjectDir(cwd, options.dir), projectPath = join(projectDir, `${appName}.xcodeproj`, "project.pbxproj");
|
|
131
|
+
if (existsSync(projectPath)) {
|
|
132
|
+
const project = await Bun.file(projectPath).text(), migrated = migrateSafariResourceBuildPhase(project, appName);
|
|
133
|
+
if (migrated !== project)
|
|
134
|
+
await Bun.write(projectPath, migrated);
|
|
135
|
+
}
|
|
136
|
+
const resources = join(projectDir, `${appName} Extension`, "Resources"), exclude = new Set(config.safariExclude ?? []);
|
|
96
137
|
if (existsSync(resources)) {
|
|
97
138
|
for (const entry of readdirSync(resources))
|
|
98
139
|
if (entry !== ".gitkeep")
|
|
@@ -100,14 +141,23 @@ export async function syncSafariResources(config, options = {}) {
|
|
|
100
141
|
}
|
|
101
142
|
await mkdir(resources, { recursive: !0 });
|
|
102
143
|
let files = 0;
|
|
144
|
+
const synced = [];
|
|
103
145
|
for (const rel of walk(outdir)) {
|
|
104
|
-
if (exclude.has(rel))
|
|
146
|
+
if (exclude.has(rel) || rel.split("/").includes(".DS_Store"))
|
|
105
147
|
continue;
|
|
106
148
|
const dest = join(resources, rel);
|
|
107
149
|
await mkdir(dirname(dest), { recursive: !0 });
|
|
108
150
|
cpSync(join(outdir, rel), dest);
|
|
151
|
+
synced.push(rel);
|
|
109
152
|
files += 1;
|
|
110
153
|
}
|
|
154
|
+
const inputs = synced.map((rel) => join(resources, rel)).join(`
|
|
155
|
+
`), outputs = synced.map((rel) => `$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/${rel}`).join(`
|
|
156
|
+
`);
|
|
157
|
+
await Bun.write(join(projectDir, `${appName} Extension`, "Resources.inputs.xcfilelist"), `${inputs}
|
|
158
|
+
`);
|
|
159
|
+
await Bun.write(join(projectDir, `${appName} Extension`, "Resources.outputs.xcfilelist"), `${outputs}
|
|
160
|
+
`);
|
|
111
161
|
return { resources, files };
|
|
112
162
|
}
|
|
113
163
|
export async function buildSafariApp(config, options = {}) {
|
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.140",
|
|
5
5
|
"description": "Build MV3 browser extensions (Chrome, Firefox, Safari) the Stacks way — manifest, content/background scripts, DNR rules, packaging, Safari container app, all config-driven.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
0C0000000000000000000011 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0000000000000000000011 /* ContentView.swift */; };
|
|
12
12
|
0C0000000000000000000012 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0B0000000000000000000012 /* Assets.xcassets */; };
|
|
13
13
|
0C0000000000000000000020 /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0000000000000000000020 /* SafariWebExtensionHandler.swift */; };
|
|
14
|
-
0C0000000000000000000023 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 0B0000000000000000000023 /* Resources */; };
|
|
15
14
|
0C0000000000000000000030 /* __APP_NAME__ Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0B0000000000000000000002 /* __APP_NAME__ Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
|
16
15
|
/* End PBXBuildFile section */
|
|
17
16
|
|
|
@@ -141,6 +140,7 @@
|
|
|
141
140
|
0D0000000000000000000020 /* Sources */,
|
|
142
141
|
0D0000000000000000000021 /* Frameworks */,
|
|
143
142
|
0D0000000000000000000022 /* Resources */,
|
|
143
|
+
0D0000000000000000000023 /* Copy Web Extension Resources */,
|
|
144
144
|
);
|
|
145
145
|
buildRules = (
|
|
146
146
|
);
|
|
@@ -201,12 +201,31 @@
|
|
|
201
201
|
isa = PBXResourcesBuildPhase;
|
|
202
202
|
buildActionMask = 2147483647;
|
|
203
203
|
files = (
|
|
204
|
-
0C0000000000000000000023 /* Resources in Resources */,
|
|
205
204
|
);
|
|
206
205
|
runOnlyForDeploymentPostprocessing = 0;
|
|
207
206
|
};
|
|
208
207
|
/* End PBXResourcesBuildPhase section */
|
|
209
208
|
|
|
209
|
+
/* Begin PBXShellScriptBuildPhase section */
|
|
210
|
+
0D0000000000000000000023 /* Copy Web Extension Resources */ = {
|
|
211
|
+
isa = PBXShellScriptBuildPhase;
|
|
212
|
+
alwaysOutOfDate = 1;
|
|
213
|
+
buildActionMask = 2147483647;
|
|
214
|
+
files = (
|
|
215
|
+
);
|
|
216
|
+
inputFileListPaths = (
|
|
217
|
+
"$(SRCROOT)/__APP_NAME__ Extension/Resources.inputs.xcfilelist",
|
|
218
|
+
);
|
|
219
|
+
name = "Copy Web Extension Resources";
|
|
220
|
+
outputFileListPaths = (
|
|
221
|
+
"$(SRCROOT)/__APP_NAME__ Extension/Resources.outputs.xcfilelist",
|
|
222
|
+
);
|
|
223
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
224
|
+
shellPath = /bin/sh;
|
|
225
|
+
shellScript = "set -e\nresources_root=\"${SRCROOT}/__APP_NAME__ Extension/Resources/\"\nwhile IFS= read -r source_file; do\n relative_path=\"${source_file#$resources_root}\"\n output_file=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/${relative_path}\"\n /bin/mkdir -p \"$(/usr/bin/dirname \"$output_file\")\"\n /bin/cp \"$source_file\" \"$output_file\"\ndone < \"$SCRIPT_INPUT_FILE_LIST_0\"\n";
|
|
226
|
+
};
|
|
227
|
+
/* End PBXShellScriptBuildPhase section */
|
|
228
|
+
|
|
210
229
|
/* Begin PBXSourcesBuildPhase section */
|
|
211
230
|
0D0000000000000000000010 /* Sources */ = {
|
|
212
231
|
isa = PBXSourcesBuildPhase;
|