react-native-update-cli 2.4.2 → 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.
Files changed (49) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/cli.json +40 -0
  4. package/lib/api.js +1 -1
  5. package/lib/locales/en.js +16 -1
  6. package/lib/locales/zh.js +16 -1
  7. package/lib/package.js +60 -6
  8. package/lib/provider.js +3 -0
  9. package/lib/utils/app-info-parser/aab.js +230 -0
  10. package/lib/utils/app-info-parser/apk.js +25 -27
  11. package/lib/utils/app-info-parser/app.js +10 -11
  12. package/lib/utils/app-info-parser/index.js +13 -8
  13. package/lib/utils/app-info-parser/ipa.js +16 -21
  14. package/lib/utils/app-info-parser/resource-finder.js +365 -305
  15. package/lib/utils/app-info-parser/utils.js +78 -63
  16. package/lib/utils/app-info-parser/xml-parser/binary.js +57 -51
  17. package/lib/utils/app-info-parser/xml-parser/manifest.js +47 -39
  18. package/lib/utils/app-info-parser/zip.js +21 -11
  19. package/lib/utils/http-helper.js +1 -1
  20. package/lib/utils/index.js +137 -0
  21. package/lib/versions.js +22 -0
  22. package/package.json +3 -2
  23. package/proto/Configuration.proto +183 -0
  24. package/proto/Resources.proto +569 -0
  25. package/src/api.ts +2 -6
  26. package/src/locales/en.ts +20 -0
  27. package/src/locales/zh.ts +18 -0
  28. package/src/modules/version-module.ts +1 -1
  29. package/src/package.ts +112 -12
  30. package/src/provider.ts +3 -0
  31. package/src/utils/app-info-parser/aab.ts +240 -0
  32. package/src/utils/app-info-parser/{apk.js → apk.ts} +30 -41
  33. package/src/utils/app-info-parser/app.ts +3 -0
  34. package/src/utils/app-info-parser/index.ts +9 -5
  35. package/src/utils/app-info-parser/{ipa.js → ipa.ts} +17 -31
  36. package/src/utils/app-info-parser/resource-finder.ts +508 -0
  37. package/src/utils/app-info-parser/utils.ts +162 -0
  38. package/src/utils/app-info-parser/xml-parser/{binary.js → binary.ts} +69 -61
  39. package/src/utils/app-info-parser/xml-parser/{manifest.js → manifest.ts} +50 -51
  40. package/src/utils/app-info-parser/zip.ts +86 -0
  41. package/src/utils/dep-versions.ts +7 -4
  42. package/src/utils/http-helper.ts +1 -1
  43. package/src/utils/index.ts +154 -0
  44. package/src/utils/latest-version/index.ts +2 -1
  45. package/src/versions.ts +27 -2
  46. package/src/utils/app-info-parser/app.js +0 -16
  47. package/src/utils/app-info-parser/resource-finder.js +0 -495
  48. package/src/utils/app-info-parser/utils.js +0 -172
  49. package/src/utils/app-info-parser/zip.js +0 -66
package/src/package.ts CHANGED
@@ -1,17 +1,25 @@
1
- import { get, getAllPackages, post, uploadFile, doDelete } from './api';
2
- import { question, saveToLocal } from './utils';
3
- import { t } from './utils/i18n';
4
-
5
- import { getPlatform, getSelectedApp } from './app';
6
-
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
7
4
  import Table from 'tty-table';
5
+ import { doDelete, getAllPackages, post, uploadFile } from './api';
6
+ import { getPlatform, getSelectedApp } from './app';
8
7
  import type { Platform } from './types';
9
- import { getApkInfo, getAppInfo, getIpaInfo } from './utils';
8
+ import {
9
+ getAabInfo,
10
+ getApkInfo,
11
+ getAppInfo,
12
+ getIpaInfo,
13
+ question,
14
+ saveToLocal,
15
+ } from './utils';
16
+ import { AabParser } from './utils/app-info-parser/aab';
10
17
  import { depVersions } from './utils/dep-versions';
11
18
  import { getCommitInfo } from './utils/git';
19
+ import { t } from './utils/i18n';
12
20
 
