@expo/repack-app 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/android/Resources.types.js +2 -1
- package/build/android/build-tools.d.ts +1 -1
- package/build/android/build-tools.js +51 -40
- package/build/android/index.d.ts +1 -1
- package/build/android/index.js +47 -38
- package/build/android/resources.d.ts +1 -1
- package/build/android/resources.js +34 -26
- package/build/cli.js +17 -12
- package/build/expo.d.ts +1 -1
- package/build/expo.js +19 -11
- package/build/index.d.ts +2 -2
- package/build/index.js +7 -2
- package/build/ios/build-tools.d.ts +1 -1
- package/build/ios/build-tools.js +29 -20
- package/build/ios/index.d.ts +1 -1
- package/build/ios/index.js +49 -39
- package/build/ios/resources.d.ts +1 -1
- package/build/ios/resources.js +26 -18
- package/build/log.js +19 -8
- package/build/types.d.ts +4 -0
- package/build/types.js +2 -1
- package/build/utils.js +19 -10
- package/package.json +6 -4
|
@@ -1,62 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
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
|
-
|
|
16
|
+
async function getAndroidBuildToolsAsync(options) {
|
|
11
17
|
if (cachedAndroidTools == null) {
|
|
12
18
|
const androidBuildToolsDir = options?.androidBuildToolsDir ?? (await findLatestBuildToolsDirAsync());
|
|
13
|
-
|
|
19
|
+
(0, node_assert_1.default)(androidBuildToolsDir != null, 'Unable to find the Android build-tools directory.');
|
|
14
20
|
cachedAndroidTools = {
|
|
15
|
-
aapt2Path:
|
|
16
|
-
apksignerPath:
|
|
17
|
-
zipalignPath:
|
|
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
|
-
|
|
32
|
+
async function createProtoBasedResourcesAsync(options) {
|
|
26
33
|
const { sourceAppPath, workingDirectory } = options;
|
|
27
34
|
const { aapt2Path } = await getAndroidBuildToolsAsync(options);
|
|
28
|
-
const protoApkPath =
|
|
29
|
-
await
|
|
35
|
+
const protoApkPath = node_path_1.default.join(workingDirectory, 'proto.apk');
|
|
36
|
+
await (0, spawn_async_1.default)(aapt2Path, ['convert', '-o', protoApkPath, '--output-format', 'proto', '--keep-raw-values', sourceAppPath], {
|
|
30
37
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
31
38
|
});
|
|
32
|
-
await
|
|
39
|
+
await (0, spawn_async_1.default)('unzip', [protoApkPath, '-d', workingDirectory], {
|
|
33
40
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
34
41
|
});
|
|
35
|
-
const removeFiles = await glob('**/*', {
|
|
42
|
+
const removeFiles = await (0, glob_1.glob)('**/*', {
|
|
36
43
|
cwd: workingDirectory,
|
|
37
44
|
maxDepth: 1,
|
|
38
45
|
ignore: ['resources.pb', 'AndroidManifest.xml', 'res/**'],
|
|
39
46
|
absolute: true,
|
|
40
47
|
});
|
|
41
|
-
await Promise.all(removeFiles.map((file) =>
|
|
48
|
+
await Promise.all(removeFiles.map((file) => promises_1.default.rm(file, { recursive: true })));
|
|
42
49
|
return {
|
|
43
|
-
androidManiestFilePath:
|
|
44
|
-
resourcesPbFilePath:
|
|
50
|
+
androidManiestFilePath: node_path_1.default.join(workingDirectory, 'AndroidManifest.xml'),
|
|
51
|
+
resourcesPbFilePath: node_path_1.default.join(workingDirectory, 'resources.pb'),
|
|
45
52
|
};
|
|
46
53
|
}
|
|
54
|
+
exports.createProtoBasedResourcesAsync = createProtoBasedResourcesAsync;
|
|
47
55
|
/**
|
|
48
56
|
* Create an APK with binary-based resources.
|
|
49
57
|
*/
|
|
50
|
-
|
|
58
|
+
async function createBinaryBasedResourcesAsync(options) {
|
|
51
59
|
const { workingDirectory } = options;
|
|
52
60
|
const { aapt2Path } = await getAndroidBuildToolsAsync(options);
|
|
53
|
-
const protoUpdatedApkPath =
|
|
54
|
-
const binaryApkPath =
|
|
55
|
-
await
|
|
61
|
+
const protoUpdatedApkPath = node_path_1.default.join(workingDirectory, 'proto-updated.apk');
|
|
62
|
+
const binaryApkPath = node_path_1.default.join(workingDirectory, 'binary.apk');
|
|
63
|
+
await (0, spawn_async_1.default)('zip', ['-r', '-0', protoUpdatedApkPath, 'resources.pb', 'AndroidManifest.xml', 'res'], {
|
|
56
64
|
cwd: workingDirectory,
|
|
57
65
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
58
66
|
});
|
|
59
|
-
await
|
|
67
|
+
await (0, spawn_async_1.default)(aapt2Path, [
|
|
60
68
|
'convert',
|
|
61
69
|
'-o',
|
|
62
70
|
binaryApkPath,
|
|
@@ -69,35 +77,36 @@ export async function createBinaryBasedResourcesAsync(options) {
|
|
|
69
77
|
});
|
|
70
78
|
return binaryApkPath;
|
|
71
79
|
}
|
|
80
|
+
exports.createBinaryBasedResourcesAsync = createBinaryBasedResourcesAsync;
|
|
72
81
|
/**
|
|
73
82
|
* Create a resigned & updated APK.
|
|
74
83
|
* @param binaryApkPath The path to the binary-based resources APK returned from `createBinaryBasedResourcesAsync()`.
|
|
75
84
|
* @param appConfigPath The path to the app.config file returned from `generateAppConfigAsync()`.
|
|
76
85
|
* @returns
|
|
77
86
|
*/
|
|
78
|
-
|
|
87
|
+
async function createResignedApkAsync(binaryApkPath, appConfigPath, options, signingOptions) {
|
|
79
88
|
const { workingDirectory } = options;
|
|
80
89
|
const { apksignerPath, zipalignPath } = await getAndroidBuildToolsAsync(options);
|
|
81
|
-
const unzipWorkingDirectory =
|
|
82
|
-
const resignedApkPath =
|
|
83
|
-
const resignedApkUnalignedPath =
|
|
84
|
-
await
|
|
85
|
-
await
|
|
90
|
+
const unzipWorkingDirectory = node_path_1.default.join(workingDirectory, 'unzip');
|
|
91
|
+
const resignedApkPath = node_path_1.default.join(workingDirectory, 'resigned.apk');
|
|
92
|
+
const resignedApkUnalignedPath = node_path_1.default.join(workingDirectory, 'resigned-unaligned.apk');
|
|
93
|
+
await promises_1.default.mkdir(unzipWorkingDirectory, { recursive: true });
|
|
94
|
+
await (0, spawn_async_1.default)('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], {
|
|
86
95
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
87
96
|
});
|
|
88
|
-
await
|
|
97
|
+
await (0, spawn_async_1.default)('unzip', ['-o', binaryApkPath, '-d', unzipWorkingDirectory], {
|
|
89
98
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
90
99
|
});
|
|
91
|
-
await
|
|
92
|
-
await
|
|
100
|
+
await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(unzipWorkingDirectory, 'assets', 'app.config'));
|
|
101
|
+
await (0, spawn_async_1.default)('zip', ['-r', '-0', resignedApkUnalignedPath, 'lib', 'resources.arsc'], {
|
|
93
102
|
cwd: unzipWorkingDirectory,
|
|
94
103
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
95
104
|
});
|
|
96
|
-
await
|
|
105
|
+
await (0, spawn_async_1.default)('zip', ['-r', resignedApkUnalignedPath, '.', '-x', 'lib/*', '-x', 'resources.arsc'], {
|
|
97
106
|
cwd: unzipWorkingDirectory,
|
|
98
107
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
99
108
|
});
|
|
100
|
-
await
|
|
109
|
+
await (0, spawn_async_1.default)(zipalignPath, ['-v', '-p', '4', resignedApkUnalignedPath, resignedApkPath], {
|
|
101
110
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
102
111
|
});
|
|
103
112
|
const signerArgs = [
|
|
@@ -114,20 +123,21 @@ export async function createResignedApkAsync(binaryApkPath, appConfigPath, optio
|
|
|
114
123
|
signerArgs.push('--key-pass', signingOptions.keyPassword);
|
|
115
124
|
}
|
|
116
125
|
signerArgs.push(resignedApkPath);
|
|
117
|
-
await
|
|
126
|
+
await (0, spawn_async_1.default)(apksignerPath, signerArgs, {
|
|
118
127
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
119
128
|
});
|
|
120
129
|
return resignedApkPath;
|
|
121
130
|
}
|
|
131
|
+
exports.createResignedApkAsync = createResignedApkAsync;
|
|
122
132
|
/**
|
|
123
133
|
* Find the latest build-tools directory in the `ANDROID_SDK_ROOT` directory.
|
|
124
134
|
*/
|
|
125
|
-
|
|
135
|
+
async function findLatestBuildToolsDirAsync() {
|
|
126
136
|
const androidSdkRoot = process.env['ANDROID_SDK_ROOT'];
|
|
127
|
-
|
|
128
|
-
const buildToolsDir =
|
|
137
|
+
(0, node_assert_1.default)(androidSdkRoot != null, 'ANDROID_SDK_ROOT environment variable is not set');
|
|
138
|
+
const buildToolsDir = node_path_1.default.join(androidSdkRoot, 'build-tools');
|
|
129
139
|
try {
|
|
130
|
-
const entries = await
|
|
140
|
+
const entries = await promises_1.default.readdir(buildToolsDir, { withFileTypes: true });
|
|
131
141
|
const dirs = entries
|
|
132
142
|
.filter((entry) => entry.isDirectory())
|
|
133
143
|
.map((dir) => dir.name)
|
|
@@ -143,7 +153,7 @@ export async function findLatestBuildToolsDirAsync() {
|
|
|
143
153
|
return versionB.length - versionA.length;
|
|
144
154
|
});
|
|
145
155
|
if (dirs.length > 0) {
|
|
146
|
-
return
|
|
156
|
+
return node_path_1.default.join(buildToolsDir, dirs[0]);
|
|
147
157
|
}
|
|
148
158
|
else {
|
|
149
159
|
console.error('No build-tools directories found.');
|
|
@@ -155,3 +165,4 @@ export async function findLatestBuildToolsDirAsync() {
|
|
|
155
165
|
return null;
|
|
156
166
|
}
|
|
157
167
|
}
|
|
168
|
+
exports.findLatestBuildToolsDirAsync = findLatestBuildToolsDirAsync;
|
package/build/android/index.d.ts
CHANGED
package/build/android/index.js
CHANGED
|
@@ -1,53 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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 log_1 = __importDefault(require("../log"));
|
|
15
|
+
const utils_1 = require("../utils");
|
|
10
16
|
/**
|
|
11
17
|
* Repack an Android app.
|
|
12
18
|
*/
|
|
13
|
-
|
|
14
|
-
const options = await normalizeOptionAsync(_options);
|
|
19
|
+
async function repackAppAndroidAsync(_options) {
|
|
20
|
+
const options = await (0, utils_1.normalizeOptionAsync)(_options);
|
|
15
21
|
const { workingDirectory } = options;
|
|
16
|
-
await
|
|
17
|
-
const { exp } = getConfig(options.projectRoot, {
|
|
22
|
+
await promises_1.default.mkdir(workingDirectory, { recursive: true });
|
|
23
|
+
const { exp } = (0, config_1.getConfig)(options.projectRoot, {
|
|
18
24
|
isPublicConfig: true,
|
|
19
25
|
skipSDKVersionRequirement: true,
|
|
20
26
|
});
|
|
21
|
-
const updatesRuntimeVersion = await resolveRuntimeVersionAsync(options, exp);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const { androidManiestFilePath, resourcesPbFilePath } = await createProtoBasedResourcesAsync(options);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
await updateAndroidManifestAsync(exp, androidManiestFilePath, options, updatesRuntimeVersion);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await updateResourcesAsync(exp, resourcesPbFilePath);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const binaryApkPath = await createBinaryBasedResourcesAsync(options);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const appConfigPath = await generateAppConfigAsync(options, exp);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const outputApk = await createResignedApkAsync(binaryApkPath, appConfigPath, options, {
|
|
27
|
+
const updatesRuntimeVersion = await (0, expo_1.resolveRuntimeVersionAsync)(options, exp);
|
|
28
|
+
log_1.default.log(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
|
|
29
|
+
log_1.default.time(`Unzipping APK and creating proto based resources`);
|
|
30
|
+
const { androidManiestFilePath, resourcesPbFilePath } = await (0, build_tools_1.createProtoBasedResourcesAsync)(options);
|
|
31
|
+
log_1.default.timeEnd(`Unzipping APK and creating proto based resources`);
|
|
32
|
+
log_1.default.time(`Updating Androidmanifest.xml`);
|
|
33
|
+
await (0, resources_1.updateAndroidManifestAsync)(exp, androidManiestFilePath, options, updatesRuntimeVersion);
|
|
34
|
+
log_1.default.timeEnd(`Updating Androidmanifest.xml`);
|
|
35
|
+
log_1.default.time(`Updating resources.pb`);
|
|
36
|
+
await (0, resources_1.updateResourcesAsync)(exp, resourcesPbFilePath);
|
|
37
|
+
log_1.default.timeEnd(`Updating resources.pb`);
|
|
38
|
+
log_1.default.time(`Creating binary based resources`);
|
|
39
|
+
const binaryApkPath = await (0, build_tools_1.createBinaryBasedResourcesAsync)(options);
|
|
40
|
+
log_1.default.timeEnd(`Creating binary based resources`);
|
|
41
|
+
log_1.default.time(`Generating app.config`);
|
|
42
|
+
const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
|
|
43
|
+
log_1.default.timeEnd(`Generating app.config`);
|
|
44
|
+
log_1.default.time(`Creating updated apk`);
|
|
45
|
+
const outputApk = await (0, build_tools_1.createResignedApkAsync)(binaryApkPath, appConfigPath, options, {
|
|
40
46
|
keyStorePath: options.androidSigningOptions?.keyStorePath ??
|
|
41
|
-
|
|
47
|
+
node_path_1.default.resolve(__dirname, '../assets/debug.keystore'),
|
|
42
48
|
keyStorePassword: options.androidSigningOptions?.keyStorePassword ?? 'android',
|
|
43
49
|
keyAlias: options.androidSigningOptions?.keyAlias,
|
|
44
50
|
keyPassword: options.androidSigningOptions?.keyPassword,
|
|
45
51
|
});
|
|
46
|
-
|
|
47
|
-
await
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
log_1.default.timeEnd(`Creating updated apk`);
|
|
53
|
+
await promises_1.default.rename(outputApk, options.outputPath);
|
|
54
|
+
if (!options.skipWorkingDirCleanup) {
|
|
55
|
+
try {
|
|
56
|
+
await promises_1.default.rmdir(workingDirectory, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
50
59
|
}
|
|
51
|
-
catch { }
|
|
52
60
|
return options.outputPath;
|
|
53
61
|
}
|
|
62
|
+
exports.repackAppAndroidAsync = repackAppAndroidAsync;
|
|
@@ -1,36 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
const root = await
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
const root = await
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
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 commander_1 = require("commander");
|
|
7
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
|
+
const index_1 = require("./index");
|
|
9
|
+
const index_2 = require("./ios/index");
|
|
10
|
+
const log_1 = __importDefault(require("./log"));
|
|
11
|
+
const program = new commander_1.Command('repack-app')
|
|
7
12
|
.requiredOption('-p, --platform <platform>', 'Platform to repack the app for')
|
|
8
13
|
.requiredOption('--source-app <path>', 'Path to the source app file')
|
|
9
14
|
.option('--android-build-tools-dir <path>', 'Path to the Android build tools directory')
|
|
@@ -25,7 +30,7 @@ async function runAsync() {
|
|
|
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,
|
|
@@ -40,7 +45,7 @@ async function runAsync() {
|
|
|
40
45
|
},
|
|
41
46
|
androidBuildToolsDir: program.opts().androidBuildToolsDir,
|
|
42
47
|
});
|
|
43
|
-
|
|
48
|
+
log_1.default.log(picocolors_1.default.green(`Updated APK created at ${outputPath}`));
|
|
44
49
|
}
|
|
45
50
|
else if (platform === 'ios') {
|
|
46
51
|
const options = {
|
|
@@ -55,12 +60,12 @@ async function runAsync() {
|
|
|
55
60
|
provisioningProfile: program.opts().provisioningProfile,
|
|
56
61
|
},
|
|
57
62
|
};
|
|
58
|
-
const outputPath = await repackAppIosAsync(options);
|
|
59
|
-
|
|
63
|
+
const outputPath = await (0, index_1.repackAppIosAsync)(options);
|
|
64
|
+
log_1.default.log(picocolors_1.default.green(`Updated IPA created at ${outputPath}`));
|
|
60
65
|
if (options.iosSigningOptions?.signingIdentity &&
|
|
61
66
|
options.iosSigningOptions?.provisioningProfile) {
|
|
62
|
-
|
|
63
|
-
await resignIpaAsync(outputPath, options.iosSigningOptions, options);
|
|
67
|
+
log_1.default.log(picocolors_1.default.green(`Resigning the IPA at ${outputPath}`));
|
|
68
|
+
await (0, index_2.resignIpaAsync)(outputPath, options.iosSigningOptions, options);
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
else {
|
package/build/expo.d.ts
CHANGED
package/build/expo.js
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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 spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
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
|
-
|
|
14
|
+
async function generateAppConfigAsync(options, config) {
|
|
9
15
|
const { workingDirectory } = options;
|
|
10
|
-
const appConfigPath =
|
|
11
|
-
await
|
|
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
|
-
|
|
24
|
+
async function resolveRuntimeVersionAsync(options, config) {
|
|
18
25
|
const { projectRoot } = options;
|
|
19
|
-
const cli =
|
|
20
|
-
|
|
21
|
-
const proc = await
|
|
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, spawn_async_1.default)(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
|
|
2
|
-
export { repackAppIosAsync } from './ios
|
|
1
|
+
export { repackAppAndroidAsync } from './android';
|
|
2
|
+
export { repackAppIosAsync } from './ios';
|
package/build/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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; } });
|
package/build/ios/build-tools.js
CHANGED
|
@@ -1,43 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
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
|
-
|
|
10
|
-
const unzipWorkingDirectory =
|
|
11
|
-
await
|
|
15
|
+
async function unzipIpaAsync(options) {
|
|
16
|
+
const unzipWorkingDirectory = node_path_1.default.join(options.workingDirectory, 'unzip');
|
|
17
|
+
await (0, spawn_async_1.default)('unzip', [options.sourceAppPath, '-d', unzipWorkingDirectory], {
|
|
12
18
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
13
19
|
});
|
|
14
|
-
const appWorkingDirectory =
|
|
15
|
-
|
|
20
|
+
const appWorkingDirectory = node_path_1.default.join(unzipWorkingDirectory, 'Payload', 'HelloWorld.app');
|
|
21
|
+
(0, node_assert_1.default)(await (0, utils_1.directoryExistsAsync)(appWorkingDirectory));
|
|
16
22
|
return appWorkingDirectory;
|
|
17
23
|
}
|
|
24
|
+
exports.unzipIpaAsync = unzipIpaAsync;
|
|
18
25
|
/**
|
|
19
26
|
* Update some binary files.
|
|
20
27
|
*/
|
|
21
|
-
|
|
22
|
-
const parentDir =
|
|
23
|
-
const newAppWorkingDirectory =
|
|
28
|
+
async function updateFilesAsync(config, appWorkingDirectory) {
|
|
29
|
+
const parentDir = node_path_1.default.dirname(appWorkingDirectory);
|
|
30
|
+
const newAppWorkingDirectory = node_path_1.default.join(parentDir, `${config.name}.app`);
|
|
24
31
|
// [0] Update the .app directory
|
|
25
|
-
await
|
|
32
|
+
await promises_1.default.rename(node_path_1.default.join(parentDir, 'HelloWorld.app'), newAppWorkingDirectory);
|
|
26
33
|
// [1] Rename the executable
|
|
27
|
-
await
|
|
34
|
+
await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory, 'HelloWorld'), node_path_1.default.join(newAppWorkingDirectory, config.name));
|
|
28
35
|
return newAppWorkingDirectory;
|
|
29
36
|
}
|
|
37
|
+
exports.updateFilesAsync = updateFilesAsync;
|
|
30
38
|
/**
|
|
31
39
|
* From the given working .app directory, create a new .ipa file.
|
|
32
40
|
*/
|
|
33
|
-
|
|
41
|
+
async function createIpaAsync(options, appWorkingDirectory) {
|
|
34
42
|
const { workingDirectory } = options;
|
|
35
|
-
await
|
|
36
|
-
await
|
|
37
|
-
const outputIpaPath =
|
|
38
|
-
await
|
|
43
|
+
await promises_1.default.mkdir(node_path_1.default.join(workingDirectory, 'Payload'), { recursive: true });
|
|
44
|
+
await promises_1.default.rename(appWorkingDirectory, node_path_1.default.join(workingDirectory, 'Payload', node_path_1.default.basename(appWorkingDirectory)));
|
|
45
|
+
const outputIpaPath = node_path_1.default.join(workingDirectory, 'repacked.ipa');
|
|
46
|
+
await (0, spawn_async_1.default)('zip', ['-r', outputIpaPath, 'Payload'], {
|
|
39
47
|
cwd: workingDirectory,
|
|
40
48
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
41
49
|
});
|
|
42
50
|
return outputIpaPath;
|
|
43
51
|
}
|
|
52
|
+
exports.createIpaAsync = createIpaAsync;
|
package/build/ios/index.d.ts
CHANGED
package/build/ios/index.js
CHANGED
|
@@ -1,54 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
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 log_1 = __importDefault(require("../log"));
|
|
16
|
+
const utils_1 = require("../utils");
|
|
11
17
|
/**
|
|
12
18
|
* Repack an iOS app.
|
|
13
19
|
*/
|
|
14
|
-
|
|
15
|
-
const options = await normalizeOptionAsync(_options);
|
|
20
|
+
async function repackAppIosAsync(_options) {
|
|
21
|
+
const options = await (0, utils_1.normalizeOptionAsync)(_options);
|
|
16
22
|
const { workingDirectory } = options;
|
|
17
|
-
await
|
|
18
|
-
const { exp } = getConfig(options.projectRoot, {
|
|
23
|
+
await promises_1.default.mkdir(workingDirectory, { recursive: true });
|
|
24
|
+
const { exp } = (0, config_1.getConfig)(options.projectRoot, {
|
|
19
25
|
isPublicConfig: true,
|
|
20
26
|
skipSDKVersionRequirement: true,
|
|
21
27
|
});
|
|
22
|
-
const updatesRuntimeVersion = await resolveRuntimeVersionAsync(options, exp);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let appWorkingDirectory = await unzipIpaAsync(options);
|
|
26
|
-
appWorkingDirectory = await updateFilesAsync(exp, appWorkingDirectory);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
await updateInfoPlistAsync(exp,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
await updateExpoPlistAsync(exp,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const appConfigPath = await generateAppConfigAsync(options, exp);
|
|
36
|
-
await
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const outputIpa = await createIpaAsync(options, appWorkingDirectory);
|
|
40
|
-
|
|
41
|
-
await
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
const updatesRuntimeVersion = await (0, expo_1.resolveRuntimeVersionAsync)(options, exp);
|
|
29
|
+
log_1.default.log(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
|
|
30
|
+
log_1.default.time(`Unzipping IPA`);
|
|
31
|
+
let appWorkingDirectory = await (0, build_tools_1.unzipIpaAsync)(options);
|
|
32
|
+
appWorkingDirectory = await (0, build_tools_1.updateFilesAsync)(exp, appWorkingDirectory);
|
|
33
|
+
log_1.default.timeEnd(`Unzipping IPA`);
|
|
34
|
+
log_1.default.time(`Updating Info.plist`);
|
|
35
|
+
await (0, resources_1.updateInfoPlistAsync)(exp, node_path_1.default.join(appWorkingDirectory, 'Info.plist'), options);
|
|
36
|
+
log_1.default.timeEnd(`Updating Info.plist`);
|
|
37
|
+
log_1.default.time(`Updating Expo.plist`);
|
|
38
|
+
await (0, resources_1.updateExpoPlistAsync)(exp, node_path_1.default.join(appWorkingDirectory, 'Expo.plist'), updatesRuntimeVersion, options);
|
|
39
|
+
log_1.default.timeEnd(`Updating Expo.plist`);
|
|
40
|
+
log_1.default.time(`Generating app.config`);
|
|
41
|
+
const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
|
|
42
|
+
await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(appWorkingDirectory, 'EXConstants.bundle', 'app.config'));
|
|
43
|
+
log_1.default.timeEnd(`Generating app.config`);
|
|
44
|
+
log_1.default.time(`Creating updated ipa`);
|
|
45
|
+
const outputIpa = await (0, build_tools_1.createIpaAsync)(options, appWorkingDirectory);
|
|
46
|
+
log_1.default.timeEnd(`Creating updated ipa`);
|
|
47
|
+
await promises_1.default.rename(outputIpa, options.outputPath);
|
|
48
|
+
if (!options.skipWorkingDirCleanup) {
|
|
49
|
+
try {
|
|
50
|
+
await promises_1.default.rmdir(workingDirectory, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
44
53
|
}
|
|
45
|
-
catch { }
|
|
46
54
|
return options.outputPath;
|
|
47
55
|
}
|
|
56
|
+
exports.repackAppIosAsync = repackAppIosAsync;
|
|
48
57
|
/**
|
|
49
58
|
* Resign an IPA by fastlane.
|
|
50
59
|
*/
|
|
51
|
-
|
|
60
|
+
async function resignIpaAsync(ipaPath, signingOptions, options) {
|
|
52
61
|
const args = [
|
|
53
62
|
'run',
|
|
54
63
|
'resign',
|
|
@@ -59,7 +68,8 @@ export async function resignIpaAsync(ipaPath, signingOptions, options) {
|
|
|
59
68
|
if (signingOptions.keychainPath) {
|
|
60
69
|
args.push(`keychain_path:${signingOptions.keychainPath}`);
|
|
61
70
|
}
|
|
62
|
-
await
|
|
71
|
+
await (0, spawn_async_1.default)('fastlane', args, {
|
|
63
72
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
64
73
|
});
|
|
65
74
|
}
|
|
75
|
+
exports.resignIpaAsync = resignIpaAsync;
|
package/build/ios/resources.d.ts
CHANGED
package/build/ios/resources.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
9
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const utils_1 = require("../utils");
|
|
6
12
|
/**
|
|
7
13
|
* Update the Info.plist file.
|
|
8
14
|
*/
|
|
9
|
-
|
|
15
|
+
async function updateInfoPlistAsync(config, infoPlistPath, options) {
|
|
10
16
|
const bundleIdentifier = config.ios?.bundleIdentifier;
|
|
11
|
-
|
|
17
|
+
(0, node_assert_1.default)(bundleIdentifier, 'The `bundleIdentifier` must be specified in the `ios` config.');
|
|
12
18
|
const firstScheme = Array.isArray(config.scheme) ? config.scheme[0] : config.scheme;
|
|
13
19
|
await updateBinaryPlistAsync(infoPlistPath, options, (data) => {
|
|
14
20
|
const urlTypes = data.CFBundleURLTypes.slice();
|
|
@@ -17,11 +23,11 @@ export async function updateInfoPlistAsync(config, infoPlistPath, options) {
|
|
|
17
23
|
const schemes = urlType.CFBundleURLSchemes.map((scheme) => {
|
|
18
24
|
return (scheme
|
|
19
25
|
// scheme in app.json
|
|
20
|
-
.replace(/^myapp$/g, requireNotNull(firstScheme))
|
|
26
|
+
.replace(/^myapp$/g, (0, utils_1.requireNotNull)(firstScheme))
|
|
21
27
|
// ios.bundleIdentifier in app.json
|
|
22
|
-
.replace(/^dev.expo.templatedefault$/g, requireNotNull(bundleIdentifier))
|
|
28
|
+
.replace(/^dev.expo.templatedefault$/g, (0, utils_1.requireNotNull)(bundleIdentifier))
|
|
23
29
|
// default scheme generated from slug in app.json
|
|
24
|
-
.replace(/^exp\+expo-template-default$/g, `exp+${requireNotNull(config.slug)}`));
|
|
30
|
+
.replace(/^exp\+expo-template-default$/g, `exp+${(0, utils_1.requireNotNull)(config.slug)}`));
|
|
25
31
|
});
|
|
26
32
|
urlType.CFBundleURLSchemes = schemes;
|
|
27
33
|
}
|
|
@@ -37,13 +43,14 @@ export async function updateInfoPlistAsync(config, infoPlistPath, options) {
|
|
|
37
43
|
};
|
|
38
44
|
});
|
|
39
45
|
}
|
|
46
|
+
exports.updateInfoPlistAsync = updateInfoPlistAsync;
|
|
40
47
|
/**
|
|
41
48
|
* Update the Expo.plist file.
|
|
42
49
|
*/
|
|
43
|
-
|
|
50
|
+
async function updateExpoPlistAsync(config, expoPlistPath, runtimeVersion, options) {
|
|
44
51
|
await updateBinaryPlistAsync(expoPlistPath, options, (data) => {
|
|
45
52
|
const updateUrl = config.updates?.url;
|
|
46
|
-
|
|
53
|
+
(0, node_assert_1.default)(updateUrl);
|
|
47
54
|
return {
|
|
48
55
|
...data,
|
|
49
56
|
EXUpdatesEnabled: true,
|
|
@@ -52,17 +59,18 @@ export async function updateExpoPlistAsync(config, expoPlistPath, runtimeVersion
|
|
|
52
59
|
};
|
|
53
60
|
});
|
|
54
61
|
}
|
|
62
|
+
exports.updateExpoPlistAsync = updateExpoPlistAsync;
|
|
55
63
|
//#region Internals
|
|
56
64
|
async function updateBinaryPlistAsync(plistPath, options, updater) {
|
|
57
|
-
await
|
|
65
|
+
await (0, spawn_async_1.default)('plutil', ['-convert', 'xml1', plistPath], {
|
|
58
66
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
59
67
|
});
|
|
60
|
-
const contents = await
|
|
61
|
-
const data = await
|
|
68
|
+
const contents = await promises_1.default.readFile(plistPath, 'utf8');
|
|
69
|
+
const data = await plist_1.default.parse(contents);
|
|
62
70
|
const updatedData = updater(data);
|
|
63
|
-
const updatedContents =
|
|
64
|
-
await
|
|
65
|
-
await
|
|
71
|
+
const updatedContents = plist_1.default.build(updatedData);
|
|
72
|
+
await promises_1.default.writeFile(plistPath, updatedContents);
|
|
73
|
+
await (0, spawn_async_1.default)('plutil', ['-convert', 'binary1', plistPath], {
|
|
66
74
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
67
75
|
});
|
|
68
76
|
}
|
package/build/log.js
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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.timeEnd = exports.time = exports.error = exports.warn = exports.log = void 0;
|
|
7
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
|
+
function log(...message) {
|
|
3
9
|
console.log(...message);
|
|
4
10
|
}
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
exports.log = log;
|
|
12
|
+
function warn(...message) {
|
|
13
|
+
console.warn(...message.map((value) => picocolors_1.default.yellow(value)));
|
|
7
14
|
}
|
|
8
|
-
|
|
15
|
+
exports.warn = warn;
|
|
16
|
+
function error(...message) {
|
|
9
17
|
console.error(...message);
|
|
10
18
|
}
|
|
11
|
-
|
|
19
|
+
exports.error = error;
|
|
20
|
+
function time(label) {
|
|
12
21
|
console.time(label);
|
|
13
22
|
}
|
|
14
|
-
|
|
23
|
+
exports.time = time;
|
|
24
|
+
function timeEnd(label) {
|
|
15
25
|
console.timeEnd(label);
|
|
16
26
|
}
|
|
17
|
-
|
|
27
|
+
exports.timeEnd = timeEnd;
|
|
28
|
+
exports.default = {
|
|
18
29
|
log,
|
|
19
30
|
warn,
|
|
20
31
|
error,
|
package/build/types.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ export interface Options {
|
|
|
37
37
|
* The options for signing the ios app.
|
|
38
38
|
*/
|
|
39
39
|
iosSigningOptions?: IosSigningOptions;
|
|
40
|
+
/**
|
|
41
|
+
* Skip the working directory cleanup.
|
|
42
|
+
*/
|
|
43
|
+
skipWorkingDirCleanup?: boolean;
|
|
40
44
|
}
|
|
41
45
|
export interface NormalizedOptions extends Options {
|
|
42
46
|
workingDirectory: NonNullable<Options['workingDirectory']>;
|
package/build/types.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
package/build/utils.js
CHANGED
|
@@ -1,27 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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"));
|
|
4
10
|
/**
|
|
5
11
|
* Check if a directory exists.
|
|
6
12
|
*/
|
|
7
|
-
|
|
8
|
-
return (await
|
|
13
|
+
async function directoryExistsAsync(file) {
|
|
14
|
+
return (await promises_1.default.stat(file).catch(() => null))?.isDirectory() ?? false;
|
|
9
15
|
}
|
|
16
|
+
exports.directoryExistsAsync = directoryExistsAsync;
|
|
10
17
|
/**
|
|
11
18
|
* Return the non-null value.
|
|
12
19
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
function requireNotNull(value) {
|
|
21
|
+
(0, node_assert_1.default)(value != null, 'Expected value to be non-null');
|
|
15
22
|
return value;
|
|
16
23
|
}
|
|
24
|
+
exports.requireNotNull = requireNotNull;
|
|
17
25
|
/**
|
|
18
26
|
* Normalize the options.
|
|
19
27
|
*/
|
|
20
|
-
|
|
28
|
+
async function normalizeOptionAsync(options) {
|
|
21
29
|
const fileExt = options.platform === 'android' ? '.apk' : '.ipa';
|
|
22
30
|
return {
|
|
23
31
|
...options,
|
|
24
|
-
workingDirectory: options.workingDirectory ?? (await
|
|
25
|
-
outputPath: options.outputPath ??
|
|
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}`),
|
|
26
34
|
};
|
|
27
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
|
+
"version": "0.0.4",
|
|
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": "
|
|
7
|
+
"type": "commonjs",
|
|
9
8
|
"scripts": {
|
|
10
9
|
"build": "expo-module build",
|
|
11
10
|
"clean": "expo-module clean",
|
|
@@ -27,7 +26,7 @@
|
|
|
27
26
|
"license": "BUSL-1.1",
|
|
28
27
|
"dependencies": {
|
|
29
28
|
"@expo/config": "^8.5.6",
|
|
30
|
-
"@expo/
|
|
29
|
+
"@expo/spawn-async": "^1.7.2",
|
|
31
30
|
"commander": "^12.0.0",
|
|
32
31
|
"glob": "^10.3.12",
|
|
33
32
|
"picocolors": "^1.0.0",
|
|
@@ -40,5 +39,8 @@
|
|
|
40
39
|
"eslint": "^8.57.0",
|
|
41
40
|
"expo-module-scripts": "^3.4.2",
|
|
42
41
|
"typescript": "^5.4.5"
|
|
42
|
+
},
|
|
43
|
+
"volta": {
|
|
44
|
+
"node": "22.1.0"
|
|
43
45
|
}
|
|
44
46
|
}
|