@expo/repack-app 0.1.5 → 0.1.6

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.
@@ -73,7 +73,7 @@ async function getApktoolPathAsync() {
73
73
  }
74
74
  return cachedApktoolPath;
75
75
  }
76
- async function addApktoolResourceAsync(decodedApkRoot, assetSet) {
76
+ async function addApktoolResourceAsync(decodedApkRoot, assetMap) {
77
77
  const apktoolPublicXmlPath = node_path_1.default.join(decodedApkRoot, 'res/values/public.xml');
78
78
  // [0] Retrieve the current max resource ID and the existing drawable names
79
79
  const contents = await promises_1.default.readFile(apktoolPublicXmlPath, 'utf8');
@@ -104,8 +104,12 @@ async function addApktoolResourceAsync(decodedApkRoot, assetSet) {
104
104
  }
105
105
  const drawableResIdBoundary = (maxDrawableResId & 0xffff0000) + 0x10000;
106
106
  const rawResIdBoundary = (maxRawResId & 0xffff0000) + 0x10000;
107
- const newAssetSet = new Set([...assetSet].filter(({ name }) => !existingNameSet.has(name)));
108
- for (const asset of newAssetSet) {
107
+ const newAssetNameSet = new Set([...assetMap.keys()].filter((name) => !existingNameSet.has(name)));
108
+ for (const assetName of newAssetNameSet) {
109
+ const asset = assetMap.get(assetName);
110
+ if (asset == null) {
111
+ continue;
112
+ }
109
113
  if (asset.type === 'drawable') {
110
114
  maxDrawableResId += 1;
111
115
  (0, node_assert_1.default)(maxDrawableResId < drawableResIdBoundary, 'Drawable resource ID boundary exceeded.');
@@ -143,7 +147,7 @@ function getAssetType(name) {
143
147
  * @param level The current depth level (starts at 0).
144
148
  * @param maxLevel The maximum depth level to process.
145
149
  */
146
- async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = undefined, assetSet = new Set()) {
150
+ async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = undefined, assetMap = new Map()) {
147
151
  await promises_1.default.mkdir(dest, { recursive: true });
148
152
  const entries = await promises_1.default.readdir(src, { withFileTypes: true });
149
153
  await Promise.all(entries.map(async (entry) => {
@@ -152,7 +156,7 @@ async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = u
152
156
  if (entry.isDirectory()) {
153
157
  const _assetType = assetType ?? getAssetType(entry.name);
154
158
  if (level < maxLevel - 1) {
155
- await copyAssetsAsync(srcPath, destPath, level + 1, maxLevel, _assetType, assetSet);
159
+ await copyAssetsAsync(srcPath, destPath, level + 1, maxLevel, _assetType, assetMap);
156
160
  }
157
161
  else {
158
162
  // When the maximum level is reached, copy the directory without further recursion
@@ -163,9 +167,9 @@ async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = u
163
167
  await promises_1.default.copyFile(srcPath, destPath);
164
168
  const { name } = node_path_1.default.parse(entry.name);
165
169
  (0, node_assert_1.default)(assetType, 'Asset type must be defined.');
166
- assetSet.add({ name, type: assetType });
170
+ assetMap.set(name, { name, type: assetType });
167
171
  }
168
172
  }));
169
- return assetSet;
173
+ return assetMap;
170
174
  }
171
175
  //#endregion Internals
@@ -105,6 +105,7 @@ async function searchDexClassesAsync(unzipApkRoot, grepAppIdPattern, options) {
105
105
  const { stdout } = await spawnAsync(dexdumpPath, ['classes*.dex', '|', 'grep', grepPattern], {
106
106
  cwd: unzipApkRoot,
107
107
  shell: true,
108
+ stdio: 'pipe',
108
109
  });
109
110
  const classes = stdout
110
111
  .split('\n')
@@ -26,7 +26,8 @@ async function repackAppAndroidAsync(_options) {
26
26
  });
27
27
  const decodedApkRoot = await (0, apktool_1.decodeApkAsync)(node_path_1.default.resolve(options.sourceAppPath), options);
28
28
  (0, node_assert_1.default)(exp.android?.package, 'Expected app ID (`android.package`) to be defined in app.json');
29
- const updatesRuntimeVersion = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot)
29
+ const useExpoUpdates = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot);
30
+ const updatesRuntimeVersion = useExpoUpdates
30
31
  ? await (0, expo_1.resolveRuntimeVersionAsync)(options, exp)
31
32
  : null;
32
33
  logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
@@ -41,6 +42,13 @@ async function repackAppAndroidAsync(_options) {
41
42
  bundleOutputPath,
42
43
  });
43
44
  logger.timeEnd(`Adding bundled resources`);
45
+ if (useExpoUpdates) {
46
+ logger.time(`Generating 'app.manifest' for expo-updates`);
47
+ const updateManifestFile = await (0, expo_1.generateUpdatesEmbeddedManifestAsync)(options);
48
+ const updateDirectory = node_path_1.default.join(decodedApkRoot, 'assets');
49
+ await promises_1.default.copyFile(updateManifestFile, node_path_1.default.join(updateDirectory, node_path_1.default.basename(updateManifestFile)));
50
+ logger.timeEnd(`Generating 'app.manifest' for expo-updates`);
51
+ }
44
52
  }
45
53
  logger.time(`Updating Androidmanifest.xml`);
46
54
  const androidManiestFilePath = node_path_1.default.join(decodedApkRoot, 'AndroidManifest.xml');
package/build/expo.d.ts CHANGED
@@ -4,6 +4,10 @@ import type { NormalizedOptions } from './types';
4
4
  * Generate the app.config file for the Android app.
5
5
  */
6
6
  export declare function generateAppConfigAsync(options: NormalizedOptions, config: ExpoConfig): Promise<string>;
7
+ /**
8
+ * Generate the embedded `app.manifest` for expo-updates
9
+ */
10
+ export declare function generateUpdatesEmbeddedManifestAsync(options: NormalizedOptions): Promise<string>;
7
11
  /**
8
12
  * Returns whether expo-updates is installed in the project.
9
13
  */
package/build/expo.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateAppConfigAsync = generateAppConfigAsync;
7
+ exports.generateUpdatesEmbeddedManifestAsync = generateUpdatesEmbeddedManifestAsync;
7
8
  exports.isExpoUpdatesInstalled = isExpoUpdatesInstalled;
8
9
  exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
9
10
  exports.getExpoConfig = getExpoConfig;
@@ -21,6 +22,21 @@ async function generateAppConfigAsync(options, config) {
21
22
  await promises_1.default.writeFile(appConfigPath, JSON.stringify(config));
22
23
  return appConfigPath;
23
24
  }
25
+ /**
26
+ * Generate the embedded `app.manifest` for expo-updates
27
+ */
28
+ async function generateUpdatesEmbeddedManifestAsync(options) {
29
+ const { platform, projectRoot, spawnAsync, workingDirectory } = options;
30
+ // [0] Resolve entry point
31
+ const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot, stdio: 'pipe' });
32
+ // [1] Generate the `app.manifest` file
33
+ const binPath = resolve_from_1.default.silent(options.projectRoot, 'expo-updates/utils/build/createUpdatesResources.js');
34
+ (0, node_assert_1.default)(binPath, 'Failed to resolve `createUpdatesResources.js` within expo-updates package');
35
+ await spawnAsync('node', [binPath, platform, projectRoot, workingDirectory, 'all', entryFile.trim()], {
36
+ cwd: projectRoot,
37
+ });
38
+ return node_path_1.default.join(workingDirectory, 'app.manifest');
39
+ }
24
40
  /**
25
41
  * Returns whether expo-updates is installed in the project.
26
42
  */
