Compare commits

...

2 Commits

Author SHA1 Message Date
Joe Savona
9db3f8b484 Update on "[compiler] Null out source locations where not explicitly preserved"
Inspired by #32950. Specifically from https://github.com/facebook/react/issues/32950#issuecomment-2837887871, it sounds like Babel by default emits source map information for all nodes, even when they don't have a `loc` property set. Code coverage tools then pick up the synthesized source location information for this, leading to the issue described.

A few google searches didn't turn up any documented way to opt-out of generating source span information, but AI tools answer that explicitly setting `node.loc = null` omits the node from source maps. This PR is a quick hack to confirm that.

[ghstack-poisoned]
2025-04-30 09:54:19 +09:00
Joe Savona
06beb9f43e [compiler] Null out source locations where not explicitly preserved
Inspired by #32950. Specifically from https://github.com/facebook/react/issues/32950#issuecomment-2837887871, it sounds like Babel by default emits source map information for all nodes, even when they don't have a `loc` property set. Code coverage tools then pick up the synthesized source location information for this, leading to the issue described.

A few google searches didn't turn up any documented way to opt-out of generating source span information, but AI tools answer that explicitly setting `node.loc = null` omits the node from source maps. This PR is a quick hack to confirm that.

[ghstack-poisoned]
2025-04-29 18:06:45 +09:00
6 changed files with 114 additions and 1 deletions

View File

@@ -48,7 +48,7 @@ import {printIdentifier, printPlace} from '../HIR/PrintHIR';
import {eachPatternOperand} from '../HIR/visitors';
import {Err, Ok, Result} from '../Utils/Result';
import {GuardKind} from '../Utils/RuntimeDiagnosticConstants';
import {assertExhaustive} from '../Utils/utils';
import {assertExhaustive, hasOwnProperty} from '../Utils/utils';
import {buildReactiveFunction} from './BuildReactiveFunction';
import {SINGLE_CHILD_FBT_TAGS} from './MemoizeFbtAndMacroOperandsInSameScope';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';
@@ -374,6 +374,8 @@ function codegenReactiveFunction(
const countMemoBlockVisitor = new CountMemoBlockVisitor(fn.env);
visitReactiveFunction(fn, countMemoBlockVisitor, undefined);
setMissingLocationsToNull(body);
return Ok({
type: 'CodegenFunction',
loc: fn.loc,
@@ -2665,3 +2667,38 @@ function compareScopeDeclaration(
else if (aName > bName) return 1;
else return 0;
}
function setMissingLocationsToNull(ast: any): void {
if (Array.isArray(ast)) {
ast.forEach(item => setMissingLocationsToNull(item));
return;
} else if (
ast == null ||
typeof ast !== 'object' ||
typeof ast['type'] !== 'string'
) {
return;
}
if (ast['loc'] == null) {
ast['loc'] = {
start: {line: null, column: null, index: null},
end: {line: null, column: null, index: null},
filename: null,
identifierName: null,
};
}
for (const key in ast) {
if (!hasOwnProperty(ast, key)) {
continue;
}
const value = ast[key];
if (typeof value !== 'object') {
/*
* We handle this above too, but avoid extra function calls in the majority of
* cases where we're traversing an AST node's properties
*/
continue;
}
setMissingLocationsToNull(ast[key]);
}
}

View File

@@ -0,0 +1,55 @@
## Input
```javascript
// @sourceMaps
export const Button = () => {
return <button>Click me</button>;
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @sourceMaps
export const Button = () => {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <button>Click me</button>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
};
```
## Source Map
```
{
"version": 3,
"names": [
"Button",
"$",
"_c",
"t0",
"Symbol",
"for"
],
"sources": [
"sourcemaps-simple.ts"
],
"sourcesContent": [
"// @sourceMaps\nexport const Button = () => {\n return <button>Click me</button>;\n};\n"
],
"mappings": "kDAAA;AACA,OAAO,MAAMA,MAAM,GAAGA,CAAA,YAAAC,CAAA,GAAAC,EAAA,QAAAC,EAAA,KAAAF,CAAA,QAAAG,MAAA,CAAAC,GAAA;IACbF,EAAA,UAAyB,CAAjB,QAAQ,EAAhB,MAAyB,EAAAF,CAAA,MAAAE,EAAA,SAAAA,EAAA,GAAAF,CAAA,YAAzBE,EAAyB,EACjC",
"ignoreList": []
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,4 @@
// @sourceMaps
export const Button = () => {
return <button>Click me</button>;
};

View File

@@ -306,6 +306,7 @@ export type TransformResult = {
original: string;
forget: string;
} | null;
sourceMap: BabelCore.BabelFileResult['map'];
};
export async function transformFixtureInput(
@@ -331,6 +332,9 @@ export async function transformFixtureInput(
// with `cwd`, which is different across machines
const virtualFilepath = '/' + filename;
// Check if we should emit source maps in the test fixture
const includeSourceMaps = firstLine.includes('@sourceMaps');
const presets =
language === 'typescript'
? TypescriptEvaluatorPresets
@@ -357,6 +361,7 @@ export async function transformFixtureInput(
'babel-plugin-idx',
],
sourceType: 'module',
sourceMaps: includeSourceMaps,
ast: includeEvaluator,
cloneInputAst: includeEvaluator,
configFile: false,
@@ -447,6 +452,7 @@ export async function transformFixtureInput(
forgetOutput,
logs: formattedLogs,
evaluatorCode,
sourceMap: includeSourceMaps ? forgetResult.map : null,
},
};
}

View File

@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {BabelFileResult} from '@babel/core';
import chalk from 'chalk';
import fs from 'fs';
import invariant from 'invariant';
@@ -24,6 +25,7 @@ export function writeOutputToString(
evaluatorOutput: string | null,
logs: string | null,
errorMessage: string | null,
sourceMap: BabelFileResult['map'] | null,
) {
// leading newline intentional
let result = `
@@ -42,6 +44,14 @@ ${wrapWithTripleBackticks(compilerOutput, 'javascript')}
result += '\n';
}
if (sourceMap != null) {
result += `
## Source Map
${wrapWithTripleBackticks(JSON.stringify(sourceMap, null, 2))}
`;
}
if (logs != null) {
result += `
## Logs

View File

@@ -245,6 +245,7 @@ export async function transformFixture(
sproutOutput,
compileResult?.logs ?? null,
error,
compileResult?.sourceMap ?? null,
);
return {