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/README.md CHANGED
@@ -223,10 +223,12 @@ Each workflow step contains:
223
223
 
224
224
  - `uploadIpa`: Upload IPA files (supports `--version` to override extracted version)
225
225
  - `uploadApk`: Upload APK files (supports `--version` to override extracted version)
226
+ - `uploadAab`: Upload AAB files (converted to APK, supports `--version`, `--includeAllSplits`, `--splits`)
226
227
  - `uploadApp`: Upload APP files (supports `--version` to override extracted version)
227
228
  - `parseApp`: Parse APP file information
228
229
  - `parseIpa`: Parse IPA file information
229
230
  - `parseApk`: Parse APK file information
231
+ - `extractApk`: Extract a universal APK from an AAB (supports `--output`, `--includeAllSplits`, `--splits`)
230
232
  - `packages`: List packages
231
233
 
232
234
  ### User Module (`user`)
package/README.zh-CN.md CHANGED
@@ -221,10 +221,12 @@ const workflowResult = await moduleManager.executeWorkflow('my-workflow', {
221
221
 
222
222
  - `uploadIpa`: 上传 IPA 文件(支持 `--version` 参数覆盖提取的版本)
223
223
  - `uploadApk`: 上传 APK 文件(支持 `--version` 参数覆盖提取的版本)
224
+ - `uploadAab`: 上传 AAB 文件(转换为 APK,支持 `--version`、`--includeAllSplits`、`--splits`)
224
225
  - `uploadApp`: 上传 APP 文件(支持 `--version` 参数覆盖提取的版本)
225
226
  - `parseApp`: 解析 APP 文件信息
226
227
  - `parseIpa`: 解析 IPA 文件信息
227
228
  - `parseApk`: 解析 APK 文件信息
229
+ - `extractApk`: 从 AAB 提取通用 APK(支持 `--output`、`--includeAllSplits`、`--splits`)
228
230
  - `packages`: 列出包
229
231
 
230
232
  ### User 模块 (`user`)
package/cli.json CHANGED
@@ -45,6 +45,19 @@
45
45
  }
46
46
  }
47
47
  },
