@flun/node-mobile-app 4.0.8 → 4.0.9
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/Android /346/236/204/345/273/272/346/214/207/345/215/227.md" +354 -354
- package/CHANGELOG.md +20 -14
- package/LICENSE +33 -33
- package/README.md +661 -652
- package/bin/cli.js +20 -15
- package/mobileAppConfig.js +165 -165
- package/package.json +8 -8
- package/src/commands/build.js +119 -119
- package/src/commands/test.js +169 -169
- package/src/config.js +112 -112
- package/src/copy.js +320 -320
- package/src/fingerprint.js +129 -129
- package/src/generate.js +103 -103
- package/src/install.js +327 -327
- package/src/optimize-node-modules.js +711 -711
- package/src/patcher.js +393 -393
- package/src/platform-ios.js +140 -140
- package/src/utils.js +280 -280
- package/template/android/gradle.properties +48 -48
- package/template/eslint.config.js +5 -5
- package/template/ios/MyApp/Info.plist +73 -73
- package//350/207/252/345/256/232/344/271/211libnode/346/214/207/345/215/227.md +477 -477
package/src/platform-ios.js
CHANGED
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { run, ensureDir } from './utils.js';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
|
|
6
|
-
const SCHEME = 'MyApp',
|
|
7
|
-
/** iOS 相关命令只能在 macOS 上跑 */
|
|
8
|
-
ensureMac = () => {
|
|
9
|
-
if (process.platform !== 'darwin') throw new Error('iOS 构建只能在 macOS 上执行(当前平台: ' + process.platform + ')');
|
|
10
|
-
},
|
|
11
|
-
/** 检查 xcodebuild 是否存在 */
|
|
12
|
-
checkIosTools = () => {
|
|
13
|
-
const r = run('which', ['xcodebuild'], { stdio: 'pipe' });
|
|
14
|
-
if (r.status !== 0) throw new Error('未找到 xcodebuild,请安装 Xcode 并运行 xcode-select --install');
|
|
15
|
-
},
|
|
16
|
-
/**
|
|
17
|
-
* 执行 pod install(若 Pods/ 已存在且 Podfile.lock 未变,cocoapods 会自动跳过)
|
|
18
|
-
*/
|
|
19
|
-
podInstall = iosDir => {
|
|
20
|
-
console.log(chalk.cyan(' pod install ...')), run('pod', ['install'], { cwd: iosDir, inherit: true });
|
|
21
|
-
},
|
|
22
|
-
/**
|
|
23
|
-
* 把 config.ios.signing.certificate 导入临时 keychain,供 xcodebuild 签名用
|
|
24
|
-
* 返回 { keychainPath, keychainPassword } 或 null(未配置 / 文件缺失)
|
|
25
|
-
*/
|
|
26
|
-
setupSigningKeychain = (buildDir, userProjectDir, config) => {
|
|
27
|
-
const signing = config.ios && config.ios.signing;
|
|
28
|
-
if (!signing || !signing.certificate) return null;
|
|
29
|
-
|
|
30
|
-
const certPath = path.resolve(userProjectDir, signing.certificate);
|
|
31
|
-
if (!fs.existsSync(certPath)) {
|
|
32
|
-
console.warn(chalk.yellow(' ⚠️ iOS 签名证书不存在,跳过: ' + certPath));
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const keychainDir = path.join(buildDir, 'ios', 'build', 'signing');
|
|
37
|
-
ensureDir(keychainDir);
|
|
38
|
-
const keychainPath = path.join(keychainDir, 'build.keychain-db'), keychainPassword = 'nmapp' + Date.now();
|
|
39
|
-
|
|
40
|
-
// 清掉同名的旧 keychain(重复构建时)
|
|
41
|
-
if (fs.existsSync(keychainPath)) fs.rmSync(keychainPath, { force: true });
|
|
42
|
-
console.log(chalk.cyan(' 创建临时 keychain 并导入证书 ...'));
|
|
43
|
-
run('security', ['create-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
|
|
44
|
-
run('security', ['set-keychain-settings', '-lut', '3600', keychainPath], { quiet: true });
|
|
45
|
-
run('security', ['unlock-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
|
|
46
|
-
run('security', [
|
|
47
|
-
'import', certPath,
|
|
48
|
-
'-k', keychainPath,
|
|
49
|
-
'-P', signing.certificatePassword || '',
|
|
50
|
-
'-T', '/usr/bin/codesign',
|
|
51
|
-
'-T', '/usr/bin/security'
|
|
52
|
-
], { quiet: true });
|
|
53
|
-
run('security', [
|
|
54
|
-
'set-key-partition-list',
|
|
55
|
-
'-S', 'apple-tool:,apple:,codesign:',
|
|
56
|
-
'-s', '-k', keychainPassword, keychainPath
|
|
57
|
-
], { quiet: true });
|
|
58
|
-
|
|
59
|
-
return { keychainPath, keychainPassword };
|
|
60
|
-
},
|
|
61
|
-
/**
|
|
62
|
-
* xcodebuild archive
|
|
63
|
-
* 返回 xcarchive 路径
|
|
64
|
-
*/
|
|
65
|
-
xcodebuildArchive = (buildDir, config, release, userProjectDir) => {
|
|
66
|
-
const iosDir = path.join(buildDir, 'ios'), workspace = path.join(iosDir, SCHEME + '.xcworkspace');
|
|
67
|
-
if (!fs.existsSync(workspace)) throw new Error('未找到 ' + workspace + '(pod install 是否成功?)');
|
|
68
|
-
|
|
69
|
-
const archiveDir = path.join(iosDir, 'build');
|
|
70
|
-
ensureDir(archiveDir);
|
|
71
|
-
const archivePath = path.join(archiveDir, SCHEME + '.xcarchive'), configName = release ? 'Release' : 'Debug',
|
|
72
|
-
teamId = (config.ios && config.ios.signing && config.ios.signing.teamId) || '',
|
|
73
|
-
args = [
|
|
74
|
-
'-workspace', workspace,
|
|
75
|
-
'-scheme', SCHEME,
|
|
76
|
-
'-configuration', configName,
|
|
77
|
-
'-destination', 'generic/platform=iOS',
|
|
78
|
-
'-archivePath', archivePath,
|
|
79
|
-
'archive',
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
if (teamId) args.push('DEVELOPMENT_TEAM=' + teamId);
|
|
83
|
-
|
|
84
|
-
// 若配置了 certificate,导入临时 keychain 并让 xcodebuild 使用
|
|
85
|
-
const keychain = setupSigningKeychain(buildDir, userProjectDir, config);
|
|
86
|
-
if (keychain) args.push('OTHER_CODE_SIGN_FLAGS=--keychain ' + keychain.keychainPath);
|
|
87
|
-
|
|
88
|
-
console.log(chalk.cyan(' xcodebuild archive ...')), run('xcodebuild', args, { cwd: iosDir, quiet: true });
|
|
89
|
-
return archivePath;
|
|
90
|
-
},
|
|
91
|
-
/**
|
|
92
|
-
* xcodebuild -exportArchive,导出 .ipa
|
|
93
|
-
* 返回 .ipa 路径
|
|
94
|
-
*/
|
|
95
|
-
xcodebuildExport = (buildDir, config, release, outDir, userProjectDir) => {
|
|
96
|
-
const iosDir = path.join(buildDir, 'ios'), exportDir = path.join(iosDir, 'build', 'export');
|
|
97
|
-
ensureDir(exportDir);
|
|
98
|
-
const archivePath = path.join(iosDir, 'build', SCHEME + '.xcarchive'), method = release ? 'ad-hoc' : 'development',
|
|
99
|
-
signing = (config.ios && config.ios.signing) || {}, teamId = signing.teamId || '',
|
|
100
|
-
// provisioningProfile 路径 → 取 basename(去扩展名),写进 exportOptions.plist
|
|
101
|
-
profilePath = signing.provisioningProfile ? path.resolve(userProjectDir, signing.provisioningProfile) : '',
|
|
102
|
-
profileName = profilePath ? path.basename(profilePath).replace(/\.mobileprovision$/i, '') : '',
|
|
103
|
-
plistPath = path.join(iosDir, 'build', 'exportOptions.plist'),
|
|
104
|
-
plist = [
|
|
105
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
106
|
-
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
107
|
-
'<plist version="1.0">',
|
|
108
|
-
'<dict>',
|
|
109
|
-
' <key>method</key>',
|
|
110
|
-
' <string>' + method + '</string>',
|
|
111
|
-
' <key>stripSwiftSymbols</key>',
|
|
112
|
-
' <true/>',
|
|
113
|
-
' <key>compileBitcode</key>',
|
|
114
|
-
' <false/>',
|
|
115
|
-
teamId ? ' <key>teamID</key>' : '',
|
|
116
|
-
teamId ? ' <string>' + teamId + '</string>' : '',
|
|
117
|
-
profileName ? ' <key>provisioningProfiles</key>' : '',
|
|
118
|
-
profileName ? ' <dict>' : '',
|
|
119
|
-
profileName ? ' <key>' + config.appId + '</key>' : '',
|
|
120
|
-
profileName ? ' <string>' + profileName + '</string>' : '',
|
|
121
|
-
profileName ? ' </dict>' : '',
|
|
122
|
-
'</dict>',
|
|
123
|
-
'</plist>',
|
|
124
|
-
].filter(Boolean).join('\n');
|
|
125
|
-
fs.writeFileSync(plistPath, plist, 'utf-8'); // 生成 exportOptions.plist
|
|
126
|
-
|
|
127
|
-
console.log(chalk.cyan(' xcodebuild -exportArchive ...'));
|
|
128
|
-
run('xcodebuild', [
|
|
129
|
-
'-exportArchive',
|
|
130
|
-
'-archivePath', archivePath,
|
|
131
|
-
'-exportOptionsPlist', plistPath,
|
|
132
|
-
'-exportPath', exportDir,
|
|
133
|
-
], { cwd: iosDir, quiet: true });
|
|
134
|
-
|
|
135
|
-
// 找 .ipa
|
|
136
|
-
const files = fs.readdirSync(exportDir).filter(f => f.endsWith('.ipa'));
|
|
137
|
-
if (files.length === 0) throw new Error('导出目录里没有 .ipa: ' + exportDir);
|
|
138
|
-
return path.join(exportDir, files[0]);
|
|
139
|
-
};
|
|
140
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { run, ensureDir } from './utils.js';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const SCHEME = 'MyApp',
|
|
7
|
+
/** iOS 相关命令只能在 macOS 上跑 */
|
|
8
|
+
ensureMac = () => {
|
|
9
|
+
if (process.platform !== 'darwin') throw new Error('iOS 构建只能在 macOS 上执行(当前平台: ' + process.platform + ')');
|
|
10
|
+
},
|
|
11
|
+
/** 检查 xcodebuild 是否存在 */
|
|
12
|
+
checkIosTools = () => {
|
|
13
|
+
const r = run('which', ['xcodebuild'], { stdio: 'pipe' });
|
|
14
|
+
if (r.status !== 0) throw new Error('未找到 xcodebuild,请安装 Xcode 并运行 xcode-select --install');
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* 执行 pod install(若 Pods/ 已存在且 Podfile.lock 未变,cocoapods 会自动跳过)
|
|
18
|
+
*/
|
|
19
|
+
podInstall = iosDir => {
|
|
20
|
+
console.log(chalk.cyan(' pod install ...')), run('pod', ['install'], { cwd: iosDir, inherit: true });
|
|
21
|
+
},
|
|
22
|
+
/**
|
|
23
|
+
* 把 config.ios.signing.certificate 导入临时 keychain,供 xcodebuild 签名用
|
|
24
|
+
* 返回 { keychainPath, keychainPassword } 或 null(未配置 / 文件缺失)
|
|
25
|
+
*/
|
|
26
|
+
setupSigningKeychain = (buildDir, userProjectDir, config) => {
|
|
27
|
+
const signing = config.ios && config.ios.signing;
|
|
28
|
+
if (!signing || !signing.certificate) return null;
|
|
29
|
+
|
|
30
|
+
const certPath = path.resolve(userProjectDir, signing.certificate);
|
|
31
|
+
if (!fs.existsSync(certPath)) {
|
|
32
|
+
console.warn(chalk.yellow(' ⚠️ iOS 签名证书不存在,跳过: ' + certPath));
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const keychainDir = path.join(buildDir, 'ios', 'build', 'signing');
|
|
37
|
+
ensureDir(keychainDir);
|
|
38
|
+
const keychainPath = path.join(keychainDir, 'build.keychain-db'), keychainPassword = 'nmapp' + Date.now();
|
|
39
|
+
|
|
40
|
+
// 清掉同名的旧 keychain(重复构建时)
|
|
41
|
+
if (fs.existsSync(keychainPath)) fs.rmSync(keychainPath, { force: true });
|
|
42
|
+
console.log(chalk.cyan(' 创建临时 keychain 并导入证书 ...'));
|
|
43
|
+
run('security', ['create-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
|
|
44
|
+
run('security', ['set-keychain-settings', '-lut', '3600', keychainPath], { quiet: true });
|
|
45
|
+
run('security', ['unlock-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
|
|
46
|
+
run('security', [
|
|
47
|
+
'import', certPath,
|
|
48
|
+
'-k', keychainPath,
|
|
49
|
+
'-P', signing.certificatePassword || '',
|
|
50
|
+
'-T', '/usr/bin/codesign',
|
|
51
|
+
'-T', '/usr/bin/security'
|
|
52
|
+
], { quiet: true });
|
|
53
|
+
run('security', [
|
|
54
|
+
'set-key-partition-list',
|
|
55
|
+
'-S', 'apple-tool:,apple:,codesign:',
|
|
56
|
+
'-s', '-k', keychainPassword, keychainPath
|
|
57
|
+
], { quiet: true });
|
|
58
|
+
|
|
59
|
+
return { keychainPath, keychainPassword };
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* xcodebuild archive
|
|
63
|
+
* 返回 xcarchive 路径
|
|
64
|
+
*/
|
|
65
|
+
xcodebuildArchive = (buildDir, config, release, userProjectDir) => {
|
|
66
|
+
const iosDir = path.join(buildDir, 'ios'), workspace = path.join(iosDir, SCHEME + '.xcworkspace');
|
|
67
|
+
if (!fs.existsSync(workspace)) throw new Error('未找到 ' + workspace + '(pod install 是否成功?)');
|
|
68
|
+
|
|
69
|
+
const archiveDir = path.join(iosDir, 'build');
|
|
70
|
+
ensureDir(archiveDir);
|
|
71
|
+
const archivePath = path.join(archiveDir, SCHEME + '.xcarchive'), configName = release ? 'Release' : 'Debug',
|
|
72
|
+
teamId = (config.ios && config.ios.signing && config.ios.signing.teamId) || '',
|
|
73
|
+
args = [
|
|
74
|
+
'-workspace', workspace,
|
|
75
|
+
'-scheme', SCHEME,
|
|
76
|
+
'-configuration', configName,
|
|
77
|
+
'-destination', 'generic/platform=iOS',
|
|
78
|
+
'-archivePath', archivePath,
|
|
79
|
+
'archive',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
if (teamId) args.push('DEVELOPMENT_TEAM=' + teamId);
|
|
83
|
+
|
|
84
|
+
// 若配置了 certificate,导入临时 keychain 并让 xcodebuild 使用
|
|
85
|
+
const keychain = setupSigningKeychain(buildDir, userProjectDir, config);
|
|
86
|
+
if (keychain) args.push('OTHER_CODE_SIGN_FLAGS=--keychain ' + keychain.keychainPath);
|
|
87
|
+
|
|
88
|
+
console.log(chalk.cyan(' xcodebuild archive ...')), run('xcodebuild', args, { cwd: iosDir, quiet: true });
|
|
89
|
+
return archivePath;
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* xcodebuild -exportArchive,导出 .ipa
|
|
93
|
+
* 返回 .ipa 路径
|
|
94
|
+
*/
|
|
95
|
+
xcodebuildExport = (buildDir, config, release, outDir, userProjectDir) => {
|
|
96
|
+
const iosDir = path.join(buildDir, 'ios'), exportDir = path.join(iosDir, 'build', 'export');
|
|
97
|
+
ensureDir(exportDir);
|
|
98
|
+
const archivePath = path.join(iosDir, 'build', SCHEME + '.xcarchive'), method = release ? 'ad-hoc' : 'development',
|
|
99
|
+
signing = (config.ios && config.ios.signing) || {}, teamId = signing.teamId || '',
|
|
100
|
+
// provisioningProfile 路径 → 取 basename(去扩展名),写进 exportOptions.plist
|
|
101
|
+
profilePath = signing.provisioningProfile ? path.resolve(userProjectDir, signing.provisioningProfile) : '',
|
|
102
|
+
profileName = profilePath ? path.basename(profilePath).replace(/\.mobileprovision$/i, '') : '',
|
|
103
|
+
plistPath = path.join(iosDir, 'build', 'exportOptions.plist'),
|
|
104
|
+
plist = [
|
|
105
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
106
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
107
|
+
'<plist version="1.0">',
|
|
108
|
+
'<dict>',
|
|
109
|
+
' <key>method</key>',
|
|
110
|
+
' <string>' + method + '</string>',
|
|
111
|
+
' <key>stripSwiftSymbols</key>',
|
|
112
|
+
' <true/>',
|
|
113
|
+
' <key>compileBitcode</key>',
|
|
114
|
+
' <false/>',
|
|
115
|
+
teamId ? ' <key>teamID</key>' : '',
|
|
116
|
+
teamId ? ' <string>' + teamId + '</string>' : '',
|
|
117
|
+
profileName ? ' <key>provisioningProfiles</key>' : '',
|
|
118
|
+
profileName ? ' <dict>' : '',
|
|
119
|
+
profileName ? ' <key>' + config.appId + '</key>' : '',
|
|
120
|
+
profileName ? ' <string>' + profileName + '</string>' : '',
|
|
121
|
+
profileName ? ' </dict>' : '',
|
|
122
|
+
'</dict>',
|
|
123
|
+
'</plist>',
|
|
124
|
+
].filter(Boolean).join('\n');
|
|
125
|
+
fs.writeFileSync(plistPath, plist, 'utf-8'); // 生成 exportOptions.plist
|
|
126
|
+
|
|
127
|
+
console.log(chalk.cyan(' xcodebuild -exportArchive ...'));
|
|
128
|
+
run('xcodebuild', [
|
|
129
|
+
'-exportArchive',
|
|
130
|
+
'-archivePath', archivePath,
|
|
131
|
+
'-exportOptionsPlist', plistPath,
|
|
132
|
+
'-exportPath', exportDir,
|
|
133
|
+
], { cwd: iosDir, quiet: true });
|
|
134
|
+
|
|
135
|
+
// 找 .ipa
|
|
136
|
+
const files = fs.readdirSync(exportDir).filter(f => f.endsWith('.ipa'));
|
|
137
|
+
if (files.length === 0) throw new Error('导出目录里没有 .ipa: ' + exportDir);
|
|
138
|
+
return path.join(exportDir, files[0]);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
141
|
export { ensureMac, checkIosTools, podInstall, xcodebuildArchive, xcodebuildExport }
|