Files
babel/packages/babel-core/test/input-source-map-resolution.js
0d4e8503bd fix: improve inputSourceMap URL handling (#17999)
* fix: improve inputSourceMap URL handling

* refactor: extract transformation/read-input-source-map-file

* chore: keep sub in git

* Improve relative upward check

* address review comments

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-05-20 14:02:38 -04:00

386 lines
11 KiB
JavaScript

import * as babel from "../lib/index.js";
import convertSourceMap from "convert-source-map";
import path from "node:path";
import fs from "node:fs";
import { commonJS } from "$repo-utils";
const { __dirname } = commonJS(import.meta.url);
// @see packages/babel-core/src/transformation/normalize-file.ts
const EXTERNAL_SOURCEMAP_REGEX =
/^\/\/[@#][ \t]+sourceMappingURL=([^\s'"`]+)[ \t]*$/m;
function dirTreeDisplay(dirPath, indent = "") {
let result = "";
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
const isLast = i === entries.length - 1;
result += indent + (isLast ? "└─ " : "β”œβ”€ ") + entry.name + "\n";
if (entry.isDirectory()) {
result += dirTreeDisplay(
path.join(dirPath, entry.name),
indent + (isLast ? " " : "β”‚ "),
);
}
}
return result;
}
function getInputSourceMapURL(inputFilePath) {
const inputCode = fs.readFileSync(inputFilePath, "utf8");
const match = inputCode.match(EXTERNAL_SOURCEMAP_REGEX);
return match ? match[1] : null;
}
function verifyInputFileSourceMapExistence(inputFilePath) {
const sourceMapURL = getInputSourceMapURL(inputFilePath);
const sourceMapPath = path.resolve(path.dirname(inputFilePath), sourceMapURL);
const sourceMap = convertSourceMap
.fromJSON(fs.readFileSync(sourceMapPath, "utf8"))
.toObject();
expect(sourceMap.sourcesContent).toHaveLength(1);
expect(sourceMap.sourcesContent[0]).toContain("const enum Msg");
}
function expectInputSourceMapIsApplied(result) {
expect(result.map.sourcesContent).toHaveLength(1);
expect(result.map.sourcesContent[0]).toContain("const enum Msg");
expect(result.map.sourcesContent[0]).toContain("This map should be loaded");
}
function expectInputSourceMapIsPresentButNotApplied(result, inputFilePath) {
expect(result.map.sourcesContent).toHaveLength(1);
expect(result.map.sourcesContent[0]).not.toContain("const enum Msg");
verifyInputFileSourceMapExistence(inputFilePath);
}
describe("input source map resolution", function () {
const base = path.join(__dirname, "fixtures/input-source-map-resolution");
let cwd;
beforeEach(function () {
cwd = process.cwd();
process.chdir(base);
});
afterEach(function () {
process.chdir(cwd);
});
it("should resolve input source map downward relative to the input file", function () {
const cwd = path.join(base, "relative-down");
process.chdir(cwd);
const inputFilePath = path.join(cwd, "input.js");
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
β”œβ”€ input.js
└─ input.js.map
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsApplied(result);
});
it("should resolve input source map downward across package.json boundaries", function () {
const cwd = path.join(base, "relative-down-into-node-modules");
process.chdir(cwd);
const inputFilePath = path.join(cwd, "input.js");
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
β”œβ”€ input.js
└─ node_modules
└─ corp-source-maps
β”œβ”€ input.js.map
└─ package.json
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"node_modules/corp-source-maps/input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsApplied(result);
});
it("should resolve input source map upward within the root boundary", function () {
const cwd = path.join(base, "relative-up-within-root");
process.chdir(cwd);
const inputFilePath = path.join(cwd, "root", "sub", "input.js");
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
└─ root
β”œβ”€ input.js.map
└─ sub
└─ input.js
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
root: path.join(cwd, "root"),
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsApplied(result);
});
it("should resolve input source map upward within the package.json boundary", function () {
const cwd = path.join(base, "relative-up-within-node-modules");
process.chdir(cwd);
const inputFilePath = path.join(
cwd,
"node_modules",
"corp-lib",
"sub",
"input.js",
);
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
└─ node_modules
└─ corp-lib
β”œβ”€ input.js.map
β”œβ”€ package.json
└─ sub
└─ input.js
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsApplied(result);
});
it("should not resolve input source map upward across the package.json boundary", function () {
const cwd = path.join(base, "invalid-relative-up-across-node-modules");
process.chdir(cwd);
const inputFilePath = path.join(
cwd,
"node_modules",
"corp-lib",
"input.js",
);
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
β”œβ”€ input.js.map
└─ node_modules
└─ corp-lib
β”œβ”€ input.js
└─ package.json
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsPresentButNotApplied(result, inputFilePath);
});
it("should not resolve input source map upward across the root boundary", function () {
const cwd = path.join(base, "invalid-relative-up-across-root");
process.chdir(cwd);
const inputFilePath = path.join(process.cwd(), "root", "sub", "input.js");
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
β”œβ”€ input.js.map
└─ root
└─ sub
└─ input.js
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
root: path.join(cwd, "root"),
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsPresentButNotApplied(result, inputFilePath);
});
it("should not resolve input source map upward across the root boundary and within the package.json boundary", function () {
const cwd = path.join(
base,
"invalid-relative-up-across-root-within-node-modules",
);
process.chdir(cwd);
const inputFilePath = path.join(
cwd,
"node_modules",
"corp-app",
"root",
"sub",
"input.js",
);
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
└─ node_modules
└─ corp-app
β”œβ”€ input.js.map
β”œβ”€ package.json
└─ root
└─ sub
└─ input.js
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
root: path.join(cwd, "node_modules", "corp-app", "root"),
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsPresentButNotApplied(result, inputFilePath);
});
it("should not resolve input source map upward across the package.json boundary and within the root boundary", function () {
const cwd = path.join(
base,
"invalid-relative-up-across-node-modules-within-root",
);
process.chdir(cwd);
const inputFilePath = path.join(
cwd,
"root",
"sub",
"node_modules",
"corp-lib",
"input.js",
);
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
└─ root
└─ sub
β”œβ”€ input.js.map
└─ node_modules
└─ corp-lib
β”œβ”€ input.js
└─ package.json
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"../../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
root: path.join(cwd, "root"),
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsPresentButNotApplied(result, inputFilePath);
});
it("should not resolve input source map across the package.json boundary using url not starting with ../", function () {
const cwd = path.join(
base,
"invalid-relative-up-across-node-modules-url-not-start-with-double-dot",
);
process.chdir(cwd);
const inputFilePath = path.join(
cwd,
"node_modules",
"corp-lib",
"input.js",
);
expect(dirTreeDisplay(cwd)).toMatchInlineSnapshot(`
"β”œβ”€ babel.config.input.js.json
β”œβ”€ input.js.map
└─ node_modules
└─ corp-lib
β”œβ”€ input.js
β”œβ”€ package.json
└─ sub
└─ mod.js
"
`);
expect(getInputSourceMapURL(inputFilePath)).toMatchInlineSnapshot(
`"sub/../../../input.js.map"`,
);
const result = babel.transformFileSync(inputFilePath, {
babelrc: false,
configFile: path.join(cwd, "./babel.config.input.js.json"),
inputSourceMap: true,
sourceMap: true,
});
expectInputSourceMapIsPresentButNotApplied(result, inputFilePath);
});
});