@expo/repack-app 0.1.9 → 0.1.11
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/README.md +110 -0
- package/bin/cli.js +98 -2
- package/{build/types.d.ts → dist/index.d.ts} +218 -143
- package/dist/index.js +34 -0
- package/package.json +23 -19
- package/build/android/apktool.d.ts +0 -21
- package/build/android/apktool.js +0 -175
- package/build/android/build-tools.d.ts +0 -25
- package/build/android/build-tools.js +0 -122
- package/build/android/index.d.ts +0 -5
- package/build/android/index.js +0 -104
- package/build/android/resources.d.ts +0 -69
- package/build/android/resources.js +0 -198
- package/build/cli.d.ts +0 -1
- package/build/cli.js +0 -95
- package/build/expo.d.ts +0 -30
- package/build/expo.js +0 -144
- package/build/index.d.ts +0 -3
- package/build/index.js +0 -22
- package/build/ios/build-tools.d.ts +0 -22
- package/build/ios/build-tools.js +0 -123
- package/build/ios/index.d.ts +0 -9
- package/build/ios/index.js +0 -109
- package/build/ios/resources.d.ts +0 -27
- package/build/ios/resources.js +0 -111
- package/build/types.js +0 -3
- package/build/utils.d.ts +0 -25
- package/build/utils.js +0 -86
- /package/{assets → dist}/apktool_2.10.0.jar +0 -0
- /package/{assets → dist}/debug.keystore +0 -0
|
@@ -1,198 +0,0 @@
|
|
|
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.updateResourcesAsync = updateResourcesAsync;
|
|
7
|
-
exports.updateAndroidManifestAsync = updateAndroidManifestAsync;
|
|
8
|
-
exports.parseXmlFileAsync = parseXmlFileAsync;
|
|
9
|
-
exports.buildXmlFileAsync = buildXmlFileAsync;
|
|
10
|
-
exports.queryAppIdFromManifest = queryAppIdFromManifest;
|
|
11
|
-
exports.queryAppIdFallbackFromManifest = queryAppIdFallbackFromManifest;
|
|
12
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
13
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
-
const xml2js_1 = require("xml2js");
|
|
16
|
-
const utils_1 = require("../utils");
|
|
17
|
-
/**
|
|
18
|
-
* Update resources in the decoded APK.
|
|
19
|
-
*/
|
|
20
|
-
async function updateResourcesAsync({ config, decodedApkRoot, }) {
|
|
21
|
-
// [0] Update the `app_name` in **res/values/strings.xml**.
|
|
22
|
-
const stringsXmlPath = node_path_1.default.join(decodedApkRoot, 'res', 'values', 'strings.xml');
|
|
23
|
-
let stringsXml = await promises_1.default.readFile(stringsXmlPath, 'utf8');
|
|
24
|
-
stringsXml = stringsXml.replace(/(<string name="app_name">)(.+)(<\/string>)/, `$1${config.name}$3`);
|
|
25
|
-
await promises_1.default.writeFile(stringsXmlPath, stringsXml);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Update the proto-based AndroidManiest.xml.
|
|
29
|
-
*/
|
|
30
|
-
async function updateAndroidManifestAsync({ config, androidManiestXml, dexClasses, originalAppId, originalAppIdFallback, updatesRuntimeVersion, }) {
|
|
31
|
-
// [0] Update the package name
|
|
32
|
-
const appId = (0, utils_1.requireNotNull)(config.android?.package);
|
|
33
|
-
androidManiestXml.manifest.$.package = appId;
|
|
34
|
-
replaceXmlAttributeValue(androidManiestXml, (value) => {
|
|
35
|
-
if (value.startsWith(originalAppId) && !dexClasses.has(value)) {
|
|
36
|
-
return value.replace(originalAppId, appId);
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
39
|
-
});
|
|
40
|
-
// [1] Update the scheme in the intent-filters
|
|
41
|
-
const intentFilterViewActionNodes = findXmlNodes('manifest', androidManiestXml.manifest, (nodeName, node) => nodeName === 'intent-filter' &&
|
|
42
|
-
findXmlNodes(nodeName, node, (nodeName, node) => nodeName === 'action' && node[0].$?.['android:name'] === 'android.intent.action.VIEW').length > 0);
|
|
43
|
-
const firstScheme = Array.isArray(config.scheme) ? config.scheme[0] : config.scheme;
|
|
44
|
-
for (const node of intentFilterViewActionNodes) {
|
|
45
|
-
replaceXmlAttributeValue(node, (value) => {
|
|
46
|
-
let newValue = value
|
|
47
|
-
// android.package in app.json
|
|
48
|
-
.replace(originalAppId, appId)
|
|
49
|
-
// default scheme generated from slug in app.json
|
|
50
|
-
.replace(/^exp\+.+$/g, `exp+${(0, utils_1.requireNotNull)(config.slug)}`);
|
|
51
|
-
// scheme in app.json
|
|
52
|
-
if (firstScheme) {
|
|
53
|
-
newValue = newValue.replace(/^myapp$/g, firstScheme);
|
|
54
|
-
}
|
|
55
|
-
// android.package in app.json (fallback for applicationIdSuffix)
|
|
56
|
-
if (originalAppIdFallback != null) {
|
|
57
|
-
newValue = newValue.replace(originalAppIdFallback, appId);
|
|
58
|
-
}
|
|
59
|
-
return newValue;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
// [2] expo-updates configuration
|
|
63
|
-
const mainApplicationNode = androidManiestXml.manifest.application[0];
|
|
64
|
-
(0, node_assert_1.default)(mainApplicationNode.$?.['android:name']?.endsWith('.MainApplication'), 'Expected application node to be named as MainApplication');
|
|
65
|
-
if (updatesRuntimeVersion) {
|
|
66
|
-
mutateExpoUpdatesConfigAsync(mainApplicationNode, config, updatesRuntimeVersion);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Parse the XML file.
|
|
71
|
-
*/
|
|
72
|
-
async function parseXmlFileAsync(filePath) {
|
|
73
|
-
const contents = await promises_1.default.readFile(filePath, 'utf8');
|
|
74
|
-
const parser = new xml2js_1.Parser();
|
|
75
|
-
const xmlJs = await parser.parseStringPromise(contents);
|
|
76
|
-
return xmlJs;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Build the XML file from the given node.
|
|
80
|
-
*/
|
|
81
|
-
async function buildXmlFileAsync(rootNode, filePath) {
|
|
82
|
-
const builder = new xml2js_1.Builder();
|
|
83
|
-
const xmlString = builder.buildObject(rootNode);
|
|
84
|
-
await promises_1.default.writeFile(filePath, xmlString);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Query the app ID from the AndroidManiest.xml.
|
|
88
|
-
*/
|
|
89
|
-
function queryAppIdFromManifest(androidManiestXml) {
|
|
90
|
-
const packageAttr = androidManiestXml.manifest.$.package;
|
|
91
|
-
(0, node_assert_1.default)(packageAttr != null, 'Expected package attribute to be present in AndroidManifest.xml');
|
|
92
|
-
return packageAttr;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Query the fallback original app ID from the AndroidManiest.xml.
|
|
96
|
-
* Sometimes if the app were using `appIdSuffix` or overriding `applicationId` from build variants,
|
|
97
|
-
* the app ID in the `package` attribute will not be the original app ID for dex files
|
|
98
|
-
* As a fallback, we use a heuristic to retrieve original app ID from application class name.
|
|
99
|
-
*/
|
|
100
|
-
function queryAppIdFallbackFromManifest(androidManiestXml, appId) {
|
|
101
|
-
const applicationClassName = androidManiestXml.manifest.application?.[0]?.$?.['android:name'];
|
|
102
|
-
if (applicationClassName) {
|
|
103
|
-
const appIdWithoutSuffix = applicationClassName.replace(/\.[^.]+$/, '');
|
|
104
|
-
if (appIdWithoutSuffix !== applicationClassName && appIdWithoutSuffix !== appId) {
|
|
105
|
-
return appIdWithoutSuffix;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
//#region Internals
|
|
111
|
-
/**
|
|
112
|
-
* Update the `expo-updates` configuration in the Android project.
|
|
113
|
-
*/
|
|
114
|
-
function mutateExpoUpdatesConfigAsync(mainApplicationNode, config, runtimeVersion) {
|
|
115
|
-
const metaDataNodes = mainApplicationNode['meta-data'] ?? [];
|
|
116
|
-
// [0] expo.modules.updates.ENABLED
|
|
117
|
-
const updateEnabledNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.ENABLED');
|
|
118
|
-
if (updateEnabledNode != null) {
|
|
119
|
-
(0, node_assert_1.default)(updateEnabledNode.$ != null);
|
|
120
|
-
updateEnabledNode.$['android:value'] = 'true';
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
metaDataNodes.push({
|
|
124
|
-
$: {
|
|
125
|
-
'android:name': 'expo.modules.updates.ENABLED',
|
|
126
|
-
'android:value': 'true',
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
// [1] expo.modules.updates.EXPO_RUNTIME_VERSION
|
|
131
|
-
const updateRuntimeVersionNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.EXPO_RUNTIME_VERSION');
|
|
132
|
-
if (updateRuntimeVersionNode != null) {
|
|
133
|
-
(0, node_assert_1.default)(updateRuntimeVersionNode.$ != null);
|
|
134
|
-
updateRuntimeVersionNode.$['android:value'] = runtimeVersion;
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
metaDataNodes.push({
|
|
138
|
-
$: {
|
|
139
|
-
'android:name': 'expo.modules.updates.EXPO_RUNTIME_VERSION',
|
|
140
|
-
'android:value': runtimeVersion,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
// [2] expo.modules.updates.EXPO_UPDATE_URL
|
|
145
|
-
const updateUrlNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.EXPO_UPDATE_URL');
|
|
146
|
-
const updateUrl = config.updates?.url;
|
|
147
|
-
(0, node_assert_1.default)(updateUrl);
|
|
148
|
-
if (updateUrlNode != null) {
|
|
149
|
-
(0, node_assert_1.default)(updateUrlNode.$ != null);
|
|
150
|
-
updateUrlNode.$['android:value'] = updateUrl;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
metaDataNodes.push({
|
|
154
|
-
$: {
|
|
155
|
-
'android:name': 'expo.modules.updates.EXPO_UPDATE_URL',
|
|
156
|
-
'android:value': updateUrl,
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Recursively find XML nodes that satisfy the given `predicate`.
|
|
163
|
-
*/
|
|
164
|
-
function findXmlNodes(rootNodeName, rootNode, predicate) {
|
|
165
|
-
if (rootNode == null) {
|
|
166
|
-
return [];
|
|
167
|
-
}
|
|
168
|
-
if (predicate(rootNodeName, rootNode)) {
|
|
169
|
-
return [rootNode];
|
|
170
|
-
}
|
|
171
|
-
return Object.keys(rootNode)
|
|
172
|
-
.filter((key) => key !== '$' && key !== '_')
|
|
173
|
-
.flatMap((key) => {
|
|
174
|
-
const node = rootNode[key];
|
|
175
|
-
return findXmlNodes(key, node, predicate);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Recursively replace the attribute values in the XML nodes's attributes.
|
|
180
|
-
*/
|
|
181
|
-
function replaceXmlAttributeValue(node, replacer) {
|
|
182
|
-
if (node == null) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const attrs = node.$ ?? {};
|
|
186
|
-
for (const [attrKey, attrValue] of Object.entries(attrs)) {
|
|
187
|
-
attrs[attrKey] = replacer(attrValue);
|
|
188
|
-
}
|
|
189
|
-
for (const [nodeKey, nodeValue] of Object.entries(node)) {
|
|
190
|
-
if (nodeKey === '$' || nodeKey === '_') {
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
replaceXmlAttributeValue(nodeValue, replacer);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
//#endregion
|
package/build/cli.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/cli.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
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 utils_1 = require("./utils");
|
|
10
|
-
const program = new commander_1.Command('repack-app')
|
|
11
|
-
.requiredOption('-p, --platform <platform>', 'Platform to repack the app for')
|
|
12
|
-
.requiredOption('--source-app <path>', 'Path to the source app file')
|
|
13
|
-
.option('--android-build-tools-dir <path>', 'Path to the Android build tools directory')
|
|
14
|
-
.option('-w, --working-directory <path>', 'Path to the working directory')
|
|
15
|
-
.option('-o, --output <path>', 'Path to the output artifact')
|
|
16
|
-
// Android signing options
|
|
17
|
-
.option('--ks <path>', 'Path to the keystore file')
|
|
18
|
-
.option('--ks-pass <password>', 'Keystore password', 'pass:android')
|
|
19
|
-
.option('--ks-key-alias <alias>', 'Keystore key alias')
|
|
20
|
-
.option('--ks-key-pass <password>', 'Keystore key password')
|
|
21
|
-
.option('-v, --verbose', 'Enable verbose logging')
|
|
22
|
-
// iOS signing options
|
|
23
|
-
.option('--signing-identity <identity>', 'Signing identity')
|
|
24
|
-
.option('--provisioning-profile <path>', 'Path to the provisioning profile')
|
|
25
|
-
// export:embed options
|
|
26
|
-
.option('--embed-bundle-assets', 'Whether to execute export:embed to embed new bundle assets')
|
|
27
|
-
.option('--bundle-assets-sourcemap-output <path>', 'Paired with --embed-bundle-assets and generate the sourcemap to the specified path')
|
|
28
|
-
// arguments
|
|
29
|
-
.argument('[project-root]', 'Path to the project root', process.cwd())
|
|
30
|
-
.version(require('../package.json').version)
|
|
31
|
-
.parse(process.argv);
|
|
32
|
-
async function runAsync() {
|
|
33
|
-
const logger = new utils_1.ConsoleLogger();
|
|
34
|
-
const platform = program.opts().platform;
|
|
35
|
-
const projectRoot = program.processedArgs[0];
|
|
36
|
-
const exportEmbedOptions = program.opts()
|
|
37
|
-
.embedBundleAssets
|
|
38
|
-
? {
|
|
39
|
-
sourcemapOutput: program.opts().bundleAssetsSourcemapOutput,
|
|
40
|
-
}
|
|
41
|
-
: undefined;
|
|
42
|
-
if (platform === 'android') {
|
|
43
|
-
const outputPath = await (0, index_1.repackAppAndroidAsync)({
|
|
44
|
-
platform: program.opts().platform,
|
|
45
|
-
projectRoot,
|
|
46
|
-
verbose: !!program.opts().verbose,
|
|
47
|
-
sourceAppPath: program.opts().sourceApp,
|
|
48
|
-
workingDirectory: program.opts().workingDirectory,
|
|
49
|
-
outputPath: program.opts().output,
|
|
50
|
-
exportEmbedOptions,
|
|
51
|
-
androidSigningOptions: {
|
|
52
|
-
keyStorePath: program.opts().ks,
|
|
53
|
-
keyStorePassword: program.opts().ksPass,
|
|
54
|
-
keyAlias: program.opts().ksKeyAlias,
|
|
55
|
-
keyPassword: program.opts().ksKeyPass,
|
|
56
|
-
},
|
|
57
|
-
androidBuildToolsDir: program.opts().androidBuildToolsDir,
|
|
58
|
-
logger,
|
|
59
|
-
});
|
|
60
|
-
logger.info(picocolors_1.default.green(`Updated APK created at ${outputPath}`));
|
|
61
|
-
}
|
|
62
|
-
else if (platform === 'ios') {
|
|
63
|
-
const iosSigningOptions = program.opts().signingIdentity && program.opts().provisioningProfile
|
|
64
|
-
? {
|
|
65
|
-
signingIdentity: program.opts().signingIdentity,
|
|
66
|
-
provisioningProfile: program.opts().provisioningProfile,
|
|
67
|
-
}
|
|
68
|
-
: undefined;
|
|
69
|
-
const options = {
|
|
70
|
-
platform: program.opts().platform,
|
|
71
|
-
projectRoot,
|
|
72
|
-
verbose: !!program.opts().verbose,
|
|
73
|
-
sourceAppPath: program.opts().sourceApp,
|
|
74
|
-
workingDirectory: program.opts().workingDirectory,
|
|
75
|
-
outputPath: program.opts().output,
|
|
76
|
-
exportEmbedOptions,
|
|
77
|
-
iosSigningOptions,
|
|
78
|
-
logger,
|
|
79
|
-
};
|
|
80
|
-
const outputPath = await (0, index_1.repackAppIosAsync)(options);
|
|
81
|
-
logger.info(picocolors_1.default.green(`Updated IPA created at ${outputPath}`));
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
throw new Error(`Unsupported platform: ${platform}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
(async () => {
|
|
88
|
-
try {
|
|
89
|
-
await runAsync();
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
console.error('Uncaught Error', e);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
})();
|
package/build/expo.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type ExpoConfig } from '@expo/config';
|
|
2
|
-
import type { NormalizedOptions } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Generate the app.config file for the Android app.
|
|
5
|
-
*/
|
|
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>;
|
|
11
|
-
/**
|
|
12
|
-
* Returns whether expo-updates is installed in the project.
|
|
13
|
-
*/
|
|
14
|
-
export declare function isExpoUpdatesInstalled(projectRoot: string): boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Resolve the `runtimeVersion` for expo-updates.
|
|
17
|
-
*/
|
|
18
|
-
export declare function resolveRuntimeVersionAsync(options: NormalizedOptions, config: ExpoConfig): Promise<string>;
|
|
19
|
-
/**
|
|
20
|
-
* A wrapper around `@expo/config`'s `getConfig` function
|
|
21
|
-
* that uses the `@expo/config` package from the project's transitive dependencies.
|
|
22
|
-
*/
|
|
23
|
-
export declare function getExpoConfig(projectRoot: string, options: Parameters<typeof import('@expo/config').getConfig>[1]): ReturnType<typeof import('@expo/config').getConfig>;
|
|
24
|
-
/**
|
|
25
|
-
* Generate JS bundle and assets for the app.
|
|
26
|
-
*/
|
|
27
|
-
export declare function generateBundleAssetsAsync(expoConfig: ExpoConfig, options: NormalizedOptions): Promise<{
|
|
28
|
-
bundleOutputPath: string;
|
|
29
|
-
assetRoot: string;
|
|
30
|
-
}>;
|
package/build/expo.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
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.generateAppConfigAsync = generateAppConfigAsync;
|
|
7
|
-
exports.generateUpdatesEmbeddedManifestAsync = generateUpdatesEmbeddedManifestAsync;
|
|
8
|
-
exports.isExpoUpdatesInstalled = isExpoUpdatesInstalled;
|
|
9
|
-
exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
|
|
10
|
-
exports.getExpoConfig = getExpoConfig;
|
|
11
|
-
exports.generateBundleAssetsAsync = generateBundleAssetsAsync;
|
|
12
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
13
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
-
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
16
|
-
/**
|
|
17
|
-
* Generate the app.config file for the Android app.
|
|
18
|
-
*/
|
|
19
|
-
async function generateAppConfigAsync(options, config) {
|
|
20
|
-
const { workingDirectory } = options;
|
|
21
|
-
const appConfigPath = node_path_1.default.join(workingDirectory, 'app.config');
|
|
22
|
-
await promises_1.default.writeFile(appConfigPath, JSON.stringify(config));
|
|
23
|
-
return appConfigPath;
|
|
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
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Returns whether expo-updates is installed in the project.
|
|
42
|
-
*/
|
|
43
|
-
function isExpoUpdatesInstalled(projectRoot) {
|
|
44
|
-
const expoConfigPath = resolveExpoConfigPath(projectRoot);
|
|
45
|
-
(0, node_assert_1.default)(expoConfigPath, `Failed to resolve '@expo/config' package in the project`);
|
|
46
|
-
const { getPackageJson } = require(expoConfigPath);
|
|
47
|
-
const packageJson = getPackageJson(projectRoot);
|
|
48
|
-
return !!(packageJson.dependencies && 'expo-updates' in packageJson.dependencies);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Resolve the `runtimeVersion` for expo-updates.
|
|
52
|
-
*/
|
|
53
|
-
async function resolveRuntimeVersionAsync(options, config) {
|
|
54
|
-
const { projectRoot, spawnAsync } = options;
|
|
55
|
-
const cli = resolve_from_1.default.silent(projectRoot, 'expo-updates/bin/cli') ??
|
|
56
|
-
(0, resolve_from_1.default)(projectRoot, 'expo-updates/bin/cli.js');
|
|
57
|
-
const { stdout } = await spawnAsync(cli, ['runtimeversion:resolve', '--platform', 'android', '--workflow', 'managed'], {
|
|
58
|
-
cwd: projectRoot,
|
|
59
|
-
stdio: 'pipe',
|
|
60
|
-
});
|
|
61
|
-
const runtimeVersion = JSON.parse(stdout).runtimeVersion;
|
|
62
|
-
return runtimeVersion ?? config.version ?? '1.0.0';
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Resolves the path to the `@expo/config` package in the project.
|
|
66
|
-
*/
|
|
67
|
-
function resolveExpoConfigPath(projectRoot) {
|
|
68
|
-
const expoPackageRoot = resolve_from_1.default.silent(projectRoot, 'expo/package.json');
|
|
69
|
-
if (expoPackageRoot) {
|
|
70
|
-
return resolve_from_1.default.silent(node_path_1.default.dirname(expoPackageRoot), '@expo/config') ?? null;
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* A wrapper around `@expo/config`'s `getConfig` function
|
|
76
|
-
* that uses the `@expo/config` package from the project's transitive dependencies.
|
|
77
|
-
*/
|
|
78
|
-
function getExpoConfig(projectRoot, options) {
|
|
79
|
-
const expoConfigPath = resolveExpoConfigPath(projectRoot);
|
|
80
|
-
(0, node_assert_1.default)(expoConfigPath, `Failed to resolve '@expo/config' package in the project`);
|
|
81
|
-
const { getConfig } = require(expoConfigPath);
|
|
82
|
-
return getConfig(projectRoot, options);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Generate JS bundle and assets for the app.
|
|
86
|
-
*/
|
|
87
|
-
async function generateBundleAssetsAsync(expoConfig, options) {
|
|
88
|
-
const { projectRoot, platform, spawnAsync, workingDirectory } = options;
|
|
89
|
-
const bundleAssetRoot = node_path_1.default.resolve(workingDirectory, 'bundles');
|
|
90
|
-
await promises_1.default.mkdir(bundleAssetRoot, { recursive: true });
|
|
91
|
-
// [0] Resolve entry point
|
|
92
|
-
const { stdout: entryFile } = await spawnAsync('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot, stdio: 'pipe' });
|
|
93
|
-
// [1] Execute export:embed
|
|
94
|
-
const isEnableHermes = isEnableHermesManaged(expoConfig, platform);
|
|
95
|
-
const bundleFileName = platform === 'android' ? 'index.android.bundle' : 'main.bundle';
|
|
96
|
-
const bundleOutputPath = node_path_1.default.join(bundleAssetRoot, bundleFileName);
|
|
97
|
-
const assetRoot = node_path_1.default.join(bundleAssetRoot, 'assets');
|
|
98
|
-
const exportEmbedArgs = [
|
|
99
|
-
'expo',
|
|
100
|
-
'export:embed',
|
|
101
|
-
'--platform',
|
|
102
|
-
platform,
|
|
103
|
-
'--entry-file',
|
|
104
|
-
entryFile.trim(),
|
|
105
|
-
'--bundle-output',
|
|
106
|
-
node_path_1.default.join(bundleAssetRoot, bundleFileName),
|
|
107
|
-
'--assets-dest',
|
|
108
|
-
node_path_1.default.join(bundleAssetRoot, 'assets'),
|
|
109
|
-
'--dev',
|
|
110
|
-
'false',
|
|
111
|
-
'--reset-cache',
|
|
112
|
-
];
|
|
113
|
-
if (isEnableHermes) {
|
|
114
|
-
exportEmbedArgs.push('--minify', 'false');
|
|
115
|
-
exportEmbedArgs.push('--bytecode', 'true');
|
|
116
|
-
exportEmbedArgs.push('--unstable-transform-profile', 'hermes');
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
exportEmbedArgs.push('--minify', 'true');
|
|
120
|
-
}
|
|
121
|
-
if (options.exportEmbedOptions?.sourcemapOutput) {
|
|
122
|
-
exportEmbedArgs.push('--sourcemap-output', options.exportEmbedOptions.sourcemapOutput);
|
|
123
|
-
}
|
|
124
|
-
await spawnAsync('npx', exportEmbedArgs, { cwd: projectRoot });
|
|
125
|
-
return {
|
|
126
|
-
bundleOutputPath,
|
|
127
|
-
assetRoot,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Determine whether Hermes is enabled for the project.
|
|
132
|
-
*/
|
|
133
|
-
function isEnableHermesManaged(expoConfig, platform) {
|
|
134
|
-
switch (platform) {
|
|
135
|
-
case 'android': {
|
|
136
|
-
return (expoConfig.android?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
|
|
137
|
-
}
|
|
138
|
-
case 'ios': {
|
|
139
|
-
return (expoConfig.ios?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
|
|
140
|
-
}
|
|
141
|
-
default:
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
package/build/index.d.ts
DELETED
package/build/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.repackAppIosAsync = exports.repackAppAndroidAsync = void 0;
|
|
18
|
-
var android_1 = require("./android");
|
|
19
|
-
Object.defineProperty(exports, "repackAppAndroidAsync", { enumerable: true, get: function () { return android_1.repackAppAndroidAsync; } });
|
|
20
|
-
var ios_1 = require("./ios");
|
|
21
|
-
Object.defineProperty(exports, "repackAppIosAsync", { enumerable: true, get: function () { return ios_1.repackAppIosAsync; } });
|
|
22
|
-
__exportStar(require("./types"), exports);
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type ExpoConfig } from '@expo/config';
|
|
2
|
-
import type { IosSigningOptions, NormalizedOptions } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* Extract the given iOS artifact and return the path to the .app directory.
|
|
5
|
-
*/
|
|
6
|
-
export declare function extractIosArtifactAsync(options: NormalizedOptions): Promise<string>;
|
|
7
|
-
/**
|
|
8
|
-
* Update some binary files.
|
|
9
|
-
*/
|
|
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>;
|
|
15
|
-
/**
|
|
16
|
-
* From the given working .app directory, create a new .ipa file.
|
|
17
|
-
*/
|
|
18
|
-
export declare function createIpaAsync(options: NormalizedOptions, appWorkingDirectory: string): Promise<string>;
|
|
19
|
-
/**
|
|
20
|
-
* Create a Fastfile lane for resigning an IPA.
|
|
21
|
-
*/
|
|
22
|
-
export declare function createResignLane(laneName: string, ipaPath: string, signingOptions: IosSigningOptions): string;
|
package/build/ios/build-tools.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
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.extractIosArtifactAsync = extractIosArtifactAsync;
|
|
7
|
-
exports.updateFilesAsync = updateFilesAsync;
|
|
8
|
-
exports.createAppAsync = createAppAsync;
|
|
9
|
-
exports.createIpaAsync = createIpaAsync;
|
|
10
|
-
exports.createResignLane = createResignLane;
|
|
11
|
-
const glob_1 = require("glob");
|
|
12
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
13
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
-
const slugify_1 = __importDefault(require("slugify"));
|
|
16
|
-
const utils_1 = require("../utils");
|
|
17
|
-
/**
|
|
18
|
-
* Extract the given iOS artifact and return the path to the .app directory.
|
|
19
|
-
*/
|
|
20
|
-
async function extractIosArtifactAsync(options) {
|
|
21
|
-
const { spawnAsync, 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 spawnAsync('unzip', [sourceAppPath, '-d', extractedWorkingDirectory]);
|
|
26
|
-
}
|
|
27
|
-
else if (sourceAppPath.endsWith('.tar.gz')) {
|
|
28
|
-
await spawnAsync('tar', ['-xzf', sourceAppPath, '-C', extractedWorkingDirectory]);
|
|
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}`);
|
|
43
|
-
return appWorkingDirectory;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Update some binary files.
|
|
47
|
-
*/
|
|
48
|
-
async function updateFilesAsync(config, appWorkingDirectory) {
|
|
49
|
-
const { dir: parentDir, name } = node_path_1.default.parse(appWorkingDirectory);
|
|
50
|
-
const sanitizedAppName = sanitizedName(config.name);
|
|
51
|
-
const newAppWorkingDirectory = node_path_1.default.join(parentDir, `${sanitizedAppName}.app`);
|
|
52
|
-
// [0] Update the .app directory
|
|
53
|
-
await promises_1.default.rename(node_path_1.default.join(parentDir, `${name}.app`), newAppWorkingDirectory);
|
|
54
|
-
// [1] Rename the executable
|
|
55
|
-
await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory, name), node_path_1.default.join(newAppWorkingDirectory, sanitizedAppName));
|
|
56
|
-
return newAppWorkingDirectory;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* From the given working .app directory, create a new .app directory.
|
|
60
|
-
*/
|
|
61
|
-
async function createAppAsync(options, appWorkingDirectory) {
|
|
62
|
-
const outputAppPath = node_path_1.default.resolve(options.workingDirectory, node_path_1.default.basename(appWorkingDirectory));
|
|
63
|
-
await (0, utils_1.copyDirAsync)(appWorkingDirectory, outputAppPath);
|
|
64
|
-
return outputAppPath;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* From the given working .app directory, create a new .ipa file.
|
|
68
|
-
*/
|
|
69
|
-
async function createIpaAsync(options, appWorkingDirectory) {
|
|
70
|
-
const { spawnAsync, workingDirectory } = options;
|
|
71
|
-
await promises_1.default.mkdir(node_path_1.default.join(workingDirectory, 'Payload'), { recursive: true });
|
|
72
|
-
await promises_1.default.rename(appWorkingDirectory, node_path_1.default.join(workingDirectory, 'Payload', node_path_1.default.basename(appWorkingDirectory)));
|
|
73
|
-
const outputIpaPath = node_path_1.default.resolve(workingDirectory, 'repacked.ipa');
|
|
74
|
-
await spawnAsync('zip', ['-r', outputIpaPath, 'Payload'], { cwd: workingDirectory });
|
|
75
|
-
return outputIpaPath;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Create a Fastfile lane for resigning an IPA.
|
|
79
|
-
*/
|
|
80
|
-
function createResignLane(laneName, ipaPath, signingOptions) {
|
|
81
|
-
const keychainPath = signingOptions.keychainPath
|
|
82
|
-
? `keychain_path: "${signingOptions.keychainPath}",`
|
|
83
|
-
: '';
|
|
84
|
-
let provisioningProfile;
|
|
85
|
-
if (typeof signingOptions.provisioningProfile === 'string') {
|
|
86
|
-
provisioningProfile = `provisioning_profile: "${signingOptions.provisioningProfile}",`;
|
|
87
|
-
}
|
|
88
|
-
else if (typeof signingOptions.provisioningProfile === 'object' &&
|
|
89
|
-
!Array.isArray(signingOptions.provisioningProfile)) {
|
|
90
|
-
let profileHash = '';
|
|
91
|
-
for (const [key, value] of Object.entries(signingOptions.provisioningProfile)) {
|
|
92
|
-
profileHash += ` "${key}" => "${value}",\n`;
|
|
93
|
-
}
|
|
94
|
-
provisioningProfile = `provisioning_profile: {\n${profileHash}\n },`;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
provisioningProfile = '';
|
|
98
|
-
}
|
|
99
|
-
const fastfileContents = `\
|
|
100
|
-
lane :${laneName} do
|
|
101
|
-
resign(
|
|
102
|
-
ipa: "${ipaPath}",
|
|
103
|
-
signing_identity: "${signingOptions.signingIdentity}",
|
|
104
|
-
${keychainPath}
|
|
105
|
-
${provisioningProfile}
|
|
106
|
-
)
|
|
107
|
-
end
|
|
108
|
-
`;
|
|
109
|
-
return fastfileContents.replace(/^\s*[\r\n]/gm, '');
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Synchronous logic as prebuild to sanitize the name of the app.
|
|
113
|
-
*/
|
|
114
|
-
function sanitizedName(name) {
|
|
115
|
-
// Default to the name `app` when every safe character has been sanitized
|
|
116
|
-
return sanitizedNameForProjects(name) || sanitizedNameForProjects((0, slugify_1.default)(name)) || 'app';
|
|
117
|
-
}
|
|
118
|
-
function sanitizedNameForProjects(name) {
|
|
119
|
-
return name
|
|
120
|
-
.replace(/[\W_]+/g, '')
|
|
121
|
-
.normalize('NFD')
|
|
122
|
-
.replace(/[\u0300-\u036f]/g, '');
|
|
123
|
-
}
|
package/build/ios/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { IosSigningOptions, Options } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Repack an iOS app.
|
|
4
|
-
*/
|
|
5
|
-
export declare function repackAppIosAsync(_options: Options): Promise<string>;
|
|
6
|
-
/**
|
|
7
|
-
* Resign an IPA by fastlane.
|
|
8
|
-
*/
|
|
9
|
-
export declare function resignIpaAsync(ipaPath: string, signingOptions: IosSigningOptions, _options: Options): Promise<void>;
|