@datadog/mobile-react-native
Version:
A client-side React Native module to interact with Datadog
129 lines (113 loc) • 5.46 kB
JavaScript
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*
* Portions of this code are adapted from Sentry's Metro configuration:
* https://github.com/getsentry/sentry-react-native/blob/17c0c2e8913030e4826d055284a735efad637312/packages/core/src/js/tools/sentryMetroSerializer.ts
*/
import { DEBUG_ID_PLACEHOLDER, addDebugIdModule, checkIfDebugIdModuleExists, createDebugIdFromBundle, createDebugIdModule, getDebugIdFromBundleSource, injectDebugIdInCode, injectDebugIdInCodeAndSourceMap } from './debugIdHelper';
import { getBaseJSBundleFunction, getBundleToStringFunction, convertSerializerOutput as getMetroBundleWithMap, getSortedModules, getSourceMapStringFunction } from './utils';
/**
* Creates a Metro serializer that adds a Debug ID module to the bundle.
* This module injects the Debug ID at runtime, making it globally accessible.
*
* @param customSerializer - Optional custom {@link MetroSerializer}. If provided, you are responsible
* for invoking `options.datadogBundleCallback` within it.
*/
export const createDatadogMetroSerializer = customSerializer => {
const serializer = customSerializer || createDefaultMetroSerializer();
return async (entryPoint, preModules, graph, options) => {
// Skip for hot reload mode
if (graph.transformOptions.hot) {
return serializer(entryPoint, preModules, graph, options);
}
// Make sure we don't add the Debug ID module twice
if (checkIfDebugIdModuleExists(preModules)) {
return serializer(entryPoint, preModules, graph, options);
}
// Create a virtual module to inject the Debug ID in a globally accessible property
const debugIdModule = createDebugIdModule(DEBUG_ID_PLACEHOLDER);
// Set the datadogBundleCallback in the options, to be used later by the serializer
options.datadogBundleCallback = createDatadogBundleCallback(debugIdModule);
// Add the Debug ID virtual module to the pre-modules
const preModulesWithDebugId = addDebugIdModule(preModules, debugIdModule);
// Run serializer
const serializerOutput = serializer(entryPoint, preModulesWithDebugId, graph, options);
// Get serialized code and sourcemap
const {
code,
map
} = await getMetroBundleWithMap(serializerOutput);
// Retrieve the Debug ID, previously injected as a snippet of code in a virtual module by `datadogBundleCallback`.
const debugId = getDebugIdFromBundleSource(code);
if (!debugId) {
throw new Error('[DATADOG METRO PLUGIN] Debug ID was not found in the bundle. Call `options.datadogBundleCallback` if you are using a custom serializer.');
}
// Inject the Debug ID as a comment in the bundle, and as a top-level property in the sourcemap.
const result = injectDebugIdInCodeAndSourceMap(debugId, code, map);
return result;
};
};
/**
* Creates a Metro Bundle Serializer like metro does by default, while also calling the datadogBundleCallback.
* https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/Server.js#L272-L307
*/
export const createDefaultMetroSerializer = () => {
return (entryPoint, preModules, graph, options) => {
// Creates the default metro bundle
// https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js#L25
const baseJSBundle = getBaseJSBundleFunction();
let bundle = baseJSBundle(entryPoint, preModules, graph, options);
// Modify the bundle through the datadogBundleCallback, if we are not in hot-reload mode
if (options.datadogBundleCallback && !graph.transformOptions.hot) {
bundle = options.datadogBundleCallback(bundle);
}
// Retrieves the processed code from the bundle
const bundleToString = getBundleToStringFunction();
const {
code
} = bundleToString(bundle);
// If we are in hot-reload mode, we skip sourcemaps generation, and only return the code.
if (graph.transformOptions.hot) {
return code;
}
// Force generation of sourcemaps
const sourceMapString = getSourceMapStringFunction();
const map = sourceMapString([...preModules, ...getSortedModules(graph, options)], {
processModuleFilter: options.processModuleFilter,
shouldAddToIgnoreList: options.shouldAddToIgnoreList
});
return {
code,
map
};
};
};
/**
* Creates a callback used to transform the given bundle by injecting the Debug ID snippet.
* @param debugIdModule - The virtual Debug ID module
* @returns The bundle callback
*/
export const createDatadogBundleCallback = debugIdModule => {
return bundle => {
const debugId = createDebugIdFromBundle(bundle);
const code = debugIdModule.getSource().toString();
debugIdModule.setSource(injectDebugIdInCode(code, debugId));
bundle.pre = injectDebugIdInCode(bundle.pre, debugId);
return bundle;
};
};
/**
* Adds Debug ID module for runtime injection, used for Expo.
*/
export function unstable_beforeAssetSerializationPlugin({
premodules,
debugId
}) {
if (!debugId || checkIfDebugIdModuleExists(premodules)) {
return premodules;
}
return [...addDebugIdModule(premodules, createDebugIdModule(debugId))];
}
//# sourceMappingURL=metroSerializer.js.map