@expo/repack-app 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/android/apktool.js +25 -14
- package/build/android/build-tools.d.ts +1 -1
- package/build/android/build-tools.js +11 -12
- package/build/android/index.js +24 -17
- package/build/cli.js +2 -2
- package/build/expo.d.ts +4 -0
- package/build/expo.js +24 -10
- package/build/ios/build-tools.js +5 -8
- package/build/ios/index.js +31 -22
- package/build/ios/resources.js +9 -6
- package/build/types.d.ts +37 -5
- package/build/types.js +1 -0
- package/build/utils.d.ts +9 -1
- package/build/utils.js +33 -0
- package/package.json +2 -3
package/build/android/apktool.js
CHANGED
|
@@ -7,7 +7,6 @@ exports.decodeApkAsync = decodeApkAsync;
|
|
|
7
7
|
exports.rebuildApkAsync = rebuildApkAsync;
|
|
8
8
|
exports.addBundleAssetsToDecodedApkAsync = addBundleAssetsToDecodedApkAsync;
|
|
9
9
|
exports.addRenameManifestPackageAsync = addRenameManifestPackageAsync;
|
|
10
|
-
const steps_1 = require("@expo/steps");
|
|
11
10
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
12
11
|
const glob_1 = require("glob");
|
|
13
12
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
@@ -18,23 +17,31 @@ let cachedApktoolPath = null;
|
|
|
18
17
|
* Decode the APK file using APKTool.
|
|
19
18
|
*/
|
|
20
19
|
async function decodeApkAsync(apkPath, options) {
|
|
21
|
-
const { workingDirectory } = options;
|
|
20
|
+
const { spawnAsync, workingDirectory } = options;
|
|
22
21
|
const apktoolPath = await getApktoolPathAsync();
|
|
23
22
|
const outputPath = 'decoded-apk';
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
await spawnAsync('java', ['-jar', apktoolPath, 'decode', apkPath, '-s', '-o', outputPath], {
|
|
24
|
+
cwd: workingDirectory,
|
|
25
|
+
});
|
|
27
26
|
return node_path_1.default.join(workingDirectory, outputPath);
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
29
|
* Rebuild the decoded APK file using APKTool.
|
|
31
30
|
*/
|
|
32
31
|
async function rebuildApkAsync(decodedApkRoot, options) {
|
|
33
|
-
const { workingDirectory } = options;
|
|
32
|
+
const { spawnAsync, workingDirectory } = options;
|
|
34
33
|
const apktoolPath = await getApktoolPathAsync();
|
|
35
34
|
const apktoolPackedApkName = 'apktool-packed.apk';
|
|
36
35
|
const apktoolPackedApkPath = node_path_1.default.resolve(workingDirectory, apktoolPackedApkName);
|
|
37
|
-
await
|
|
36
|
+
await spawnAsync('java', [
|
|
37
|
+
'-jar',
|
|
38
|
+
apktoolPath,
|
|
39
|
+
'build',
|
|
40
|
+
'-o',
|
|
41
|
+
apktoolPackedApkPath,
|
|
42
|
+
'--use-aapt2',
|
|
43
|
+
decodedApkRoot,
|
|
44
|
+
]);
|
|
38
45
|
return apktoolPackedApkPath;
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
@@ -66,7 +73,7 @@ async function getApktoolPathAsync() {
|
|
|
66
73
|
}
|
|
67
74
|
return cachedApktoolPath;
|
|
68
75
|
}
|
|
69
|
-
async function addApktoolResourceAsync(decodedApkRoot,
|
|
76
|
+
async function addApktoolResourceAsync(decodedApkRoot, assetMap) {
|
|
70
77
|
const apktoolPublicXmlPath = node_path_1.default.join(decodedApkRoot, 'res/values/public.xml');
|
|
71
78
|
// [0] Retrieve the current max resource ID and the existing drawable names
|
|
72
79
|
const contents = await promises_1.default.readFile(apktoolPublicXmlPath, 'utf8');
|
|
@@ -97,8 +104,12 @@ async function addApktoolResourceAsync(decodedApkRoot, assetSet) {
|
|
|
97
104
|
}
|
|
98
105
|
const drawableResIdBoundary = (maxDrawableResId & 0xffff0000) + 0x10000;
|
|
99
106
|
const rawResIdBoundary = (maxRawResId & 0xffff0000) + 0x10000;
|
|
100
|
-
const
|
|
101
|
-
for (const
|
|
107
|
+
const newAssetNameSet = new Set([...assetMap.keys()].filter((name) => !existingNameSet.has(name)));
|
|
108
|
+
for (const assetName of newAssetNameSet) {
|
|
109
|
+
const asset = assetMap.get(assetName);
|
|
110
|
+
if (asset == null) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
102
113
|
if (asset.type === 'drawable') {
|
|
103
114
|
maxDrawableResId += 1;
|
|
104
115
|
(0, node_assert_1.default)(maxDrawableResId < drawableResIdBoundary, 'Drawable resource ID boundary exceeded.');
|
|
@@ -136,7 +147,7 @@ function getAssetType(name) {
|
|
|
136
147
|
* @param level The current depth level (starts at 0).
|
|
137
148
|
* @param maxLevel The maximum depth level to process.
|
|
138
149
|
*/
|
|
139
|
-
async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = undefined,
|
|
150
|
+
async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = undefined, assetMap = new Map()) {
|
|
140
151
|
await promises_1.default.mkdir(dest, { recursive: true });
|
|
141
152
|
const entries = await promises_1.default.readdir(src, { withFileTypes: true });
|
|
142
153
|
await Promise.all(entries.map(async (entry) => {
|
|
@@ -145,7 +156,7 @@ async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = u
|
|
|
145
156
|
if (entry.isDirectory()) {
|
|
146
157
|
const _assetType = assetType ?? getAssetType(entry.name);
|
|
147
158
|
if (level < maxLevel - 1) {
|
|
148
|
-
await copyAssetsAsync(srcPath, destPath, level + 1, maxLevel, _assetType,
|
|
159
|
+
await copyAssetsAsync(srcPath, destPath, level + 1, maxLevel, _assetType, assetMap);
|
|
149
160
|
}
|
|
150
161
|
else {
|
|
151
162
|
// When the maximum level is reached, copy the directory without further recursion
|
|
@@ -156,9 +167,9 @@ async function copyAssetsAsync(src, dest, level = 0, maxLevel = 2, assetType = u
|
|
|
156
167
|
await promises_1.default.copyFile(srcPath, destPath);
|
|
157
168
|
const { name } = node_path_1.default.parse(entry.name);
|
|
158
169
|
(0, node_assert_1.default)(assetType, 'Asset type must be defined.');
|
|
159
|
-
|
|
170
|
+
assetMap.set(name, { name, type: assetType });
|
|
160
171
|
}
|
|
161
172
|
}));
|
|
162
|
-
return
|
|
173
|
+
return assetMap;
|
|
163
174
|
}
|
|
164
175
|
//#endregion Internals
|
|
@@ -22,4 +22,4 @@ export declare function findLatestBuildToolsDirAsync(): Promise<string | null>;
|
|
|
22
22
|
/**
|
|
23
23
|
* Search for classes in the APK with the given app ID pattern passing to grep.
|
|
24
24
|
*/
|
|
25
|
-
export declare function searchDexClassesAsync(unzipApkRoot: string, grepAppIdPattern: string): Promise<Set<string>>;
|
|
25
|
+
export declare function searchDexClassesAsync(unzipApkRoot: string, grepAppIdPattern: string, options: NormalizedOptions): Promise<Set<string>>;
|
|
@@ -7,7 +7,6 @@ exports.getAndroidBuildToolsAsync = getAndroidBuildToolsAsync;
|
|
|
7
7
|
exports.createResignedApkAsync = createResignedApkAsync;
|
|
8
8
|
exports.findLatestBuildToolsDirAsync = findLatestBuildToolsDirAsync;
|
|
9
9
|
exports.searchDexClassesAsync = searchDexClassesAsync;
|
|
10
|
-
const steps_1 = require("@expo/steps");
|
|
11
10
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
12
11
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
13
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -34,17 +33,15 @@ async function getAndroidBuildToolsAsync(options) {
|
|
|
34
33
|
* @returns
|
|
35
34
|
*/
|
|
36
35
|
async function createResignedApkAsync(unzippedApkRoot, options, signingOptions) {
|
|
37
|
-
const { workingDirectory } = options;
|
|
36
|
+
const { spawnAsync, workingDirectory } = options;
|
|
38
37
|
const { apksignerPath, zipalignPath } = await getAndroidBuildToolsAsync(options);
|
|
39
38
|
const resignedApkPath = node_path_1.default.join(workingDirectory, 'resigned.apk');
|
|
40
39
|
const resignedApkUnalignedPath = node_path_1.default.resolve(workingDirectory, 'resigned-unaligned.apk');
|
|
41
|
-
await
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
: { cwd: unzippedApkRoot });
|
|
47
|
-
await (0, steps_1.spawnAsync)(zipalignPath, ['-v', '-p', '4', resignedApkUnalignedPath, resignedApkPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
|
|
40
|
+
await spawnAsync('zip', ['-r', '-0', resignedApkUnalignedPath, 'lib', 'resources.arsc'], {
|
|
41
|
+
cwd: unzippedApkRoot,
|
|
42
|
+
});
|
|
43
|
+
await spawnAsync('zip', ['-r', resignedApkUnalignedPath, '.', '-x', 'lib/*', '-x', 'resources.arsc'], { cwd: unzippedApkRoot });
|
|
44
|
+
await spawnAsync(zipalignPath, ['-v', '-p', '4', resignedApkUnalignedPath, resignedApkPath]);
|
|
48
45
|
const signerArgs = [
|
|
49
46
|
'sign',
|
|
50
47
|
'--ks',
|
|
@@ -59,7 +56,7 @@ async function createResignedApkAsync(unzippedApkRoot, options, signingOptions)
|
|
|
59
56
|
signerArgs.push('--key-pass', signingOptions.keyPassword);
|
|
60
57
|
}
|
|
61
58
|
signerArgs.push(resignedApkPath);
|
|
62
|
-
await
|
|
59
|
+
await spawnAsync(apksignerPath, signerArgs);
|
|
63
60
|
return resignedApkPath;
|
|
64
61
|
}
|
|
65
62
|
/**
|
|
@@ -101,12 +98,14 @@ async function findLatestBuildToolsDirAsync() {
|
|
|
101
98
|
/**
|
|
102
99
|
* Search for classes in the APK with the given app ID pattern passing to grep.
|
|
103
100
|
*/
|
|
104
|
-
async function searchDexClassesAsync(unzipApkRoot, grepAppIdPattern) {
|
|
101
|
+
async function searchDexClassesAsync(unzipApkRoot, grepAppIdPattern, options) {
|
|
102
|
+
const { spawnAsync } = options;
|
|
105
103
|
const { dexdumpPath } = await getAndroidBuildToolsAsync();
|
|
106
104
|
const grepPattern = `"^ Class descriptor : 'L${grepAppIdPattern.replace(/\./g, '/')}\\/"`;
|
|
107
|
-
const { stdout } = await
|
|
105
|
+
const { stdout } = await spawnAsync(dexdumpPath, ['classes*.dex', '|', 'grep', grepPattern], {
|
|
108
106
|
cwd: unzipApkRoot,
|
|
109
107
|
shell: true,
|
|
108
|
+
stdio: 'pipe',
|
|
110
109
|
});
|
|
111
110
|
const classes = stdout
|
|
112
111
|
.split('\n')
|
package/build/android/index.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.repackAppAndroidAsync = repackAppAndroidAsync;
|
|
7
|
-
const steps_1 = require("@expo/steps");
|
|
8
7
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
8
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -19,7 +18,7 @@ const utils_1 = require("../utils");
|
|
|
19
18
|
*/
|
|
20
19
|
async function repackAppAndroidAsync(_options) {
|
|
21
20
|
const options = await (0, utils_1.normalizeOptionAsync)(_options);
|
|
22
|
-
const {
|
|
21
|
+
const { logger, spawnAsync, workingDirectory } = options;
|
|
23
22
|
await promises_1.default.mkdir(workingDirectory, { recursive: true });
|
|
24
23
|
const { exp } = (0, expo_1.getExpoConfig)(options.projectRoot, {
|
|
25
24
|
isPublicConfig: true,
|
|
@@ -27,27 +26,35 @@ async function repackAppAndroidAsync(_options) {
|
|
|
27
26
|
});
|
|
28
27
|
const decodedApkRoot = await (0, apktool_1.decodeApkAsync)(node_path_1.default.resolve(options.sourceAppPath), options);
|
|
29
28
|
(0, node_assert_1.default)(exp.android?.package, 'Expected app ID (`android.package`) to be defined in app.json');
|
|
30
|
-
const
|
|
29
|
+
const useExpoUpdates = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot);
|
|
30
|
+
const updatesRuntimeVersion = useExpoUpdates
|
|
31
31
|
? await (0, expo_1.resolveRuntimeVersionAsync)(options, exp)
|
|
32
32
|
: null;
|
|
33
33
|
logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
|
|
34
34
|
if (options.exportEmbedOptions != null) {
|
|
35
|
-
logger.
|
|
35
|
+
logger.time(`Generating bundle`);
|
|
36
36
|
const { assetRoot, bundleOutputPath } = await (0, expo_1.generateBundleAssetsAsync)(exp, options);
|
|
37
|
-
logger.
|
|
38
|
-
logger.
|
|
37
|
+
logger.timeEnd(`Generating bundle`);
|
|
38
|
+
logger.time(`Adding bundled resources`);
|
|
39
39
|
await (0, apktool_1.addBundleAssetsToDecodedApkAsync)({
|
|
40
40
|
decodedApkRoot,
|
|
41
41
|
assetRoot,
|
|
42
42
|
bundleOutputPath,
|
|
43
43
|
});
|
|
44
|
-
logger.
|
|
44
|
+
logger.timeEnd(`Adding bundled resources`);
|
|
45
|
+
if (useExpoUpdates) {
|
|
46
|
+
logger.time(`Generating 'app.manifest' for expo-updates`);
|
|
47
|
+
const updateManifestFile = await (0, expo_1.generateUpdatesEmbeddedManifestAsync)(options);
|
|
48
|
+
const updateDirectory = node_path_1.default.join(decodedApkRoot, 'assets');
|
|
49
|
+
await promises_1.default.copyFile(updateManifestFile, node_path_1.default.join(updateDirectory, node_path_1.default.basename(updateManifestFile)));
|
|
50
|
+
logger.timeEnd(`Generating 'app.manifest' for expo-updates`);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
|
-
logger.
|
|
53
|
+
logger.time(`Updating Androidmanifest.xml`);
|
|
47
54
|
const androidManiestFilePath = node_path_1.default.join(decodedApkRoot, 'AndroidManifest.xml');
|
|
48
55
|
const androidManiestXml = await (0, resources_1.parseXmlFileAsync)(androidManiestFilePath);
|
|
49
56
|
const originalAppId = await (0, resources_1.queryAppIdFromManifestAsync)(androidManiestXml);
|
|
50
|
-
const dexClasses = await (0, build_tools_1.searchDexClassesAsync)(decodedApkRoot, originalAppId);
|
|
57
|
+
const dexClasses = await (0, build_tools_1.searchDexClassesAsync)(decodedApkRoot, originalAppId, options);
|
|
51
58
|
await (0, resources_1.updateAndroidManifestAsync)({
|
|
52
59
|
config: exp,
|
|
53
60
|
androidManiestXml,
|
|
@@ -58,19 +65,19 @@ async function repackAppAndroidAsync(_options) {
|
|
|
58
65
|
});
|
|
59
66
|
await (0, apktool_1.addRenameManifestPackageAsync)(decodedApkRoot, exp.android?.package);
|
|
60
67
|
await (0, resources_1.buildXmlFileAsync)(androidManiestXml, androidManiestFilePath);
|
|
61
|
-
logger.
|
|
62
|
-
logger.
|
|
68
|
+
logger.timeEnd(`Updating Androidmanifest.xml`);
|
|
69
|
+
logger.time(`Updating resources`);
|
|
63
70
|
await (0, resources_1.updateResourcesAsync)({ config: exp, decodedApkRoot });
|
|
64
|
-
logger.
|
|
65
|
-
logger.
|
|
71
|
+
logger.timeEnd(`Updating resources`);
|
|
72
|
+
logger.time(`Generating app config`);
|
|
66
73
|
const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
|
|
67
74
|
await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(decodedApkRoot, 'assets', 'app.config'));
|
|
68
|
-
logger.
|
|
69
|
-
logger.
|
|
75
|
+
logger.timeEnd(`Generating app config`);
|
|
76
|
+
logger.time(`Creating updated apk`);
|
|
70
77
|
const apktoolPackedApkPath = await (0, apktool_1.rebuildApkAsync)(decodedApkRoot, options);
|
|
71
78
|
const unzippedApkRoot = node_path_1.default.join(workingDirectory, 'unzip');
|
|
72
79
|
await promises_1.default.mkdir(unzippedApkRoot, { recursive: true });
|
|
73
|
-
await
|
|
80
|
+
await spawnAsync('unzip', ['-o', apktoolPackedApkPath, '-d', unzippedApkRoot]);
|
|
74
81
|
const outputApk = await (0, build_tools_1.createResignedApkAsync)(unzippedApkRoot, options, {
|
|
75
82
|
keyStorePath: options.androidSigningOptions?.keyStorePath ??
|
|
76
83
|
node_path_1.default.resolve(__dirname, '../../assets/debug.keystore'),
|
|
@@ -78,7 +85,7 @@ async function repackAppAndroidAsync(_options) {
|
|
|
78
85
|
keyAlias: options.androidSigningOptions?.keyAlias,
|
|
79
86
|
keyPassword: options.androidSigningOptions?.keyPassword,
|
|
80
87
|
});
|
|
81
|
-
logger.
|
|
88
|
+
logger.timeEnd(`Creating updated apk`);
|
|
82
89
|
await promises_1.default.rename(outputApk, options.outputPath);
|
|
83
90
|
if (!options.skipWorkingDirCleanup) {
|
|
84
91
|
try {
|
package/build/cli.js
CHANGED
|
@@ -3,10 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const logger_1 = require("@expo/logger");
|
|
7
6
|
const commander_1 = require("commander");
|
|
8
7
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
8
|
const index_1 = require("./index");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
10
|
const program = new commander_1.Command('repack-app')
|
|
11
11
|
.requiredOption('-p, --platform <platform>', 'Platform to repack the app for')
|
|
12
12
|
.requiredOption('--source-app <path>', 'Path to the source app file')
|
|
@@ -30,7 +30,7 @@ const program = new commander_1.Command('repack-app')
|
|
|
30
30
|
.version(require('../package.json').version)
|
|
31
31
|
.parse(process.argv);
|
|
32
32
|
async function runAsync() {
|
|
33
|
-
const logger =
|
|
33
|
+
const logger = new utils_1.ConsoleLogger();
|
|
34
34
|
const platform = program.opts().platform;
|
|
35
35
|
const projectRoot = program.processedArgs[0];
|
|
36
36
|
const exportEmbedOptions = program.opts()
|
package/build/expo.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import type { NormalizedOptions } from './types';
|
|
|
4
4
|
* Generate the app.config file for the Android app.
|
|
5
5
|
*/
|
|
6
6
|
export declare function generateAppConfigAsync(options: NormalizedOptions, config: ExpoConfig): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Generate the embedded `app.manifest` for expo-updates
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateUpdatesEmbeddedManifestAsync(options: NormalizedOptions): Promise<string>;
|
|
7
11
|
/**
|
|
8
12
|
* Returns whether expo-updates is installed in the project.
|
|
9
13
|
*/
|
package/build/expo.js
CHANGED
|
@@ -4,11 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateAppConfigAsync = generateAppConfigAsync;
|
|
7
|
+
exports.generateUpdatesEmbeddedManifestAsync = generateUpdatesEmbeddedManifestAsync;
|
|
7
8
|
exports.isExpoUpdatesInstalled = isExpoUpdatesInstalled;
|
|
8
9
|
exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
|
|
9
10
|
exports.getExpoConfig = getExpoConfig;
|
|
10
11
|
exports.generateBundleAssetsAsync = generateBundleAssetsAsync;
|
|
11
|
-
const steps_1 = require("@expo/steps");
|
|
12
12
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
13
13
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -22,6 +22,21 @@ async function generateAppConfigAsync(options, config) {
|
|
|
22
22
|
await promises_1.default.writeFile(appConfigPath, JSON.stringify(config));
|
|
23
23
|
return appConfigPath;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate the embedded `app.manifest` for expo-updates
|
|
27
|
+
*/
|
|
28
|
+
async function generateUpdatesEmbeddedManifestAsync(options) {
|
|
29
|
+
const { platform, projectRoot, spawnAsync, workingDirectory } = options;
|
|
30
|
+
// [0] Resolve entry point
|
|
31
|
+
const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot, stdio: 'pipe' });
|
|
32
|
+
// [1] Generate the `app.manifest` file
|
|
33
|
+
const binPath = resolve_from_1.default.silent(options.projectRoot, 'expo-updates/utils/build/createUpdatesResources.js');
|
|
34
|
+
(0, node_assert_1.default)(binPath, 'Failed to resolve `createUpdatesResources.js` within expo-updates package');
|
|
35
|
+
await spawnAsync('node', [binPath, platform, projectRoot, workingDirectory, 'all', entryFile.trim()], {
|
|
36
|
+
cwd: projectRoot,
|
|
37
|
+
});
|
|
38
|
+
return node_path_1.default.join(workingDirectory, 'app.manifest');
|
|
39
|
+
}
|
|
25
40
|
/**
|
|
26
41
|
* Returns whether expo-updates is installed in the project.
|
|
27
42
|
*/
|
|
@@ -36,13 +51,14 @@ function isExpoUpdatesInstalled(projectRoot) {
|
|
|
36
51
|
* Resolve the `runtimeVersion` for expo-updates.
|
|
37
52
|
*/
|
|
38
53
|
async function resolveRuntimeVersionAsync(options, config) {
|
|
39
|
-
const { projectRoot } = options;
|
|
54
|
+
const { projectRoot, spawnAsync } = options;
|
|
40
55
|
const cli = resolve_from_1.default.silent(projectRoot, 'expo-updates/bin/cli') ??
|
|
41
56
|
(0, resolve_from_1.default)(projectRoot, 'expo-updates/bin/cli.js');
|
|
42
|
-
const
|
|
57
|
+
const { stdout } = await spawnAsync(cli, ['runtimeversion:resolve', '--platform', 'android', '--workflow', 'managed'], {
|
|
43
58
|
cwd: projectRoot,
|
|
59
|
+
stdio: 'pipe',
|
|
44
60
|
});
|
|
45
|
-
const runtimeVersion = JSON.parse(
|
|
61
|
+
const runtimeVersion = JSON.parse(stdout).runtimeVersion;
|
|
46
62
|
return runtimeVersion ?? config.version ?? '1.0.0';
|
|
47
63
|
}
|
|
48
64
|
/**
|
|
@@ -69,11 +85,11 @@ function getExpoConfig(projectRoot, options) {
|
|
|
69
85
|
* Generate JS bundle and assets for the app.
|
|
70
86
|
*/
|
|
71
87
|
async function generateBundleAssetsAsync(expoConfig, options) {
|
|
72
|
-
const { projectRoot, platform, workingDirectory } = options;
|
|
88
|
+
const { projectRoot, platform, spawnAsync, workingDirectory } = options;
|
|
73
89
|
const bundleAssetRoot = node_path_1.default.resolve(workingDirectory, 'bundles');
|
|
74
90
|
await promises_1.default.mkdir(bundleAssetRoot, { recursive: true });
|
|
75
91
|
// [0] Resolve entry point
|
|
76
|
-
const { stdout: entryFile } = await
|
|
92
|
+
const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot, stdio: 'pipe' });
|
|
77
93
|
// [1] Execute export:embed
|
|
78
94
|
const isEnableHermes = isEnableHermesManaged(expoConfig, platform);
|
|
79
95
|
const bundleFileName = platform === 'android' ? 'index.android.bundle' : 'main.bundle';
|
|
@@ -96,7 +112,7 @@ async function generateBundleAssetsAsync(expoConfig, options) {
|
|
|
96
112
|
];
|
|
97
113
|
if (isEnableHermes) {
|
|
98
114
|
exportEmbedArgs.push('--minify', 'false');
|
|
99
|
-
exportEmbedArgs.push('--
|
|
115
|
+
exportEmbedArgs.push('--bytecode', 'true');
|
|
100
116
|
exportEmbedArgs.push('--unstable-transform-profile', 'hermes');
|
|
101
117
|
}
|
|
102
118
|
else {
|
|
@@ -105,9 +121,7 @@ async function generateBundleAssetsAsync(expoConfig, options) {
|
|
|
105
121
|
if (options.exportEmbedOptions?.sourcemapOutput) {
|
|
106
122
|
exportEmbedArgs.push('--sourcemap-output', options.exportEmbedOptions.sourcemapOutput);
|
|
107
123
|
}
|
|
108
|
-
await
|
|
109
|
-
? { logger: options.logger, stdio: 'pipe', cwd: projectRoot }
|
|
110
|
-
: { cwd: projectRoot });
|
|
124
|
+
await spawnAsync('npx', exportEmbedArgs, { cwd: projectRoot });
|
|
111
125
|
return {
|
|
112
126
|
bundleOutputPath,
|
|
113
127
|
assetRoot,
|
package/build/ios/build-tools.js
CHANGED
|
@@ -8,7 +8,6 @@ exports.updateFilesAsync = updateFilesAsync;
|
|
|
8
8
|
exports.createAppAsync = createAppAsync;
|
|
9
9
|
exports.createIpaAsync = createIpaAsync;
|
|
10
10
|
exports.createResignLane = createResignLane;
|
|
11
|
-
const steps_1 = require("@expo/steps");
|
|
12
11
|
const glob_1 = require("glob");
|
|
13
12
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
14
13
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
@@ -18,14 +17,14 @@ const utils_1 = require("../utils");
|
|
|
18
17
|
* Extract the given iOS artifact and return the path to the .app directory.
|
|
19
18
|
*/
|
|
20
19
|
async function extractIosArtifactAsync(options) {
|
|
21
|
-
const { sourceAppPath } = options;
|
|
20
|
+
const { spawnAsync, sourceAppPath } = options;
|
|
22
21
|
const extractedWorkingDirectory = node_path_1.default.join(options.workingDirectory, 'extracted');
|
|
23
22
|
await promises_1.default.mkdir(extractedWorkingDirectory, { recursive: true });
|
|
24
23
|
if (sourceAppPath.endsWith('.zip') || sourceAppPath.endsWith('.ipa')) {
|
|
25
|
-
await
|
|
24
|
+
await spawnAsync('unzip', [sourceAppPath, '-d', extractedWorkingDirectory]);
|
|
26
25
|
}
|
|
27
26
|
else if (sourceAppPath.endsWith('.tar.gz')) {
|
|
28
|
-
await
|
|
27
|
+
await spawnAsync('tar', ['-xzf', sourceAppPath, '-C', extractedWorkingDirectory]);
|
|
29
28
|
}
|
|
30
29
|
else if (sourceAppPath.endsWith('.app')) {
|
|
31
30
|
const basename = node_path_1.default.basename(sourceAppPath);
|
|
@@ -66,13 +65,11 @@ async function createAppAsync(options, appWorkingDirectory) {
|
|
|
66
65
|
* From the given working .app directory, create a new .ipa file.
|
|
67
66
|
*/
|
|
68
67
|
async function createIpaAsync(options, appWorkingDirectory) {
|
|
69
|
-
const { workingDirectory } = options;
|
|
68
|
+
const { spawnAsync, workingDirectory } = options;
|
|
70
69
|
await promises_1.default.mkdir(node_path_1.default.join(workingDirectory, 'Payload'), { recursive: true });
|
|
71
70
|
await promises_1.default.rename(appWorkingDirectory, node_path_1.default.join(workingDirectory, 'Payload', node_path_1.default.basename(appWorkingDirectory)));
|
|
72
71
|
const outputIpaPath = node_path_1.default.resolve(workingDirectory, 'repacked.ipa');
|
|
73
|
-
await
|
|
74
|
-
? { logger: options.logger, stdio: 'pipe', cwd: workingDirectory }
|
|
75
|
-
: { cwd: workingDirectory });
|
|
72
|
+
await spawnAsync('zip', ['-r', outputIpaPath, 'Payload'], { cwd: workingDirectory });
|
|
76
73
|
return outputIpaPath;
|
|
77
74
|
}
|
|
78
75
|
/**
|
package/build/ios/index.js
CHANGED
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.repackAppIosAsync = repackAppIosAsync;
|
|
7
7
|
exports.resignIpaAsync = resignIpaAsync;
|
|
8
|
-
const steps_1 = require("@expo/steps");
|
|
9
8
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
10
9
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -26,39 +25,50 @@ async function repackAppIosAsync(_options) {
|
|
|
26
25
|
skipSDKVersionRequirement: true,
|
|
27
26
|
});
|
|
28
27
|
(0, node_assert_1.default)(exp.ios?.bundleIdentifier, 'Expected app ID (`ios.bundleIdentifier`) to be defined in app.json');
|
|
29
|
-
const
|
|
28
|
+
const useExpoUpdates = (0, expo_1.isExpoUpdatesInstalled)(options.projectRoot);
|
|
29
|
+
const updatesRuntimeVersion = useExpoUpdates
|
|
30
30
|
? await (0, expo_1.resolveRuntimeVersionAsync)(options, exp)
|
|
31
31
|
: null;
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (useExpoUpdates) {
|
|
33
|
+
logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
|
|
34
|
+
}
|
|
35
|
+
logger.time(`Extracting artifact from ${options.sourceAppPath}`);
|
|
34
36
|
let appWorkingDirectory = await (0, build_tools_1.extractIosArtifactAsync)(options);
|
|
35
37
|
appWorkingDirectory = await (0, build_tools_1.updateFilesAsync)(exp, appWorkingDirectory);
|
|
36
38
|
const infoPlistPath = node_path_1.default.join(appWorkingDirectory, 'Info.plist');
|
|
37
39
|
const originalAppId = await (0, resources_1.queryAppIdFromPlistAsync)(infoPlistPath, options);
|
|
38
|
-
logger.
|
|
40
|
+
logger.timeEnd(`Extracting artifact from ${options.sourceAppPath}`);
|
|
39
41
|
if (options.exportEmbedOptions != null) {
|
|
40
|
-
logger.
|
|
42
|
+
logger.time(`Generating bundle`);
|
|
41
43
|
const { assetRoot, bundleOutputPath } = await (0, expo_1.generateBundleAssetsAsync)(exp, options);
|
|
42
|
-
logger.
|
|
43
|
-
logger.
|
|
44
|
+
logger.timeEnd(`Generating bundle`);
|
|
45
|
+
logger.time(`Adding bundled resources`);
|
|
44
46
|
await (0, resources_1.addBundleAssetsAsync)({
|
|
45
47
|
appWorkingDirectory,
|
|
46
48
|
assetRoot,
|
|
47
49
|
bundleOutputPath,
|
|
48
50
|
});
|
|
49
|
-
logger.
|
|
51
|
+
logger.timeEnd(`Adding bundled resources`);
|
|
52
|
+
if (useExpoUpdates) {
|
|
53
|
+
logger.time(`Generating 'app.manifest' for expo-updates`);
|
|
54
|
+
const updateManifestFile = await (0, expo_1.generateUpdatesEmbeddedManifestAsync)(options);
|
|
55
|
+
const updateDirectory = node_path_1.default.join(appWorkingDirectory, 'EXUpdates.bundle');
|
|
56
|
+
(0, node_assert_1.default)(await (0, utils_1.directoryExistsAsync)(updateDirectory), 'Expected EXUpdates.bundle directory to exist');
|
|
57
|
+
await promises_1.default.copyFile(updateManifestFile, node_path_1.default.join(updateDirectory, node_path_1.default.basename(updateManifestFile)));
|
|
58
|
+
logger.timeEnd(`Generating 'app.manifest' for expo-updates`);
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
|
-
logger.
|
|
61
|
+
logger.time(`Updating Info.plist`);
|
|
52
62
|
await (0, resources_1.updateInfoPlistAsync)({ config: exp, infoPlistPath, originalAppId, options });
|
|
53
|
-
logger.
|
|
54
|
-
logger.
|
|
63
|
+
logger.timeEnd(`Updating Info.plist`);
|
|
64
|
+
logger.time(`Updating Expo.plist`);
|
|
55
65
|
await (0, resources_1.updateExpoPlistAsync)(exp, node_path_1.default.join(appWorkingDirectory, 'Expo.plist'), updatesRuntimeVersion, options);
|
|
56
|
-
logger.
|
|
57
|
-
logger.
|
|
66
|
+
logger.timeEnd(`Updating Expo.plist`);
|
|
67
|
+
logger.time(`Generating app.config`);
|
|
58
68
|
const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
|
|
59
69
|
await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(appWorkingDirectory, 'EXConstants.bundle', 'app.config'));
|
|
60
|
-
logger.
|
|
61
|
-
logger.
|
|
70
|
+
logger.timeEnd(`Generating app.config`);
|
|
71
|
+
logger.time(`Creating updated artifact`);
|
|
62
72
|
let outputArtifact;
|
|
63
73
|
if (options.outputPath.endsWith('.app')) {
|
|
64
74
|
outputArtifact = await (0, build_tools_1.createAppAsync)(options, appWorkingDirectory);
|
|
@@ -66,12 +76,12 @@ async function repackAppIosAsync(_options) {
|
|
|
66
76
|
else {
|
|
67
77
|
outputArtifact = await (0, build_tools_1.createIpaAsync)(options, appWorkingDirectory);
|
|
68
78
|
}
|
|
69
|
-
logger.
|
|
79
|
+
logger.timeEnd(`Creating updated artifact`);
|
|
70
80
|
if (options.iosSigningOptions) {
|
|
71
81
|
(0, node_assert_1.default)(outputArtifact.endsWith('.ipa'), 'Signing is only supported for .ipa files');
|
|
72
|
-
logger.
|
|
82
|
+
logger.time(`Resigning the IPA at ${outputArtifact}`);
|
|
73
83
|
await resignIpaAsync(outputArtifact, options.iosSigningOptions, options);
|
|
74
|
-
logger.
|
|
84
|
+
logger.timeEnd(`Resigning the IPA at ${outputArtifact}`);
|
|
75
85
|
}
|
|
76
86
|
await promises_1.default.rename(outputArtifact, options.outputPath);
|
|
77
87
|
if (!options.skipWorkingDirCleanup) {
|
|
@@ -87,13 +97,12 @@ async function repackAppIosAsync(_options) {
|
|
|
87
97
|
*/
|
|
88
98
|
async function resignIpaAsync(ipaPath, signingOptions, _options) {
|
|
89
99
|
const options = await (0, utils_1.normalizeOptionAsync)(_options);
|
|
100
|
+
const { spawnAsync } = options;
|
|
90
101
|
const resignWorkingDirectory = node_path_1.default.join(options.workingDirectory, 'fastlane');
|
|
91
102
|
await promises_1.default.mkdir(resignWorkingDirectory, { recursive: true });
|
|
92
103
|
const fastfileContents = (0, build_tools_1.createResignLane)('resign_ipa', ipaPath, signingOptions);
|
|
93
104
|
await promises_1.default.writeFile(node_path_1.default.join(resignWorkingDirectory, 'Fastfile'), fastfileContents);
|
|
94
|
-
await
|
|
95
|
-
logger: options.logger,
|
|
96
|
-
stdio: 'pipe',
|
|
105
|
+
await spawnAsync('fastlane', ['resign_ipa'], {
|
|
97
106
|
cwd: resignWorkingDirectory,
|
|
98
107
|
env: options.env,
|
|
99
108
|
});
|
package/build/ios/resources.js
CHANGED
|
@@ -8,7 +8,6 @@ exports.updateExpoPlistAsync = updateExpoPlistAsync;
|
|
|
8
8
|
exports.queryAppIdFromPlistAsync = queryAppIdFromPlistAsync;
|
|
9
9
|
exports.addBundleAssetsAsync = addBundleAssetsAsync;
|
|
10
10
|
const plist_1 = __importDefault(require("@expo/plist"));
|
|
11
|
-
const steps_1 = require("@expo/steps");
|
|
12
11
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
13
12
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -36,15 +35,18 @@ async function updateInfoPlistAsync({ config, infoPlistPath, originalAppId, opti
|
|
|
36
35
|
urlType.CFBundleURLSchemes = schemes;
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
|
-
const
|
|
40
|
-
return {
|
|
38
|
+
const result = {
|
|
41
39
|
...data,
|
|
42
40
|
CFBundleDisplayName: config.name,
|
|
43
41
|
CFBundleName: config.name,
|
|
44
42
|
CFBundleExecutable: config.name,
|
|
45
43
|
CFBundleIdentifier: bundleIdentifier,
|
|
46
|
-
NSUserActivityTypes: userActivityTypes,
|
|
47
44
|
};
|
|
45
|
+
const userActivityTypes = data.NSUserActivityTypes?.map((activityType) => activityType.replace(originalAppId, bundleIdentifier));
|
|
46
|
+
if (userActivityTypes) {
|
|
47
|
+
result.NSUserActivityTypes = userActivityTypes;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
52
|
/**
|
|
@@ -92,12 +94,13 @@ async function addBundleAssetsAsync({ appWorkingDirectory, assetRoot, bundleOutp
|
|
|
92
94
|
}
|
|
93
95
|
//#region Internals
|
|
94
96
|
async function updateBinaryPlistAsync(plistPath, options, updater) {
|
|
95
|
-
|
|
97
|
+
const { spawnAsync } = options;
|
|
98
|
+
await spawnAsync('plutil', ['-convert', 'xml1', plistPath]);
|
|
96
99
|
const contents = await promises_1.default.readFile(plistPath, 'utf8');
|
|
97
100
|
const data = await plist_1.default.parse(contents);
|
|
98
101
|
const updatedData = updater(data);
|
|
99
102
|
const updatedContents = plist_1.default.build(updatedData);
|
|
100
103
|
await promises_1.default.writeFile(plistPath, updatedContents);
|
|
101
|
-
await
|
|
104
|
+
await spawnAsync('plutil', ['-convert', 'binary1', plistPath]);
|
|
102
105
|
}
|
|
103
106
|
//#endregion
|
package/build/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChildProcess, SpawnOptions } from 'child_process';
|
|
2
2
|
export interface Options {
|
|
3
3
|
/**
|
|
4
4
|
* The platform to repack the app for.
|
|
@@ -12,10 +12,6 @@ export interface Options {
|
|
|
12
12
|
* The prebuilt app file path that acts as a source for repacking.
|
|
13
13
|
*/
|
|
14
14
|
sourceAppPath: string;
|
|
15
|
-
/**
|
|
16
|
-
* The bunyan logger instance.
|
|
17
|
-
*/
|
|
18
|
-
logger: bunyan;
|
|
19
15
|
/**
|
|
20
16
|
* Working directory.
|
|
21
17
|
*/
|
|
@@ -54,10 +50,22 @@ export interface Options {
|
|
|
54
50
|
* env variables
|
|
55
51
|
*/
|
|
56
52
|
env?: Record<string, string | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* Customize the logger instance.
|
|
55
|
+
* @default `console`
|
|
56
|
+
*/
|
|
57
|
+
logger?: Logger;
|
|
58
|
+
/**
|
|
59
|
+
* Customize the spawn process function.
|
|
60
|
+
* @default `@expo/spawn-async`
|
|
61
|
+
*/
|
|
62
|
+
spawnAsync?: SpawnProcessAsync;
|
|
57
63
|
}
|
|
58
64
|
export interface NormalizedOptions extends Options {
|
|
59
65
|
workingDirectory: NonNullable<Options['workingDirectory']>;
|
|
60
66
|
outputPath: NonNullable<Options['outputPath']>;
|
|
67
|
+
logger: NonNullable<Options['logger']>;
|
|
68
|
+
spawnAsync: NonNullable<Options['spawnAsync']>;
|
|
61
69
|
}
|
|
62
70
|
export interface AndroidSigningOptions {
|
|
63
71
|
/**
|
|
@@ -109,3 +117,27 @@ export interface ExportEmbedOptions {
|
|
|
109
117
|
*/
|
|
110
118
|
sourcemapOutput?: string;
|
|
111
119
|
}
|
|
120
|
+
export interface Logger {
|
|
121
|
+
debug(...message: any[]): void;
|
|
122
|
+
info(...message: any[]): void;
|
|
123
|
+
warn(...message: any[]): void;
|
|
124
|
+
error(...message: any[]): void;
|
|
125
|
+
time(label: string): void;
|
|
126
|
+
timeEnd(label: string): void;
|
|
127
|
+
}
|
|
128
|
+
export interface SpawnProcessOptions extends SpawnOptions {
|
|
129
|
+
}
|
|
130
|
+
export interface SpawnProcessPromise<T> extends Promise<T> {
|
|
131
|
+
child: ChildProcess;
|
|
132
|
+
}
|
|
133
|
+
export interface SpawnProcessResult {
|
|
134
|
+
pid?: number;
|
|
135
|
+
output: string[];
|
|
136
|
+
stdout: string;
|
|
137
|
+
stderr: string;
|
|
138
|
+
status: number | null;
|
|
139
|
+
signal: string | null;
|
|
140
|
+
}
|
|
141
|
+
export interface SpawnProcessAsync {
|
|
142
|
+
(command: string, args: string[], options?: SpawnProcessOptions): SpawnProcessPromise<SpawnProcessResult>;
|
|
143
|
+
}
|
package/build/types.js
CHANGED
package/build/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NormalizedOptions, Options } from './types';
|
|
1
|
+
import type { Logger, NormalizedOptions, Options } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Check if a directory exists.
|
|
4
4
|
*/
|
|
@@ -11,6 +11,14 @@ export declare function requireNotNull<T>(value: T | null | undefined): T;
|
|
|
11
11
|
* Normalize the options.
|
|
12
12
|
*/
|
|
13
13
|
export declare function normalizeOptionAsync(options: Options): Promise<NormalizedOptions>;
|
|
14
|
+
export declare class ConsoleLogger implements Logger {
|
|
15
|
+
debug(...message: any[]): void;
|
|
16
|
+
info(...message: any[]): void;
|
|
17
|
+
warn(...message: any[]): void;
|
|
18
|
+
error(...message: any[]): void;
|
|
19
|
+
time(label: string): void;
|
|
20
|
+
timeEnd(label: string): void;
|
|
21
|
+
}
|
|
14
22
|
/**
|
|
15
23
|
* Copy a directory recursively.
|
|
16
24
|
*/
|
package/build/utils.js
CHANGED
|
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ConsoleLogger = void 0;
|
|
6
7
|
exports.directoryExistsAsync = directoryExistsAsync;
|
|
7
8
|
exports.requireNotNull = requireNotNull;
|
|
8
9
|
exports.normalizeOptionAsync = normalizeOptionAsync;
|
|
9
10
|
exports.copyDirAsync = copyDirAsync;
|
|
11
|
+
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
10
12
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
11
13
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
12
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -32,6 +34,37 @@ async function normalizeOptionAsync(options) {
|
|
|
32
34
|
...options,
|
|
33
35
|
workingDirectory: options.workingDirectory ?? (await promises_1.default.mkdtemp(node_path_1.default.join(require('temp-dir'), 'repack-app-'))),
|
|
34
36
|
outputPath: options.outputPath ?? node_path_1.default.join(options.projectRoot, `repacked${fileExt}`),
|
|
37
|
+
logger: options.logger ?? new ConsoleLogger(),
|
|
38
|
+
spawnAsync: options.spawnAsync ?? createDefaultSpawnAsync(!!options.verbose),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
class ConsoleLogger {
|
|
42
|
+
debug(...message) {
|
|
43
|
+
console.debug(...message);
|
|
44
|
+
}
|
|
45
|
+
info(...message) {
|
|
46
|
+
console.log(...message);
|
|
47
|
+
}
|
|
48
|
+
warn(...message) {
|
|
49
|
+
console.warn(...message);
|
|
50
|
+
}
|
|
51
|
+
error(...message) {
|
|
52
|
+
console.error(...message);
|
|
53
|
+
}
|
|
54
|
+
time(label) {
|
|
55
|
+
console.time(label);
|
|
56
|
+
}
|
|
57
|
+
timeEnd(label) {
|
|
58
|
+
console.timeEnd(label);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ConsoleLogger = ConsoleLogger;
|
|
62
|
+
function createDefaultSpawnAsync(verbose) {
|
|
63
|
+
return function defaultSpawnAsync(command, args, options) {
|
|
64
|
+
return (0, spawn_async_1.default)(command, args, {
|
|
65
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
66
|
+
...options,
|
|
67
|
+
});
|
|
35
68
|
};
|
|
36
69
|
}
|
|
37
70
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/repack-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Repacking tool for Expo apps",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -25,9 +25,8 @@
|
|
|
25
25
|
"author": "650 Industries, Inc.",
|
|
26
26
|
"license": "BUSL-1.1",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@expo/logger": "^1.0.117",
|
|
29
28
|
"@expo/plist": "^0.2.0",
|
|
30
|
-
"@expo/
|
|
29
|
+
"@expo/spawn-async": "^1.7.2",
|
|
31
30
|
"commander": "^13.0.0",
|
|
32
31
|
"glob": "^11.0.0",
|
|
33
32
|
"picocolors": "^1.1.1",
|