13
21
  export async function listPackage(appId: string) {
14
- const allPkgs = await getAllPackages(appId);
22
+ const allPkgs = (await getAllPackages(appId)) || [];
15
23
 
16
24
  const header = [
17
25
  { value: t('nativePackageId') },
@@ -49,7 +57,7 @@ export async function choosePackage(appId: string) {
49
57
 
50
58
  while (true) {
51
59
  const id = await question(t('enterNativePackageId'));
52
- const app = list.find((v) => v.id.toString() === id);
60
+ const app = list?.find((v) => v.id.toString() === id);
53
61
  if (app) {
54
62
  return app;
55
63
  }
@@ -143,6 +151,48 @@ export const packageCommands = {
143
151
  saveToLocal(fn, `${appId}/package/${id}.apk`);
144
152
  console.log(t('apkUploadSuccess', { id, version: versionName, buildTime }));
145
153
  },
154
+ uploadAab: async ({
155
+ args,
156
+ options,
157
+ }: {
158
+ args: string[];
159
+ options: Record<string, any>;
160
+ }) => {
161
+ const source = args[0];
162
+ if (!source || !source.endsWith('.aab')) {
163
+ throw new Error(t('usageUploadAab'));
164
+ }
165
+
166
+ const output = path.join(
167
+ os.tmpdir(),
168
+ `${path.basename(source, path.extname(source))}-${Date.now()}.apk`,
169
+ );
170
+
171
+ const includeAllSplits =
172
+ options.includeAllSplits === true || options.includeAllSplits === 'true';
173
+ const splits = options.splits
174
+ ? String(options.splits)
175
+ .split(',')
176
+ .map((item) => item.trim())
177
+ .filter(Boolean)
178
+ : null;
179
+
180
+ const parser = new AabParser(source);
181
+ try {
182
+ await parser.extractApk(output, {
183
+ includeAllSplits,
184
+ splits,
185
+ });
186
+ await packageCommands.uploadApk({
187
+ args: [output],
188
+ options,
189
+ });
190
+ } finally {
191
+ if (await fs.pathExists(output)) {
192
+ await fs.remove(output);
193
+ }
194
+ }
195
+ },
146
196
  uploadApp: async ({
147
197
  args,
148
198
  options,
@@ -207,6 +257,49 @@ export const packageCommands = {
207
257
  }
208
258
  console.log(await getApkInfo(fn));
209
259
  },
260
+ parseAab: async ({ args }: { args: string[] }) => {
261
+ const fn = args[0];
262
+ if (!fn || !fn.endsWith('.aab')) {
263
+ throw new Error(t('usageParseAab'));
264
+ }
265
+ console.log(await getAabInfo(fn));
266
+ },
267
+ extractApk: async ({
268
+ args,
269
+ options,
270
+ }: {
271
+ args: string[];
272
+ options: Record<string, any>;
273
+ }) => {
274
+ const source = args[0];
275
+ if (!source || !source.endsWith('.aab')) {
276
+ throw new Error(t('usageExtractApk'));
277
+ }
278
+
279
+ const output =
280
+ options.output ||
281
+ path.join(
282
+ path.dirname(source),
283
+ `${path.basename(source, path.extname(source))}.apk`,
284
+ );
285
+
286
+ const includeAllSplits =
287
+ options.includeAllSplits === true || options.includeAllSplits === 'true';
288
+ const splits = options.splits
289
+ ? String(options.splits)
290
+ .split(',')
291
+ .map((item) => item.trim())
292
+ .filter(Boolean)
293
+ : null;
294
+
295
+ const parser = new AabParser(source);
296
+ await parser.extractApk(output, {
297
+ includeAllSplits,
298
+ splits,
299
+ });
300
+
301
+ console.log(t('apkExtracted', { output }));
302
+ },
210
303
  packages: async ({ options }: { options: { platform: Platform } }) => {
211
304
  const platform = await getPlatform(options.platform);
212
305
  const { appId } = await getSelectedApp(platform);
@@ -217,12 +310,17 @@ export const packageCommands = {
217
310
  options,
218
311
  }: {
219
312
  args: string[];
220
- options: { appId?: string, packageId?: string, packageVersion?: string };
313
+ options: {
314
+ appId?: string;
315
+ packageId?: string;
316
+ packageVersion?: string;
317
+ platform?: Platform;
318
+ };
221
319
  }) => {
222
320
  let { appId, packageId, packageVersion } = options;
223
321
 
224
322
  if (!appId) {
225
- const platform = await getPlatform();
323
+ const platform = await getPlatform(options.platform);
226
324
  appId = (await getSelectedApp(platform)).appId as string;
227
325
  }
228
326
 
@@ -232,7 +330,9 @@ export const packageCommands = {
232
330
  if (!allPkgs) {
233
331
  throw new Error(t('noPackagesFound', { appId }));
234
332
  }
235
- const selectedPackage = allPkgs.find((pkg) => pkg.name === packageVersion);
333
+ const selectedPackage = allPkgs.find(
334
+ (pkg) => pkg.name === packageVersion,
335
+ );
236
336
  if (!selectedPackage) {
237
337
  throw new Error(t('packageNotFound', { packageVersion }));
238
338
  }
package/src/provider.ts CHANGED
@@ -122,6 +122,9 @@ export class CLIProviderImpl implements CLIProvider {
122
122
  case 'apk':
123
123
  await packageCommands.uploadApk(context);
124
124
  break;
125
+ case 'aab':
126
+ await packageCommands.uploadAab(context);
127
+ break;
125
128
  case 'app':
126
129
  await packageCommands.uploadApp(context);
127
130
  break;
@@ -0,0 +1,240 @@
1
+ import { spawn } from 'child_process';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import fs from 'fs-extra';
5
+ import { open as openZipFile } from 'yauzl';
6
+ import { t } from '../i18n';
7
+ import { ResourceFinder } from './resource-finder';
8
+ import { mapInfoResource } from './utils';
9
+ import { ManifestParser } from './xml-parser/manifest';
10
+ import { Zip } from './zip';
11
+
12
+ export class AabParser extends Zip {
13
+ file: string | File;
14
+
15
+ constructor(file: string | File) {
16
+ super(file);
17
+ this.file = file;
18
+ }
19
+
20
+ async extractApk(
21
+ outputPath: string,
22
+ {
23
+ includeAllSplits,
24
+ splits,
25
+ }: { includeAllSplits?: boolean; splits?: string[] | null },
26
+ ) {
27
+ const normalizedSplits = Array.isArray(splits)
28
+ ? splits.map((item) => item.trim()).filter(Boolean)
29
+ : [];
30
+ const modules = includeAllSplits
31
+ ? null
32
+ : Array.from(new Set(['base', ...normalizedSplits]));
33
+ const modulesArgs = modules ? [`--modules=${modules.join(',')}`] : [];
34
+
35
+ const runCommand = (
36
+ command: string,
37
+ args: string[],
38
+ options: { stdio?: 'inherit'; env?: NodeJS.ProcessEnv } = {},
39
+ ) =>
40
+ new Promise<void>((resolve, reject) => {
41
+ const inheritStdio = options.stdio === 'inherit';
42
+ const child = spawn(command, args, {
43
+ stdio: inheritStdio ? 'inherit' : ['ignore', 'pipe', 'pipe'],
44
+ env: options.env,
45
+ });
46
+ let stderr = '';
47
+ if (!inheritStdio) {
48
+ child.stderr?.on('data', (chunk) => {
49
+ stderr += chunk.toString();
50
+ });
51
+ }
52
+ child.on('error', reject);
53
+ child.on('close', (code) => {
54
+ if (code === 0) {
55
+ resolve();
56
+ return;
57
+ }
58
+ reject(
59
+ new Error(
60
+ stderr.trim() || `Command failed: ${command} (code ${code})`,
61
+ ),
62
+ );
63
+ });
64
+ });
65
+
66
+ // Create a temp file for the .apks output
67
+ const tempDir = os.tmpdir();
68
+ const tempApksPath = path.join(tempDir, `temp-${Date.now()}.apks`);
69
+
70
+ const needsNpxDownload = async () => {
71
+ try {
72
+ await runCommand('npx', [
73
+ '--no-install',
74
+ 'node-bundletool',
75
+ '--version',
76
+ ]);
77
+ return false;
78
+ } catch {
79
+ return true;
80
+ }
81
+ };
82
+
83
+ try {
84
+ // 1. Build APKS (universal mode)
85
+ // We assume bundletool is in the path.
86
+ // User might need keystore to sign it properly but for simple extraction we stick to default debug key if possible or unsigned?
87
+ // actually bundletool build-apks signs with debug key by default if no keystore provided.
88
+
89
+ try {
90
+ await runCommand('bundletool', [
91
+ 'build-apks',
92
+ '--mode=universal',
93
+ `--bundle=${this.file}`,
94
+ `--output=${tempApksPath}`,
95
+ '--overwrite',
96
+ ...modulesArgs,
97
+ ]);
98
+ } catch (e) {
99
+ // Fallback to npx node-bundletool if bundletool is not in PATH
100
+ // We use -y to avoid interactive prompt for installation
101
+ if (await needsNpxDownload()) {
102
+ console.log(t('aabBundletoolDownloadHint'));
103
+ }
104
+ await runCommand(
105
+ 'npx',
106
+ [
107
+ '-y',
108
+ 'node-bundletool',
109
+ 'build-apks',
110
+ '--mode=universal',
111
+ `--bundle=${this.file}`,
112
+ `--output=${tempApksPath}`,
113
+ '--overwrite',
114
+ ...modulesArgs,
115
+ ],
116
+ {
117
+ stdio: 'inherit',
118
+ env: {
119
+ ...process.env,
120
+ npm_config_progress: 'true',
121
+ },
122
+ },
123
+ );
124
+ }
125
+
126
+ // 2. Extract universal.apk from the .apks (zip) file
127
+ await new Promise<void>((resolve, reject) => {
128
+ openZipFile(tempApksPath, { lazyEntries: true }, (err, zipfile) => {
129
+ if (err || !zipfile) {
130
+ reject(err || new Error(t('aabOpenApksFailed')));
131
+ return;
132
+ }
133
+
134
+ let found = false;
135
+ zipfile.readEntry();
136
+ zipfile.on('entry', (entry) => {
137
+ if (entry.fileName === 'universal.apk') {
138
+ found = true;
139
+ zipfile.openReadStream(entry, (err, readStream) => {
140
+ if (err || !readStream) {
141
+ reject(err || new Error(t('aabReadUniversalApkFailed')));
142
+ return;
143
+ }
144
+ const writeStream = fs.createWriteStream(outputPath);
145
+ readStream.pipe(writeStream);
146
+ writeStream.on('close', () => {
147
+ zipfile.close();
148
+ resolve();
149
+ });
150
+ writeStream.on('error', reject);
151
+ });
152
+ } else {
153
+ zipfile.readEntry();
154
+ }
155
+ });
156
+
157
+ zipfile.on('end', () => {
158
+ if (!found) reject(new Error(t('aabUniversalApkNotFound')));
159
+ });
160
+ zipfile.on('error', reject);
161
+ });
162
+ });
163
+ } finally {
164
+ // Cleanup
165
+ if (await fs.pathExists(tempApksPath)) {
166
+ await fs.remove(tempApksPath);
167
+ }
168
+ }
169
+ }
170
+
171
+ /**
172
+ * 解析 AAB 文件信息(类似 APK parser 的 parse 方法)
173
+ * 注意:AAB 中的 AndroidManifest.xml 在 base/manifest/AndroidManifest.xml
174
+ */
175
+ async parse() {
176
+ const manifestPath = 'base/manifest/AndroidManifest.xml';
177
+ const ResourceName = /^base\/resources\.arsc$/;
178
+
179
+ try {
180
+ const manifestBuffer = await this.getEntry(
181
+ new RegExp(`^${escapeRegExp(manifestPath)}$`),
182
+ );
183
+
184
+ if (!manifestBuffer) {
185
+ throw new Error(t('aabManifestNotFound'));
186
+ }
187
+
188
+ let apkInfo = this._parseManifest(manifestBuffer as Buffer);
189
+
190
+ try {
191
+ const resourceBuffer = await this.getEntry(ResourceName);
192
+ if (resourceBuffer) {
193
+ const resourceMap = this._parseResourceMap(resourceBuffer as Buffer);
194
+ apkInfo = mapInfoResource(apkInfo, resourceMap);
195
+ }
196
+ } catch (e: any) {
197
+ console.warn(t('aabParseResourcesWarning', { error: e?.message ?? e }));
198
+ }
199
+
200
+ return apkInfo;
201
+ } catch (error: any) {
202
+ throw new Error(t('aabParseFailed', { error: error.message ?? error }));
203
+ }
204
+ }
205
+ /**
206
+ * Parse manifest
207
+ * @param {Buffer} buffer // manifest file's buffer
208
+ */
209
+ private _parseManifest(buffer: Buffer) {
210
+ try {
211
+ const parser = new ManifestParser(buffer, {
212
+ ignore: [
213
+ 'application.activity',
214
+ 'application.service',
215
+ 'application.receiver',
216
+ 'application.provider',
217
+ 'permission-group',
218
+ ],
219
+ });
220
+ return parser.parse();
221
+ } catch (e: any) {
222
+ throw new Error(t('aabParseManifestError', { error: e?.message ?? e }));
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Parse resourceMap
228
+ * @param {Buffer} buffer // resourceMap file's buffer
229
+ */
230
+ private _parseResourceMap(buffer: Buffer) {
231
+ try {
232
+ return new ResourceFinder().processResourceTable(buffer);
233
+ } catch (e: any) {
234
+ throw new Error(t('aabParseResourcesError', { error: e?.message ?? e }));
235
+ }
236
+ }
237
+ }
238
+
239
+ const escapeRegExp = (value: string) =>
240
+ value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -1,54 +1,43 @@
1
- const Zip = require('./zip');
2
- const {
3
- mapInfoResource,
4
- findApkIconPath,
5
- getBase64FromBuffer,
6
- } = require('./utils');
1
+ import { ResourceFinder } from './resource-finder';
2
+ import { findApkIconPath, getBase64FromBuffer, mapInfoResource } from './utils';
3
+ import { ManifestParser } from './xml-parser/manifest';
4
+ import { Zip } from './zip';
5
+
7
6
  const ManifestName = /^androidmanifest\.xml$/;
8
7
  const ResourceName = /^resources\.arsc$/;
9
8
 
10
- const ManifestXmlParser = require('./xml-parser/manifest');
11
- const ResourceFinder = require('./resource-finder');
12
-
13
- class ApkParser extends Zip {
14
- /**
15
- * parser for parsing .apk file
16
- * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
17
- */
18
- constructor(file) {
19
- super(file);
20
- if (!(this instanceof ApkParser)) {
21
- return new ApkParser(file);
22
- }
23
- }
24
- parse() {
9
+ export class ApkParser extends Zip {
10
+ parse(): Promise<any> {
25
11
  return new Promise((resolve, reject) => {
26
12
  this.getEntries([ManifestName, ResourceName])
27
- .then((buffers) => {
28
- if (!buffers[ManifestName]) {
13
+ .then((buffers: any) => {
14
+ const manifestBuffer = buffers[ManifestName];
15
+ if (!manifestBuffer) {
29
16
  throw new Error("AndroidManifest.xml can't be found.");
30
17
  }
31
- let apkInfo = this._parseManifest(buffers[ManifestName]);
32
- let resourceMap;
18
+ let apkInfo: any;
19
+ let resourceMap: any;
20
+
21
+ apkInfo = this._parseManifest(manifestBuffer as Buffer);
22
+
33
23
  if (!buffers[ResourceName]) {
34
24
  resolve(apkInfo);
35
25
  } else {
36
- // parse resourceMap
37
- resourceMap = this._parseResourceMap(buffers[ResourceName]);
38
- // update apkInfo with resourceMap
26
+ resourceMap = this._parseResourceMap(
27
+ buffers[ResourceName] as Buffer,
28
+ );
39
29
  apkInfo = mapInfoResource(apkInfo, resourceMap);
40
30
 
41
- // find icon path and parse icon
42
31
  const iconPath = findApkIconPath(apkInfo);
43
32
  if (iconPath) {
44
33
  this.getEntry(iconPath)
45
- .then((iconBuffer) => {
34
+ .then((iconBuffer: Buffer | null) => {
46
35
  apkInfo.icon = iconBuffer
47
36
  ? getBase64FromBuffer(iconBuffer)
48
37
  : null;
49
38
  resolve(apkInfo);
50
39
  })
51
- .catch((e) => {
40
+ .catch((e: any) => {
52
41
  apkInfo.icon = null;
53
42
  resolve(apkInfo);
54
43
  console.warn('[Warning] failed to parse icon: ', e);
@@ -59,18 +48,19 @@ class ApkParser extends Zip {
59
48
  }
60
49
  }
61
50
  })
62
- .catch((e) => {
51
+ .catch((e: any) => {
63
52
  reject(e);
64
53
  });
65
54
  });
66
55
  }
56
+
67
57
  /**
68
58
  * Parse manifest
69
59
  * @param {Buffer} buffer // manifest file's buffer
70
60
  */
71
- _parseManifest(buffer) {
61
+ private _parseManifest(buffer: Buffer) {
72
62
  try {
73
- const parser = new ManifestXmlParser(buffer, {
63
+ const parser = new ManifestParser(buffer, {
74
64
  ignore: [
75
65
  'application.activity',
76
66
  'application.service',
@@ -80,21 +70,20 @@ class ApkParser extends Zip {
80
70
  ],
81
71
  });
82
72
  return parser.parse();
83
- } catch (e) {
84
- throw new Error('Parse AndroidManifest.xml error: ', e);
73
+ } catch (e: any) {
74
+ throw new Error(`Parse AndroidManifest.xml error: ${e.message || e}`);
85
75
  }
86
76
  }
77
+
87
78
  /**
88
79
  * Parse resourceMap
89
80
  * @param {Buffer} buffer // resourceMap file's buffer
90
81
  */
91
- _parseResourceMap(buffer) {
82
+ private _parseResourceMap(buffer: Buffer) {
92
83
  try {
93
84
  return new ResourceFinder().processResourceTable(buffer);
94
- } catch (e) {
95
- throw new Error('Parser resources.arsc error: ' + e);
85
+ } catch (e: any) {
86
+ throw new Error(`Parser resources.arsc error: ${e}`);
96
87
  }
97
88
  }
98
89
  }
99
-
100
- module.exports = ApkParser;
@@ -0,0 +1,3 @@
1
+ import { Zip } from './zip';
2
+
3
+ export class AppParser extends Zip {}
@@ -1,7 +1,8 @@
1
- const ApkParser = require('./apk');
2
- const IpaParser = require('./ipa');
3
- const AppParser = require('./app');
4
- const supportFileTypes = ['ipa', 'apk', 'app'];
1
+ import { AabParser } from './aab';
2
+ import { ApkParser } from './apk';
3
+ import { AppParser } from './app';
4
+ import { IpaParser } from './ipa';
5
+ const supportFileTypes = ['ipa', 'apk', 'app', 'aab'];
5
6
 
6
7
  class AppInfoParser {
7
8
  file: string | File;
@@ -20,7 +21,7 @@ class AppInfoParser {
20
21
  const fileType = splits[splits.length - 1].toLowerCase();
21
22
  if (!supportFileTypes.includes(fileType)) {
22
23
  throw new Error(
23
- 'Unsupported file type, only support .ipa or .apk or .app file.',
24
+ 'Unsupported file type, only support .ipa, .apk, .app, or .aab file.',
24
25
  );
25
26
  }
26
27
  this.file = file;
@@ -35,6 +36,9 @@ class AppInfoParser {
35
36
  case 'app':
36
37
  this.parser = new AppParser(this.file);
37
38
  break;
39
+ case 'aab':
40
+ this.parser = new AabParser(this.file);
41
+ break;
38
42
  }
39
43
  }
40
44
  parse() {
@@ -2,49 +2,37 @@ const parsePlist = require('plist').parse;
2
2
  const parseBplist = require('bplist-parser').parseBuffer;
3
3
  const cgbiToPng = require('cgbi-to-png');
4
4
 
5
- const Zip = require('./zip');
6
- const { findIpaIconPath, getBase64FromBuffer, isBrowser } = require('./utils');
5
+ import { findIpaIconPath, getBase64FromBuffer, isBrowser } from './utils';
6
+ import { Zip } from './zip';
7
7
 
8
8
  const PlistName = /payload\/[^\/]+?.app\/info.plist$/i;
9
9
  const ProvisionName = /payload\/.+?\.app\/embedded.mobileprovision/;
10
10
 
11
- class IpaParser extends Zip {
12
- /**
13
- * parser for parsing .ipa file
14
- * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
15
- */
16
- constructor(file) {
17
- super(file);
18
- if (!(this instanceof IpaParser)) {
19
- return new IpaParser(file);
20
- }
21
- }
22
- parse() {
11
+ export class IpaParser extends Zip {
12
+ parse(): Promise<any> {
23
13
  return new Promise((resolve, reject) => {
24
14
  this.getEntries([PlistName, ProvisionName])
25
- .then((buffers) => {
26
- if (!buffers[PlistName]) {
15
+ .then((buffers: any) => {
16
+ if (!buffers[PlistName as any]) {
27
17
  throw new Error("Info.plist can't be found.");
28
18
  }
29
- const plistInfo = this._parsePlist(buffers[PlistName]);
30
- // parse mobile provision
31
- const provisionInfo = this._parseProvision(buffers[ProvisionName]);
19
+ const plistInfo = this._parsePlist(buffers[PlistName as any]);
20
+ const provisionInfo = this._parseProvision(
21
+ buffers[ProvisionName as any],
22
+ );
32
23
  plistInfo.mobileProvision = provisionInfo;
33
24
 
34
- // find icon path and parse icon
35
25
  const iconRegex = new RegExp(
36
26
  findIpaIconPath(plistInfo).toLowerCase(),
37
27
  );
38
28
  this.getEntry(iconRegex)
39
- .then((iconBuffer) => {
29
+ .then((iconBuffer: any) => {
40
30
  try {
41
- // In general, the ipa file's icon has been specially processed, should be converted
42
31
  plistInfo.icon = iconBuffer
43
32
  ? getBase64FromBuffer(cgbiToPng.revert(iconBuffer))
44
33
  : null;
45
34
  } catch (err) {
46
35
  if (isBrowser()) {
47
- // Normal conversion in other cases
48
36
  plistInfo.icon = iconBuffer
49
37
  ? getBase64FromBuffer(
50
38
  window.btoa(String.fromCharCode(...iconBuffer)),
@@ -57,11 +45,11 @@ class IpaParser extends Zip {
57
45
  }
58
46
  resolve(plistInfo);
59
47
  })
60
- .catch((e) => {
48
+ .catch((e: any) => {
61
49
  reject(e);
62
50
  });
63
51
  })
64
- .catch((e) => {
52
+ .catch((e: any) => {
65
53
  reject(e);
66
54
  });
67
55
  });
@@ -70,8 +58,8 @@ class IpaParser extends Zip {
70
58
  * Parse plist
71
59
  * @param {Buffer} buffer // plist file's buffer
72
60
  */
73
- _parsePlist(buffer) {
74
- let result;
61
+ private _parsePlist(buffer: Buffer) {
62
+ let result: any;
75
63
  const bufferType = buffer[0];
76
64
  if (bufferType === 60 || bufferType === '<' || bufferType === 239) {
77
65
  result = parsePlist(buffer.toString());
@@ -86,8 +74,8 @@ class IpaParser extends Zip {
86
74
  * parse provision
87
75
  * @param {Buffer} buffer // provision file's buffer
88
76
  */
89
- _parseProvision(buffer) {
90
- let info = {};
77
+ private _parseProvision(buffer: Buffer | undefined) {
78
+ let info: Record<string, any> = {};
91
79
  if (buffer) {
92
80
  let content = buffer.toString('utf-8');
93
81
  const firstIndex = content.indexOf('<?xml');
@@ -100,5 +88,3 @@ class IpaParser extends Zip {
100
88
  return info;
101
89
  }
102
90
  }
103
-
104
- module.exports = IpaParser;