wxt-module-safari-xcode 0.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rxliuli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Safari Xcode Module
2
+
3
+ A WXT module that automatically converts Safari extensions to Xcode projects and configures related settings after the build is complete.
4
+
5
+ ## Features
6
+
7
+ - Automatically runs `xcrun safari-web-extension-converter` to convert the extension to an Xcode project
8
+ - Updates Xcode project configuration (version number, app category, development team, etc.)
9
+ - Updates all Info.plist files
10
+
11
+ ## Usage
12
+
13
+ ### 1. Enable the module in wxt.config.ts
14
+
15
+ ```typescript
16
+ import { defineConfig } from 'wxt'
17
+
18
+ export default defineConfig({
19
+ modules: ['wxt-module-safari-xcode'],
20
+ safariXcode: {
21
+ projectName: 'Your Project Name',
22
+ appCategory: 'public.app-category.productivity',
23
+ bundleIdentifier: 'com.example.your-extension',
24
+ developmentTeam: 'ABC1234567', // 可选,Apple Developer Team ID
25
+ },
26
+ // ... other configurations
27
+ })
28
+ ```
29
+
30
+ ### 2. Build Safari Extension
31
+
32
+ ```bash
33
+ pnpm wxt build -b safari
34
+ ```
35
+
36
+ The module will automatically convert the extension to an Xcode project after the build completes.
37
+
38
+ ## Configuration Options
39
+
40
+ | Option | Type | Required | Description |
41
+ | ------------------ | -------- | -------- | --------------------------------------------------------------------------------------------- |
42
+ | `projectName` | `string` | ❌ | Safari project name. Defaults to `manifest.name` if not provided |
43
+ | `appCategory` | `string` | ✅ | App category, e.g., `'public.app-category.productivity'` |
44
+ | `bundleIdentifier` | `string` | ✅ | Bundle identifier, e.g., `'com.example.app'` |
45
+ | `developmentTeam` | `string` | ❌ | Apple Developer Team ID, e.g., `'ABC1234567'`. If not provided, must be set manually in Xcode |
46
+
47
+ ## How It Works
48
+
49
+ This module uses WXT's `build:done` hook to perform the following steps after the build completes:
50
+
51
+ 1. Run `xcrun safari-web-extension-converter` to convert the extension to a Safari Xcode project
52
+ 2. Read the version number from `package.json`
53
+ 3. Update the Xcode project configuration file (`.xcodeproj/project.pbxproj`)
54
+ - Set `MARKETING_VERSION` to the version from package.json
55
+ - Set `CURRENT_PROJECT_VERSION` to numeric version (major _ 10000 + minor _ 100 + patch)
56
+ - Configure app category
57
+ - Configure development team (if provided)
58
+ 4. Update all Info.plist files and add `CFBundleVersion`
59
+
60
+ ## Notes
61
+
62
+ - This module only executes when building for Safari browser (`wxt build -b safari`)
63
+ - Requires macOS and Xcode Command Line Tools
64
+ - If you want to read configuration from environment variables, ensure `.env.local` is added to `.gitignore`
65
+
66
+ ## Examples
67
+
68
+ ### Complete Configuration Example
69
+
70
+ ```typescript
71
+ import { defineConfig } from 'wxt'
72
+
73
+ export default defineConfig({
74
+ modules: ['@wxt-dev/module-react', 'wxt-module-safari-xcode'],
75
+ safariXcode: {
76
+ projectName: 'My Awesome Extension',
77
+ appCategory: 'public.app-category.productivity',
78
+ bundleIdentifier: 'com.mycompany.awesome-extension',
79
+ developmentTeam: 'ABC1234567',
80
+ },
81
+ manifest: {
82
+ name: 'My Awesome Extension',
83
+ version: '0.1.0',
84
+ },
85
+ })
86
+ ```
87
+
88
+ 生成的 Xcode 项目将位于:`.output/My Awesome Extension/My Awesome Extension.xcodeproj`
@@ -0,0 +1,32 @@
1
+ import * as wxt from 'wxt';
2
+
3
+ interface SafariXcodeOptions {
4
+ /**
5
+ * Safari project name
6
+ * Defaults to manifest.name if not provided
7
+ */
8
+ projectName?: string;
9
+ /**
10
+ * App category, e.g., 'public.app-category.productivity'
11
+ */
12
+ appCategory: string;
13
+ /**
14
+ * Bundle identifier
15
+ */
16
+ bundleIdentifier: string;
17
+ /**
18
+ * Apple Developer Team ID (optional)
19
+ * Example: 'ABC1234567'
20
+ * If not provided, the generated Xcode project will need to have the development team set manually
21
+ */
22
+ developmentTeam?: string;
23
+ }
24
+ declare const _default: wxt.WxtModule<SafariXcodeOptions>;
25
+
26
+ declare module 'wxt' {
27
+ interface InlineConfig {
28
+ safariXcode?: SafariXcodeOptions;
29
+ }
30
+ }
31
+
32
+ export { type SafariXcodeOptions, _default as default };
package/dist/index.js ADDED
@@ -0,0 +1,117 @@
1
+ // src/index.ts
2
+ import "wxt";
3
+ import { defineWxtModule } from "wxt/modules";
4
+ import { $ } from "zx";
5
+
6
+ // src/safari-utils.ts
7
+ import { globby } from "zx";
8
+ import path from "path";
9
+ import fs from "fs/promises";
10
+ async function updateProjectConfig(options) {
11
+ const projectConfigPath = path.resolve(
12
+ options.rootPath,
13
+ `.output/${options.projectName}/${options.projectName}.xcodeproj/project.pbxproj`
14
+ );
15
+ const packageJsonModule = await import(path.resolve(options.rootPath, "package.json"), {
16
+ with: { type: "json" }
17
+ });
18
+ const packageJson = packageJsonModule.default;
19
+ const content = await fs.readFile(projectConfigPath, "utf-8");
20
+ const newContent = content.replaceAll(
21
+ "MARKETING_VERSION = 1.0;",
22
+ `MARKETING_VERSION = ${packageJson.version};`
23
+ ).replace(
24
+ new RegExp(
25
+ `INFOPLIST_KEY_CFBundleDisplayName = ("?${options.projectName}"?);`,
26
+ "g"
27
+ ),
28
+ `INFOPLIST_KEY_CFBundleDisplayName = $1;
29
+ INFOPLIST_KEY_LSApplicationCategoryType = "${options.appCategory}";`
30
+ ).replace(
31
+ new RegExp(`GCC_WARN_UNUSED_VARIABLE = YES;`, "g"),
32
+ `GCC_WARN_UNUSED_VARIABLE = YES;
33
+ INFOPLIST_KEY_LSApplicationCategoryType = "${options.appCategory}";`
34
+ ).replace(
35
+ new RegExp(
36
+ `INFOPLIST_KEY_CFBundleDisplayName = ("?${options.projectName}"?);`,
37
+ "g"
38
+ ),
39
+ `INFOPLIST_KEY_CFBundleDisplayName = $1;
40
+ INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;`
41
+ ).replaceAll(
42
+ `COPY_PHASE_STRIP = NO;`,
43
+ options.developmentTeam ? `COPY_PHASE_STRIP = NO;
44
+ DEVELOPMENT_TEAM = ${options.developmentTeam};` : "COPY_PHASE_STRIP = NO;"
45
+ ).replace(
46
+ /CURRENT_PROJECT_VERSION = \d+;/g,
47
+ `CURRENT_PROJECT_VERSION = ${parseProjectVersion(packageJson.version)};`
48
+ );
49
+ await fs.writeFile(projectConfigPath, newContent);
50
+ }
51
+ async function updateInfoPlist(options) {
52
+ const projectPath = path.resolve(options.rootPath, ".output", options.projectName);
53
+ const files = await globby("**/*.plist", {
54
+ cwd: projectPath
55
+ });
56
+ for (const file of files) {
57
+ const content = await fs.readFile(path.resolve(projectPath, file), "utf-8");
58
+ await fs.writeFile(
59
+ path.resolve(projectPath, file),
60
+ content.replaceAll(
61
+ "</dict>\n</plist>",
62
+ " <key>CFBundleVersion</key>\n <string>$(CURRENT_PROJECT_VERSION)</string>\n</dict>\n</plist>"
63
+ )
64
+ );
65
+ }
66
+ }
67
+ function parseProjectVersion(version) {
68
+ const [major, minor, patch] = version.split(".").map(Number);
69
+ return major * 1e4 + minor * 100 + patch;
70
+ }
71
+
72
+ // src/index.ts
73
+ var index_default = defineWxtModule({
74
+ name: "safari-xcode",
75
+ configKey: "safariXcode",
76
+ async setup(wxt, options) {
77
+ if (wxt.config.browser !== "safari") {
78
+ return;
79
+ }
80
+ const { appCategory, bundleIdentifier, developmentTeam } = options ?? {};
81
+ const projectName = options?.projectName ?? wxt.config.manifest.name;
82
+ if (!projectName || !appCategory || !bundleIdentifier) {
83
+ wxt.logger.warn(
84
+ "Safari Xcode module is not configured properly. Please provide projectName, appCategory and bundleIdentifier."
85
+ );
86
+ return;
87
+ }
88
+ wxt.hook("build:done", async (wxt2) => {
89
+ wxt2.logger.info("Converting Safari extension to Xcode project...");
90
+ try {
91
+ wxt2.logger.info("Running safari-web-extension-converter...");
92
+ await $`xcrun safari-web-extension-converter --bundle-identifier ${bundleIdentifier} --force --project-location .output .output/safari-mv3`;
93
+ wxt2.logger.info("Updating Xcode project config...");
94
+ await updateProjectConfig({
95
+ projectName,
96
+ appCategory,
97
+ developmentTeam,
98
+ rootPath: wxt2.config.root
99
+ });
100
+ wxt2.logger.info("Updating Info.plist files...");
101
+ await updateInfoPlist({
102
+ projectName,
103
+ appCategory,
104
+ developmentTeam,
105
+ rootPath: wxt2.config.root
106
+ });
107
+ wxt2.logger.success("Safari Xcode project created successfully!");
108
+ } catch (error) {
109
+ wxt2.logger.error("Safari Xcode conversion failed:", error);
110
+ throw error;
111
+ }
112
+ });
113
+ }
114
+ });
115
+ export {
116
+ index_default as default
117
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "wxt-module-safari-xcode",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "sideEffects": false,
16
+ "devDependencies": {
17
+ "tsup": "^8.5.1",
18
+ "typescript": "^5.9.3",
19
+ "wxt": "^0.20.13"
20
+ },
21
+ "optionalDependencies": {
22
+ "wxt": "^0.20.13"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "registry": "https://registry.npmjs.org/"
27
+ },
28
+ "dependencies": {
29
+ "zx": "^8.8.5"
30
+ },
31
+ "scripts": {
32
+ "setup": "pnpm build",
33
+ "build": "tsup src/index.ts --format esm --dts --out-dir dist --clean --external wxt",
34
+ "dev": "pnpm build --watch"
35
+ }
36
+ }