@expo/repack-app 0.0.6 → 0.1.1
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/assets/apktool_2.10.0.jar +0 -0
- package/build/android/apktool.d.ts +21 -0
- package/build/android/apktool.js +164 -0
- package/build/android/build-tools.d.ts +7 -14
- package/build/android/build-tools.js +28 -63
- package/build/android/index.js +47 -18
- package/build/android/resources.d.ts +54 -3
- package/build/android/resources.js +110 -116
- package/build/cli.js +18 -4
- package/build/expo.d.ts +16 -0
- package/build/expo.js +99 -3
- package/build/index.d.ts +1 -0
- package/build/index.js +15 -0
- package/build/ios/build-tools.d.ts +5 -1
- package/build/ios/build-tools.js +39 -5
- package/build/ios/index.d.ts +1 -1
- package/build/ios/index.js +36 -20
- package/build/ios/resources.d.ts +19 -2
- package/build/ios/resources.js +51 -7
- package/build/types.d.ts +15 -3
- package/build/utils.js +3 -4
- package/package.json +17 -13
- package/assets/Configuration.proto +0 -216
- package/assets/Resources.proto +0 -648
- package/build/android/Resources.types.d.ts +0 -93
- package/build/android/Resources.types.js +0 -5
|
@@ -3,176 +3,170 @@ 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.updateResourcesAsync = updateResourcesAsync;
|
|
7
|
+
exports.updateAndroidManifestAsync = updateAndroidManifestAsync;
|
|
8
|
+
exports.parseXmlFileAsync = parseXmlFileAsync;
|
|
9
|
+
exports.buildXmlFileAsync = buildXmlFileAsync;
|
|
10
|
+
exports.queryAppIdFromManifestAsync = queryAppIdFromManifestAsync;
|
|
7
11
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
8
12
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const
|
|
14
|
+
const xml2js_1 = require("xml2js");
|
|
11
15
|
const utils_1 = require("../utils");
|
|
12
16
|
/**
|
|
13
|
-
* Update resources
|
|
17
|
+
* Update resources in the decoded APK.
|
|
14
18
|
*/
|
|
15
|
-
async function updateResourcesAsync(config,
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
(0, node_assert_1.default)(config.android?.package, 'Expected android.package to be defined');
|
|
22
|
-
resourceTable.package[0].packageName = config.android.package;
|
|
23
|
-
const stringType = resourceTable.package[0].type.find((type) => type.name === 'string');
|
|
24
|
-
const stringEntries = stringType?.entry;
|
|
25
|
-
// [1] Update the `app_name` in **res/values/strings.xml**.
|
|
26
|
-
const appNameEntry = stringEntries?.find((entry) => entry.name === 'app_name');
|
|
27
|
-
(0, node_assert_1.default)(appNameEntry?.configValue?.[0].value?.item?.str?.value === 'HelloWorld', 'Expected app_name to be predefined "HelloWorld"');
|
|
28
|
-
appNameEntry.configValue[0].value.item.str.value = config.name;
|
|
29
|
-
await encodeProtoFile(resourceTableType, resourcesPbFilePath, resourceTable);
|
|
19
|
+
async function updateResourcesAsync({ config, decodedApkRoot, }) {
|
|
20
|
+
// [0] Update the `app_name` in **res/values/strings.xml**.
|
|
21
|
+
const stringsXmlPath = node_path_1.default.join(decodedApkRoot, 'res', 'values', 'strings.xml');
|
|
22
|
+
let stringsXml = await promises_1.default.readFile(stringsXmlPath, 'utf8');
|
|
23
|
+
stringsXml = stringsXml.replace(/(<string name="app_name">)(.+)(<\/string>)/, `$1${config.name}$3`);
|
|
24
|
+
await promises_1.default.writeFile(stringsXmlPath, stringsXml);
|
|
30
25
|
}
|
|
31
|
-
exports.updateResourcesAsync = updateResourcesAsync;
|
|
32
26
|
/**
|
|
33
27
|
* Update the proto-based AndroidManiest.xml.
|
|
34
28
|
*/
|
|
35
|
-
async function updateAndroidManifestAsync(config,
|
|
36
|
-
const root = await protobufjs_1.default.load(node_path_1.default.join(__dirname, '../../assets', 'Resources.proto'));
|
|
37
|
-
const xmlNodeType = root.lookupType('aapt.pb.XmlNode');
|
|
38
|
-
const rootNode = await decodeProtoFile(xmlNodeType, androidManiestFilePath);
|
|
29
|
+
async function updateAndroidManifestAsync({ config, androidManiestXml, dexClasses, originalAppId, updatesRuntimeVersion, }) {
|
|
39
30
|
// [0] Update the package name
|
|
40
|
-
|
|
31
|
+
const appId = (0, utils_1.requireNotNull)(config.android?.package);
|
|
32
|
+
androidManiestXml.manifest.$.package = appId;
|
|
33
|
+
replaceXmlAttributeValue(androidManiestXml, (value) => {
|
|
34
|
+
if (value.startsWith(originalAppId) && !dexClasses.has(value)) {
|
|
35
|
+
return value.replace(originalAppId, appId);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
});
|
|
41
39
|
// [1] Update the scheme in the intent-filters
|
|
42
|
-
const intentFilterViewActionNodes = findXmlNodes(
|
|
43
|
-
findXmlNodes(node, (node) => node
|
|
44
|
-
node.element?.attribute[0]?.value === 'android.intent.action.VIEW').length > 0);
|
|
40
|
+
const intentFilterViewActionNodes = findXmlNodes('manifest', androidManiestXml.manifest, (nodeName, node) => nodeName === 'intent-filter' &&
|
|
41
|
+
findXmlNodes(nodeName, node, (nodeName, node) => nodeName === 'action' && node[0].$?.['android:name'] === 'android.intent.action.VIEW').length > 0);
|
|
45
42
|
const firstScheme = Array.isArray(config.scheme) ? config.scheme[0] : config.scheme;
|
|
46
43
|
for (const node of intentFilterViewActionNodes) {
|
|
47
44
|
replaceXmlAttributeValue(node, (value) => value
|
|
48
45
|
// scheme in app.json
|
|
49
46
|
.replace(/^myapp$/g, (0, utils_1.requireNotNull)(firstScheme))
|
|
50
47
|
// android.package in app.json
|
|
51
|
-
.replace(
|
|
48
|
+
.replace(originalAppId, appId)
|
|
52
49
|
// default scheme generated from slug in app.json
|
|
53
|
-
.replace(/^exp
|
|
50
|
+
.replace(/^exp\+.+$/g, `exp+${(0, utils_1.requireNotNull)(config.slug)}`));
|
|
54
51
|
}
|
|
55
52
|
// [2] expo-updates configuration
|
|
56
|
-
const mainApplicationNode =
|
|
57
|
-
|
|
58
|
-
(
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const mainApplicationNode = androidManiestXml.manifest.application[0];
|
|
54
|
+
(0, node_assert_1.default)(mainApplicationNode.$?.['android:name']?.endsWith('.MainApplication'), 'Expected application node to be named as MainApplication');
|
|
55
|
+
if (updatesRuntimeVersion) {
|
|
56
|
+
mutateExpoUpdatesConfigAsync(mainApplicationNode, config, updatesRuntimeVersion);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse the XML file.
|
|
61
|
+
*/
|
|
62
|
+
async function parseXmlFileAsync(filePath) {
|
|
63
|
+
const contents = await promises_1.default.readFile(filePath, 'utf8');
|
|
64
|
+
const parser = new xml2js_1.Parser();
|
|
65
|
+
const xmlJs = await parser.parseStringPromise(contents);
|
|
66
|
+
return xmlJs;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the XML file from the given node.
|
|
70
|
+
*/
|
|
71
|
+
async function buildXmlFileAsync(rootNode, filePath) {
|
|
72
|
+
const builder = new xml2js_1.Builder();
|
|
73
|
+
const xmlString = builder.buildObject(rootNode);
|
|
74
|
+
await promises_1.default.writeFile(filePath, xmlString);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Query the app ID from the AndroidManiest.xml.
|
|
78
|
+
*/
|
|
79
|
+
async function queryAppIdFromManifestAsync(androidManiestXml) {
|
|
80
|
+
const packageAttr = androidManiestXml.manifest.$.package;
|
|
81
|
+
(0, node_assert_1.default)(packageAttr != null, 'Expected package attribute to be present in AndroidManifest.xml');
|
|
82
|
+
return packageAttr;
|
|
61
83
|
}
|
|
62
|
-
exports.updateAndroidManifestAsync = updateAndroidManifestAsync;
|
|
63
84
|
//#region Internals
|
|
64
85
|
/**
|
|
65
86
|
* Update the `expo-updates` configuration in the Android project.
|
|
66
87
|
*/
|
|
67
88
|
function mutateExpoUpdatesConfigAsync(mainApplicationNode, config, runtimeVersion) {
|
|
68
|
-
const
|
|
69
|
-
child.element?.attribute.find((attr) => attr.name === 'name' && attr.value === 'expo.modules.updates.ENABLED'));
|
|
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]);
|
|
89
|
+
const metaDataNodes = mainApplicationNode['meta-data'] ?? [];
|
|
72
90
|
// [0] expo.modules.updates.ENABLED
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
const updateEnabledNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.ENABLED');
|
|
92
|
+
if (updateEnabledNode != null) {
|
|
93
|
+
(0, node_assert_1.default)(updateEnabledNode.$ != null);
|
|
94
|
+
updateEnabledNode.$['android:value'] = 'true';
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
metaDataNodes.push({
|
|
98
|
+
$: {
|
|
99
|
+
'android:name': 'expo.modules.updates.ENABLED',
|
|
100
|
+
'android:value': 'true',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
76
104
|
// [1] expo.modules.updates.EXPO_RUNTIME_VERSION
|
|
77
|
-
const updateRuntimeVersionNode =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
105
|
+
const updateRuntimeVersionNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.EXPO_RUNTIME_VERSION');
|
|
106
|
+
if (updateRuntimeVersionNode != null) {
|
|
107
|
+
(0, node_assert_1.default)(updateRuntimeVersionNode.$ != null);
|
|
108
|
+
updateRuntimeVersionNode.$['android:value'] = runtimeVersion;
|
|
81
109
|
}
|
|
82
110
|
else {
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
metaDataNodes.push({
|
|
112
|
+
$: {
|
|
113
|
+
'android:name': 'expo.modules.updates.EXPO_RUNTIME_VERSION',
|
|
114
|
+
'android:value': runtimeVersion,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
85
117
|
}
|
|
86
118
|
// [2] expo.modules.updates.EXPO_UPDATE_URL
|
|
87
|
-
const updateUrlNode =
|
|
88
|
-
child.element?.attribute.find((attr) => attr.name === 'name' && attr.value === 'expo.modules.updates.EXPO_UPDATE_URL'));
|
|
119
|
+
const updateUrlNode = metaDataNodes.find((node) => node.$?.['android:name'] === 'expo.modules.updates.EXPO_UPDATE_URL');
|
|
89
120
|
const updateUrl = config.updates?.url;
|
|
90
121
|
(0, node_assert_1.default)(updateUrl);
|
|
91
|
-
if (updateUrlNode
|
|
92
|
-
|
|
122
|
+
if (updateUrlNode != null) {
|
|
123
|
+
(0, node_assert_1.default)(updateUrlNode.$ != null);
|
|
124
|
+
updateUrlNode.$['android:value'] = updateUrl;
|
|
93
125
|
}
|
|
94
126
|
else {
|
|
95
|
-
|
|
96
|
-
|
|
127
|
+
metaDataNodes.push({
|
|
128
|
+
$: {
|
|
129
|
+
'android:name': 'expo.modules.updates.EXPO_UPDATE_URL',
|
|
130
|
+
'android:value': updateUrl,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
97
133
|
}
|
|
98
134
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Decode the proto-encoded file from `filePath` with the given `protoTypeString` and return as JSON object.
|
|
101
|
-
*/
|
|
102
|
-
async function decodeProtoFile(protoType, filePath) {
|
|
103
|
-
const buffer = await promises_1.default.readFile(filePath);
|
|
104
|
-
const message = await protoType.decode(buffer);
|
|
105
|
-
const json = protoType.toObject(message, {
|
|
106
|
-
longs: String,
|
|
107
|
-
enums: String,
|
|
108
|
-
bytes: String,
|
|
109
|
-
defaults: true,
|
|
110
|
-
arrays: true,
|
|
111
|
-
objects: true,
|
|
112
|
-
oneofs: true,
|
|
113
|
-
});
|
|
114
|
-
return json;
|
|
115
|
-
}
|
|
116
|
-
async function encodeProtoFile(protoType, filePath, json) {
|
|
117
|
-
const updatedMessage = protoType.fromObject(json);
|
|
118
|
-
const updatedBuffer = protoType.encode(updatedMessage).finish();
|
|
119
|
-
await promises_1.default.writeFile(filePath, updatedBuffer);
|
|
120
|
-
}
|
|
121
135
|
/**
|
|
122
136
|
* Recursively find XML nodes that satisfy the given `predicate`.
|
|
123
137
|
*/
|
|
124
|
-
function findXmlNodes(rootNode, predicate) {
|
|
125
|
-
if (rootNode
|
|
138
|
+
function findXmlNodes(rootNodeName, rootNode, predicate) {
|
|
139
|
+
if (rootNode == null) {
|
|
126
140
|
return [];
|
|
127
141
|
}
|
|
128
|
-
if (predicate(rootNode)) {
|
|
142
|
+
if (predicate(rootNodeName, rootNode)) {
|
|
129
143
|
return [rootNode];
|
|
130
144
|
}
|
|
131
|
-
return
|
|
145
|
+
return Object.keys(rootNode)
|
|
146
|
+
.filter((key) => key !== '$' && key !== '_')
|
|
147
|
+
.flatMap((key) => {
|
|
148
|
+
const node = rootNode[key];
|
|
149
|
+
return findXmlNodes(key, node, predicate);
|
|
150
|
+
});
|
|
132
151
|
}
|
|
133
152
|
/**
|
|
134
153
|
* Recursively replace the attribute values in the XML nodes's attributes.
|
|
135
154
|
*/
|
|
136
155
|
function replaceXmlAttributeValue(node, replacer) {
|
|
137
|
-
if (node
|
|
156
|
+
if (node == null) {
|
|
138
157
|
return;
|
|
139
158
|
}
|
|
140
|
-
const attrs = node
|
|
141
|
-
for (const
|
|
142
|
-
|
|
159
|
+
const attrs = node.$ ?? {};
|
|
160
|
+
for (const [attrKey, attrValue] of Object.entries(attrs)) {
|
|
161
|
+
attrs[attrKey] = replacer(attrValue);
|
|
143
162
|
}
|
|
144
|
-
for (const
|
|
145
|
-
|
|
163
|
+
for (const [nodeKey, nodeValue] of Object.entries(node)) {
|
|
164
|
+
if (nodeKey === '$' || nodeKey === '_') {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
replaceXmlAttributeValue(nodeValue, replacer);
|
|
169
|
+
}
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Create a new `meta-data` node with the given `name` and `value`.
|
|
150
|
-
* We use the `updateEnabledNode` as the source to clone most of the properties.
|
|
151
|
-
*/
|
|
152
|
-
function createUpdateStringNode(updateEnabledNode, name, value) {
|
|
153
|
-
return {
|
|
154
|
-
...updateEnabledNode,
|
|
155
|
-
element: {
|
|
156
|
-
...(0, utils_1.requireNotNull)(updateEnabledNode.element),
|
|
157
|
-
attribute: [
|
|
158
|
-
{
|
|
159
|
-
namespaceUri: 'http://schemas.android.com/apk/res/android',
|
|
160
|
-
name: 'name',
|
|
161
|
-
value: name,
|
|
162
|
-
source: null,
|
|
163
|
-
resourceId: 16842755,
|
|
164
|
-
compiledItem: null,
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
namespaceUri: 'http://schemas.android.com/apk/res/android',
|
|
168
|
-
name: 'value',
|
|
169
|
-
value,
|
|
170
|
-
source: null,
|
|
171
|
-
resourceId: 16842788,
|
|
172
|
-
compiledItem: null,
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
},
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
172
|
//#endregion
|
package/build/cli.js
CHANGED
|
@@ -22,6 +22,9 @@ const program = new commander_1.Command('repack-app')
|
|
|
22
22
|
// iOS signing options
|
|
23
23
|
.option('--signing-identity <identity>', 'Signing identity')
|
|
24
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')
|
|
25
28
|
// arguments
|
|
26
29
|
.argument('<project-root>', 'Path to the project root')
|
|
27
30
|
.parse(process.argv);
|
|
@@ -29,6 +32,12 @@ async function runAsync() {
|
|
|
29
32
|
const logger = (0, logger_1.createLogger)({ name: 'repack-app' });
|
|
30
33
|
const platform = program.opts().platform;
|
|
31
34
|
const projectRoot = program.args[0];
|
|
35
|
+
const exportEmbedOptions = program.opts()
|
|
36
|
+
.embedBundleAssets
|
|
37
|
+
? {
|
|
38
|
+
sourcemapOutput: program.opts().bundleAssetsSourcemapOutput,
|
|
39
|
+
}
|
|
40
|
+
: undefined;
|
|
32
41
|
if (platform === 'android') {
|
|
33
42
|
const outputPath = await (0, index_1.repackAppAndroidAsync)({
|
|
34
43
|
platform: program.opts().platform,
|
|
@@ -37,6 +46,7 @@ async function runAsync() {
|
|
|
37
46
|
sourceAppPath: program.opts().sourceApp,
|
|
38
47
|
workingDirectory: program.opts().workingDirectory,
|
|
39
48
|
outputPath: program.opts().output,
|
|
49
|
+
exportEmbedOptions,
|
|
40
50
|
androidSigningOptions: {
|
|
41
51
|
keyStorePath: program.opts().ks,
|
|
42
52
|
keyStorePassword: program.opts().ksPass,
|
|
@@ -49,6 +59,12 @@ async function runAsync() {
|
|
|
49
59
|
logger.info(picocolors_1.default.green(`Updated APK created at ${outputPath}`));
|
|
50
60
|
}
|
|
51
61
|
else if (platform === 'ios') {
|
|
62
|
+
const iosSigningOptions = program.opts().signingIdentity && program.opts().provisioningProfile
|
|
63
|
+
? {
|
|
64
|
+
signingIdentity: program.opts().signingIdentity,
|
|
65
|
+
provisioningProfile: program.opts().provisioningProfile,
|
|
66
|
+
}
|
|
67
|
+
: undefined;
|
|
52
68
|
const options = {
|
|
53
69
|
platform: program.opts().platform,
|
|
54
70
|
projectRoot,
|
|
@@ -56,10 +72,8 @@ async function runAsync() {
|
|
|
56
72
|
sourceAppPath: program.opts().sourceApp,
|
|
57
73
|
workingDirectory: program.opts().workingDirectory,
|
|
58
74
|
outputPath: program.opts().output,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
provisioningProfile: program.opts().provisioningProfile,
|
|
62
|
-
},
|
|
75
|
+
exportEmbedOptions,
|
|
76
|
+
iosSigningOptions,
|
|
63
77
|
logger,
|
|
64
78
|
};
|
|
65
79
|
const outputPath = await (0, index_1.repackAppIosAsync)(options);
|
package/build/expo.d.ts
CHANGED
|
@@ -4,7 +4,23 @@ 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
|
+
* Returns whether expo-updates is installed in the project.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isExpoUpdatesInstalled(projectRoot: string): boolean;
|
|
7
11
|
/**
|
|
8
12
|
* Resolve the `runtimeVersion` for expo-updates.
|
|
9
13
|
*/
|
|
10
14
|
export declare function resolveRuntimeVersionAsync(options: NormalizedOptions, config: ExpoConfig): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* A wrapper around `@expo/config`'s `getConfig` function
|
|
17
|
+
* that uses the `@expo/config` package from the project's transitive dependencies.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getExpoConfig(projectRoot: string, options: Parameters<typeof import('@expo/config').getConfig>[1]): ReturnType<typeof import('@expo/config').getConfig>;
|
|
20
|
+
/**
|
|
21
|
+
* Generate JS bundle and assets for the app.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateBundleAssetsAsync(expoConfig: ExpoConfig, options: NormalizedOptions): Promise<{
|
|
24
|
+
bundleOutputPath: string;
|
|
25
|
+
assetRoot: string;
|
|
26
|
+
}>;
|
package/build/expo.js
CHANGED
|
@@ -3,8 +3,13 @@ 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.generateAppConfigAsync = generateAppConfigAsync;
|
|
7
|
+
exports.isExpoUpdatesInstalled = isExpoUpdatesInstalled;
|
|
8
|
+
exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
|
|
9
|
+
exports.getExpoConfig = getExpoConfig;
|
|
10
|
+
exports.generateBundleAssetsAsync = generateBundleAssetsAsync;
|
|
7
11
|
const steps_1 = require("@expo/steps");
|
|
12
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
8
13
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
15
|
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
@@ -17,7 +22,16 @@ async function generateAppConfigAsync(options, config) {
|
|
|
17
22
|
await promises_1.default.writeFile(appConfigPath, JSON.stringify(config));
|
|
18
23
|
return appConfigPath;
|
|
19
24
|
}
|
|
20
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Returns whether expo-updates is installed in the project.
|
|
27
|
+
*/
|
|
28
|
+
function isExpoUpdatesInstalled(projectRoot) {
|
|
29
|
+
const expoConfigPath = resolveExpoConfigPath(projectRoot);
|
|
30
|
+
(0, node_assert_1.default)(expoConfigPath, `Failed to resolve '@expo/config' package in the project`);
|
|
31
|
+
const { getPackageJson } = require(expoConfigPath);
|
|
32
|
+
const packageJson = getPackageJson(projectRoot);
|
|
33
|
+
return !!(packageJson.dependencies && 'expo-updates' in packageJson.dependencies);
|
|
34
|
+
}
|
|
21
35
|
/**
|
|
22
36
|
* Resolve the `runtimeVersion` for expo-updates.
|
|
23
37
|
*/
|
|
@@ -31,4 +45,86 @@ async function resolveRuntimeVersionAsync(options, config) {
|
|
|
31
45
|
const runtimeVersion = JSON.parse(proc.stdout).runtimeVersion;
|
|
32
46
|
return runtimeVersion ?? config.version ?? '1.0.0';
|
|
33
47
|
}
|
|
34
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the path to the `@expo/config` package in the project.
|
|
50
|
+
*/
|
|
51
|
+
function resolveExpoConfigPath(projectRoot) {
|
|
52
|
+
const expoPackageRoot = resolve_from_1.default.silent(projectRoot, 'expo/package.json');
|
|
53
|
+
if (expoPackageRoot) {
|
|
54
|
+
return resolve_from_1.default.silent(node_path_1.default.dirname(expoPackageRoot), '@expo/config') ?? null;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A wrapper around `@expo/config`'s `getConfig` function
|
|
60
|
+
* that uses the `@expo/config` package from the project's transitive dependencies.
|
|
61
|
+
*/
|
|
62
|
+
function getExpoConfig(projectRoot, options) {
|
|
63
|
+
const expoConfigPath = resolveExpoConfigPath(projectRoot);
|
|
64
|
+
(0, node_assert_1.default)(expoConfigPath, `Failed to resolve '@expo/config' package in the project`);
|
|
65
|
+
const { getConfig } = require(expoConfigPath);
|
|
66
|
+
return getConfig(projectRoot, options);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate JS bundle and assets for the app.
|
|
70
|
+
*/
|
|
71
|
+
async function generateBundleAssetsAsync(expoConfig, options) {
|
|
72
|
+
const { projectRoot, platform, workingDirectory } = options;
|
|
73
|
+
const bundleAssetRoot = node_path_1.default.resolve(workingDirectory, 'bundles');
|
|
74
|
+
await promises_1.default.mkdir(bundleAssetRoot, { recursive: true });
|
|
75
|
+
// [0] Resolve entry point
|
|
76
|
+
const { stdout: entryFile } = await (0, steps_1.spawnAsync)('node', ['-e', "require('expo/scripts/resolveAppEntry')", projectRoot, platform, 'absolute'], { cwd: projectRoot });
|
|
77
|
+
// [1] Execute export:embed
|
|
78
|
+
const isEnableHermes = isEnableHermesManaged(expoConfig, platform);
|
|
79
|
+
const bundleFileName = platform === 'android' ? 'index.android.bundle' : 'main.bundle';
|
|
80
|
+
const bundleOutputPath = node_path_1.default.join(bundleAssetRoot, bundleFileName);
|
|
81
|
+
const assetRoot = node_path_1.default.join(bundleAssetRoot, 'assets');
|
|
82
|
+
const exportEmbedArgs = [
|
|
83
|
+
'expo',
|
|
84
|
+
'export:embed',
|
|
85
|
+
'--platform',
|
|
86
|
+
platform,
|
|
87
|
+
'--entry-file',
|
|
88
|
+
entryFile.trim(),
|
|
89
|
+
'--bundle-output',
|
|
90
|
+
node_path_1.default.join(bundleAssetRoot, bundleFileName),
|
|
91
|
+
'--assets-dest',
|
|
92
|
+
node_path_1.default.join(bundleAssetRoot, 'assets'),
|
|
93
|
+
'--dev',
|
|
94
|
+
'false',
|
|
95
|
+
'--reset-cache',
|
|
96
|
+
];
|
|
97
|
+
if (isEnableHermes) {
|
|
98
|
+
exportEmbedArgs.push('--minify', 'false');
|
|
99
|
+
exportEmbedArgs.push('--bundle-bytecode', 'true');
|
|
100
|
+
exportEmbedArgs.push('--unstable-transform-profile', 'hermes');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
exportEmbedArgs.push('--minify', 'true');
|
|
104
|
+
}
|
|
105
|
+
if (options.exportEmbedOptions?.sourcemapOutput) {
|
|
106
|
+
exportEmbedArgs.push('--sourcemap-output', options.exportEmbedOptions.sourcemapOutput);
|
|
107
|
+
}
|
|
108
|
+
await (0, steps_1.spawnAsync)('npx', exportEmbedArgs, options.verbose
|
|
109
|
+
? { logger: options.logger, stdio: 'pipe', cwd: projectRoot }
|
|
110
|
+
: { cwd: projectRoot });
|
|
111
|
+
return {
|
|
112
|
+
bundleOutputPath,
|
|
113
|
+
assetRoot,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Determine whether Hermes is enabled for the project.
|
|
118
|
+
*/
|
|
119
|
+
function isEnableHermesManaged(expoConfig, platform) {
|
|
120
|
+
switch (platform) {
|
|
121
|
+
case 'android': {
|
|
122
|
+
return (expoConfig.android?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
|
|
123
|
+
}
|
|
124
|
+
case 'ios': {
|
|
125
|
+
return (expoConfig.ios?.jsEngine ?? expoConfig.jsEngine) !== 'jsc';
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
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
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.repackAppIosAsync = exports.repackAppAndroidAsync = void 0;
|
|
4
18
|
var android_1 = require("./android");
|
|
5
19
|
Object.defineProperty(exports, "repackAppAndroidAsync", { enumerable: true, get: function () { return android_1.repackAppAndroidAsync; } });
|
|
6
20
|
var ios_1 = require("./ios");
|
|
7
21
|
Object.defineProperty(exports, "repackAppIosAsync", { enumerable: true, get: function () { return ios_1.repackAppIosAsync; } });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ExpoConfig } from '@expo/config';
|
|
2
|
-
import type { NormalizedOptions } from '../types';
|
|
2
|
+
import type { IosSigningOptions, NormalizedOptions } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Unzip the IPA file.
|
|
5
5
|
*/
|
|
@@ -12,3 +12,7 @@ export declare function updateFilesAsync(config: ExpoConfig, appWorkingDirectory
|
|
|
12
12
|
* From the given working .app directory, create a new .ipa file.
|
|
13
13
|
*/
|
|
14
14
|
export declare function createIpaAsync(options: NormalizedOptions, appWorkingDirectory: string): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Create a Fastfile lane for resigning an IPA.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createResignLane(laneName: string, ipaPath: string, signingOptions: IosSigningOptions): string;
|
package/build/ios/build-tools.js
CHANGED
|
@@ -3,7 +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
|
-
exports.
|
|
6
|
+
exports.unzipIpaAsync = unzipIpaAsync;
|
|
7
|
+
exports.updateFilesAsync = updateFilesAsync;
|
|
8
|
+
exports.createIpaAsync = createIpaAsync;
|
|
9
|
+
exports.createResignLane = createResignLane;
|
|
7
10
|
const steps_1 = require("@expo/steps");
|
|
8
11
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
12
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
@@ -19,7 +22,6 @@ async function unzipIpaAsync(options) {
|
|
|
19
22
|
(0, node_assert_1.default)(await (0, utils_1.directoryExistsAsync)(appWorkingDirectory));
|
|
20
23
|
return appWorkingDirectory;
|
|
21
24
|
}
|
|
22
|
-
exports.unzipIpaAsync = unzipIpaAsync;
|
|
23
25
|
/**
|
|
24
26
|
* Update some binary files.
|
|
25
27
|
*/
|
|
@@ -32,7 +34,6 @@ async function updateFilesAsync(config, appWorkingDirectory) {
|
|
|
32
34
|
await promises_1.default.rename(node_path_1.default.join(newAppWorkingDirectory, 'HelloWorld'), node_path_1.default.join(newAppWorkingDirectory, config.name));
|
|
33
35
|
return newAppWorkingDirectory;
|
|
34
36
|
}
|
|
35
|
-
exports.updateFilesAsync = updateFilesAsync;
|
|
36
37
|
/**
|
|
37
38
|
* From the given working .app directory, create a new .ipa file.
|
|
38
39
|
*/
|
|
@@ -40,10 +41,43 @@ async function createIpaAsync(options, appWorkingDirectory) {
|
|
|
40
41
|
const { workingDirectory } = options;
|
|
41
42
|
await promises_1.default.mkdir(node_path_1.default.join(workingDirectory, 'Payload'), { recursive: true });
|
|
42
43
|
await promises_1.default.rename(appWorkingDirectory, node_path_1.default.join(workingDirectory, 'Payload', node_path_1.default.basename(appWorkingDirectory)));
|
|
43
|
-
const outputIpaPath = node_path_1.default.
|
|
44
|
+
const outputIpaPath = node_path_1.default.resolve(workingDirectory, 'repacked.ipa');
|
|
44
45
|
await (0, steps_1.spawnAsync)('zip', ['-r', outputIpaPath, 'Payload'], options.verbose
|
|
45
46
|
? { logger: options.logger, stdio: 'pipe', cwd: workingDirectory }
|
|
46
47
|
: { cwd: workingDirectory });
|
|
47
48
|
return outputIpaPath;
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Create a Fastfile lane for resigning an IPA.
|
|
52
|
+
*/
|
|
53
|
+
function createResignLane(laneName, ipaPath, signingOptions) {
|
|
54
|
+
const keychainPath = signingOptions.keychainPath
|
|
55
|
+
? `keychain_path: "${signingOptions.keychainPath}",`
|
|
56
|
+
: '';
|
|
57
|
+
let provisioningProfile;
|
|
58
|
+
if (typeof signingOptions.provisioningProfile === 'string') {
|
|
59
|
+
provisioningProfile = `provisioning_profile: "${signingOptions.provisioningProfile}",`;
|
|
60
|
+
}
|
|
61
|
+
else if (typeof signingOptions.provisioningProfile === 'object' &&
|
|
62
|
+
!Array.isArray(signingOptions.provisioningProfile)) {
|
|
63
|
+
let profileHash = '';
|
|
64
|
+
for (const [key, value] of Object.entries(signingOptions.provisioningProfile)) {
|
|
65
|
+
profileHash += ` "${key}" => "${value}",\n`;
|
|
66
|
+
}
|
|
67
|
+
provisioningProfile = `provisioning_profile: {\n${profileHash}\n },`;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
provisioningProfile = '';
|
|
71
|
+
}
|
|
72
|
+
const fastfileContents = `\
|
|
73
|
+
lane :${laneName} do
|
|
74
|
+
resign(
|
|
75
|
+
ipa: "${ipaPath}",
|
|
76
|
+
signing_identity: "${signingOptions.signingIdentity}",
|
|
77
|
+
${keychainPath}
|
|
78
|
+
${provisioningProfile}
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
`;
|
|
82
|
+
return fastfileContents.replace(/^\s*[\r\n]/gm, '');
|
|
83
|
+
}
|
package/build/ios/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export declare function repackAppIosAsync(_options: Options): Promise<string>;
|
|
|
6
6
|
/**
|
|
7
7
|
* Resign an IPA by fastlane.
|
|
8
8
|
*/
|
|
9
|
-
export declare function resignIpaAsync(ipaPath: string, signingOptions: IosSigningOptions,
|
|
9
|
+
export declare function resignIpaAsync(ipaPath: string, signingOptions: IosSigningOptions, _options: Options): Promise<void>;
|