react-native-update-cli 2.5.0 → 2.6.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/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/cli.json +26 -0
- package/lib/api.js +1 -1
- package/lib/locales/en.js +13 -1
- package/lib/locales/zh.js +13 -1
- package/lib/package.js +53 -6
- package/lib/provider.js +3 -0
- package/lib/utils/app-info-parser/aab.js +165 -201
- package/lib/utils/app-info-parser/apk.js +25 -27
- package/lib/utils/app-info-parser/app.js +10 -11
- package/lib/utils/app-info-parser/index.js +8 -8
- package/lib/utils/app-info-parser/ipa.js +16 -21
- package/lib/utils/app-info-parser/resource-finder.js +365 -305
- package/lib/utils/app-info-parser/utils.js +78 -63
- package/lib/utils/app-info-parser/xml-parser/binary.js +57 -51
- package/lib/utils/app-info-parser/xml-parser/manifest.js +47 -39
- package/lib/utils/app-info-parser/zip.js +21 -11
- package/lib/utils/http-helper.js +1 -1
- package/package.json +1 -1
- package/src/api.ts +2 -6
- package/src/locales/en.ts +17 -0
- package/src/locales/zh.ts +15 -0
- package/src/modules/version-module.ts +1 -1
- package/src/package.ts +102 -11
- package/src/provider.ts +3 -0
- package/src/utils/app-info-parser/aab.ts +240 -0
- package/src/utils/app-info-parser/{apk.js → apk.ts} +30 -41
- package/src/utils/app-info-parser/app.ts +3 -0
- package/src/utils/app-info-parser/index.ts +4 -4
- package/src/utils/app-info-parser/{ipa.js → ipa.ts} +17 -31
- package/src/utils/app-info-parser/resource-finder.ts +508 -0
- package/src/utils/app-info-parser/utils.ts +162 -0
- package/src/utils/app-info-parser/xml-parser/{binary.js → binary.ts} +69 -61
- package/src/utils/app-info-parser/xml-parser/{manifest.js → manifest.ts} +50 -51
- package/src/utils/app-info-parser/zip.ts +86 -0
- package/src/utils/dep-versions.ts +7 -4
- package/src/utils/http-helper.ts +1 -1
- package/src/utils/latest-version/index.ts +2 -1
- package/src/versions.ts +1 -1
- package/src/utils/app-info-parser/aab.js +0 -326
- package/src/utils/app-info-parser/app.js +0 -16
- package/src/utils/app-info-parser/resource-finder.js +0 -495
- package/src/utils/app-info-parser/utils.js +0 -172
- package/src/utils/app-info-parser/zip.js +0 -66
|
@@ -1,193 +1,157 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "AabParser", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return AabParser;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _child_process = require("child_process");
|
|
12
|
+
const _os = /*#__PURE__*/ _interop_require_default(require("os"));
|
|
13
|
+
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
14
|
+
const _fsextra = /*#__PURE__*/ _interop_require_default(require("fs-extra"));
|
|
15
|
+
const _yauzl = require("yauzl");
|
|
16
|
+
const _i18n = require("../i18n");
|
|
17
|
+
const _resourcefinder = require("./resource-finder");
|
|
18
|
+
const _utils = require("./utils");
|
|
19
|
+
const _manifest = require("./xml-parser/manifest");
|
|
20
|
+
const _zip = require("./zip");
|
|
21
|
+
function _interop_require_default(obj) {
|
|
22
|
+
return obj && obj.__esModule ? obj : {
|
|
23
|
+
default: obj
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
class AabParser extends _zip.Zip {
|
|
27
|
+
async extractApk(outputPath, { includeAllSplits, splits }) {
|
|
28
|
+
const normalizedSplits = Array.isArray(splits) ? splits.map((item)=>item.trim()).filter(Boolean) : [];
|
|
29
|
+
const modules = includeAllSplits ? null : Array.from(new Set([
|
|
30
|
+
'base',
|
|
31
|
+
...normalizedSplits
|
|
32
|
+
]));
|
|
33
|
+
const modulesArgs = modules ? [
|
|
34
|
+
`--modules=${modules.join(',')}`
|
|
35
|
+
] : [];
|
|
36
|
+
const runCommand = (command, args, options = {})=>new Promise((resolve, reject)=>{
|
|
37
|
+
const inheritStdio = options.stdio === 'inherit';
|
|
38
|
+
const child = (0, _child_process.spawn)(command, args, {
|
|
39
|
+
stdio: inheritStdio ? 'inherit' : [
|
|
40
|
+
'ignore',
|
|
41
|
+
'pipe',
|
|
42
|
+
'pipe'
|
|
43
|
+
],
|
|
44
|
+
env: options.env
|
|
45
|
+
});
|
|
46
|
+
let stderr = '';
|
|
47
|
+
if (!inheritStdio) {
|
|
48
|
+
var _child_stderr;
|
|
49
|
+
(_child_stderr = child.stderr) == null ? void 0 : _child_stderr.on('data', (chunk)=>{
|
|
50
|
+
stderr += chunk.toString();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
child.on('error', reject);
|
|
54
|
+
child.on('close', (code)=>{
|
|
55
|
+
if (code === 0) {
|
|
56
|
+
resolve();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
reject(new Error(stderr.trim() || `Command failed: ${command} (code ${code})`));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// Create a temp file for the .apks output
|
|
63
|
+
const tempDir = _os.default.tmpdir();
|
|
64
|
+
const tempApksPath = _path.default.join(tempDir, `temp-${Date.now()}.apks`);
|
|
65
|
+
const needsNpxDownload = async ()=>{
|
|
66
|
+
try {
|
|
67
|
+
await runCommand('npx', [
|
|
68
|
+
'--no-install',
|
|
69
|
+
'node-bundletool',
|
|
70
|
+
'--version'
|
|
71
|
+
]);
|
|
72
|
+
return false;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return true;
|
|
23
75
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
// 1. Build APKS (universal mode)
|
|
79
|
+
// We assume bundletool is in the path.
|
|
80
|
+
// User might need keystore to sign it properly but for simple extraction we stick to default debug key if possible or unsigned?
|
|
81
|
+
// actually bundletool build-apks signs with debug key by default if no keystore provided.
|
|
82
|
+
try {
|
|
83
|
+
await runCommand('bundletool', [
|
|
84
|
+
'build-apks',
|
|
85
|
+
'--mode=universal',
|
|
86
|
+
`--bundle=${this.file}`,
|
|
87
|
+
`--output=${tempApksPath}`,
|
|
88
|
+
'--overwrite',
|
|
89
|
+
...modulesArgs
|
|
90
|
+
]);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
// Fallback to npx node-bundletool if bundletool is not in PATH
|
|
93
|
+
// We use -y to avoid interactive prompt for installation
|
|
94
|
+
if (await needsNpxDownload()) {
|
|
95
|
+
console.log((0, _i18n.t)('aabBundletoolDownloadHint'));
|
|
29
96
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
97
|
+
await runCommand('npx', [
|
|
98
|
+
'-y',
|
|
99
|
+
'node-bundletool',
|
|
100
|
+
'build-apks',
|
|
101
|
+
'--mode=universal',
|
|
102
|
+
`--bundle=${this.file}`,
|
|
103
|
+
`--output=${tempApksPath}`,
|
|
104
|
+
'--overwrite',
|
|
105
|
+
...modulesArgs
|
|
106
|
+
], {
|
|
107
|
+
stdio: 'inherit',
|
|
108
|
+
env: {
|
|
109
|
+
...process.env,
|
|
110
|
+
npm_config_progress: 'true'
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// 2. Extract universal.apk from the .apks (zip) file
|
|
115
|
+
await new Promise((resolve, reject)=>{
|
|
116
|
+
(0, _yauzl.open)(tempApksPath, {
|
|
117
|
+
lazyEntries: true
|
|
118
|
+
}, (err, zipfile)=>{
|
|
119
|
+
if (err || !zipfile) {
|
|
120
|
+
reject(err || new Error((0, _i18n.t)('aabOpenApksFailed')));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
let found = false;
|
|
124
|
+
zipfile.readEntry();
|
|
125
|
+
zipfile.on('entry', (entry)=>{
|
|
126
|
+
if (entry.fileName === 'universal.apk') {
|
|
127
|
+
found = true;
|
|
39
128
|
zipfile.openReadStream(entry, (err, readStream)=>{
|
|
40
|
-
if (err) {
|
|
41
|
-
|
|
129
|
+
if (err || !readStream) {
|
|
130
|
+
reject(err || new Error((0, _i18n.t)('aabReadUniversalApkFailed')));
|
|
131
|
+
return;
|
|
42
132
|
}
|
|
43
|
-
const
|
|
44
|
-
readStream.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
resolve(
|
|
133
|
+
const writeStream = _fsextra.default.createWriteStream(outputPath);
|
|
134
|
+
readStream.pipe(writeStream);
|
|
135
|
+
writeStream.on('close', ()=>{
|
|
136
|
+
zipfile.close();
|
|
137
|
+
resolve();
|
|
48
138
|
});
|
|
49
|
-
|
|
139
|
+
writeStream.on('error', reject);
|
|
50
140
|
});
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
zipfile.on('entry', async (entry)=>{
|
|
54
|
-
const fileName = entry.fileName;
|
|
55
|
-
// 跳过目录
|
|
56
|
-
if (fileName.endsWith('/')) {
|
|
57
|
-
zipfile.readEntry();
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
pendingReads++;
|
|
61
|
-
try {
|
|
62
|
-
const buffer = await processEntry(entry, fileName);
|
|
63
|
-
if (fileName.startsWith('base/')) {
|
|
64
|
-
// 将 base/manifest/AndroidManifest.xml 转换为 androidmanifest.xml(APK 中通常是小写)
|
|
65
|
-
// 将 base/resources.arsc 转换为 resources.arsc
|
|
66
|
-
let apkPath = fileName.replace(/^base\//, '');
|
|
67
|
-
if (apkPath === 'manifest/AndroidManifest.xml') {
|
|
68
|
-
apkPath = 'androidmanifest.xml';
|
|
69
|
-
}
|
|
70
|
-
baseEntries.push({
|
|
71
|
-
buffer,
|
|
72
|
-
zipPath: fileName,
|
|
73
|
-
apkPath
|
|
74
|
-
});
|
|
75
|
-
} else if (fileName.startsWith('split/')) {
|
|
76
|
-
splitEntries.push({
|
|
77
|
-
buffer,
|
|
78
|
-
zipPath: fileName
|
|
79
|
-
});
|
|
80
|
-
} else if (fileName.startsWith('META-INF/')) {
|
|
81
|
-
metaInfEntries.push({
|
|
82
|
-
buffer,
|
|
83
|
-
zipPath: fileName,
|
|
84
|
-
apkPath: fileName
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
// BundleConfig.pb 和其他文件不需要包含在 APK 中
|
|
88
|
-
pendingReads--;
|
|
89
|
-
zipfile.readEntry();
|
|
90
|
-
} catch (error) {
|
|
91
|
-
pendingReads--;
|
|
92
|
-
if (!hasError) {
|
|
93
|
-
hasError = true;
|
|
94
|
-
reject(error);
|
|
95
|
-
}
|
|
141
|
+
} else {
|
|
96
142
|
zipfile.readEntry();
|
|
97
143
|
}
|
|
98
144
|
});
|
|
99
|
-
zipfile.on('end',
|
|
100
|
-
|
|
101
|
-
while(pendingReads > 0){
|
|
102
|
-
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
103
|
-
}
|
|
104
|
-
if (hasError) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
// 2. 创建新的 APK 文件
|
|
109
|
-
const zipFile = new yazl.ZipFile();
|
|
110
|
-
// 3. 添加 base 目录的所有文件
|
|
111
|
-
for (const { buffer, apkPath } of baseEntries){
|
|
112
|
-
zipFile.addBuffer(buffer, apkPath);
|
|
113
|
-
}
|
|
114
|
-
// 4. 添加 split APK 的内容(如果需要)
|
|
115
|
-
if (includeAllSplits || splits) {
|
|
116
|
-
const splitsToInclude = splits ? splitEntries.filter((se)=>splits.some((s)=>se.zipPath.includes(s))) : splitEntries;
|
|
117
|
-
await this.mergeSplitApksFromBuffers(zipFile, splitsToInclude);
|
|
118
|
-
}
|
|
119
|
-
// 5. 添加 META-INF(签名信息,虽然可能无效,但保留结构)
|
|
120
|
-
for (const { buffer, apkPath } of metaInfEntries){
|
|
121
|
-
zipFile.addBuffer(buffer, apkPath);
|
|
122
|
-
}
|
|
123
|
-
// 6. 写入文件
|
|
124
|
-
zipFile.outputStream.pipe(fs.createWriteStream(outputPath)).on('close', ()=>{
|
|
125
|
-
resolve(outputPath);
|
|
126
|
-
}).on('error', (err)=>{
|
|
127
|
-
reject(err);
|
|
128
|
-
});
|
|
129
|
-
zipFile.end();
|
|
130
|
-
} catch (error) {
|
|
131
|
-
reject(error);
|
|
132
|
-
}
|
|
145
|
+
zipfile.on('end', ()=>{
|
|
146
|
+
if (!found) reject(new Error((0, _i18n.t)('aabUniversalApkNotFound')));
|
|
133
147
|
});
|
|
134
148
|
zipfile.on('error', reject);
|
|
135
|
-
|
|
136
|
-
} catch (error) {
|
|
137
|
-
reject(error);
|
|
138
|
-
}
|
|
149
|
+
});
|
|
139
150
|
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
*/ async mergeSplitApksFromBuffers(zipFile, splitEntries) {
|
|
145
|
-
for (const { buffer: splitBuffer } of splitEntries){
|
|
146
|
-
if (splitBuffer) {
|
|
147
|
-
// 创建一个临时的 ZIP 文件来读取 split APK
|
|
148
|
-
const tempSplitPath = path.join(os.tmpdir(), `split_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.apk`);
|
|
149
|
-
try {
|
|
150
|
-
await fs.writeFile(tempSplitPath, splitBuffer);
|
|
151
|
-
await new Promise((resolve, reject)=>{
|
|
152
|
-
openZipFile(tempSplitPath, {
|
|
153
|
-
lazyEntries: true
|
|
154
|
-
}, async (err, splitZipfile)=>{
|
|
155
|
-
if (err) {
|
|
156
|
-
return reject(err);
|
|
157
|
-
}
|
|
158
|
-
splitZipfile.on('entry', (splitEntry)=>{
|
|
159
|
-
// 跳过 META-INF,因为签名信息不需要合并
|
|
160
|
-
if (splitEntry.fileName.startsWith('META-INF/')) {
|
|
161
|
-
splitZipfile.readEntry();
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
splitZipfile.openReadStream(splitEntry, (err, readStream)=>{
|
|
165
|
-
if (err) {
|
|
166
|
-
splitZipfile.readEntry();
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
const chunks = [];
|
|
170
|
-
readStream.on('data', (chunk)=>chunks.push(chunk));
|
|
171
|
-
readStream.on('end', ()=>{
|
|
172
|
-
const buffer = Buffer.concat(chunks);
|
|
173
|
-
// 注意:如果文件已存在(在 base 中),split 中的会覆盖 base 中的
|
|
174
|
-
zipFile.addBuffer(buffer, splitEntry.fileName);
|
|
175
|
-
splitZipfile.readEntry();
|
|
176
|
-
});
|
|
177
|
-
readStream.on('error', ()=>{
|
|
178
|
-
splitZipfile.readEntry();
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
splitZipfile.on('end', resolve);
|
|
183
|
-
splitZipfile.on('error', reject);
|
|
184
|
-
splitZipfile.readEntry();
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
} finally{
|
|
188
|
-
// 清理临时文件
|
|
189
|
-
await fs.remove(tempSplitPath).catch(()=>{});
|
|
190
|
-
}
|
|
151
|
+
} finally{
|
|
152
|
+
// Cleanup
|
|
153
|
+
if (await _fsextra.default.pathExists(tempApksPath)) {
|
|
154
|
+
await _fsextra.default.remove(tempApksPath);
|
|
191
155
|
}
|
|
192
156
|
}
|
|
193
157
|
}
|
|
@@ -195,31 +159,32 @@ class AabParser extends Zip {
|
|
|
195
159
|
* 解析 AAB 文件信息(类似 APK parser 的 parse 方法)
|
|
196
160
|
* 注意:AAB 中的 AndroidManifest.xml 在 base/manifest/AndroidManifest.xml
|
|
197
161
|
*/ async parse() {
|
|
198
|
-
// 尝试从 base/manifest/AndroidManifest.xml 读取 manifest
|
|
199
|
-
// 但 AAB 中的 manifest 可能是二进制格式,需要特殊处理
|
|
200
162
|
const manifestPath = 'base/manifest/AndroidManifest.xml';
|
|
201
163
|
const ResourceName = /^base\/resources\.arsc$/;
|
|
202
164
|
try {
|
|
203
|
-
const manifestBuffer = await this.getEntry(new RegExp(`^${manifestPath
|
|
165
|
+
const manifestBuffer = await this.getEntry(new RegExp(`^${escapeRegExp(manifestPath)}$`));
|
|
204
166
|
if (!manifestBuffer) {
|
|
205
|
-
throw new Error(
|
|
167
|
+
throw new Error((0, _i18n.t)('aabManifestNotFound'));
|
|
206
168
|
}
|
|
207
169
|
let apkInfo = this._parseManifest(manifestBuffer);
|
|
208
|
-
// 尝试解析 resources.arsc
|
|
209
170
|
try {
|
|
210
171
|
const resourceBuffer = await this.getEntry(ResourceName);
|
|
211
172
|
if (resourceBuffer) {
|
|
212
173
|
const resourceMap = this._parseResourceMap(resourceBuffer);
|
|
213
|
-
|
|
214
|
-
apkInfo = mapInfoResource(apkInfo, resourceMap);
|
|
174
|
+
apkInfo = (0, _utils.mapInfoResource)(apkInfo, resourceMap);
|
|
215
175
|
}
|
|
216
176
|
} catch (e) {
|
|
217
|
-
|
|
218
|
-
console.warn(
|
|
177
|
+
var _e_message;
|
|
178
|
+
console.warn((0, _i18n.t)('aabParseResourcesWarning', {
|
|
179
|
+
error: (_e_message = e == null ? void 0 : e.message) != null ? _e_message : e
|
|
180
|
+
}));
|
|
219
181
|
}
|
|
220
182
|
return apkInfo;
|
|
221
183
|
} catch (error) {
|
|
222
|
-
|
|
184
|
+
var _error_message;
|
|
185
|
+
throw new Error((0, _i18n.t)('aabParseFailed', {
|
|
186
|
+
error: (_error_message = error.message) != null ? _error_message : error
|
|
187
|
+
}));
|
|
223
188
|
}
|
|
224
189
|
}
|
|
225
190
|
/**
|
|
@@ -227,8 +192,7 @@ class AabParser extends Zip {
|
|
|
227
192
|
* @param {Buffer} buffer // manifest file's buffer
|
|
228
193
|
*/ _parseManifest(buffer) {
|
|
229
194
|
try {
|
|
230
|
-
const
|
|
231
|
-
const parser = new ManifestXmlParser(buffer, {
|
|
195
|
+
const parser = new _manifest.ManifestParser(buffer, {
|
|
232
196
|
ignore: [
|
|
233
197
|
'application.activity',
|
|
234
198
|
'application.service',
|
|
@@ -239,7 +203,10 @@ class AabParser extends Zip {
|
|
|
239
203
|
});
|
|
240
204
|
return parser.parse();
|
|
241
205
|
} catch (e) {
|
|
242
|
-
|
|
206
|
+
var _e_message;
|
|
207
|
+
throw new Error((0, _i18n.t)('aabParseManifestError', {
|
|
208
|
+
error: (_e_message = e == null ? void 0 : e.message) != null ? _e_message : e
|
|
209
|
+
}));
|
|
243
210
|
}
|
|
244
211
|
}
|
|
245
212
|
/**
|
|
@@ -247,20 +214,17 @@ class AabParser extends Zip {
|
|
|
247
214
|
* @param {Buffer} buffer // resourceMap file's buffer
|
|
248
215
|
*/ _parseResourceMap(buffer) {
|
|
249
216
|
try {
|
|
250
|
-
|
|
251
|
-
return new ResourceFinder().processResourceTable(buffer);
|
|
217
|
+
return new _resourcefinder.ResourceFinder().processResourceTable(buffer);
|
|
252
218
|
} catch (e) {
|
|
253
|
-
|
|
219
|
+
var _e_message;
|
|
220
|
+
throw new Error((0, _i18n.t)('aabParseResourcesError', {
|
|
221
|
+
error: (_e_message = e == null ? void 0 : e.message) != null ? _e_message : e
|
|
222
|
+
}));
|
|
254
223
|
}
|
|
255
224
|
}
|
|
256
|
-
|
|
257
|
-
* parser for parsing .aab file
|
|
258
|
-
* @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
|
|
259
|
-
*/ constructor(file){
|
|
225
|
+
constructor(file){
|
|
260
226
|
super(file);
|
|
261
|
-
|
|
262
|
-
return new AabParser(file);
|
|
263
|
-
}
|
|
227
|
+
this.file = file;
|
|
264
228
|
}
|
|
265
229
|
}
|
|
266
|
-
|
|
230
|
+
const escapeRegExp = (value)=>value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
@@ -1,34 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ApkParser", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ApkParser;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _resourcefinder = require("./resource-finder");
|
|
12
|
+
const _utils = require("./utils");
|
|
13
|
+
const _manifest = require("./xml-parser/manifest");
|
|
14
|
+
const _zip = require("./zip");
|
|
4
15
|
const ManifestName = /^androidmanifest\.xml$/;
|
|
5
16
|
const ResourceName = /^resources\.arsc$/;
|
|
6
|
-
|
|
7
|
-
const ResourceFinder = require('./resource-finder');
|
|
8
|
-
class ApkParser extends Zip {
|
|
17
|
+
class ApkParser extends _zip.Zip {
|
|
9
18
|
parse() {
|
|
10
19
|
return new Promise((resolve, reject)=>{
|
|
11
20
|
this.getEntries([
|
|
12
21
|
ManifestName,
|
|
13
22
|
ResourceName
|
|
14
23
|
]).then((buffers)=>{
|
|
15
|
-
|
|
24
|
+
const manifestBuffer = buffers[ManifestName];
|
|
25
|
+
if (!manifestBuffer) {
|
|
16
26
|
throw new Error("AndroidManifest.xml can't be found.");
|
|
17
27
|
}
|
|
18
|
-
let apkInfo
|
|
28
|
+
let apkInfo;
|
|
19
29
|
let resourceMap;
|
|
30
|
+
apkInfo = this._parseManifest(manifestBuffer);
|
|
20
31
|
if (!buffers[ResourceName]) {
|
|
21
32
|
resolve(apkInfo);
|
|
22
33
|
} else {
|
|
23
|
-
// parse resourceMap
|
|
24
34
|
resourceMap = this._parseResourceMap(buffers[ResourceName]);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// find icon path and parse icon
|
|
28
|
-
const iconPath = findApkIconPath(apkInfo);
|
|
35
|
+
apkInfo = (0, _utils.mapInfoResource)(apkInfo, resourceMap);
|
|
36
|
+
const iconPath = (0, _utils.findApkIconPath)(apkInfo);
|
|
29
37
|
if (iconPath) {
|
|
30
38
|
this.getEntry(iconPath).then((iconBuffer)=>{
|
|
31
|
-
apkInfo.icon = iconBuffer ? getBase64FromBuffer(iconBuffer) : null;
|
|
39
|
+
apkInfo.icon = iconBuffer ? (0, _utils.getBase64FromBuffer)(iconBuffer) : null;
|
|
32
40
|
resolve(apkInfo);
|
|
33
41
|
}).catch((e)=>{
|
|
34
42
|
apkInfo.icon = null;
|
|
@@ -50,7 +58,7 @@ class ApkParser extends Zip {
|
|
|
50
58
|
* @param {Buffer} buffer // manifest file's buffer
|
|
51
59
|
*/ _parseManifest(buffer) {
|
|
52
60
|
try {
|
|
53
|
-
const parser = new
|
|
61
|
+
const parser = new _manifest.ManifestParser(buffer, {
|
|
54
62
|
ignore: [
|
|
55
63
|
'application.activity',
|
|
56
64
|
'application.service',
|
|
@@ -61,7 +69,7 @@ class ApkParser extends Zip {
|
|
|
61
69
|
});
|
|
62
70
|
return parser.parse();
|
|
63
71
|
} catch (e) {
|
|
64
|
-
throw new Error(
|
|
72
|
+
throw new Error(`Parse AndroidManifest.xml error: ${e.message || e}`);
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
@@ -69,19 +77,9 @@ class ApkParser extends Zip {
|
|
|
69
77
|
* @param {Buffer} buffer // resourceMap file's buffer
|
|
70
78
|
*/ _parseResourceMap(buffer) {
|
|
71
79
|
try {
|
|
72
|
-
return new ResourceFinder().processResourceTable(buffer);
|
|
80
|
+
return new _resourcefinder.ResourceFinder().processResourceTable(buffer);
|
|
73
81
|
} catch (e) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* parser for parsing .apk file
|
|
79
|
-
* @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
|
|
80
|
-
*/ constructor(file){
|
|
81
|
-
super(file);
|
|
82
|
-
if (!(this instanceof ApkParser)) {
|
|
83
|
-
return new ApkParser(file);
|
|
82
|
+
throw new Error(`Parser resources.arsc error: ${e}`);
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
|
-
module.exports = ApkParser;
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (!(this instanceof AppParser)) {
|
|
10
|
-
return new AppParser(file);
|
|
11
|
-
}
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "AppParser", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return AppParser;
|
|
12
9
|
}
|
|
10
|
+
});
|
|
11
|
+
const _zip = require("./zip");
|
|
12
|
+
class AppParser extends _zip.Zip {
|
|
13
13
|
}
|
|
14
|
-
module.exports = AppParser;
|
|
@@ -8,10 +8,10 @@ Object.defineProperty(exports, "default", {
|
|
|
8
8
|
return _default;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
11
|
+
const _aab = require("./aab");
|
|
12
|
+
const _apk = require("./apk");
|
|
13
|
+
const _app = require("./app");
|
|
14
|
+
const _ipa = require("./ipa");
|
|
15
15
|
const supportFileTypes = [
|
|
16
16
|
'ipa',
|
|
17
17
|
'apk',
|
|
@@ -37,16 +37,16 @@ class AppInfoParser {
|
|
|
37
37
|
this.file = file;
|
|
38
38
|
switch(fileType){
|
|
39
39
|
case 'ipa':
|
|
40
|
-
this.parser = new IpaParser(this.file);
|
|
40
|
+
this.parser = new _ipa.IpaParser(this.file);
|
|
41
41
|
break;
|
|
42
42
|
case 'apk':
|
|
43
|
-
this.parser = new ApkParser(this.file);
|
|
43
|
+
this.parser = new _apk.ApkParser(this.file);
|
|
44
44
|
break;
|
|
45
45
|
case 'app':
|
|
46
|
-
this.parser = new AppParser(this.file);
|
|
46
|
+
this.parser = new _app.AppParser(this.file);
|
|
47
47
|
break;
|
|
48
48
|
case 'aab':
|
|
49
|
-
this.parser = new AabParser(this.file);
|
|
49
|
+
this.parser = new _aab.AabParser(this.file);
|
|
50
50
|
break;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "IpaParser", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return IpaParser;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _utils = require("./utils");
|
|
12
|
+
const _zip = require("./zip");
|
|
2
13
|
const parsePlist = require('plist').parse;
|
|
3
14
|
const parseBplist = require('bplist-parser').parseBuffer;
|
|
4
15
|
const cgbiToPng = require('cgbi-to-png');
|
|
5
|
-
const Zip = require('./zip');
|
|
6
|
-
const { findIpaIconPath, getBase64FromBuffer, isBrowser } = require('./utils');
|
|
7
16
|
const PlistName = /payload\/[^\/]+?.app\/info.plist$/i;
|
|
8
17
|
const ProvisionName = /payload\/.+?\.app\/embedded.mobileprovision/;
|
|
9
|
-
class IpaParser extends Zip {
|
|
18
|
+
class IpaParser extends _zip.Zip {
|
|
10
19
|
parse() {
|
|
11
20
|
return new Promise((resolve, reject)=>{
|
|
12
21
|
this.getEntries([
|
|
@@ -17,19 +26,15 @@ class IpaParser extends Zip {
|
|
|
17
26
|
throw new Error("Info.plist can't be found.");
|
|
18
27
|
}
|
|
19
28
|
const plistInfo = this._parsePlist(buffers[PlistName]);
|
|
20
|
-
// parse mobile provision
|
|
21
29
|
const provisionInfo = this._parseProvision(buffers[ProvisionName]);
|
|
22
30
|
plistInfo.mobileProvision = provisionInfo;
|
|
23
|
-
|
|
24
|
-
const iconRegex = new RegExp(findIpaIconPath(plistInfo).toLowerCase());
|
|
31
|
+
const iconRegex = new RegExp((0, _utils.findIpaIconPath)(plistInfo).toLowerCase());
|
|
25
32
|
this.getEntry(iconRegex).then((iconBuffer)=>{
|
|
26
33
|
try {
|
|
27
|
-
|
|
28
|
-
plistInfo.icon = iconBuffer ? getBase64FromBuffer(cgbiToPng.revert(iconBuffer)) : null;
|
|
34
|
+
plistInfo.icon = iconBuffer ? (0, _utils.getBase64FromBuffer)(cgbiToPng.revert(iconBuffer)) : null;
|
|
29
35
|
} catch (err) {
|
|
30
|
-
if (isBrowser()) {
|
|
31
|
-
|
|
32
|
-
plistInfo.icon = iconBuffer ? getBase64FromBuffer(window.btoa(String.fromCharCode(...iconBuffer))) : null;
|
|
36
|
+
if ((0, _utils.isBrowser)()) {
|
|
37
|
+
plistInfo.icon = iconBuffer ? (0, _utils.getBase64FromBuffer)(window.btoa(String.fromCharCode(...iconBuffer))) : null;
|
|
33
38
|
} else {
|
|
34
39
|
plistInfo.icon = null;
|
|
35
40
|
console.warn('[Warning] failed to parse icon: ', err);
|
|
@@ -75,14 +80,4 @@ class IpaParser extends Zip {
|
|
|
75
80
|
}
|
|
76
81
|
return info;
|
|
77
82
|
}
|
|
78
|
-
/**
|
|
79
|
-
* parser for parsing .ipa file
|
|
80
|
-
* @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
|
|
81
|
-
*/ constructor(file){
|
|
82
|
-
super(file);
|
|
83
|
-
if (!(this instanceof IpaParser)) {
|
|
84
|
-
return new IpaParser(file);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
83
|
}
|
|
88
|
-
module.exports = IpaParser;
|