138 lines
4.4 KiB
JavaScript
138 lines
4.4 KiB
JavaScript
![]() |
const { loadAsync } = require('@expo/metro-config');
|
||
|
const fs = require('fs');
|
||
|
const Server = require('metro/src/Server');
|
||
|
const path = require('path');
|
||
|
const uuid = require('uuid/v4');
|
||
|
|
||
|
const filterPlatformAssetScales = require('./filterPlatformAssetScales');
|
||
|
|
||
|
(async function() {
|
||
|
const platform = process.argv[2];
|
||
|
const possibleProjectRoot = process.argv[3];
|
||
|
const destinationDir = process.argv[4];
|
||
|
const entryFile = process.env.ENTRY_FILE || 'index.js';
|
||
|
|
||
|
// Remove projectRoot validation when we no longer support React Native <= 62
|
||
|
let projectRoot;
|
||
|
if (fs.existsSync(path.join(possibleProjectRoot, entryFile))) {
|
||
|
projectRoot = possibleProjectRoot;
|
||
|
} else if (fs.existsSync(path.join(possibleProjectRoot, '..', entryFile))) {
|
||
|
projectRoot = path.resolve(possibleProjectRoot, '..');
|
||
|
}
|
||
|
|
||
|
let assets;
|
||
|
try {
|
||
|
assets = await fetchAssetManifestAsync(platform, projectRoot, entryFile);
|
||
|
} catch (e) {
|
||
|
throw new Error(
|
||
|
"Error loading assets JSON from Metro. Ensure you've followed all expo-updates installation steps correctly. " +
|
||
|
e.message
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const manifest = {
|
||
|
id: uuid(),
|
||
|
commitTime: new Date().getTime(),
|
||
|
assets: [],
|
||
|
};
|
||
|
|
||
|
assets.forEach(function(asset) {
|
||
|
if (!asset.fileHashes) {
|
||
|
throw new Error(
|
||
|
'The hashAssetFiles Metro plugin is not configured. You need to add a metro.config.js to your project that configures Metro to use this plugin. See https://github.com/expo/expo/blob/master/packages/expo-updates/README.md#metroconfigjs for an example.'
|
||
|
);
|
||
|
}
|
||
|
filterPlatformAssetScales(platform, asset.scales).forEach(function(scale, index) {
|
||
|
const assetInfoForManifest = {
|
||
|
name: asset.name,
|
||
|
type: asset.type,
|
||
|
scale,
|
||
|
packagerHash: asset.fileHashes[index],
|
||
|
subdirectory: asset.httpServerLocation,
|
||
|
};
|
||
|
if (platform === 'ios') {
|
||
|
assetInfoForManifest.nsBundleDir = getIosDestinationDir(asset);
|
||
|
assetInfoForManifest.nsBundleFilename =
|
||
|
scale === 1 ? asset.name : asset.name + '@' + scale + 'x';
|
||
|
} else if (platform === 'android') {
|
||
|
assetInfoForManifest.scales = asset.scales;
|
||
|
assetInfoForManifest.resourcesFilename = getAndroidResourceIdentifier(asset);
|
||
|
assetInfoForManifest.resourcesFolder = getAndroidResourceFolderName(asset);
|
||
|
}
|
||
|
manifest.assets.push(assetInfoForManifest);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
fs.writeFileSync(path.join(destinationDir, 'app.manifest'), JSON.stringify(manifest));
|
||
|
})().catch(e => {
|
||
|
console.error(e);
|
||
|
process.exit(1);
|
||
|
});
|
||
|
|
||
|
// See https://developer.android.com/guide/topics/resources/drawable-resource.html
|
||
|
const drawableFileTypes = new Set(['gif', 'jpeg', 'jpg', 'png', 'svg', 'webp', 'xml']);
|
||
|
function getAndroidResourceFolderName(asset) {
|
||
|
return drawableFileTypes.has(asset.type) ? 'drawable' : 'raw';
|
||
|
}
|
||
|
|
||
|
// copied from react-native/Libraries/Image/assetPathUtils.js
|
||
|
function getAndroidResourceIdentifier(asset) {
|
||
|
var folderPath = getBasePath(asset);
|
||
|
return (folderPath + '/' + asset.name)
|
||
|
.toLowerCase()
|
||
|
.replace(/\//g, '_') // Encode folder structure in file name
|
||
|
.replace(/([^a-z0-9_])/g, '') // Remove illegal chars
|
||
|
.replace(/^assets_/, ''); // Remove "assets_" prefix
|
||
|
}
|
||
|
|
||
|
function getIosDestinationDir(asset) {
|
||
|
// react-native-cli replaces `..` with `_` when embedding assets in the iOS app bundle
|
||
|
// https://github.com/react-native-community/cli/blob/0a93be1a42ed1fb05bb0ebf3b82d58b2dd920614/packages/cli/src/commands/bundle/getAssetDestPathIOS.ts
|
||
|
return getBasePath(asset).replace(/\.\.\//g, '_');
|
||
|
}
|
||
|
|
||
|
// copied from react-native/Libraries/Image/assetPathUtils.js
|
||
|
function getBasePath(asset) {
|
||
|
var basePath = asset.httpServerLocation;
|
||
|
if (basePath[0] === '/') {
|
||
|
basePath = basePath.substr(1);
|
||
|
}
|
||
|
return basePath;
|
||
|
}
|
||
|
|
||
|
// Spawn a Metro server to get the asset manifest
|
||
|
async function fetchAssetManifestAsync(platform, projectRoot, entryFile) {
|
||
|
// Project-level babel config does not load unless we change to the
|
||
|
// projectRoot before instantiating the server
|
||
|
process.chdir(projectRoot);
|
||
|
|
||
|
const config = await loadAsync(projectRoot);
|
||
|
const server = new Server(config);
|
||
|
|
||
|
const requestOpts = {
|
||
|
entryFile,
|
||
|
dev: false,
|
||
|
minify: false,
|
||
|
platform,
|
||
|
};
|
||
|
|
||
|
let assetManifest;
|
||
|
let error;
|
||
|
try {
|
||
|
assetManifest = await server.getAssets({
|
||
|
...Server.DEFAULT_BUNDLE_OPTIONS,
|
||
|
...requestOpts,
|
||
|
});
|
||
|
} catch (e) {
|
||
|
error = e;
|
||
|
} finally {
|
||
|
server.end();
|
||
|
}
|
||
|
|
||
|
if (error) {
|
||
|
throw error;
|
||
|
}
|
||
|
|
||
|
return assetManifest;
|
||
|
}
|