@flun/node-mobile-app 4.1.2 → 4.2.0
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 +1 -1
- package/CHANGELOG.md +15 -22
- package/README.md +67 -13
- package/bin/cli.js +3 -3
- package/mobileAppConfig.js +17 -4
- package/package.json +1 -1
- package/scripts/copy-files.js +3 -2
- package/src/commands/build.js +105 -99
- package/src/commands/clean.js +12 -6
- package/src/commands/test.js +141 -133
- package/src/config.js +88 -52
- package/src/copy.js +302 -215
- package/src/fingerprint.js +96 -77
- package/src/generate.js +33 -21
- package/src/install.js +176 -162
- package/src/optimize-node-modules.js +5 -4
- package/src/patcher.js +438 -358
- package/src/platform-ios.js +122 -106
- package/src/utils.js +351 -252
- package/template/.prettierrc.js +8 -2
- package/template/App.tsx +98 -87
- package/template/babel.config.js +8 -2
- package/template/eslint.config.js +8 -2
- package/template/metro.config.js +9 -3
- package//350/207/252/345/256/232/344/271/211libnode/346/214/207/345/215/227.md +2 -2
package/src/platform-ios.js
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { run, ensureDir } from './utils.js';
|
|
4
1
|
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { ensureDir, run } from './utils.js';
|
|
5
5
|
|
|
6
|
+
/** 应用 Scheme 名称,与 iOS 工程一致 */
|
|
6
7
|
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
8
|
/**
|
|
23
9
|
* 把 config.ios.signing.certificate 导入临时 keychain,供 xcodebuild 签名用
|
|
24
10
|
* 返回 { keychainPath, keychainPassword } 或 null(未配置 / 文件缺失)
|
|
@@ -44,98 +30,128 @@ const SCHEME = 'MyApp',
|
|
|
44
30
|
run('security', ['set-keychain-settings', '-lut', '3600', keychainPath], { quiet: true });
|
|
45
31
|
run('security', ['unlock-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
|
|
46
32
|
run('security', [
|
|
47
|
-
'import', certPath,
|
|
48
|
-
'-
|
|
49
|
-
'-P', signing.certificatePassword || '',
|
|
50
|
-
'-T', '/usr/bin/codesign',
|
|
51
|
-
'-T', '/usr/bin/security'
|
|
33
|
+
'import', certPath, '-k', keychainPath, '-P', signing.certificatePassword || '',
|
|
34
|
+
'-T', '/usr/bin/codesign', '-T', '/usr/bin/security'
|
|
52
35
|
], { quiet: true });
|
|
53
36
|
run('security', [
|
|
54
|
-
'set-key-partition-list',
|
|
55
|
-
'-S', 'apple-tool:,apple:,codesign:',
|
|
56
|
-
'-s', '-k', keychainPassword, keychainPath
|
|
37
|
+
'set-key-partition-list', '-S', 'apple-tool:,apple:,codesign:', '-s', '-k', keychainPassword, keychainPath
|
|
57
38
|
], { quiet: true });
|
|
58
39
|
|
|
59
40
|
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
41
|
};
|
|
140
42
|
|
|
141
|
-
|
|
43
|
+
/**
|
|
44
|
+
* 检查 xcodebuild 是否存在,缺失时抛错提示安装 Xcode
|
|
45
|
+
* >查看定义:@see {@link checkIosTools}
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
const checkIosTools = () => {
|
|
49
|
+
const r = run('which', ['xcodebuild'], { stdio: 'pipe' });
|
|
50
|
+
if (r.status !== 0) throw new Error('未找到 xcodebuild,请安装 Xcode 并运行 xcode-select --install');
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* iOS 相关命令只能在 macOS 上跑,非 darwin 抛错
|
|
55
|
+
* >查看定义:@see {@link ensureMac}
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
const ensureMac = () => {
|
|
59
|
+
if (process.platform !== 'darwin') throw new Error('iOS 构建只能在 macOS 上执行(当前平台: ' + process.platform + ')');
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 执行 pod install(若 Pods/ 已存在且 Podfile.lock 未变,cocoapods 会自动跳过)
|
|
64
|
+
* >查看定义:@see {@link podInstall}
|
|
65
|
+
* @param {string} iosDir iOS 工程目录
|
|
66
|
+
* @returns {void}
|
|
67
|
+
*/
|
|
68
|
+
const podInstall = iosDir => {
|
|
69
|
+
console.log(chalk.cyan(' pod install ...')), run('pod', ['install'], { cwd: iosDir, inherit: true });
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* xcodebuild archive,返回 xcarchive 路径
|
|
74
|
+
* >查看定义:@see {@link xcodebuildArchive}
|
|
75
|
+
* @param {string} buildDir 构建目录
|
|
76
|
+
* @param {object} config 用户配置
|
|
77
|
+
* @param {boolean} release 是否 Release 配置
|
|
78
|
+
* @param {string} userProjectDir 用户项目目录
|
|
79
|
+
* @returns {string} xcarchive 路径
|
|
80
|
+
*/
|
|
81
|
+
const xcodebuildArchive = (buildDir, config, release, userProjectDir) => {
|
|
82
|
+
const iosDir = path.join(buildDir, 'ios'), workspace = path.join(iosDir, SCHEME + '.xcworkspace');
|
|
83
|
+
if (!fs.existsSync(workspace)) throw new Error('未找到 ' + workspace + '(pod install 是否成功?)');
|
|
84
|
+
|
|
85
|
+
const archiveDir = path.join(iosDir, 'build');
|
|
86
|
+
ensureDir(archiveDir);
|
|
87
|
+
const archivePath = path.join(archiveDir, SCHEME + '.xcarchive'), configName = release ? 'Release' : 'Debug',
|
|
88
|
+
teamId = (config.ios && config.ios.signing && config.ios.signing.teamId) || '',
|
|
89
|
+
args = [
|
|
90
|
+
'-workspace', workspace, '-scheme', SCHEME, '-configuration', configName,
|
|
91
|
+
'-destination', 'generic/platform=iOS', '-archivePath', archivePath, 'archive'
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
if (teamId) args.push('DEVELOPMENT_TEAM=' + teamId);
|
|
95
|
+
|
|
96
|
+
// 若配置了 certificate,导入临时 keychain 并让 xcodebuild 使用
|
|
97
|
+
const keychain = setupSigningKeychain(buildDir, userProjectDir, config);
|
|
98
|
+
if (keychain) args.push('OTHER_CODE_SIGN_FLAGS=--keychain ' + keychain.keychainPath);
|
|
99
|
+
|
|
100
|
+
console.log(chalk.cyan(' xcodebuild archive ...')), run('xcodebuild', args, { cwd: iosDir, quiet: true });
|
|
101
|
+
return archivePath;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* xcodebuild -exportArchive,导出 .ipa,返回 .ipa 路径
|
|
106
|
+
* >查看定义:@see {@link xcodebuildExport}
|
|
107
|
+
* @param {string} buildDir 构建目录
|
|
108
|
+
* @param {object} config 用户配置
|
|
109
|
+
* @param {boolean} release 是否 Release 导出
|
|
110
|
+
* @param {string} outDir 输出目录
|
|
111
|
+
* @param {string} userProjectDir 用户项目目录
|
|
112
|
+
* @returns {string} .ipa 文件路径
|
|
113
|
+
*/
|
|
114
|
+
const xcodebuildExport = (buildDir, config, release, outDir, userProjectDir) => {
|
|
115
|
+
const iosDir = path.join(buildDir, 'ios'), exportDir = path.join(iosDir, 'build', 'export');
|
|
116
|
+
ensureDir(exportDir);
|
|
117
|
+
const archivePath = path.join(iosDir, 'build', SCHEME + '.xcarchive'), method = release ? 'ad-hoc' : 'development',
|
|
118
|
+
signing = (config.ios && config.ios.signing) || {}, teamId = signing.teamId || '',
|
|
119
|
+
// provisioningProfile 路径 → 取 basename(去扩展名),写进 exportOptions.plist
|
|
120
|
+
profilePath = signing.provisioningProfile ? path.resolve(userProjectDir, signing.provisioningProfile) : '',
|
|
121
|
+
profileName = profilePath ? path.basename(profilePath).replace(/\.mobileprovision$/i, '') : '',
|
|
122
|
+
plistPath = path.join(iosDir, 'build', 'exportOptions.plist'),
|
|
123
|
+
plist = [
|
|
124
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
125
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
126
|
+
'<plist version="1.0">',
|
|
127
|
+
'<dict>',
|
|
128
|
+
' <key>method</key>',
|
|
129
|
+
' <string>' + method + '</string>',
|
|
130
|
+
' <key>stripSwiftSymbols</key>',
|
|
131
|
+
' <true/>',
|
|
132
|
+
' <key>compileBitcode</key>',
|
|
133
|
+
' <false/>',
|
|
134
|
+
teamId ? ' <key>teamID</key>' : '',
|
|
135
|
+
teamId ? ' <string>' + teamId + '</string>' : '',
|
|
136
|
+
profileName ? ' <key>provisioningProfiles</key>' : '',
|
|
137
|
+
profileName ? ' <dict>' : '',
|
|
138
|
+
profileName ? ' <key>' + config.appId + '</key>' : '',
|
|
139
|
+
profileName ? ' <string>' + profileName + '</string>' : '',
|
|
140
|
+
profileName ? ' </dict>' : '',
|
|
141
|
+
'</dict>',
|
|
142
|
+
'</plist>',
|
|
143
|
+
].filter(Boolean).join('\n');
|
|
144
|
+
fs.writeFileSync(plistPath, plist, 'utf-8'); // 生成 exportOptions.plist
|
|
145
|
+
|
|
146
|
+
console.log(chalk.cyan(' xcodebuild -exportArchive ...'));
|
|
147
|
+
run('xcodebuild', [
|
|
148
|
+
'-exportArchive', '-archivePath', archivePath, '-exportOptionsPlist', plistPath, '-exportPath', exportDir
|
|
149
|
+
], { cwd: iosDir, quiet: true });
|
|
150
|
+
|
|
151
|
+
// 找 .ipa
|
|
152
|
+
const files = fs.readdirSync(exportDir).filter(f => f.endsWith('.ipa'));
|
|
153
|
+
if (files.length === 0) throw new Error('导出目录里没有 .ipa: ' + exportDir);
|
|
154
|
+
return path.join(exportDir, files[0]);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export { checkIosTools, ensureMac, podInstall, xcodebuildArchive, xcodebuildExport };
|