@expo/repack-app 0.1.2 → 0.1.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/cli.js +4 -3
- package/build/ios/build-tools.d.ts +6 -2
- package/build/ios/build-tools.js +38 -10
- package/build/ios/index.js +16 -9
- package/build/ios/resources.js +1 -15
- package/build/utils.d.ts +4 -0
- package/build/utils.js +18 -0
- package/package.json +2 -2
package/build/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ const program = new commander_1.Command('repack-app')
|
|
|
12
12
|
.requiredOption('--source-app <path>', 'Path to the source app file')
|
|
13
13
|
.option('--android-build-tools-dir <path>', 'Path to the Android build tools directory')
|
|
14
14
|
.option('-w, --working-directory <path>', 'Path to the working directory')
|
|
15
|
-
.option('-o, --output <path>', 'Path to the output
|
|
15
|
+
.option('-o, --output <path>', 'Path to the output artifact')
|
|
16
16
|
// Android signing options
|
|
17
17
|
.option('--ks <path>', 'Path to the keystore file')
|
|
18
18
|
.option('--ks-pass <password>', 'Keystore password', 'pass:android')
|
|
@@ -26,12 +26,13 @@ const program = new commander_1.Command('repack-app')
|
|
|
26
26
|
.option('--embed-bundle-assets', 'Whether to execute export:embed to embed new bundle assets')
|
|
27
27
|
.option('--bundle-assets-sourcemap-output <path>', 'Paired with --embed-bundle-assets and generate the sourcemap to the specified path')
|
|
28
28
|
// arguments
|
|
29
|
-
.argument('
|
|
29
|
+
.argument('[project-root]', 'Path to the project root', process.cwd())
|
|
30
|
+
.version(require('../package.json').version)
|
|
30
31
|
.parse(process.argv);
|
|
31
32
|
async function runAsync() {
|
|
32
33
|
const logger = (0, logger_1.createLogger)({ name: 'repack-app' });
|
|
33
34
|
const platform = program.opts().platform;
|
|
34
|
-
const projectRoot = program.
|
|
35
|
+
const projectRoot = program.processedArgs[0];
|
|
35
36
|
const exportEmbedOptions = program.opts()
|
|
36
37
|
.embedBundleAssets
|
|
37
38
|
? {
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { type ExpoConfig } from '@expo/config';
|
|
2
2
|
import type { IosSigningOptions, NormalizedOptions } from '../types';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Extract the given iOS artifact and return the path to the .app directory.
|
|
5
5
|
*/
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function extractIosArtifactAsync(options: NormalizedOptions): Promise<string>;
|
|
7
7
|
/**
|
|
8
8
|
* Update some binary files.
|
|
9
9
|
*/
|
|
10
10
|
export declare function updateFilesAsync(config: ExpoConfig, appWorkingDirectory: string): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* From the given working .app directory, create a new .app directory.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAppAsync(options: NormalizedOptions, appWorkingDirectory: string): Promise<string>;
|
|
11
15
|
/**
|
|
12
16
|
* From the given working .app directory, create a new .ipa file.
|
|
13
17
|
*/
|
package/build/ios/build-tools.js
CHANGED
|
@@ -3,37 +3,65 @@ 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.
|
|
6
|
+
exports.extractIosArtifactAsync = extractIosArtifactAsync;
|
|
7
7
|
exports.updateFilesAsync = updateFilesAsync;
|
|
8
|
+
exports.createAppAsync = createAppAsync;
|
|
8
9
|
exports.createIpaAsync = createIpaAsync;
|
|
9
10
|
exports.createResignLane = createResignLane;
|
|
10
11
|
const steps_1 = require("@expo/steps");
|
|
12
|
+
const glob_1 = require("glob");
|
|
11
13
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
12
14
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
13
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
14
16
|
const utils_1 = require("../utils");
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
18
|
+
* Extract the given iOS artifact and return the path to the .app directory.
|
|
17
19
|
*/
|
|
18
|
-
async function
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(
|
|
20
|
+
async function extractIosArtifactAsync(options) {
|
|
21
|
+
const { sourceAppPath } = options;
|
|
22
|
+
const extractedWorkingDirectory = node_path_1.default.join(options.workingDirectory, 'extracted');
|
|
23
|
+
await promises_1.default.mkdir(extractedWorkingDirectory, { recursive: true });
|
|
24
|
+
if (sourceAppPath.endsWith('.zip') || sourceAppPath.endsWith('.ipa')) {
|
|
25
|
+
await (0, steps_1.spawnAsync)('unzip', [sourceAppPath, '-d', extractedWorkingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
|
|
26
|
+
}
|
|
27
|
+
else if (sourceAppPath.endsWith('.tar.gz')) {
|
|
28
|
+
await (0, steps_1.spawnAsync)('tar', ['-xzf', sourceAppPath, '-C', extractedWorkingDirectory], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
|
|
29
|
+
}
|
|
30
|
+
else if (sourceAppPath.endsWith('.app')) {
|
|
31
|
+
const basename = node_path_1.default.basename(sourceAppPath);
|
|
32
|
+
await (0, utils_1.copyDirAsync)(sourceAppPath, node_path_1.default.join(extractedWorkingDirectory, basename));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
throw new Error('Unsupported file type. Please provide a .zip, .tar.gz, or .app file.');
|
|
36
|
+
}
|
|
37
|
+
const matches = await (0, glob_1.glob)('{Payload/*.app,*.app}', {
|
|
38
|
+
cwd: extractedWorkingDirectory,
|
|
39
|
+
absolute: true,
|
|
40
|
+
});
|
|
41
|
+
const appWorkingDirectory = matches[0];
|
|
42
|
+
(0, node_assert_1.default)(appWorkingDirectory, `Failed to find the .app directory in the extracted artifact: ${extractedWorkingDirectory}`);
|
|
23
43
|
return appWorkingDirectory;
|
|
24
44
|
}
|
|
25
45
|
/**
|
|
26
46
|
* Update some binary files.
|
|
27
47
|
*/
|
|
28
48
|
async function updateFilesAsync(config, appWorkingDirectory) {
|
|
29
|
-
const parentDir = node_path_1.default.
|
|
49
|
+
const { dir: parentDir, name } = node_path_1.default.parse(appWorkingDirectory);
|
|
30
50
|
const newAppWorkingDirectory = node_path_1.default.join(parentDir, `${config.name}.app`);
|
|
31
51
|
// [0] Update the .app directory
|
|
32
|
-
await promises_1.default.rename(node_path_1.default.join(parentDir,
|
|
52
|
+
await promises_1.default.rename(node_path_1.default.join(parentDir, `${name}.app`), newAppWorkingDirectory);
|
|
33
53
|
// [1] Rename the executable
|
|
34
|
-
await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory,
|
|
54
|
+
await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory, name), node_path_1.default.join(newAppWorkingDirectory, config.name));
|
|
35
55
|
return newAppWorkingDirectory;
|
|
36
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* From the given working .app directory, create a new .app directory.
|
|
59
|
+
*/
|
|
60
|
+
async function createAppAsync(options, appWorkingDirectory) {
|
|
61
|
+
const outputAppPath = node_path_1.default.resolve(options.workingDirectory, node_path_1.default.basename(appWorkingDirectory));
|
|
62
|
+
await (0, utils_1.copyDirAsync)(appWorkingDirectory, outputAppPath);
|
|
63
|
+
return outputAppPath;
|
|
64
|
+
}
|
|
37
65
|
/**
|
|
38
66
|
* From the given working .app directory, create a new .ipa file.
|
|
39
67
|
*/
|
package/build/ios/index.js
CHANGED
|
@@ -30,12 +30,12 @@ async function repackAppIosAsync(_options) {
|
|
|
30
30
|
? await (0, expo_1.resolveRuntimeVersionAsync)(options, exp)
|
|
31
31
|
: null;
|
|
32
32
|
logger.info(picocolors_1.default.dim(`Resolved runtime version: ${updatesRuntimeVersion}`));
|
|
33
|
-
logger.info(`
|
|
34
|
-
let appWorkingDirectory = await (0, build_tools_1.
|
|
33
|
+
logger.info(`Extracting artifact from ${options.sourceAppPath}`);
|
|
34
|
+
let appWorkingDirectory = await (0, build_tools_1.extractIosArtifactAsync)(options);
|
|
35
35
|
appWorkingDirectory = await (0, build_tools_1.updateFilesAsync)(exp, appWorkingDirectory);
|
|
36
36
|
const infoPlistPath = node_path_1.default.join(appWorkingDirectory, 'Info.plist');
|
|
37
37
|
const originalAppId = await (0, resources_1.queryAppIdFromPlistAsync)(infoPlistPath, options);
|
|
38
|
-
logger.info(`Finished
|
|
38
|
+
logger.info(`Finished extracting artifact from ${options.sourceAppPath} ✅`);
|
|
39
39
|
if (options.exportEmbedOptions != null) {
|
|
40
40
|
logger.info(`Generating bundle`);
|
|
41
41
|
const { assetRoot, bundleOutputPath } = await (0, expo_1.generateBundleAssetsAsync)(exp, options);
|
|
@@ -58,15 +58,22 @@ async function repackAppIosAsync(_options) {
|
|
|
58
58
|
const appConfigPath = await (0, expo_1.generateAppConfigAsync)(options, exp);
|
|
59
59
|
await promises_1.default.copyFile(appConfigPath, node_path_1.default.join(appWorkingDirectory, 'EXConstants.bundle', 'app.config'));
|
|
60
60
|
logger.info(`Finished generating app.config ✅`);
|
|
61
|
-
logger.info(`Creating updated
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
logger.info(`Creating updated artifact`);
|
|
62
|
+
let outputArtifact;
|
|
63
|
+
if (options.outputPath.endsWith('.app')) {
|
|
64
|
+
outputArtifact = await (0, build_tools_1.createAppAsync)(options, appWorkingDirectory);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
outputArtifact = await (0, build_tools_1.createIpaAsync)(options, appWorkingDirectory);
|
|
68
|
+
}
|
|
69
|
+
logger.info(`Finished creating updated artifact ✅`);
|
|
64
70
|
if (options.iosSigningOptions) {
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
(0, node_assert_1.default)(outputArtifact.endsWith('.ipa'), 'Signing is only supported for .ipa files');
|
|
72
|
+
logger.info(`Resigning the IPA at ${outputArtifact}`);
|
|
73
|
+
await resignIpaAsync(outputArtifact, options.iosSigningOptions, options);
|
|
67
74
|
logger.info(`Finished resigning the IPA ✅`);
|
|
68
75
|
}
|
|
69
|
-
await promises_1.default.rename(
|
|
76
|
+
await promises_1.default.rename(outputArtifact, options.outputPath);
|
|
70
77
|
if (!options.skipWorkingDirCleanup) {
|
|
71
78
|
try {
|
|
72
79
|
await promises_1.default.rm(workingDirectory, { recursive: true });
|
package/build/ios/resources.js
CHANGED
|
@@ -88,7 +88,7 @@ async function addBundleAssetsAsync({ appWorkingDirectory, assetRoot, bundleOutp
|
|
|
88
88
|
await promises_1.default.copyFile(bundleOutputPath, node_path_1.default.join(appWorkingDirectory, 'main.jsbundle'));
|
|
89
89
|
// export:embed --assets-dest on iOS uses the app root as target directory,
|
|
90
90
|
// so we just need to copy the assets to the app root.
|
|
91
|
-
await copyDirAsync(assetRoot, appWorkingDirectory);
|
|
91
|
+
await (0, utils_1.copyDirAsync)(assetRoot, appWorkingDirectory);
|
|
92
92
|
}
|
|
93
93
|
//#region Internals
|
|
94
94
|
async function updateBinaryPlistAsync(plistPath, options, updater) {
|
|
@@ -100,18 +100,4 @@ async function updateBinaryPlistAsync(plistPath, options, updater) {
|
|
|
100
100
|
await promises_1.default.writeFile(plistPath, updatedContents);
|
|
101
101
|
await (0, steps_1.spawnAsync)('plutil', ['-convert', 'binary1', plistPath], options.verbose ? { logger: options.logger, stdio: 'pipe' } : undefined);
|
|
102
102
|
}
|
|
103
|
-
async function copyDirAsync(src, dst) {
|
|
104
|
-
await promises_1.default.mkdir(dst, { recursive: true });
|
|
105
|
-
const entries = await promises_1.default.readdir(src, { withFileTypes: true });
|
|
106
|
-
for (const entry of entries) {
|
|
107
|
-
const srcPath = node_path_1.default.join(src, entry.name);
|
|
108
|
-
const dstPath = node_path_1.default.join(dst, entry.name);
|
|
109
|
-
if (entry.isDirectory()) {
|
|
110
|
-
await copyDirAsync(srcPath, dstPath);
|
|
111
|
-
}
|
|
112
|
-
else if (entry.isFile()) {
|
|
113
|
-
await promises_1.default.copyFile(srcPath, dstPath);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
103
|
//#endregion
|
package/build/utils.d.ts
CHANGED
|
@@ -11,3 +11,7 @@ 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
|
+
/**
|
|
15
|
+
* Copy a directory recursively.
|
|
16
|
+
*/
|
|
17
|
+
export declare function copyDirAsync(src: string, dst: string): Promise<void>;
|
package/build/utils.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.directoryExistsAsync = directoryExistsAsync;
|
|
7
7
|
exports.requireNotNull = requireNotNull;
|
|
8
8
|
exports.normalizeOptionAsync = normalizeOptionAsync;
|
|
9
|
+
exports.copyDirAsync = copyDirAsync;
|
|
9
10
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
10
11
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -33,3 +34,20 @@ async function normalizeOptionAsync(options) {
|
|
|
33
34
|
outputPath: options.outputPath ?? node_path_1.default.join(options.projectRoot, `repacked${fileExt}`),
|
|
34
35
|
};
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Copy a directory recursively.
|
|
39
|
+
*/
|
|
40
|
+
async function copyDirAsync(src, dst) {
|
|
41
|
+
await promises_1.default.mkdir(dst, { recursive: true });
|
|
42
|
+
const entries = await promises_1.default.readdir(src, { withFileTypes: true });
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
const srcPath = node_path_1.default.join(src, entry.name);
|
|
45
|
+
const dstPath = node_path_1.default.join(dst, entry.name);
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
await copyDirAsync(srcPath, dstPath);
|
|
48
|
+
}
|
|
49
|
+
else if (entry.isFile()) {
|
|
50
|
+
await promises_1.default.copyFile(srcPath, dstPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/repack-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Repacking tool for Expo apps",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"glob": "^11.0.0",
|
|
33
33
|
"picocolors": "^1.1.1",
|
|
34
34
|
"resolve-from": "^5.0.0",
|
|
35
|
-
"temp-dir": "^
|
|
35
|
+
"temp-dir": "^2.0.0",
|
|
36
36
|
"xml2js": "^0.6.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|