@@ -40,6 +56,7 @@ async function resolveRuntimeVersionAsync(options, config) {
40
56
  (0, resolve_from_1.default)(projectRoot, 'expo-updates/bin/cli.js');
41
57
  const { stdout } = await spawnAsync(cli, ['runtimeversion:resolve', '--platform', 'android', '--workflow', 'managed'], {
42
58
  cwd: projectRoot,
59
+ stdio: 'pipe',
43
60
  });
44
61
  const runtimeVersion = JSON.parse(stdout).runtimeVersion;
45
62
  return runtimeVersion ?? config.version ?? '1.0.0';
@@ -72,7 +89,7 @@ async function generateBundleAssetsAsync(expoConfig, options) {
72
89
  const bundleAssetRoot = node_path_1.default.resolve(workingDirectory, 'bundles');
73
90
  await promises_1.default.mkdir(bundleAssetRoot, { recursive: true });
74
91
  // [0] Resolve entry point
75
- const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot });
92
+ const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot, stdio: 'pipe' });
76
93
  // [1] Execute export:embed
77
94
  const isEnableHermes = isEnableHermesManaged(expoConfig, platform);
78
95
  const bundleFileName = platform === 'android' ? 'index.android.bundle' : 'main.bundle';
@@ -25,10 +25,13 @@ async function repackAppIosAsync(_options) {
25
25
  skipSDKVersionRequirement: true,
26
26
  });
27
27
  (0, node_assert_1.default)(exp.ios?.bundleIdentifier, 'Expected app ID (`ios.bundleIdentifier`) to be defined in app.json');
28
- const updatesRuntimeVersion = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot)
28
+ const useExpoUpdates = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot);
29
+ const updatesRuntimeVersion = useExpoUpdates
29
30
  ? await (0, expo_1.resolveRuntimeVersionAsync)(options, exp)
30
31
  : null;
31
- logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
32
+ if (useExpoUpdates) {
33
+ logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
34
+ }
32
35
  logger.time(`Extracting artifact from ${options.sourceAppPath}`);
33
36
  let appWorkingDirectory = await (0, build_tools_1.extractIosArtifactAsync)(options);
34
37
  appWorkingDirectory = await (0, build_tools_1.updateFilesAsync)(exp, appWorkingDirectory);
@@ -46,6 +49,14 @@ async function repackAppIosAsync(_options) {
46
49
  bundleOutputPath,
47
50
  });
48
51
  logger.timeEnd(`Adding bundled resources`);
52
+ if (useExpoUpdates) {
53
+ logger.time(`Generating 'app.manifest' for expo-updates`);
54
+ const updateManifestFile = await (0, expo_1.generateUpdatesEmbeddedManifestAsync)(options);
55
+ const updateDirectory = node_path_1.default.join(appWorkingDirectory, 'EXUpdates.bundle');
56
+ (0, node_assert_1.default)(await (0, utils_1.directoryExistsAsync)(updateDirectory), 'Expected EXUpdates.bundle directory to exist');
57
+ await promises_1.default.copyFile(updateManifestFile, node_path_1.default.join(updateDirectory, node_path_1.default.basename(updateManifestFile)));
58
+ logger.timeEnd(`Generating 'app.manifest' for expo-updates`);
59
+ }
49
60
  }
50
61
  logger.time(`Updating Info.plist`);
51
62
  await (0, resources_1.updateInfoPlistAsync)({ config: exp, infoPlistPath, originalAppId, options });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/repack-app",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Repacking tool for Expo apps",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",