import fs from 'fs';
import path from 'path';
import os from 'os';
import type { SourceMap } from '@margelo/hermes-profile-transformer';
import type { MetroBundleOptions } from './getMetroBundleOptions';
type Config = any;
function getTempFilePath(filename: string) {
return path.join(os.tmpdir(), filename);
}
function writeJsonSync(targetPath: string, data: any) {
let json;
try {
json = JSON.stringify(data);
} catch (e) {
throw new Error(
`Failed to serialize data to json before writing to ${targetPath}: ${(e as Error).message}`
);
}
try {
fs.writeFileSync(targetPath, json, 'utf-8');
} catch (e) {
throw new Error(`Failed to write json to ${targetPath}: ${(e as Error).message}`);
}
}
async function getSourcemapFromServer(
port: string,
{ platform, dev, minify, host }: MetroBundleOptions
): Promise<SourceMap | undefined> {
console.log('Getting source maps from Metro packager server');
const requestURL = `http://${host}:${port}/index.map?platform=${platform}&dev=${dev}&minify=${minify}`;
console.log(`Downloading from ${requestURL}`);
try {
const res = await fetch(requestURL);
const data = await res.json();
if (typeof data !== 'object') {
console.log(
`Failed to fetch source map from "${requestURL}", unexpected response format.`
);
return undefined;
}
return data as SourceMap;
} catch (e) {
console.log(`Failed to fetch source map from "${requestURL}"`);
return undefined;
}
}
* Generate a sourcemap by fetching it from a running metro server
*/
export async function generateSourcemap(
port: string,
bundleOptions: MetroBundleOptions
): Promise<string | undefined> {
const sourceMapPath = getTempFilePath('index.map');
const sourceMapResult = await getSourcemapFromServer(port, bundleOptions);
if (sourceMapResult) {
console.log('Using source maps from Metro packager server');
writeJsonSync(sourceMapPath, sourceMapResult);
console.log(
`Successfully obtained the source map and stored it in ${sourceMapPath}`
);
return sourceMapPath;
} else {
console.log('Error: Cannot obtain source maps from Metro packager server');
return undefined;
}
}
*
* @param ctx
*/
export async function findSourcemap(
ctx: Config,
port: string,
bundleOptions: MetroBundleOptions
): Promise<string | undefined> {
const intermediateBuildPath = path.join(
ctx.root,
'android',
'app',
'build',
'intermediates',
'sourcemaps',
'react',
'debug',
'index.android.bundle.packager.map'
);
const generatedBuildPath = path.join(
ctx.root,
'android',
'app',
'build',
'generated',
'sourcemaps',
'react',
'debug',
'index.android.bundle.map'
);
if (fs.existsSync(generatedBuildPath)) {
console.log(`Getting the source map from ${generateSourcemap}`);
return generatedBuildPath;
} else if (fs.existsSync(intermediateBuildPath)) {
console.log(`Getting the source map from ${intermediateBuildPath}`);
return intermediateBuildPath;
} else {
return generateSourcemap(port, bundleOptions);
}
}