48
+ "uploadAab": {
49
+ "options": {
50
+ "version": {
51
+ "hasValue": true
52
+ },
53
+ "includeAllSplits": {
54
+ "default": false
55
+ },
56
+ "splits": {
57
+ "hasValue": true
58
+ }
59
+ }
60
+ },
48
61
  "uploadApp": {
49
62
  "options": {
50
63
  "version": {
@@ -55,6 +68,20 @@
55
68
  "parseApp": {},
56
69
  "parseIpa": {},
57
70
  "parseApk": {},
71
+ "parseAab": {},
72
+ "extractApk": {
73
+ "options": {
74
+ "output": {
75
+ "hasValue": true
76
+ },
77
+ "includeAllSplits": {
78
+ "default": false
79
+ },
80
+ "splits": {
81
+ "hasValue": true
82
+ }
83
+ }
84
+ },
58
85
  "packages": {
59
86
  "options": {
60
87
  "platform": {
@@ -172,6 +199,19 @@
172
199
  }
173
200
  }
174
201
  },
202
+ "deleteVersion": {
203
+ "options": {
204
+ "platform": {
205
+ "hasValue": true
206
+ },
207
+ "versionId": {
208
+ "hasValue": true
209
+ },
210
+ "appId": {
211
+ "hasValue": true
212
+ }
213
+ }
214
+ },
175
215
  "build": {
176
216
  "description": "Bundle javascript and copy assets."
177
217
  },
package/lib/api.js CHANGED
@@ -51,10 +51,10 @@ const _formdata = /*#__PURE__*/ _interop_require_default(require("form-data"));
51
51
  const _nodefetch = /*#__PURE__*/ _interop_require_default(require("node-fetch"));
52
52
  const _progress = /*#__PURE__*/ _interop_require_default(require("progress"));
53
53
  const _tcpping = /*#__PURE__*/ _interop_require_default(require("tcp-ping"));
54
+ const _httphelper = require("./utils/http-helper");
54
55
  const _packagejson = /*#__PURE__*/ _interop_require_default(require("../package.json"));
55
56
  const _constants = require("./utils/constants");
56
57
  const _i18n = require("./utils/i18n");
57
- const _httphelper = require("./utils/http-helper");
58
58
  function _interop_require_default(obj) {
59
59
  return obj && obj.__esModule ? obj : {
60
60
  default: obj
package/lib/locales/en.js CHANGED
@@ -11,6 +11,15 @@ Object.defineProperty(exports, "default", {
11
11
  const _default = {
12
12
  addedToGitignore: 'Added {{line}} to .gitignore',
13
13
  androidCrunchPngsWarning: 'The crunchPngs option of android seems not disabled (Please ignore this warning if already disabled), which may cause abnormal consumption of mobile network traffic. Please refer to https://cresc.dev/docs/getting-started#disable-crunchpngs-on-android \n',
14
+ aabOpenApksFailed: 'Failed to open generated .apks file',
15
+ aabReadUniversalApkFailed: 'Failed to read universal.apk',
16
+ aabUniversalApkNotFound: 'universal.apk not found in generated .apks',
17
+ aabBundletoolDownloadHint: 'bundletool not found. Downloading node-bundletool via npx (first run may take a while).',
18
+ aabManifestNotFound: "AndroidManifest.xml can't be found in AAB base/manifest/",
19
+ aabParseResourcesWarning: '[Warning] Failed to parse resources.arsc: {{error}}',
20
+ aabParseFailed: 'Failed to parse AAB: {{error}}',
21
+ aabParseManifestError: 'Parse AndroidManifest.xml error: {{error}}',
22
+ aabParseResourcesError: 'Parser resources.arsc error: {{error}}',
14
23
  appId: 'App ID',
15
24
  appIdMismatchApk: 'App ID mismatch! Current APK: {{appIdInPkg}}, current update.json: {{appId}}',
16
25
  appIdMismatchApp: 'App ID mismatch! Current APP: {{appIdInPkg}}, current update.json: {{appId}}',
@@ -100,10 +109,13 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
100
109
  uploadingSourcemap: 'Uploading sourcemap',
101
110
  usageDiff: 'Usage: cresc {{command}} <origin> <next>',
102
111
  usageParseApk: 'Usage: cresc parseApk <apk file>',
112
+ usageParseAab: 'Usage: cresc parseAab <aab file>',
113
+ usageExtractApk: 'Usage: cresc extractApk <aab file> [--output <apk file>] [--includeAllSplits] [--splits <split names>]',
103
114
  usageParseApp: 'Usage: cresc parseApp <app file>',
104
115
  usageParseIpa: 'Usage: cresc parseIpa <ipa file>',
105
116
  usageUnderDevelopment: 'Usage is under development now.',
106
117
  usageUploadApk: 'Usage: cresc uploadApk <apk file>',
118
+ usageUploadAab: 'Usage: cresc uploadAab <aab file> [--includeAllSplits] [--splits <split names>]',
107
119
  usageUploadApp: 'Usage: cresc uploadApp <app file>',
108
120
  usageUploadIpa: 'Usage: cresc uploadIpa <ipa file>',
109
121
  versionBind: 'Bound hot update {{version}} to native version {{nativeVersion}} (id: {{id}})',
@@ -119,8 +131,11 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
119
131
  deletePackageSuccess: 'Native package {{packageId}} deleted successfully',
120
132
  deletePackageError: 'Failed to delete native package {{packageId}}: {{error}}',
121
133
  usageDeletePackage: 'Usage: cresc deletePackage [packageId] --appId [appId]',
134
+ deleteVersionSuccess: 'Version {{versionId}} deleted successfully',
135
+ deleteVersionError: 'Failed to delete version {{versionId}}: {{error}}',
122
136
  bundleFileNotFound: 'Bundle file not found! Please use default bundle file name and path.',
123
137
  diffPackageGenerated: '{{- output}} generated.',
124
138
  nodeBsdiffRequired: 'This function needs "node-bsdiff". Please run "{{scriptName}} install node-bsdiff" to install',
125
- nodeHdiffpatchRequired: 'This function needs "node-hdiffpatch". Please run "{{scriptName}} install node-hdiffpatch" to install'
139
+ nodeHdiffpatchRequired: 'This function needs "node-hdiffpatch". Please run "{{scriptName}} install node-hdiffpatch" to install',
140
+ apkExtracted: 'APK extracted to {{output}}'
126
141
  };
package/lib/locales/zh.js CHANGED
@@ -11,6 +11,15 @@ Object.defineProperty(exports, "default", {
11
11
  const _default = {
12
12
  addedToGitignore: '已将 {{line}} 添加到 .gitignore',
13
13
  androidCrunchPngsWarning: 'android 的 crunchPngs 选项似乎尚未禁用(如已禁用则请忽略此提示),这可能导致热更包体积异常增大,具体请参考 https://pushy.reactnative.cn/docs/getting-started.html#%E7%A6%81%E7%94%A8-android-%E7%9A%84-crunch-%E4%BC%98%E5%8C%96 \n',
14
+ aabOpenApksFailed: '无法打开生成的 .apks 文件',
15
+ aabReadUniversalApkFailed: '无法读取 universal.apk',
16
+ aabUniversalApkNotFound: '在生成的 .apks 中未找到 universal.apk',
17
+ aabBundletoolDownloadHint: '未找到 bundletool,正在通过 npx 下载 node-bundletool(首次下载可能需要一些时间)。',
18
+ aabManifestNotFound: '在 AAB 的 base/manifest/ 中找不到 AndroidManifest.xml',
19
+ aabParseResourcesWarning: '[警告] 解析 resources.arsc 失败:{{error}}',
20
+ aabParseFailed: '解析 AAB 失败:{{error}}',
21
+ aabParseManifestError: '解析 AndroidManifest.xml 出错:{{error}}',
22
+ aabParseResourcesError: '解析 resources.arsc 出错:{{error}}',
14
23
  appId: '应用 id',
15
24
  appIdMismatchApk: 'appId不匹配!当前apk: {{appIdInPkg}}, 当前update.json: {{appId}}',
16
25
  appIdMismatchApp: 'appId不匹配!当前app: {{appIdInPkg}}, 当前update.json: {{appId}}',
@@ -100,9 +109,12 @@ const _default = {
100
109
  uploadingSourcemap: '正在上传 sourcemap',
101
110
  usageDiff: '用法:pushy {{command}} <origin> <next>',
102
111
  usageParseApk: '使用方法: pushy parseApk apk后缀文件',
112
+ usageParseAab: '使用方法: pushy parseAab aab后缀文件',
113
+ usageExtractApk: '使用方法: pushy extractApk aab后缀文件 [--output apk文件] [--includeAllSplits] [--splits 分包名列表]',
103
114
  usageParseApp: '使用方法: pushy parseApp app后缀文件',
104
115
  usageParseIpa: '使用方法: pushy parseIpa ipa后缀文件',
105
116
  usageUploadApk: '使用方法: pushy uploadApk apk后缀文件',
117
+ usageUploadAab: '使用方法: pushy uploadAab aab后缀文件 [--includeAllSplits] [--splits 分包名列表]',
106
118
  usageUploadApp: '使用方法: pushy uploadApp app后缀文件',
107
119
  usageUploadIpa: '使用方法: pushy uploadIpa ipa后缀文件',
108
120
  versionBind: '已将热更包 {{version}} 绑定到原生版本 {{nativeVersion}} (id: {{id}})',
@@ -118,8 +130,11 @@ const _default = {
118
130
  deletePackageSuccess: '原生包 {{packageId}} 删除成功',
119
131
  deletePackageError: '删除原生包 {{packageId}} 失败: {{error}}',
120
132
  usageDeletePackage: '使用方法: pushy deletePackage [packageId] --appId [appId]',
133
+ deleteVersionSuccess: '热更包 {{versionId}} 删除成功',
134
+ deleteVersionError: '删除热更包 {{versionId}} 失败: {{error}}',
121
135
  bundleFileNotFound: '未找到 bundle 文件!请使用默认的 bundle 文件名和路径。',
122
136
  diffPackageGenerated: '{{- output}} 已生成。',
123
137
  nodeBsdiffRequired: '此功能需要 "node-bsdiff"。请运行 "{{scriptName}} install node-bsdiff" 来安装',
124
- nodeHdiffpatchRequired: '此功能需要 "node-hdiffpatch"。请运行 "{{scriptName}} install node-hdiffpatch" 来安装'
138
+ nodeHdiffpatchRequired: '此功能需要 "node-hdiffpatch"。请运行 "{{scriptName}} install node-hdiffpatch" 来安装',
139
+ apkExtracted: 'APK 已提取到 {{output}}'
125
140
  };
package/lib/package.js CHANGED
@@ -19,20 +19,24 @@ _export(exports, {
19
19
  return packageCommands;
20
20
  }
21
21
  });
22
+ const _os = /*#__PURE__*/ _interop_require_default(require("os"));
23
+ const _path = /*#__PURE__*/ _interop_require_default(require("path"));
24
+ const _fsextra = /*#__PURE__*/ _interop_require_default(require("fs-extra"));
25
+ const _ttytable = /*#__PURE__*/ _interop_require_default(require("tty-table"));
22
26
  const _api = require("./api");
23
- const _utils = require("./utils");
24
- const _i18n = require("./utils/i18n");
25
27
  const _app = require("./app");
26
- const _ttytable = /*#__PURE__*/ _interop_require_default(require("tty-table"));
28
+ const _utils = require("./utils");
29
+ const _aab = require("./utils/app-info-parser/aab");
27
30
  const _depversions = require("./utils/dep-versions");
28
31
  const _git = require("./utils/git");
32
+ const _i18n = require("./utils/i18n");
29
33
  function _interop_require_default(obj) {
30
34
  return obj && obj.__esModule ? obj : {
31
35
  default: obj
32
36
  };
33
37
  }
34
38
  async function listPackage(appId) {
35
- const allPkgs = await (0, _api.getAllPackages)(appId);
39
+ const allPkgs = await (0, _api.getAllPackages)(appId) || [];
36
40
  const header = [
37
41
  {
38
42
  value: (0, _i18n.t)('nativePackageId')
@@ -75,7 +79,7 @@ async function choosePackage(appId) {
75
79
  const list = await listPackage(appId);
76
80
  while(true){
77
81
  const id = await (0, _utils.question)((0, _i18n.t)('enterNativePackageId'));
78
- const app = list.find((v)=>v.id.toString() === id);
82
+ const app = list == null ? void 0 : list.find((v)=>v.id.toString() === id);
79
83
  if (app) {
80
84
  return app;
81
85
  }
@@ -170,6 +174,32 @@ const packageCommands = {
170
174
  buildTime
171
175
  }));
172
176
  },
177
+ uploadAab: async ({ args, options })=>{
178
+ const source = args[0];
179
+ if (!source || !source.endsWith('.aab')) {
180
+ throw new Error((0, _i18n.t)('usageUploadAab'));
181
+ }
182
+ const output = _path.default.join(_os.default.tmpdir(), `${_path.default.basename(source, _path.default.extname(source))}-${Date.now()}.apk`);
183
+ const includeAllSplits = options.includeAllSplits === true || options.includeAllSplits === 'true';
184
+ const splits = options.splits ? String(options.splits).split(',').map((item)=>item.trim()).filter(Boolean) : null;
185
+ const parser = new _aab.AabParser(source);
186
+ try {
187
+ await parser.extractApk(output, {
188
+ includeAllSplits,
189
+ splits
190
+ });
191
+ await packageCommands.uploadApk({
192
+ args: [
193
+ output
194
+ ],
195
+ options
196
+ });
197
+ } finally{
198
+ if (await _fsextra.default.pathExists(output)) {
199
+ await _fsextra.default.remove(output);
200
+ }
201
+ }
202
+ },
173
203
  uploadApp: async ({ args, options })=>{
174
204
  const fn = args[0];
175
205
  if (!fn || !fn.endsWith('.app')) {
@@ -235,6 +265,30 @@ const packageCommands = {
235
265
  }
236
266
  console.log(await (0, _utils.getApkInfo)(fn));
237
267
  },
268
+ parseAab: async ({ args })=>{
269
+ const fn = args[0];
270
+ if (!fn || !fn.endsWith('.aab')) {
271
+ throw new Error((0, _i18n.t)('usageParseAab'));
272
+ }
273
+ console.log(await (0, _utils.getAabInfo)(fn));
274
+ },
275
+ extractApk: async ({ args, options })=>{
276
+ const source = args[0];
277
+ if (!source || !source.endsWith('.aab')) {
278
+ throw new Error((0, _i18n.t)('usageExtractApk'));
279
+ }
280
+ const output = options.output || _path.default.join(_path.default.dirname(source), `${_path.default.basename(source, _path.default.extname(source))}.apk`);
281
+ const includeAllSplits = options.includeAllSplits === true || options.includeAllSplits === 'true';
282
+ const splits = options.splits ? String(options.splits).split(',').map((item)=>item.trim()).filter(Boolean) : null;
283
+ const parser = new _aab.AabParser(source);
284
+ await parser.extractApk(output, {
285
+ includeAllSplits,
286
+ splits
287
+ });
288
+ console.log((0, _i18n.t)('apkExtracted', {
289
+ output
290
+ }));
291
+ },
238
292
  packages: async ({ options })=>{
239
293
  const platform = await (0, _app.getPlatform)(options.platform);
240
294
  const { appId } = await (0, _app.getSelectedApp)(platform);
@@ -243,7 +297,7 @@ const packageCommands = {
243
297
  deletePackage: async ({ args, options })=>{
244
298
  let { appId, packageId, packageVersion } = options;
245
299
  if (!appId) {
246
- const platform = await (0, _app.getPlatform)();
300
+ const platform = await (0, _app.getPlatform)(options.platform);
247
301
  appId = (await (0, _app.getSelectedApp)(platform)).appId;
248
302
  }
249
303
  // If no packageId provided as argument, let user choose from list
package/lib/provider.js CHANGED
@@ -147,6 +147,9 @@ class CLIProviderImpl {
147
147
  case 'apk':
148
148
  await packageCommands.uploadApk(context);
149
149
  break;
150
+ case 'aab':
151
+ await packageCommands.uploadAab(context);
152
+ break;
150
153
  case 'app':
151
154
  await packageCommands.uploadApp(context);
152
155
  break;
@@ -0,0 +1,230 @@
1
+ "use strict";
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;
75
+ }
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'));
96
+ }
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;
128
+ zipfile.openReadStream(entry, (err, readStream)=>{
129
+ if (err || !readStream) {
130
+ reject(err || new Error((0, _i18n.t)('aabReadUniversalApkFailed')));
131
+ return;
132
+ }
133
+ const writeStream = _fsextra.default.createWriteStream(outputPath);
134
+ readStream.pipe(writeStream);
135
+ writeStream.on('close', ()=>{
136
+ zipfile.close();
137
+ resolve();
138
+ });
139
+ writeStream.on('error', reject);
140
+ });
141
+ } else {
142
+ zipfile.readEntry();
143
+ }
144
+ });
145
+ zipfile.on('end', ()=>{
146
+ if (!found) reject(new Error((0, _i18n.t)('aabUniversalApkNotFound')));
147
+ });
148
+ zipfile.on('error', reject);
149
+ });
150
+ });
151
+ } finally{
152
+ // Cleanup
153
+ if (await _fsextra.default.pathExists(tempApksPath)) {
154
+ await _fsextra.default.remove(tempApksPath);
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * 解析 AAB 文件信息(类似 APK parser 的 parse 方法)
160
+ * 注意:AAB 中的 AndroidManifest.xml 在 base/manifest/AndroidManifest.xml
161
+ */ async parse() {
162
+ const manifestPath = 'base/manifest/AndroidManifest.xml';
163
+ const ResourceName = /^base\/resources\.arsc$/;
164
+ try {
165
+ const manifestBuffer = await this.getEntry(new RegExp(`^${escapeRegExp(manifestPath)}$`));
166
+ if (!manifestBuffer) {
167
+ throw new Error((0, _i18n.t)('aabManifestNotFound'));
168
+ }
169
+ let apkInfo = this._parseManifest(manifestBuffer);
170
+ try {
171
+ const resourceBuffer = await this.getEntry(ResourceName);
172
+ if (resourceBuffer) {
173
+ const resourceMap = this._parseResourceMap(resourceBuffer);
174
+ apkInfo = (0, _utils.mapInfoResource)(apkInfo, resourceMap);
175
+ }
176
+ } catch (e) {
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
+ }));
181
+ }
182
+ return apkInfo;
183
+ } catch (error) {
184
+ var _error_message;
185
+ throw new Error((0, _i18n.t)('aabParseFailed', {
186
+ error: (_error_message = error.message) != null ? _error_message : error
187
+ }));
188
+ }
189
+ }
190
+ /**
191
+ * Parse manifest
192
+ * @param {Buffer} buffer // manifest file's buffer
193
+ */ _parseManifest(buffer) {
194
+ try {
195
+ const parser = new _manifest.ManifestParser(buffer, {
196
+ ignore: [
197
+ 'application.activity',
198
+ 'application.service',
199
+ 'application.receiver',
200
+ 'application.provider',
201
+ 'permission-group'
202
+ ]
203
+ });
204
+ return parser.parse();
205
+ } catch (e) {
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
+ }));
210
+ }
211
+ }
212
+ /**
213
+ * Parse resourceMap
214
+ * @param {Buffer} buffer // resourceMap file's buffer
215
+ */ _parseResourceMap(buffer) {
216
+ try {
217
+ return new _resourcefinder.ResourceFinder().processResourceTable(buffer);
218
+ } catch (e) {
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
+ }));
223
+ }
224
+ }
225
+ constructor(file){
226
+ super(file);
227
+ this.file = file;
228
+ }
229
+ }
230
+ const escapeRegExp = (value)=>value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -1,34 +1,42 @@
1
1
  "use strict";
2
- const Zip = require('./zip');
3
- const { mapInfoResource, findApkIconPath, getBase64FromBuffer } = require('./utils');
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
- const ManifestXmlParser = require('./xml-parser/manifest');
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
- if (!buffers[ManifestName]) {
24
+ const manifestBuffer = buffers[ManifestName];
25
+ if (!manifestBuffer) {
16
26
  throw new Error("AndroidManifest.xml can't be found.");
17
27
  }
18
- let apkInfo = this._parseManifest(buffers[ManifestName]);
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
- // update apkInfo with resourceMap
26
- apkInfo = mapInfoResource(apkInfo, resourceMap);
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 ManifestXmlParser(buffer, {
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('Parse AndroidManifest.xml error: ', e);
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('Parser resources.arsc error: ' + e);
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
- const Zip = require('./zip');
3
- class AppParser extends Zip {
4
- /**
5
- * parser for parsing .apk file
6
- * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
7
- */ constructor(file){
8
- super(file);
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;