зеркало из https://github.com/microsoft/rnx-kit.git
fix(metro-serializer-esbuild): fix source maps not pointing to source (#1856)
This commit is contained in:
Родитель
7d82009815
Коммит
61151646d4
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@rnx-kit/metro-serializer-esbuild": patch
|
||||
---
|
||||
|
||||
Fix source maps not pointing to source
|
|
@ -152,6 +152,15 @@ use environment names. See the full documentation for a list of supported names.
|
|||
|
||||
Defaults to `hermes0.7.0`.
|
||||
|
||||
### `sourceMapPaths`
|
||||
|
||||
Determines whether paths in the output source map are absolute or relative to
|
||||
the directory containing the source map.
|
||||
|
||||
Values: `absolute` | `relative`
|
||||
|
||||
Defaults to `relative`.
|
||||
|
||||
### `analyze`
|
||||
|
||||
Sets whether esbuild should output a report at the end of bundling.
|
||||
|
|
|
@ -7,12 +7,14 @@ import type { Dependencies, Graph, Module, SerializerOptions } from "metro";
|
|||
import type { SerializerConfigT } from "metro-config";
|
||||
import * as path from "path";
|
||||
import * as semver from "semver";
|
||||
import { absolutizeSourceMap, generateSourceMappingURL } from "./sourceMap";
|
||||
|
||||
export { esbuildTransformerConfig } from "./esbuildTransformerConfig";
|
||||
|
||||
export type Options = Pick<BuildOptions, "logLevel" | "minify" | "target"> & {
|
||||
analyze?: boolean | "verbose";
|
||||
fabric?: boolean;
|
||||
sourceMapPaths?: "absolute" | "relative";
|
||||
};
|
||||
|
||||
function assertVersion(requiredVersion: string): void {
|
||||
|
@ -28,22 +30,6 @@ function escapePath(path: string): string {
|
|||
return path.replace(/\\+/g, "\\\\");
|
||||
}
|
||||
|
||||
function fixSourceMap(outputPath: string, text: string): string {
|
||||
/**
|
||||
* All paths in the source map are relative to the directory
|
||||
* containing the source map.
|
||||
*
|
||||
* See https://esbuild.github.io/api/#source-root
|
||||
*/
|
||||
const sourceRoot = path.dirname(outputPath);
|
||||
const sourcemap = JSON.parse(text);
|
||||
const sources = sourcemap.sources.map((file: string) =>
|
||||
path.resolve(sourceRoot, file)
|
||||
);
|
||||
|
||||
return JSON.stringify({ ...sourcemap, sources });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified module has any side effects.
|
||||
*
|
||||
|
@ -104,7 +90,28 @@ function isRedundantPolyfill(modulePath: string): boolean {
|
|||
}
|
||||
|
||||
function outputOf(module: Module | undefined): string | undefined {
|
||||
return module?.output?.map(({ data }) => data.code).join("\n");
|
||||
if (!module) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const jsModules = module.output.filter(({ type }) => type.startsWith("js/"));
|
||||
if (jsModules.length !== 1) {
|
||||
throw new Error(
|
||||
`Modules must have exactly one JS output, but ${module.path} has ${jsModules.length}`
|
||||
);
|
||||
}
|
||||
|
||||
const code = jsModules[0].data.code;
|
||||
const moduleWithModuleNameOnly = {
|
||||
...module,
|
||||
// esbuild only needs the base file name. It derives the path from the
|
||||
// imported path, and appends the file name to it. If we don't trim the path
|
||||
// here, we will end up with "double" paths, e.g.
|
||||
// `src/Users/<user>/Source/rnx-kit/packages/test-app/src/App.native.tsx`.
|
||||
path: path.basename(module.path),
|
||||
};
|
||||
|
||||
return `${code}\n${generateSourceMappingURL([moduleWithModuleNameOnly])}\n`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,7 +335,10 @@ export function MetroSerializer(
|
|||
if (outputPath === "<stdout>" || outputPath.endsWith(outfile)) {
|
||||
result.code = text;
|
||||
} else if (outputPath.endsWith(sourcemapfile)) {
|
||||
result.map = fixSourceMap(outputPath, text);
|
||||
result.map =
|
||||
buildOptions?.sourceMapPaths === "absolute"
|
||||
? absolutizeSourceMap(outputPath, text)
|
||||
: text;
|
||||
}
|
||||
});
|
||||
if (metafile) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import type { Module } from "metro";
|
||||
// @ts-expect-error No declaration file for module
|
||||
import sourceMapString from "metro/src/DeltaBundler/Serializers/sourceMapString";
|
||||
import * as path from "path";
|
||||
|
||||
const sourceMappingOptions = {
|
||||
processModuleFilter: () => true,
|
||||
excludeSource: false,
|
||||
};
|
||||
|
||||
export function absolutizeSourceMap(outputPath: string, text: string): string {
|
||||
/**
|
||||
* All paths in the source map are relative to the directory
|
||||
* containing the source map.
|
||||
*
|
||||
* See https://esbuild.github.io/api/#source-root
|
||||
*/
|
||||
const sourceRoot = path.dirname(outputPath);
|
||||
const sourcemap = JSON.parse(text);
|
||||
const sources = sourcemap.sources.map((file: string) =>
|
||||
path.resolve(sourceRoot, file)
|
||||
);
|
||||
|
||||
return JSON.stringify({ ...sourcemap, sources });
|
||||
}
|
||||
|
||||
export function getInlineSourceMappingURL(modules: readonly Module[]): string {
|
||||
const sourceMap = sourceMapString(modules, sourceMappingOptions);
|
||||
const base64 = Buffer.from(sourceMap).toString("base64");
|
||||
return `data:application/json;charset=utf-8;base64,${base64}`;
|
||||
}
|
||||
|
||||
export function generateSourceMappingURL(modules: readonly Module[]): string {
|
||||
return `//# sourceMappingURL=${getInlineSourceMappingURL(modules)}`;
|
||||
}
|
|
@ -5,7 +5,14 @@ require_relative '../../../node_modules/react-native-test-app/test_app'
|
|||
workspace 'SampleCrossApp.xcworkspace'
|
||||
|
||||
use_flipper! false
|
||||
use_test_app! do |target|
|
||||
|
||||
options = {
|
||||
:fabric_enabled => false,
|
||||
:hermes_enabled => false,
|
||||
:turbomodule_enabled => false,
|
||||
}
|
||||
|
||||
use_test_app! options do |target|
|
||||
target.app do
|
||||
pod 'MSAL', :modular_headers => true
|
||||
end
|
||||
|
|
|
@ -289,11 +289,11 @@ PODS:
|
|||
- ReactTestApp-DevSupport (1.6.8):
|
||||
- React-Core
|
||||
- React-jsi
|
||||
- ReactTestApp-MSAL (1.0.4):
|
||||
- ReactTestApp-MSAL (1.0.5):
|
||||
- MSAL
|
||||
- RNXAuth
|
||||
- ReactTestApp-Resources (1.0.0-dev)
|
||||
- RNXAuth (0.1.4):
|
||||
- RNXAuth (0.1.5):
|
||||
- React-Core
|
||||
- Yoga (1.14.0)
|
||||
|
||||
|
@ -448,9 +448,9 @@ SPEC CHECKSUMS:
|
|||
React-runtimeexecutor: 8cdd80915ed6dabf2221a689f1f7ddb50ea5e9f3
|
||||
ReactCommon: 5b1b43a7d81a1ac4eec85f7c4db3283a14a3b13d
|
||||
ReactTestApp-DevSupport: 477807a2c223e21ea8567c0c0a5880d561e11534
|
||||
ReactTestApp-MSAL: 4fa86fda02c24009c9dd4b5647d45e0c45421f41
|
||||
ReactTestApp-MSAL: 803fb430f3d9bf6bc022964c64ac3b64fea88f73
|
||||
ReactTestApp-Resources: 74a1cf509f4e7962b16361ea4e73cba3648fff5d
|
||||
RNXAuth: 72b9e2b11d9f56e0f0860da43732dcccad0bcacb
|
||||
RNXAuth: 1f5001db635f153c400b657b91e06e9b997620e8
|
||||
Yoga: 2f6a78c58dcc2963bd8e34d96a4246d9dff2e3a7
|
||||
|
||||
PODFILE CHECKSUM: 438d9afb225dc2a99c6750449d7231544f5ad6f3
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env node
|
||||
// @ts-check
|
||||
|
||||
import { originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
||||
import * as assert from "assert";
|
||||
import * as fs from "fs";
|
||||
|
||||
const findInSourceFile = (() => {
|
||||
/** @type {Record<string, string[]>} */
|
||||
const sourceMap = {};
|
||||
return (/** @type {string} */ sourcePath, /** @type {string} */ needle) => {
|
||||
if (!sourceMap[sourcePath]) {
|
||||
sourceMap[sourcePath] = fs
|
||||
.readFileSync(sourcePath, { encoding: "utf-8" })
|
||||
.split("\n");
|
||||
}
|
||||
|
||||
const bundle = sourceMap[sourcePath];
|
||||
const lines = bundle.length;
|
||||
for (let i = 0; i < lines; ++i) {
|
||||
const column = bundle[i].indexOf(needle);
|
||||
if (column >= 0) {
|
||||
return { line: i + 1, column };
|
||||
}
|
||||
}
|
||||
|
||||
return { line: -1, column: -1 };
|
||||
};
|
||||
})();
|
||||
|
||||
const { [2]: bundlePath } = process.argv;
|
||||
if (!bundlePath || !fs.existsSync(bundlePath)) {
|
||||
console.log(`usage: validateSourceMap.mjs /path/to/jsbundle`);
|
||||
} else {
|
||||
const sourcemap = fs.readFileSync(bundlePath + ".map", { encoding: "utf-8" });
|
||||
const tracer = new TraceMap(JSON.parse(sourcemap));
|
||||
|
||||
// TODO: This is probably not the best way to validate source maps, but I
|
||||
// couldn't find one that was up-to-date and didn't throw false positives.
|
||||
[
|
||||
{ source: "src/App.native.tsx", needle: "function App(" },
|
||||
{ source: "src/App.native.tsx", needle: "function Button(" },
|
||||
{ source: "src/App.native.tsx", needle: "function DevMenu(" },
|
||||
{ source: "src/App.native.tsx", needle: "function Feature(" },
|
||||
{ source: "src/App.native.tsx", needle: "function Separator(" },
|
||||
{ source: "src/App.native.tsx", needle: "function useStyles(" },
|
||||
].forEach(({ source, needle }) => {
|
||||
assert.deepEqual(
|
||||
originalPositionFor(tracer, findInSourceFile(bundlePath, needle)),
|
||||
{ source, name: null, ...findInSourceFile(source, needle) }
|
||||
);
|
||||
});
|
||||
}
|
Загрузка…
Ссылка в новой задаче