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 +21 -0
- package/README.md +88 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +117 -0
- package/package.json +36 -0
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`
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|