You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
11 KiB
211 lines
11 KiB
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.getHermesBytecodeBundleVersionAsync = exports.isHermesBytecodeBundleAsync = exports.maybeInconsistentEngineIosAsync = exports.maybeInconsistentEngineAndroidAsync = exports.maybeThrowFromInconsistentEngineAsync = exports.parseGradleProperties = exports.createHermesSourcemapAsync = exports.buildHermesBundleAsync = exports.isEnableHermesManaged = void 0;
|
|
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
const os_1 = __importDefault(require("os"));
|
|
const path_1 = __importDefault(require("path"));
|
|
const process_1 = __importDefault(require("process"));
|
|
const semver_1 = __importDefault(require("semver"));
|
|
const importMetroFromProject_1 = require("./metro/importMetroFromProject");
|
|
function isEnableHermesManaged(expoConfig, platform) {
|
|
var _a, _b, _c, _d;
|
|
switch (platform) {
|
|
case 'android': {
|
|
if (!gteSdkVersion(expoConfig, '42.0.0')) {
|
|
// Hermes on Android is supported after SDK 42.
|
|
return false;
|
|
}
|
|
return ((_b = (_a = expoConfig.android) === null || _a === void 0 ? void 0 : _a.jsEngine) !== null && _b !== void 0 ? _b : expoConfig.jsEngine) === 'hermes';
|
|
}
|
|
case 'ios': {
|
|
if (!gteSdkVersion(expoConfig, '43.0.0')) {
|
|
// Hermes on iOS is supported after SDK 43.
|
|
return false;
|
|
}
|
|
return ((_d = (_c = expoConfig.ios) === null || _c === void 0 ? void 0 : _c.jsEngine) !== null && _d !== void 0 ? _d : expoConfig.jsEngine) === 'hermes';
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
exports.isEnableHermesManaged = isEnableHermesManaged;
|
|
async function buildHermesBundleAsync(projectRoot, code, map, optimize = false) {
|
|
const tempDir = path_1.default.join(os_1.default.tmpdir(), `expo-bundler-${process_1.default.pid}`);
|
|
await fs_extra_1.default.ensureDir(tempDir);
|
|
try {
|
|
const tempBundleFile = path_1.default.join(tempDir, 'index.bundle');
|
|
const tempSourcemapFile = path_1.default.join(tempDir, 'index.bundle.map');
|
|
await fs_extra_1.default.writeFile(tempBundleFile, code);
|
|
await fs_extra_1.default.writeFile(tempSourcemapFile, map);
|
|
const tempHbcFile = path_1.default.join(tempDir, 'index.hbc');
|
|
const hermesCommand = (0, importMetroFromProject_1.importHermesCommandFromProject)(projectRoot);
|
|
const args = ['-emit-binary', '-out', tempHbcFile, tempBundleFile, '-output-source-map'];
|
|
if (optimize) {
|
|
args.push('-O');
|
|
}
|
|
await (0, spawn_async_1.default)(hermesCommand, args);
|
|
const [hbc, sourcemap] = await Promise.all([
|
|
fs_extra_1.default.readFile(tempHbcFile),
|
|
createHermesSourcemapAsync(projectRoot, map, `${tempHbcFile}.map`),
|
|
]);
|
|
return {
|
|
hbc,
|
|
sourcemap,
|
|
};
|
|
}
|
|
finally {
|
|
await fs_extra_1.default.remove(tempDir);
|
|
}
|
|
}
|
|
exports.buildHermesBundleAsync = buildHermesBundleAsync;
|
|
async function createHermesSourcemapAsync(projectRoot, sourcemap, hermesMapFile) {
|
|
const composeSourceMaps = (0, importMetroFromProject_1.importMetroSourceMapComposeSourceMapsFromProject)(projectRoot);
|
|
const bundlerSourcemap = JSON.parse(sourcemap);
|
|
const hermesSourcemap = await fs_extra_1.default.readJSON(hermesMapFile);
|
|
return JSON.stringify(composeSourceMaps([bundlerSourcemap, hermesSourcemap]));
|
|
}
|
|
exports.createHermesSourcemapAsync = createHermesSourcemapAsync;
|
|
function parseGradleProperties(content) {
|
|
const result = {};
|
|
for (let line of content.split('\n')) {
|
|
line = line.trim();
|
|
if (!line || line.startsWith('#')) {
|
|
continue;
|
|
}
|
|
const sepIndex = line.indexOf('=');
|
|
const key = line.substr(0, sepIndex);
|
|
const value = line.substr(sepIndex + 1);
|
|
result[key] = value;
|
|
}
|
|
return result;
|
|
}
|
|
exports.parseGradleProperties = parseGradleProperties;
|
|
async function maybeThrowFromInconsistentEngineAsync(projectRoot, configFilePath, platform, isHermesManaged) {
|
|
const configFileName = path_1.default.basename(configFilePath);
|
|
if (platform === 'android' &&
|
|
(await maybeInconsistentEngineAndroidAsync(projectRoot, isHermesManaged))) {
|
|
throw new Error(`JavaScript engine configuration is inconsistent between ${configFileName} and Android native project.\n` +
|
|
`In ${configFileName}: Hermes is ${isHermesManaged ? 'enabled' : 'not enabled'}\n` +
|
|
`In Android native project: Hermes is ${isHermesManaged ? 'not enabled' : 'enabled'}\n` +
|
|
`Please check the following files for inconsistencies:\n` +
|
|
` - ${configFilePath}\n` +
|
|
` - ${path_1.default.join(projectRoot, 'android', 'gradle.properties')}\n` +
|
|
` - ${path_1.default.join(projectRoot, 'android', 'app', 'build.gradle')}\n` +
|
|
'Learn more: https://expo.fyi/hermes-android-config');
|
|
}
|
|
if (platform === 'ios' && (await maybeInconsistentEngineIosAsync(projectRoot, isHermesManaged))) {
|
|
throw new Error(`JavaScript engine configuration is inconsistent between ${configFileName} and iOS native project.\n` +
|
|
`In ${configFileName}: Hermes is ${isHermesManaged ? 'enabled' : 'not enabled'}\n` +
|
|
`In iOS native project: Hermes is ${isHermesManaged ? 'not enabled' : 'enabled'}\n` +
|
|
`Please check the following files for inconsistencies:\n` +
|
|
` - ${configFilePath}\n` +
|
|
` - ${path_1.default.join(projectRoot, 'ios', 'Podfile')}\n` +
|
|
` - ${path_1.default.join(projectRoot, 'ios', 'Podfile.properties.json')}\n` +
|
|
'Learn more: https://expo.fyi/hermes-ios-config');
|
|
}
|
|
}
|
|
exports.maybeThrowFromInconsistentEngineAsync = maybeThrowFromInconsistentEngineAsync;
|
|
async function maybeInconsistentEngineAndroidAsync(projectRoot, isHermesManaged) {
|
|
// Trying best to check android native project if by chance to be consistent between app config
|
|
// Check android/app/build.gradle for "enableHermes: true"
|
|
const appBuildGradlePath = path_1.default.join(projectRoot, 'android', 'app', 'build.gradle');
|
|
if (fs_extra_1.default.existsSync(appBuildGradlePath)) {
|
|
const content = await fs_extra_1.default.readFile(appBuildGradlePath, 'utf8');
|
|
const isPropsReference = content.search(/^\s*enableHermes:\s*\(findProperty\('expo.jsEngine'\) \?: "jsc"\) == "hermes",?\s+/m) >= 0;
|
|
const isHermesBare = content.search(/^\s*enableHermes:\s*true,?\s+/m) >= 0;
|
|
if (!isPropsReference && isHermesManaged !== isHermesBare) {
|
|
return true;
|
|
}
|
|
}
|
|
// Check gradle.properties from prebuild template
|
|
const gradlePropertiesPath = path_1.default.join(projectRoot, 'android', 'gradle.properties');
|
|
if (fs_extra_1.default.existsSync(gradlePropertiesPath)) {
|
|
const props = parseGradleProperties(await fs_extra_1.default.readFile(gradlePropertiesPath, 'utf8'));
|
|
const isHermesBare = props['expo.jsEngine'] === 'hermes';
|
|
if (isHermesManaged !== isHermesBare) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
exports.maybeInconsistentEngineAndroidAsync = maybeInconsistentEngineAndroidAsync;
|
|
async function maybeInconsistentEngineIosAsync(projectRoot, isHermesManaged) {
|
|
// Trying best to check ios native project if by chance to be consistent between app config
|
|
// Check ios/Podfile for ":hermes_enabled => true"
|
|
const podfilePath = path_1.default.join(projectRoot, 'ios', 'Podfile');
|
|
if (fs_extra_1.default.existsSync(podfilePath)) {
|
|
const content = await fs_extra_1.default.readFile(podfilePath, 'utf8');
|
|
const hermesPropReferences = [
|
|
// sdk 45
|
|
/^\s*:hermes_enabled\s*=>\s*flags\[:hermes_enabled\]\s*\|\|\s*podfile_properties\['expo.jsEngine'\]\s*==\s*'hermes',?/m,
|
|
// <= sdk 44
|
|
/^\s*:hermes_enabled\s*=>\s*podfile_properties\['expo.jsEngine'\] == 'hermes',?\s+/m,
|
|
];
|
|
const isPropsReference = hermesPropReferences.reduce((prev, curr) => prev || content.search(curr) >= 0, false);
|
|
const isHermesBare = content.search(/^\s*:hermes_enabled\s*=>\s*true,?\s+/m) >= 0;
|
|
if (!isPropsReference && isHermesManaged !== isHermesBare) {
|
|
return true;
|
|
}
|
|
}
|
|
// Check Podfile.properties.json from prebuild template
|
|
const podfilePropertiesPath = path_1.default.join(projectRoot, 'ios', 'Podfile.properties.json');
|
|
if (fs_extra_1.default.existsSync(podfilePropertiesPath)) {
|
|
const props = await parsePodfilePropertiesAsync(podfilePropertiesPath);
|
|
const isHermesBare = props['expo.jsEngine'] === 'hermes';
|
|
if (isHermesManaged !== isHermesBare) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
exports.maybeInconsistentEngineIosAsync = maybeInconsistentEngineIosAsync;
|
|
// https://github.com/facebook/hermes/blob/release-v0.5/include/hermes/BCGen/HBC/BytecodeFileFormat.h#L24-L25
|
|
const HERMES_MAGIC_HEADER = 'c61fbc03c103191f';
|
|
async function isHermesBytecodeBundleAsync(file) {
|
|
const header = await readHermesHeaderAsync(file);
|
|
return header.slice(0, 8).toString('hex') === HERMES_MAGIC_HEADER;
|
|
}
|
|
exports.isHermesBytecodeBundleAsync = isHermesBytecodeBundleAsync;
|
|
async function getHermesBytecodeBundleVersionAsync(file) {
|
|
const header = await readHermesHeaderAsync(file);
|
|
if (header.slice(0, 8).toString('hex') !== HERMES_MAGIC_HEADER) {
|
|
throw new Error('Invalid hermes bundle file');
|
|
}
|
|
return header.readUInt32LE(8);
|
|
}
|
|
exports.getHermesBytecodeBundleVersionAsync = getHermesBytecodeBundleVersionAsync;
|
|
async function readHermesHeaderAsync(file) {
|
|
const fd = await fs_extra_1.default.open(file, 'r');
|
|
const buffer = Buffer.alloc(12);
|
|
await fs_extra_1.default.read(fd, buffer, 0, 12, null);
|
|
await fs_extra_1.default.close(fd);
|
|
return buffer;
|
|
}
|
|
// Cloned from xdl/src/Versions.ts, we cannot use that because of circular dependency
|
|
function gteSdkVersion(expJson, sdkVersion) {
|
|
if (!expJson.sdkVersion) {
|
|
return false;
|
|
}
|
|
if (expJson.sdkVersion === 'UNVERSIONED') {
|
|
return true;
|
|
}
|
|
try {
|
|
return semver_1.default.gte(expJson.sdkVersion, sdkVersion);
|
|
}
|
|
catch {
|
|
throw new Error(`${expJson.sdkVersion} is not a valid version. Must be in the form of x.y.z`);
|
|
}
|
|
}
|
|
async function parsePodfilePropertiesAsync(podfilePropertiesPath) {
|
|
try {
|
|
return JSON.parse(await fs_extra_1.default.readFile(podfilePropertiesPath, 'utf8'));
|
|
}
|
|
catch {
|
|
return {};
|
|
}
|
|
}
|
|
//# sourceMappingURL=HermesBundler.js.map
|