@expo/repack-app 0.0.3 → 0.0.5

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.
@@ -1,4 +1,5 @@
1
+ "use strict";
1
2
  /**
2
3
  * Types mapped from the Resoruces.proto file.
3
4
  */
4
- export {};
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,4 +1,4 @@
1
- import type { AndroidSigningOptions, NormalizedOptions } from '../types.js';
1
+ import type { AndroidSigningOptions, NormalizedOptions } from '../types';
2
2
  export interface AndroidTools {
3
3
  aapt2Path: string;
4
4
  apksignerPath: string;
@@ -1,62 +1,65 @@
1
- import { glob } from 'glob';
2
- import assert from 'node:assert';
3
- import fs from 'node:fs/promises';
4
- import path from 'node:path';
5
- import { spawnAsync } from '../cjs-wrapper.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.findLatestBuildToolsDirAsync = exports.createResignedApkAsync = exports.createBinaryBasedResourcesAsync = exports.createProtoBasedResourcesAsync = exports.getAndroidBuildToolsAsync = void 0;
7
+ const steps_1 = require("@expo/steps");
8
+ const glob_1 = require("glob");
9
+ const node_assert_1 = __importDefault(require("node:assert"));
10
+ const promises_1 = __importDefault(require("node:fs/promises"));
11
+ const node_path_1 = __importDefault(require("node:path"));
6
12
  let cachedAndroidTools = null;
7
13
  /**
8
14
  * Get the paths to the Android build-tools.
9
15
  */
10
- export async function getAndroidBuildToolsAsync(options) {
16
+ async function getAndroidBuildToolsAsync(options) {
11
17
  if (cachedAndroidTools == null) {
12
18
  const androidBuildToolsDir = options?.androidBuildToolsDir ?? (await findLatestBuildToolsDirAsync());
13
- assert(androidBuildToolsDir != null, 'Unable to find the Android build-tools directory.');
19
+ (0, node_assert_1.default)(androidBuildToolsDir != null, 'Unable to find the Android build-tools directory.');
14
20
  cachedAndroidTools = {
15
- aapt2Path: path.join(androidBuildToolsDir, 'aapt2'),
16
- apksignerPath: path.join(androidBuildToolsDir, 'apksigner'),
17
- zipalignPath: path.join(androidBuildToolsDir, 'zipalign'),
21
+ aapt2Path: node_path_1.default.join(androidBuildToolsDir, 'aapt2'),
22
+ apksignerPath: node_path_1.default.join(androidBuildToolsDir, 'apksigner'),
23
+ zipalignPath: node_path_1.default.join(androidBuildToolsDir, 'zipalign'),
18
24
  };
19
25
  }
20
26
  return cachedAndroidTools;
21
27
  }
28
+ exports.getAndroidBuildToolsAsync = getAndroidBuildToolsAsync;
22
29
  /**
23
30
  * Create an APK with proto-based resources.
24
31
  */
25
- export async function createProtoBasedResourcesAsync(options) {
32
+ async function createProtoBasedResourcesAsync(options) {
26
33
  const { sourceAppPath, workingDirectory } = options;
27
34
  const { aapt2Path } = await getAndroidBuildToolsAsync(options);
28
- const protoApkPath = path.join(workingDirectory, 'proto.apk');
29
- await spawnAsync(aapt2Path, ['convert', '-o', protoApkPath, '--output-format', 'proto', '--keep-raw-values', sourceAppPath], {
30
- stdio: options.verbose ? 'inherit' : 'ignore',
31
- });
32
- await spawnAsync('unzip', [protoApkPath, '-d', workingDirectory], {
33
- stdio: options.verbose ? 'inherit' : 'ignore',
34
- });
35
- const removeFiles = await glob('**/*', {
35
+ const protoApkPath = node_path_1.default.join(workingDirectory, 'proto.apk');
36
+ await (0, steps_1.spawnAsync)(aapt2Path, ['convert', '-o', protoApkPath, '--output-format', 'proto', '--keep-raw-values', sourceAppPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
37
+ await (0, steps_1.spawnAsync)('unzip', [protoApkPath, '-d', workingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
38
+ const removeFiles = await (0, glob_1.glob)('**/*', {
36
39
  cwd: workingDirectory,
37
40
  maxDepth: 1,
38
41
  ignore: ['resources.pb', 'AndroidManifest.xml', 'res/**'],
39
42
  absolute: true,
40
43
  });
41
- await Promise.all(removeFiles.map((file) => fs.rm(file, { recursive: true })));
44
+ await Promise.all(removeFiles.map((file) => promises_1.default.rm(file, { recursive: true })));
42
45
  return {
43
- androidManiestFilePath: path.join(workingDirectory, 'AndroidManifest.xml'),
44
- resourcesPbFilePath: path.join(workingDirectory, 'resources.pb'),
46
+ androidManiestFilePath: node_path_1.default.join(workingDirectory, 'AndroidManifest.xml'),
47
+ resourcesPbFilePath: node_path_1.default.join(workingDirectory, 'resources.pb'),
45
48
  };
46
49
  }
50
+ exports.createProtoBasedResourcesAsync = createProtoBasedResourcesAsync;
47
51
  /**
48
52
  * Create an APK with binary-based resources.
49
53
  */
50
- export async function createBinaryBasedResourcesAsync(options) {
54
+ async function createBinaryBasedResourcesAsync(options) {
51
55
  const { workingDirectory } = options;
52
56
  const { aapt2Path } = await getAndroidBuildToolsAsync(options);
53
- const protoUpdatedApkPath = path.join(workingDirectory, 'proto-updated.apk');
54
- const binaryApkPath = path.join(workingDirectory, 'binary.apk');
55
- await spawnAsync('zip', ['-r', '-0', protoUpdatedApkPath, 'resources.pb', 'AndroidManifest.xml', 'res'], {
56
- cwd: workingDirectory,
57
- stdio: options.verbose ? 'inherit' : 'ignore',
58
- });
59
- await spawnAsync(aapt2Path, [
57
+ const protoUpdatedApkPath = node_path_1.default.join(workingDirectory, 'proto-updated.apk');
58
+ const binaryApkPath = node_path_1.default.join(workingDirectory, 'binary.apk');
59
+ await (0, steps_1.spawnAsync)('zip', ['-r', '-0', protoUpdatedApkPath, 'resources.pb', 'AndroidManifest.xml', 'res'], options.verbose
60
+ ? { logger: options.logger, stdio: 'pipe', cwd: workingDirectory }
61
+ : { cwd: workingDirectory });
62
+ await (0, steps_1.spawnAsync)(aapt2Path, [
60
63
  'convert',
61
64
  '-o',
62
65
  binaryApkPath,
@@ -64,42 +67,33 @@ export async function createBinaryBasedResourcesAsync(options) {
64
67
  'binary',
65
68
  '--keep-raw-values',
66
69
  protoUpdatedApkPath,
67
- ], {
68
- stdio: options.verbose ? 'inherit' : 'ignore',
69
- });
70
+ ], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
70
71
  return binaryApkPath;
71
72
  }
73
+ exports.createBinaryBasedResourcesAsync = createBinaryBasedResourcesAsync;
72
74
  /**
73
75
  * Create a resigned & updated APK.
74
76
  * @param binaryApkPath The path to the binary-based resources APK returned from `createBinaryBasedResourcesAsync()`.
75
77
  * @param appConfigPath The path to the app.config file returned from `generateAppConfigAsync()`.
76
78
  * @returns
77
79
  */
78
- export async function createResignedApkAsync(binaryApkPath, appConfigPath, options, signingOptions) {
80
+ async function createResignedApkAsync(binaryApkPath, appConfigPath, options, signingOptions) {
79
81
  const { workingDirectory } = options;
80
82
  const { apksignerPath, zipalignPath } = await getAndroidBuildToolsAsync(options);
81
- const unzipWorkingDirectory = path.join(workingDirectory, 'unzip');
82
- const resignedApkPath = path.join(workingDirectory, 'resigned.apk');
83
- const resignedApkUnalignedPath = path.join(workingDirectory, 'resigned-unaligned.apk');
84
- await fs.mkdir(unzipWorkingDirectory, { recursive: true });
85
- await spawnAsync('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], {
86
- stdio: options.verbose ? 'inherit' : 'ignore',
87
- });
88
- await spawnAsync('unzip', ['-o', binaryApkPath, '-d', unzipWorkingDirectory], {
89
- stdio: options.verbose ? 'inherit' : 'ignore',
90
- });
91
- await fs.copyFile(appConfigPath, path.join(unzipWorkingDirectory, 'assets', 'app.config'));
92
- await spawnAsync('zip', ['-r', '-0', resignedApkUnalignedPath, 'lib', 'resources.arsc'], {
93
- cwd: unzipWorkingDirectory,
94
- stdio: options.verbose ? 'inherit' : 'ignore',
95
- });
96
- await spawnAsync('zip', ['-r', resignedApkUnalignedPath, '.', '-x', 'lib/*', '-x', 'resources.arsc'], {
97
- cwd: unzipWorkingDirectory,
98
- stdio: options.verbose ? 'inherit' : 'ignore',
99
- });
100
- await spawnAsync(zipalignPath, ['-v', '-p', '4', resignedApkUnalignedPath, resignedApkPath], {
101
- stdio: options.verbose ? 'inherit' : 'ignore',
102
- });
83
+ const unzipWorkingDirectory = node_path_1.default.join(workingDirectory, 'unzip');
84
+ const resignedApkPath = node_path_1.default.join(workingDirectory, 'resigned.apk');
85
+ const resignedApkUnalignedPath = node_path_1.default.join(workingDirectory, 'resigned-unaligned.apk');
86
+ await promises_1.default.mkdir(unzipWorkingDirectory, { recursive: true });
87
+ await (0, steps_1.spawnAsync)('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
88
+ await (0, steps_1.spawnAsync)('unzip', ['-o', binaryApkPath, '-d', unzipWorkingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
89
+ await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(unzipWorkingDirectory, 'assets', 'app.config'));
90
+ await (0, steps_1.spawnAsync)('zip', ['-r', '-0', resignedApkUnalignedPath, 'lib', 'resources.arsc'], options.verbose
91
+ ? { logger: options.logger, stdio: 'pipe', cwd: unzipWorkingDirectory }
92
+ : { cwd: unzipWorkingDirectory });
93
+ await (0, steps_1.spawnAsync)('zip', ['-r', resignedApkUnalignedPath, '.', '-x', 'lib/*', '-x', 'resources.arsc'], options.verbose
94
+ ? { logger: options.logger, stdio: 'pipe', cwd: unzipWorkingDirectory }
95
+ : { cwd: unzipWorkingDirectory });
96
+ await (0, steps_1.spawnAsync)(zipalignPath, ['-v', '-p', '4', resignedApkUnalignedPath, resignedApkPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
103
97
  const signerArgs = [
104
98
  'sign',
105
99
  '--ks',
@@ -114,20 +108,19 @@ export async function createResignedApkAsync(binaryApkPath, appConfigPath, optio
114
108
  signerArgs.push('--key-pass', signingOptions.keyPassword);
115
109
  }
116
110
  signerArgs.push(resignedApkPath);
117
- await spawnAsync(apksignerPath, signerArgs, {
118
- stdio: options.verbose ? 'inherit' : 'ignore',
119
- });
111
+ await (0, steps_1.spawnAsync)(apksignerPath, signerArgs, options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
120
112
  return resignedApkPath;
121
113
  }
114
+ exports.createResignedApkAsync = createResignedApkAsync;
122
115
  /**
123
116
  * Find the latest build-tools directory in the `ANDROID_SDK_ROOT` directory.
124
117
  */
125
- export async function findLatestBuildToolsDirAsync() {
118
+ async function findLatestBuildToolsDirAsync() {
126
119
  const androidSdkRoot = process.env['ANDROID_SDK_ROOT'];
127
- assert(androidSdkRoot != null, 'ANDROID_SDK_ROOT environment variable is not set');
128
- const buildToolsDir = path.join(androidSdkRoot, 'build-tools');
120
+ (0, node_assert_1.default)(androidSdkRoot != null, 'ANDROID_SDK_ROOT environment variable is not set');
121
+ const buildToolsDir = node_path_1.default.join(androidSdkRoot, 'build-tools');
129
122
  try {
130
- const entries = await fs.readdir(buildToolsDir, { withFileTypes: true });
123
+ const entries = await promises_1.default.readdir(buildToolsDir, { withFileTypes: true });
131
124
  const dirs = entries
132
125
  .filter((entry) => entry.isDirectory())
133
126
  .map((dir) => dir.name)
@@ -143,7 +136,7 @@ export async function findLatestBuildToolsDirAsync() {
143
136
  return versionB.length - versionA.length;
144
137
  });
145
138
  if (dirs.length > 0) {
146
- return path.join(buildToolsDir, dirs[0]);
139
+ return node_path_1.default.join(buildToolsDir, dirs[0]);
147
140
  }
148
141
  else {
149
142
  console.error('No build-tools directories found.');
@@ -155,3 +148,4 @@ export async function findLatestBuildToolsDirAsync() {
155
148
  return null;
156
149
  }
157
150
  }
151
+ exports.findLatestBuildToolsDirAsync = findLatestBuildToolsDirAsync;
@@ -1,4 +1,4 @@
1
- import type { Options } from '../types.js';
1
+ import type { Options } from '../types';
2
2
  /**
3
3
  * Repack an Android app.
4
4
  */
@@ -1,53 +1,61 @@
1
- import { getConfig } from '@expo/config';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import pico from 'picocolors';
5
- import { createBinaryBasedResourcesAsync, createProtoBasedResourcesAsync, createResignedApkAsync, } from './build-tools.js';
6
- import { updateAndroidManifestAsync, updateResourcesAsync } from './resources.js';
7
- import { generateAppConfigAsync, resolveRuntimeVersionAsync } from '../expo.js';
8
- import logger from '../log.js';
9
- import { normalizeOptionAsync } from '../utils.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.repackAppAndroidAsync = void 0;
7
+ const config_1 = require("@expo/config");
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const picocolors_1 = __importDefault(require("picocolors"));
11
+ const build_tools_1 = require("./build-tools");
12
+ const resources_1 = require("./resources");
13
+ const expo_1 = require("../expo");
14
+ const utils_1 = require("../utils");
10
15
  /**
11
16
  * Repack an Android app.
12
17
  */
13
- export async function repackAppAndroidAsync(_options) {
14
- const options = await normalizeOptionAsync(_options);
15
- const { workingDirectory } = options;
16
- await fs.mkdir(workingDirectory, { recursive: true });
17
- const { exp } = getConfig(options.projectRoot, {
18
+ async function repackAppAndroidAsync(_options) {
19
+ const options = await (0, utils_1.normalizeOptionAsync)(_options);
20
+ const { workingDirectory, logger } = options;
21
+ await promises_1.default.mkdir(workingDirectory, { recursive: true });
22
+ const { exp } = (0, config_1.getConfig)(options.projectRoot, {
18
23
  isPublicConfig: true,
19
24
  skipSDKVersionRequirement: true,
20
25
  });
21
- const updatesRuntimeVersion = await resolveRuntimeVersionAsync(options, exp);
22
- logger.log(pico.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
23
- logger.time(`Unzipping APK and creating proto based resources`);
24
- const { androidManiestFilePath, resourcesPbFilePath } = await createProtoBasedResourcesAsync(options);
25
- logger.timeEnd(`Unzipping APK and creating proto based resources`);
26
- logger.time(`Updating Androidmanifest.xml`);
27
- await updateAndroidManifestAsync(exp, androidManiestFilePath, options, updatesRuntimeVersion);
28
- logger.timeEnd(`Updating Androidmanifest.xml`);
29
- logger.time(`Updating resources.pb`);
30
- await updateResourcesAsync(exp, resourcesPbFilePath);
31
- logger.timeEnd(`Updating resources.pb`);
32
- logger.time(`Creating binary based resources`);
33
- const binaryApkPath = await createBinaryBasedResourcesAsync(options);
34
- logger.timeEnd(`Creating binary based resources`);
35
- logger.time(`Generating app.config`);
36
- const appConfigPath = await generateAppConfigAsync(options, exp);
37
- logger.timeEnd(`Generating app.config`);
38
- logger.time(`Creating updated apk`);
39
- const outputApk = await createResignedApkAsync(binaryApkPath, appConfigPath, options, {
26
+ const updatesRuntimeVersion = await (0, expo_1.resolveRuntimeVersionAsync)(options, exp);
27
+ logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
28
+ logger.info(`Unzipping APK and creating proto based resources`);
29
+ const { androidManiestFilePath, resourcesPbFilePath } = await (0, build_tools_1.createProtoBasedResourcesAsync)(options);
30
+ logger.info(`Finished unzipping APK and creating proto based resources ✅`);
31
+ logger.info(`Updating Androidmanifest.xml`);
32
+ await (0, resources_1.updateAndroidManifestAsync)(exp, androidManiestFilePath, options, updatesRuntimeVersion);
33
+ logger.info(`Finished updating Androidmanifest.xml ✅`);
34
+ logger.info(`Updating resources.pb`);
35
+ await (0, resources_1.updateResourcesAsync)(exp, resourcesPbFilePath);
36
+ logger.info(`Finished updating resources.pb ✅`);
37
+ logger.info(`Creating binary based resources`);
38
+ const binaryApkPath = await (0, build_tools_1.createBinaryBasedResourcesAsync)(options);
39
+ logger.info(`Finished creating binary based resources ✅`);
40
+ logger.info(`Generating app config`);
41
+ const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
42
+ logger.info(`Finished generating app config ✅`);
43
+ logger.info(`Creating updated apk`);
44
+ const outputApk = await (0, build_tools_1.createResignedApkAsync)(binaryApkPath, appConfigPath, options, {
40
45
  keyStorePath: options.androidSigningOptions?.keyStorePath ??
41
- path.resolve(import.meta.dirname, '../assets/debug.keystore'),
46
+ node_path_1.default.resolve(__dirname, '../assets/debug.keystore'),
42
47
  keyStorePassword: options.androidSigningOptions?.keyStorePassword ?? 'android',
43
48
  keyAlias: options.androidSigningOptions?.keyAlias,
44
49
  keyPassword: options.androidSigningOptions?.keyPassword,
45
50
  });
46
- logger.timeEnd(`Creating updated apk`);
47
- await fs.rename(outputApk, options.outputPath);
48
- try {
49
- await fs.rmdir(workingDirectory, { recursive: true });
51
+ logger.info(`Finished creating updated apk ✅`);
52
+ await promises_1.default.rename(outputApk, options.outputPath);
53
+ if (!options.skipWorkingDirCleanup) {
54
+ try {
55
+ await promises_1.default.rmdir(workingDirectory, { recursive: true });
56
+ }
57
+ catch { }
50
58
  }
51
- catch { }
52
59
  return options.outputPath;
53
60
  }
61
+ exports.repackAppAndroidAsync = repackAppAndroidAsync;
@@ -1,5 +1,5 @@
1
1
  import { type ExpoConfig } from '@expo/config';
2
- import type { NormalizedOptions } from '../types.js';
2
+ import type { NormalizedOptions } from '../types';
3
3
  /**
4
4
  * Update resources inside the **resources.pb** file.
5
5
  */
@@ -1,36 +1,43 @@
1
- import assert from 'node:assert';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import protobuf from 'protobufjs';
5
- import { requireNotNull } from '../utils.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.updateAndroidManifestAsync = exports.updateResourcesAsync = void 0;
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const protobufjs_1 = __importDefault(require("protobufjs"));
11
+ const utils_1 = require("../utils");
6
12
  /**
7
13
  * Update resources inside the **resources.pb** file.
8
14
  */
9
- export async function updateResourcesAsync(config, resourcesPbFilePath) {
10
- const root = await protobuf.load(path.join(import.meta.dirname, '../../assets', 'Resources.proto'));
15
+ async function updateResourcesAsync(config, resourcesPbFilePath) {
16
+ const root = await protobufjs_1.default.load(node_path_1.default.join(__dirname, '../../assets', 'Resources.proto'));
11
17
  const resourceTableType = root.lookupType('aapt.pb.ResourceTable');
12
18
  const resourceTable = await decodeProtoFile(resourceTableType, resourcesPbFilePath);
13
19
  // [0] Update the package name
14
- assert(resourceTable.package.length === 1, 'Expected only one package');
15
- assert(config.android?.package, 'Expected android.package to be defined');
20
+ (0, node_assert_1.default)(resourceTable.package.length === 1, 'Expected only one package');
21
+ (0, node_assert_1.default)(config.android?.package, 'Expected android.package to be defined');
16
22
  resourceTable.package[0].packageName = config.android.package;
17
23
  const stringType = resourceTable.package[0].type.find((type) => type.name === 'string');
18
24
  const stringEntries = stringType?.entry;
19
25
  // [1] Update the `app_name` in **res/values/strings.xml**.
20
26
  const appNameEntry = stringEntries?.find((entry) => entry.name === 'app_name');
21
- assert(appNameEntry?.configValue?.[0].value?.item?.str?.value === 'HelloWorld', 'Expected app_name to be predefined "HelloWorld"');
27
+ (0, node_assert_1.default)(appNameEntry?.configValue?.[0].value?.item?.str?.value === 'HelloWorld', 'Expected app_name to be predefined "HelloWorld"');
22
28
  appNameEntry.configValue[0].value.item.str.value = config.name;
23
29
  await encodeProtoFile(resourceTableType, resourcesPbFilePath, resourceTable);
24
30
  }
31
+ exports.updateResourcesAsync = updateResourcesAsync;
25
32
  /**
26
33
  * Update the proto-based AndroidManiest.xml.
27
34
  */
28
- export async function updateAndroidManifestAsync(config, androidManiestFilePath, options, updatesRuntimeVersion) {
29
- const root = await protobuf.load(path.join(import.meta.dirname, '../../assets', 'Resources.proto'));
35
+ async function updateAndroidManifestAsync(config, androidManiestFilePath, options, updatesRuntimeVersion) {
36
+ const root = await protobufjs_1.default.load(node_path_1.default.join(__dirname, '../../assets', 'Resources.proto'));
30
37
  const xmlNodeType = root.lookupType('aapt.pb.XmlNode');
31
38
  const rootNode = await decodeProtoFile(xmlNodeType, androidManiestFilePath);
32
39
  // [0] Update the package name
33
- replaceXmlAttributeValue(rootNode, (value) => value.replace(/dev\.expo\.templatedefault\.appid/g, requireNotNull(config.android?.package)));
40
+ replaceXmlAttributeValue(rootNode, (value) => value.replace(/dev\.expo\.templatedefault\.appid/g, (0, utils_1.requireNotNull)(config.android?.package)));
34
41
  // [1] Update the scheme in the intent-filters
35
42
  const intentFilterViewActionNodes = findXmlNodes(rootNode, (node) => node.element?.name === 'intent-filter' &&
36
43
  findXmlNodes(node, (node) => node.element?.name === 'action' &&
@@ -39,19 +46,20 @@ export async function updateAndroidManifestAsync(config, androidManiestFilePath,
39
46
  for (const node of intentFilterViewActionNodes) {
40
47
  replaceXmlAttributeValue(node, (value) => value
41
48
  // scheme in app.json
42
- .replace(/^myapp$/g, requireNotNull(firstScheme))
49
+ .replace(/^myapp$/g, (0, utils_1.requireNotNull)(firstScheme))
43
50
  // android.package in app.json
44
- .replace(/^dev\.expo\.templatedefault$/g, requireNotNull(config.android?.package))
51
+ .replace(/^dev\.expo\.templatedefault$/g, (0, utils_1.requireNotNull)(config.android?.package))
45
52
  // default scheme generated from slug in app.json
46
- .replace(/^exp\+expo-template-default$/g, `exp+${requireNotNull(config.slug)}`));
53
+ .replace(/^exp\+expo-template-default$/g, `exp+${(0, utils_1.requireNotNull)(config.slug)}`));
47
54
  }
48
55
  // [2] expo-updates configuration
49
56
  const mainApplicationNode = findXmlNodes(rootNode, (node) => node.element?.name === 'application' &&
50
57
  node.element?.attribute.find((attr) => attr.name === 'name' && attr.value.endsWith('.MainApplication')) != null)[0];
51
- assert(mainApplicationNode != null, 'Expected application node to be present');
58
+ (0, node_assert_1.default)(mainApplicationNode != null, 'Expected application node to be present');
52
59
  mutateExpoUpdatesConfigAsync(mainApplicationNode, config, updatesRuntimeVersion);
53
60
  await encodeProtoFile(xmlNodeType, androidManiestFilePath, rootNode);
54
61
  }
62
+ exports.updateAndroidManifestAsync = updateAndroidManifestAsync;
55
63
  //#region Internals
56
64
  /**
57
65
  * Update the `expo-updates` configuration in the Android project.
@@ -59,11 +67,11 @@ export async function updateAndroidManifestAsync(config, androidManiestFilePath,
59
67
  function mutateExpoUpdatesConfigAsync(mainApplicationNode, config, runtimeVersion) {
60
68
  const updateEnabledNodeIndex = mainApplicationNode.element?.child.findIndex((child) => child.element?.name === 'meta-data' &&
61
69
  child.element?.attribute.find((attr) => attr.name === 'name' && attr.value === 'expo.modules.updates.ENABLED'));
62
- assert(updateEnabledNodeIndex != null && updateEnabledNodeIndex >= 0, `Expected 'expo.modules.updates.ENABLED' node to be present`);
63
- const updateEnabledNode = requireNotNull(mainApplicationNode.element?.child[updateEnabledNodeIndex]);
70
+ (0, node_assert_1.default)(updateEnabledNodeIndex != null && updateEnabledNodeIndex >= 0, `Expected 'expo.modules.updates.ENABLED' node to be present`);
71
+ const updateEnabledNode = (0, utils_1.requireNotNull)(mainApplicationNode.element?.child[updateEnabledNodeIndex]);
64
72
  // [0] expo.modules.updates.ENABLED
65
73
  const updateEnabledPrimValue = updateEnabledNode.element?.attribute?.[1]?.compiledItem?.prim;
66
- assert(updateEnabledPrimValue != null, 'Expected updateEnabledPrimValue to be present');
74
+ (0, node_assert_1.default)(updateEnabledPrimValue != null, 'Expected updateEnabledPrimValue to be present');
67
75
  updateEnabledPrimValue.booleanValue = true;
68
76
  // [1] expo.modules.updates.EXPO_RUNTIME_VERSION
69
77
  const updateRuntimeVersionNode = mainApplicationNode.element?.child.find((child) => child.element?.name === 'meta-data' &&
@@ -72,19 +80,19 @@ function mutateExpoUpdatesConfigAsync(mainApplicationNode, config, runtimeVersio
72
80
  mainApplicationNode.element?.child.splice(updateEnabledNodeIndex + 1, 0, createUpdateStringNode(updateEnabledNode, 'expo.modules.updates.EXPO_RUNTIME_VERSION', runtimeVersion));
73
81
  }
74
82
  else {
75
- assert(updateRuntimeVersionNode.element != null);
83
+ (0, node_assert_1.default)(updateRuntimeVersionNode.element != null);
76
84
  updateRuntimeVersionNode.element.attribute[1].value = runtimeVersion;
77
85
  }
78
86
  // [2] expo.modules.updates.EXPO_UPDATE_URL
79
87
  const updateUrlNode = mainApplicationNode.element?.child.find((child) => child.element?.name === 'meta-data' &&
80
88
  child.element?.attribute.find((attr) => attr.name === 'name' && attr.value === 'expo.modules.updates.EXPO_UPDATE_URL'));
81
89
  const updateUrl = config.updates?.url;
82
- assert(updateUrl);
90
+ (0, node_assert_1.default)(updateUrl);
83
91
  if (updateUrlNode == null) {
84
92
  mainApplicationNode.element?.child.splice(updateEnabledNodeIndex + 1, 0, createUpdateStringNode(updateEnabledNode, 'expo.modules.updates.EXPO_UPDATE_URL', updateUrl));
85
93
  }
86
94
  else {
87
- assert(updateUrlNode.element != null);
95
+ (0, node_assert_1.default)(updateUrlNode.element != null);
88
96
  updateUrlNode.element.attribute[1].value = updateUrl;
89
97
  }
90
98
  }
@@ -92,7 +100,7 @@ function mutateExpoUpdatesConfigAsync(mainApplicationNode, config, runtimeVersio
92
100
  * Decode the proto-encoded file from `filePath` with the given `protoTypeString` and return as JSON object.
93
101
  */
94
102
  async function decodeProtoFile(protoType, filePath) {
95
- const buffer = await fs.readFile(filePath);
103
+ const buffer = await promises_1.default.readFile(filePath);
96
104
  const message = await protoType.decode(buffer);
97
105
  const json = protoType.toObject(message, {
98
106
  longs: String,
@@ -108,7 +116,7 @@ async function decodeProtoFile(protoType, filePath) {
108
116
  async function encodeProtoFile(protoType, filePath, json) {
109
117
  const updatedMessage = protoType.fromObject(json);
110
118
  const updatedBuffer = protoType.encode(updatedMessage).finish();
111
- await fs.writeFile(filePath, updatedBuffer);
119
+ await promises_1.default.writeFile(filePath, updatedBuffer);
112
120
  }
113
121
  /**
114
122
  * Recursively find XML nodes that satisfy the given `predicate`.
@@ -145,7 +153,7 @@ function createUpdateStringNode(updateEnabledNode, name, value) {
145
153
  return {
146
154
  ...updateEnabledNode,
147
155
  element: {
148
- ...requireNotNull(updateEnabledNode.element),
156
+ ...(0, utils_1.requireNotNull)(updateEnabledNode.element),
149
157
  attribute: [
150
158
  {
151
159
  namespaceUri: 'http://schemas.android.com/apk/res/android',
package/build/cli.js CHANGED
@@ -1,9 +1,13 @@
1
- import { Command } from 'commander';
2
- import pico from 'picocolors';
3
- import { repackAppAndroidAsync, repackAppIosAsync } from './index.js';
4
- import { resignIpaAsync } from './ios/index.js';
5
- import logger from './log.js';
6
- const program = new Command('repack-app')
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const logger_1 = require("@expo/logger");
7
+ const commander_1 = require("commander");
8
+ const picocolors_1 = __importDefault(require("picocolors"));
9
+ const index_1 = require("./index");
10
+ const program = new commander_1.Command('repack-app')
7
11
  .requiredOption('-p, --platform <platform>', 'Platform to repack the app for')
8
12
  .requiredOption('--source-app <path>', 'Path to the source app file')
9
13
  .option('--android-build-tools-dir <path>', 'Path to the Android build tools directory')
@@ -22,10 +26,11 @@ const program = new Command('repack-app')
22
26
  .argument('<project-root>', 'Path to the project root')
23
27
  .parse(process.argv);
24
28
  async function runAsync() {
29
+ const logger = (0, logger_1.createLogger)({ name: 'repack-app' });
25
30
  const platform = program.opts().platform;
26
31
  const projectRoot = program.args[0];
27
32
  if (platform === 'android') {
28
- const outputPath = await repackAppAndroidAsync({
33
+ const outputPath = await (0, index_1.repackAppAndroidAsync)({
29
34
  platform: program.opts().platform,
30
35
  projectRoot,
31
36
  verbose: !!program.opts().verbose,
@@ -39,8 +44,9 @@ async function runAsync() {
39
44
  keyPassword: program.opts().ksKeyPass,
40
45
  },
41
46
  androidBuildToolsDir: program.opts().androidBuildToolsDir,
47
+ logger,
42
48
  });
43
- logger.log(pico.green(`Updated APK created at ${outputPath}`));
49
+ logger.info(picocolors_1.default.green(`Updated APK created at ${outputPath}`));
44
50
  }
45
51
  else if (platform === 'ios') {
46
52
  const options = {
@@ -54,14 +60,10 @@ async function runAsync() {
54
60
  signingIdentity: program.opts().signingIdentity,
55
61
  provisioningProfile: program.opts().provisioningProfile,
56
62
  },
63
+ logger,
57
64
  };
58
- const outputPath = await repackAppIosAsync(options);
59
- logger.log(pico.green(`Updated IPA created at ${outputPath}`));
60
- if (options.iosSigningOptions?.signingIdentity &&
61
- options.iosSigningOptions?.provisioningProfile) {
62
- logger.log(pico.green(`Resigning the IPA at ${outputPath}`));
63
- await resignIpaAsync(outputPath, options.iosSigningOptions, options);
64
- }
65
+ const outputPath = await (0, index_1.repackAppIosAsync)(options);
66
+ logger.info(picocolors_1.default.green(`Updated IPA created at ${outputPath}`));
65
67
  }
66
68
  else {
67
69
  throw new Error(`Unsupported platform: ${platform}`);
package/build/expo.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ExpoConfig } from '@expo/config';
2
- import type { NormalizedOptions } from './types.js';
2
+ import type { NormalizedOptions } from './types';
3
3
  /**
4
4
  * Generate the app.config file for the Android app.
5
5
  */
package/build/expo.js CHANGED
@@ -1,26 +1,34 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import resolveFrom from 'resolve-from';
4
- import { spawnAsync } from './cjs-wrapper.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveRuntimeVersionAsync = exports.generateAppConfigAsync = void 0;
7
+ const steps_1 = require("@expo/steps");
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const resolve_from_1 = __importDefault(require("resolve-from"));
5
11
  /**
6
12
  * Generate the app.config file for the Android app.
7
13
  */
8
- export async function generateAppConfigAsync(options, config) {
14
+ async function generateAppConfigAsync(options, config) {
9
15
  const { workingDirectory } = options;
10
- const appConfigPath = path.join(workingDirectory, 'app.config');
11
- await fs.writeFile(appConfigPath, JSON.stringify(config));
16
+ const appConfigPath = node_path_1.default.join(workingDirectory, 'app.config');
17
+ await promises_1.default.writeFile(appConfigPath, JSON.stringify(config));
12
18
  return appConfigPath;
13
19
  }
20
+ exports.generateAppConfigAsync = generateAppConfigAsync;
14
21
  /**
15
22
  * Resolve the `runtimeVersion` for expo-updates.
16
23
  */
17
- export async function resolveRuntimeVersionAsync(options, config) {
24
+ async function resolveRuntimeVersionAsync(options, config) {
18
25
  const { projectRoot } = options;
19
- const cli = resolveFrom.silent(projectRoot, 'expo-updates/bin/cli') ??
20
- resolveFrom(projectRoot, 'expo-updates/bin/cli.js');
21
- const proc = await spawnAsync(cli, ['runtimeversion:resolve', '--platform', 'android', '--workflow', 'managed'], {
26
+ const cli = resolve_from_1.default.silent(projectRoot, 'expo-updates/bin/cli') ??
27
+ (0, resolve_from_1.default)(projectRoot, 'expo-updates/bin/cli.js');
28
+ const proc = await (0, steps_1.spawnAsync)(cli, ['runtimeversion:resolve', '--platform', 'android', '--workflow', 'managed'], {
22
29
  cwd: projectRoot,
23
30
  });
24
31
  const runtimeVersion = JSON.parse(proc.stdout).runtimeVersion;
25
32
  return runtimeVersion ?? config.version ?? '1.0.0';
26
33
  }
34
+ exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
package/build/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { repackAppAndroidAsync } from './android/index.js';
2
- export { repackAppIosAsync } from './ios/index.js';
1
+ export { repackAppAndroidAsync } from './android';
2
+ export { repackAppIosAsync } from './ios';
package/build/index.js CHANGED
@@ -1,2 +1,7 @@
1
- export { repackAppAndroidAsync } from './android/index.js';
2
- export { repackAppIosAsync } from './ios/index.js';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.repackAppIosAsync = exports.repackAppAndroidAsync = void 0;
4
+ var android_1 = require("./android");
5
+ Object.defineProperty(exports, "repackAppAndroidAsync", { enumerable: true, get: function () { return android_1.repackAppAndroidAsync; } });
6
+ var ios_1 = require("./ios");
7
+ Object.defineProperty(exports, "repackAppIosAsync", { enumerable: true, get: function () { return ios_1.repackAppIosAsync; } });
@@ -1,5 +1,5 @@
1
1
  import { type ExpoConfig } from '@expo/config';
2
- import type { NormalizedOptions } from '../types.js';
2
+ import type { NormalizedOptions } from '../types';
3
3
  /**
4
4
  * Unzip the IPA file.
5
5
  */
@@ -1,43 +1,49 @@
1
- import assert from 'node:assert';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { spawnAsync } from '../cjs-wrapper.js';
5
- import { directoryExistsAsync } from '../utils.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createIpaAsync = exports.updateFilesAsync = exports.unzipIpaAsync = void 0;
7
+ const steps_1 = require("@expo/steps");
8
+ const node_assert_1 = __importDefault(require("node:assert"));
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const utils_1 = require("../utils");
6
12
  /**
7
13
  * Unzip the IPA file.
8
14
  */
9
- export async function unzipIpaAsync(options) {
10
- const unzipWorkingDirectory = path.join(options.workingDirectory, 'unzip');
11
- await spawnAsync('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], {
12
- stdio: options.verbose ? 'inherit' : 'ignore',
13
- });
14
- const appWorkingDirectory = path.join(unzipWorkingDirectory, 'Payload', 'HelloWorld.app');
15
- assert(await directoryExistsAsync(appWorkingDirectory));
15
+ async function unzipIpaAsync(options) {
16
+ const unzipWorkingDirectory = node_path_1.default.join(options.workingDirectory, 'unzip');
17
+ await (0, steps_1.spawnAsync)('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
18
+ const appWorkingDirectory = node_path_1.default.join(unzipWorkingDirectory, 'Payload', 'HelloWorld.app');
19
+ (0, node_assert_1.default)(await (0, utils_1.directoryExistsAsync)(appWorkingDirectory));
16
20
  return appWorkingDirectory;
17
21
  }
22
+ exports.unzipIpaAsync = unzipIpaAsync;
18
23
  /**
19
24
  * Update some binary files.
20
25
  */
21
- export async function updateFilesAsync(config, appWorkingDirectory) {
22
- const parentDir = path.dirname(appWorkingDirectory);
23
- const newAppWorkingDirectory = path.join(parentDir, `${config.name}.app`);
26
+ async function updateFilesAsync(config, appWorkingDirectory) {
27
+ const parentDir = node_path_1.default.dirname(appWorkingDirectory);
28
+ const newAppWorkingDirectory = node_path_1.default.join(parentDir, `${config.name}.app`);
24
29
  // [0] Update the .app directory
25
- await fs.rename(path.join(parentDir, 'HelloWorld.app'), newAppWorkingDirectory);
30
+ await promises_1.default.rename(node_path_1.default.join(parentDir, 'HelloWorld.app'), newAppWorkingDirectory);
26
31
  // [1] Rename the executable
27
- await fs.rename(path.join(newAppWorkingDirectory, 'HelloWorld'), path.join(newAppWorkingDirectory, config.name));
32
+ await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory, 'HelloWorld'), node_path_1.default.join(newAppWorkingDirectory, config.name));
28
33
  return newAppWorkingDirectory;
29
34
  }
35
+ exports.updateFilesAsync = updateFilesAsync;
30
36
  /**
31
37
  * From the given working .app directory, create a new .ipa file.
32
38
  */
33
- export async function createIpaAsync(options, appWorkingDirectory) {
39
+ async function createIpaAsync(options, appWorkingDirectory) {
34
40
  const { workingDirectory } = options;
35
- await fs.mkdir(path.join(workingDirectory, 'Payload'), { recursive: true });
36
- await fs.rename(appWorkingDirectory, path.join(workingDirectory, 'Payload', path.basename(appWorkingDirectory)));
37
- const outputIpaPath = path.join(workingDirectory, 'repacked.ipa');
38
- await spawnAsync('zip', ['-r', outputIpaPath, 'Payload'], {
39
- cwd: workingDirectory,
40
- stdio: options.verbose ? 'inherit' : 'ignore',
41
- });
41
+ await promises_1.default.mkdir(node_path_1.default.join(workingDirectory, 'Payload'), { recursive: true });
42
+ await promises_1.default.rename(appWorkingDirectory, node_path_1.default.join(workingDirectory, 'Payload', node_path_1.default.basename(appWorkingDirectory)));
43
+ const outputIpaPath = node_path_1.default.join(workingDirectory, 'repacked.ipa');
44
+ await (0, steps_1.spawnAsync)('zip', ['-r', outputIpaPath, 'Payload'], options.verbose
45
+ ? { logger: options.logger, stdio: 'pipe', cwd: workingDirectory }
46
+ : { cwd: workingDirectory });
42
47
  return outputIpaPath;
43
48
  }
49
+ exports.createIpaAsync = createIpaAsync;
@@ -1,4 +1,4 @@
1
- import type { IosSigningOptions, Options } from '../types.js';
1
+ import type { IosSigningOptions, Options } from '../types';
2
2
  /**
3
3
  * Repack an iOS app.
4
4
  */
@@ -1,54 +1,67 @@
1
- import { getConfig } from '@expo/config';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import pico from 'picocolors';
5
- import { createIpaAsync, unzipIpaAsync, updateFilesAsync } from './build-tools.js';
6
- import { updateExpoPlistAsync, updateInfoPlistAsync } from './resources.js';
7
- import { spawnAsync } from '../cjs-wrapper.js';
8
- import { generateAppConfigAsync, resolveRuntimeVersionAsync } from '../expo.js';
9
- import logger from '../log.js';
10
- import { normalizeOptionAsync } from '../utils.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resignIpaAsync = exports.repackAppIosAsync = void 0;
7
+ const config_1 = require("@expo/config");
8
+ const steps_1 = require("@expo/steps");
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const picocolors_1 = __importDefault(require("picocolors"));
12
+ const build_tools_1 = require("./build-tools");
13
+ const resources_1 = require("./resources");
14
+ const expo_1 = require("../expo");
15
+ const utils_1 = require("../utils");
11
16
  /**
12
17
  * Repack an iOS app.
13
18
  */
14
- export async function repackAppIosAsync(_options) {
15
- const options = await normalizeOptionAsync(_options);
16
- const { workingDirectory } = options;
17
- await fs.mkdir(workingDirectory, { recursive: true });
18
- const { exp } = getConfig(options.projectRoot, {
19
+ async function repackAppIosAsync(_options) {
20
+ const options = await (0, utils_1.normalizeOptionAsync)(_options);
21
+ const { workingDirectory, logger } = options;
22
+ await promises_1.default.mkdir(workingDirectory, { recursive: true });
23
+ const { exp } = (0, config_1.getConfig)(options.projectRoot, {
19
24
  isPublicConfig: true,
20
25
  skipSDKVersionRequirement: true,
21
26
  });
22
- const updatesRuntimeVersion = await resolveRuntimeVersionAsync(options, exp);
23
- logger.log(pico.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
24
- logger.time(`Unzipping IPA`);
25
- let appWorkingDirectory = await unzipIpaAsync(options);
26
- appWorkingDirectory = await updateFilesAsync(exp, appWorkingDirectory);
27
- logger.timeEnd(`Unzipping IPA`);
28
- logger.time(`Updating Info.plist`);
29
- await updateInfoPlistAsync(exp, path.join(appWorkingDirectory, 'Info.plist'), options);
30
- logger.timeEnd(`Updating Info.plist`);
31
- logger.time(`Updating Expo.plist`);
32
- await updateExpoPlistAsync(exp, path.join(appWorkingDirectory, 'Expo.plist'), updatesRuntimeVersion, options);
33
- logger.timeEnd(`Updating Expo.plist`);
34
- logger.time(`Generating app.config`);
35
- const appConfigPath = await generateAppConfigAsync(options, exp);
36
- await fs.copyFile(appConfigPath, path.join(appWorkingDirectory, 'EXConstants.bundle', 'app.config'));
37
- logger.timeEnd(`Generating app.config`);
38
- logger.time(`Creating updated ipa`);
39
- const outputIpa = await createIpaAsync(options, appWorkingDirectory);
40
- logger.timeEnd(`Creating updated ipa`);
41
- await fs.rename(outputIpa, options.outputPath);
42
- try {
43
- await fs.rmdir(workingDirectory, { recursive: true });
27
+ const updatesRuntimeVersion = await (0, expo_1.resolveRuntimeVersionAsync)(options, exp);
28
+ logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
29
+ logger.info(`Unzipping IPA`);
30
+ let appWorkingDirectory = await (0, build_tools_1.unzipIpaAsync)(options);
31
+ appWorkingDirectory = await (0, build_tools_1.updateFilesAsync)(exp, appWorkingDirectory);
32
+ logger.info(`Finished unzipping IPA ✅`);
33
+ logger.info(`Updating Info.plist`);
34
+ await (0, resources_1.updateInfoPlistAsync)(exp, node_path_1.default.join(appWorkingDirectory, 'Info.plist'), options);
35
+ logger.info(`Finished updating Info.plist ✅`);
36
+ logger.info(`Updating Expo.plist`);
37
+ await (0, resources_1.updateExpoPlistAsync)(exp, node_path_1.default.join(appWorkingDirectory, 'Expo.plist'), updatesRuntimeVersion, options);
38
+ logger.info(`Finished updating Expo.plist ✅`);
39
+ logger.info(`Generating app.config`);
40
+ const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
41
+ await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(appWorkingDirectory, 'EXConstants.bundle', 'app.config'));
42
+ logger.info(`Finished generating app.config ✅`);
43
+ logger.info(`Creating updated ipa`);
44
+ const outputIpa = await (0, build_tools_1.createIpaAsync)(options, appWorkingDirectory);
45
+ logger.info(`Finished creating updated ipa ✅`);
46
+ if (options.iosSigningOptions) {
47
+ logger.info(`Resigning the IPA at ${outputIpa}`);
48
+ await resignIpaAsync(outputIpa, options.iosSigningOptions, options);
49
+ logger.info(`Finished resigning the IPA ✅`);
50
+ }
51
+ await promises_1.default.rename(outputIpa, options.outputPath);
52
+ if (!options.skipWorkingDirCleanup) {
53
+ try {
54
+ await promises_1.default.rmdir(workingDirectory, { recursive: true });
55
+ }
56
+ catch { }
44
57
  }
45
- catch { }
46
58
  return options.outputPath;
47
59
  }
60
+ exports.repackAppIosAsync = repackAppIosAsync;
48
61
  /**
49
62
  * Resign an IPA by fastlane.
50
63
  */
51
- export async function resignIpaAsync(ipaPath, signingOptions, options) {
64
+ async function resignIpaAsync(ipaPath, signingOptions, options) {
52
65
  const args = [
53
66
  'run',
54
67
  'resign',
@@ -59,7 +72,6 @@ export async function resignIpaAsync(ipaPath, signingOptions, options) {
59
72
  if (signingOptions.keychainPath) {
60
73
  args.push(`keychain_path:${signingOptions.keychainPath}`);
61
74
  }
62
- await spawnAsync('fastlane', args, {
63
- stdio: options.verbose ? 'inherit' : 'ignore',
64
- });
75
+ await (0, steps_1.spawnAsync)('fastlane', args, { logger: options.logger, stdio: 'pipe' });
65
76
  }
77
+ exports.resignIpaAsync = resignIpaAsync;
@@ -1,5 +1,5 @@
1
1
  import { type ExpoConfig } from '@expo/config';
2
- import type { NormalizedOptions } from '../types.js';
2
+ import type { NormalizedOptions } from '../types';
3
3
  /**
4
4
  * Update the Info.plist file.
5
5
  */
@@ -1,13 +1,20 @@
1
- import assert from 'node:assert';
2
- import fs from 'node:fs/promises';
3
- import { plist, spawnAsync } from '../cjs-wrapper.js';
4
- import { requireNotNull } from '../utils.js';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.updateExpoPlistAsync = exports.updateInfoPlistAsync = void 0;
7
+ const plist_1 = __importDefault(require("@expo/plist"));
8
+ const steps_1 = require("@expo/steps");
9
+ const node_assert_1 = __importDefault(require("node:assert"));
10
+ const promises_1 = __importDefault(require("node:fs/promises"));
11
+ const utils_1 = require("../utils");
5
12
  /**
6
13
  * Update the Info.plist file.
7
14
  */
8
- export async function updateInfoPlistAsync(config, infoPlistPath, options) {
15
+ async function updateInfoPlistAsync(config, infoPlistPath, options) {
9
16
  const bundleIdentifier = config.ios?.bundleIdentifier;
10
- assert(bundleIdentifier, 'The `bundleIdentifier` must be specified in the `ios` config.');
17
+ (0, node_assert_1.default)(bundleIdentifier, 'The `bundleIdentifier` must be specified in the `ios` config.');
11
18
  const firstScheme = Array.isArray(config.scheme) ? config.scheme[0] : config.scheme;
12
19
  await updateBinaryPlistAsync(infoPlistPath, options, (data) => {
13
20
  const urlTypes = data.CFBundleURLTypes.slice();
@@ -16,11 +23,11 @@ export async function updateInfoPlistAsync(config, infoPlistPath, options) {
16
23
  const schemes = urlType.CFBundleURLSchemes.map((scheme) => {
17
24
  return (scheme
18
25
  // scheme in app.json
19
- .replace(/^myapp$/g, requireNotNull(firstScheme))
26
+ .replace(/^myapp$/g, (0, utils_1.requireNotNull)(firstScheme))
20
27
  // ios.bundleIdentifier in app.json
21
- .replace(/^dev.expo.templatedefault$/g, requireNotNull(bundleIdentifier))
28
+ .replace(/^dev.expo.templatedefault$/g, (0, utils_1.requireNotNull)(bundleIdentifier))
22
29
  // default scheme generated from slug in app.json
23
- .replace(/^exp\+expo-template-default$/g, `exp+${requireNotNull(config.slug)}`));
30
+ .replace(/^exp\+expo-template-default$/g, `exp+${(0, utils_1.requireNotNull)(config.slug)}`));
24
31
  });
25
32
  urlType.CFBundleURLSchemes = schemes;
26
33
  }
@@ -36,13 +43,14 @@ export async function updateInfoPlistAsync(config, infoPlistPath, options) {
36
43
  };
37
44
  });
38
45
  }
46
+ exports.updateInfoPlistAsync = updateInfoPlistAsync;
39
47
  /**
40
48
  * Update the Expo.plist file.
41
49
  */
42
- export async function updateExpoPlistAsync(config, expoPlistPath, runtimeVersion, options) {
50
+ async function updateExpoPlistAsync(config, expoPlistPath, runtimeVersion, options) {
43
51
  await updateBinaryPlistAsync(expoPlistPath, options, (data) => {
44
52
  const updateUrl = config.updates?.url;
45
- assert(updateUrl);
53
+ (0, node_assert_1.default)(updateUrl);
46
54
  return {
47
55
  ...data,
48
56
  EXUpdatesEnabled: true,
@@ -51,18 +59,15 @@ export async function updateExpoPlistAsync(config, expoPlistPath, runtimeVersion
51
59
  };
52
60
  });
53
61
  }
62
+ exports.updateExpoPlistAsync = updateExpoPlistAsync;
54
63
  //#region Internals
55
64
  async function updateBinaryPlistAsync(plistPath, options, updater) {
56
- await spawnAsync('plutil', ['-convert', 'xml1', plistPath], {
57
- stdio: options.verbose ? 'inherit' : 'ignore',
58
- });
59
- const contents = await fs.readFile(plistPath, 'utf8');
60
- const data = await plist.parse(contents);
65
+ await (0, steps_1.spawnAsync)('plutil', ['-convert', 'xml1', plistPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
66
+ const contents = await promises_1.default.readFile(plistPath, 'utf8');
67
+ const data = await plist_1.default.parse(contents);
61
68
  const updatedData = updater(data);
62
- const updatedContents = plist.build(updatedData);
63
- await fs.writeFile(plistPath, updatedContents);
64
- await spawnAsync('plutil', ['-convert', 'binary1', plistPath], {
65
- stdio: options.verbose ? 'inherit' : 'ignore',
66
- });
69
+ const updatedContents = plist_1.default.build(updatedData);
70
+ await promises_1.default.writeFile(plistPath, updatedContents);
71
+ await (0, steps_1.spawnAsync)('plutil', ['-convert', 'binary1', plistPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
67
72
  }
68
73
  //#endregion
package/build/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference types="bunyan" />
2
+ import type { bunyan } from '@expo/logger';
1
3
  export interface Options {
2
4
  /**
3
5
  * The platform to repack the app for.
@@ -11,6 +13,10 @@ export interface Options {
11
13
  * The prebuilt app file path that acts as a source for repacking.
12
14
  */
13
15
  sourceAppPath: string;
16
+ /**
17
+ * The bunyan logger instance.
18
+ */
19
+ logger: bunyan;
14
20
  /**
15
21
  * Working directory.
16
22
  */
@@ -37,6 +43,10 @@ export interface Options {
37
43
  * The options for signing the ios app.
38
44
  */
39
45
  iosSigningOptions?: IosSigningOptions;
46
+ /**
47
+ * Skip the working directory cleanup.
48
+ */
49
+ skipWorkingDirCleanup?: boolean;
40
50
  }
41
51
  export interface NormalizedOptions extends Options {
42
52
  workingDirectory: NonNullable<Options['workingDirectory']>;
package/build/types.js CHANGED
@@ -1 +1,2 @@
1
- export {};
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/build/utils.js CHANGED
@@ -1,29 +1,36 @@
1
- import assert from 'node:assert';
2
- import fs from 'node:fs/promises';
3
- import { createRequire } from 'node:module';
4
- import path from 'node:path';
5
- const require = createRequire(import.meta.url);
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeOptionAsync = exports.requireNotNull = exports.directoryExistsAsync = void 0;
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
6
10
  /**
7
11
  * Check if a directory exists.
8
12
  */
9
- export async function directoryExistsAsync(file) {
10
- return (await fs.stat(file).catch(() => null))?.isDirectory() ?? false;
13
+ async function directoryExistsAsync(file) {
14
+ return (await promises_1.default.stat(file).catch(() => null))?.isDirectory() ?? false;
11
15
  }
16
+ exports.directoryExistsAsync = directoryExistsAsync;
12
17
  /**
13
18
  * Return the non-null value.
14
19
  */
15
- export function requireNotNull(value) {
16
- assert(value != null, 'Expected value to be non-null');
20
+ function requireNotNull(value) {
21
+ (0, node_assert_1.default)(value != null, 'Expected value to be non-null');
17
22
  return value;
18
23
  }
24
+ exports.requireNotNull = requireNotNull;
19
25
  /**
20
26
  * Normalize the options.
21
27
  */
22
- export async function normalizeOptionAsync(options) {
28
+ async function normalizeOptionAsync(options) {
23
29
  const fileExt = options.platform === 'android' ? '.apk' : '.ipa';
24
30
  return {
25
31
  ...options,
26
- workingDirectory: options.workingDirectory ?? (await fs.mkdtemp(path.join(require('temp-dir'), 'repack-app-'))),
27
- outputPath: options.outputPath ?? path.join(options.projectRoot, `repacked${fileExt}`),
32
+ workingDirectory: options.workingDirectory ?? (await promises_1.default.mkdtemp(node_path_1.default.join(require('temp-dir'), 'repack-app-'))),
33
+ outputPath: options.outputPath ?? node_path_1.default.join(options.projectRoot, `repacked${fileExt}`),
28
34
  };
29
35
  }
36
+ exports.normalizeOptionAsync = normalizeOptionAsync;
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@expo/repack-app",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Repacking tool for Expo apps",
5
5
  "main": "build/index.js",
6
- "module": "build/index.js",
7
6
  "types": "build/index.d.ts",
8
- "type": "module",
7
+ "type": "commonjs",
9
8
  "scripts": {
10
9
  "build": "expo-module build",
11
10
  "clean": "expo-module clean",
@@ -27,8 +26,9 @@
27
26
  "license": "BUSL-1.1",
28
27
  "dependencies": {
29
28
  "@expo/config": "^8.5.6",
30
- "@expo/turtle-spawn": "^1.0.57",
31
- "commander": "^12.0.0",
29
+ "@expo/logger": "^1.0.57",
30
+ "@expo/steps": "^1.0.108",
31
+ "commander": "^11.0.0",
32
32
  "glob": "^10.3.12",
33
33
  "picocolors": "^1.0.0",
34
34
  "protobufjs": "^7.2.6",
@@ -40,5 +40,8 @@
40
40
  "eslint": "^8.57.0",
41
41
  "expo-module-scripts": "^3.4.2",
42
42
  "typescript": "^5.4.5"
43
+ },
44
+ "volta": {
45
+ "node": "22.1.0"
43
46
  }
44
47
  }
@@ -1,5 +0,0 @@
1
- export declare const spawnAsync: typeof import("@expo/turtle-spawn").default;
2
- export declare const plist: {
3
- parse: typeof import("@expo/plist/build/parse").parse;
4
- build: typeof import("@expo/plist/build/build").build;
5
- };
@@ -1,5 +0,0 @@
1
- import { createRequire } from 'node:module';
2
- const require = createRequire(import.meta.url);
3
- export const spawnAsync = require('@expo/turtle-spawn')
4
- .default;
5
- export const plist = require('@expo/plist').default;
package/build/log.d.ts DELETED
@@ -1,13 +0,0 @@
1
- export declare function log(...message: string[]): void;
2
- export declare function warn(...message: string[]): void;
3
- export declare function error(...message: string[]): void;
4
- export declare function time(label?: string): void;
5
- export declare function timeEnd(label?: string): void;
6
- declare const _default: {
7
- log: typeof log;
8
- warn: typeof warn;
9
- error: typeof error;
10
- time: typeof time;
11
- timeEnd: typeof timeEnd;
12
- };
13
- export default _default;
package/build/log.js DELETED
@@ -1,23 +0,0 @@
1
- import pico from 'picocolors';
2
- export function log(...message) {
3
- console.log(...message);
4
- }
5
- export function warn(...message) {
6
- console.warn(...message.map((value) => pico.yellow(value)));
7
- }
8
- export function error(...message) {
9
- console.error(...message);
10
- }
11
- export function time(label) {
12
- console.time(label);
13
- }
14
- export function timeEnd(label) {
15
- console.timeEnd(label);
16
- }
17
- export default {
18
- log,
19
- warn,
20
- error,
21
- time,
22
- timeEnd,
23
- };