mirror of
https://github.com/hanzoai/babel.git
synced 2026-09-24 16:56:45 +00:00
fix(code-frame): use 0-based columns to match Babel AST locations (#17316) `codeFrameColumns` treated the `column` property as 1-based, but Babel AST locations use 0-based columns. This caused the caret/underline to be off by one character when passing AST `.loc` objects directly. - Removed the `- 1` offset in marker spacing calculation - Removed the `+ 1` in multiline start marker count - Changed default column sentinel from `0` to `null` so that `column: 0` is treated as a valid position - Updated falsiness checks (`!startColumn`) to null checks - Removed `+ 1` workarounds in babel-core and babel-traverse callers This is a breaking change for external consumers of `@babel/code-frame` who passed 1-based columns manually.
215 lines
5.4 KiB
JavaScript
215 lines
5.4 KiB
JavaScript
import { stripVTControlCharacters, styleText } from "node:util";
|
|
|
|
import { codeFrameColumns } from "../lib/index.js";
|
|
|
|
const gutter = input => styleText("gray", input, { validateStream: false });
|
|
const yellow = input => styleText("yellow", input, { validateStream: false });
|
|
const marker = input =>
|
|
styleText(["red", "bold"], input, { validateStream: false });
|
|
const reset = input => styleText("reset", input, { validateStream: false });
|
|
const cyan = input => styleText("cyan", input, { validateStream: false });
|
|
|
|
function stubColorSupport(supported) {
|
|
const original = process.env.FORCE_COLOR;
|
|
|
|
beforeEach(function () {
|
|
process.env.FORCE_COLOR = supported ? "" : "false";
|
|
});
|
|
|
|
afterEach(function () {
|
|
process.env.FORCE_COLOR = original;
|
|
});
|
|
}
|
|
|
|
describe("highlight", function () {
|
|
describe("when colors are supported", () => {
|
|
stubColorSupport(true);
|
|
|
|
test("opts.highlightCode", function () {
|
|
const rawLines = "console.log('babel')";
|
|
const result = codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 1, column: 8 } },
|
|
{ highlightCode: true },
|
|
);
|
|
const stripped = stripVTControlCharacters(result);
|
|
expect(result.length).toBeGreaterThan(stripped.length);
|
|
expect(stripped).toEqual(
|
|
["> 1 | console.log('babel')", " | ^"].join("\n"),
|
|
);
|
|
|
|
const codeResult = result.match(/console.*?babel/)[0];
|
|
const codeStripped = stripVTControlCharacters(codeResult);
|
|
expect(codeResult.length).toBeGreaterThan(codeStripped.length);
|
|
});
|
|
|
|
test("opts.highlightCode with multiple columns and lines", function () {
|
|
// prettier-ignore
|
|
const rawLines = [
|
|
"function a(b, c) {",
|
|
" return b + c;",
|
|
"}"
|
|
].join("\n");
|
|
|
|
const result = codeFrameColumns(
|
|
rawLines,
|
|
{
|
|
start: {
|
|
line: 1,
|
|
column: 0,
|
|
},
|
|
end: {
|
|
line: 3,
|
|
column: 1,
|
|
},
|
|
},
|
|
{
|
|
highlightCode: true,
|
|
message: "Message about things",
|
|
},
|
|
);
|
|
const stripped = stripVTControlCharacters(result);
|
|
expect(result.length).toBeGreaterThan(stripped.length);
|
|
expect(stripped).toEqual(
|
|
// prettier-ignore
|
|
[
|
|
"> 1 | function a(b, c) {",
|
|
" | ^^^^^^^^^^^^^^^^^^",
|
|
"> 2 | return b + c;",
|
|
" | ^^^^^^^^^^^^^^^",
|
|
"> 3 | }",
|
|
" | ^ Message about things",
|
|
].join('\n'),
|
|
);
|
|
});
|
|
|
|
test("opts.forceColor", function () {
|
|
const rawLines = ["", "", "", ""].join("\n");
|
|
expect(
|
|
codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 3, column: null } },
|
|
{
|
|
linesAbove: 1,
|
|
linesBelow: 1,
|
|
forceColor: true,
|
|
},
|
|
),
|
|
).toEqual(
|
|
reset(
|
|
[
|
|
" " + gutter(" 2 |"),
|
|
marker(">") + gutter(" 3 |"),
|
|
" " + gutter(" 4 |"),
|
|
].join("\n"),
|
|
),
|
|
);
|
|
});
|
|
|
|
test("jsx", function () {
|
|
const rawLines = ["<div />"].join("\n");
|
|
|
|
expect(
|
|
JSON.stringify(
|
|
codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 0, column: null } },
|
|
{
|
|
linesAbove: 1,
|
|
linesBelow: 1,
|
|
forceColor: true,
|
|
},
|
|
),
|
|
),
|
|
).toEqual(
|
|
JSON.stringify(
|
|
reset(
|
|
" " +
|
|
gutter(" 1 |") +
|
|
" " +
|
|
yellow("<") +
|
|
yellow("div") +
|
|
" " +
|
|
yellow("/") +
|
|
yellow(">"),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
it("unicode capitalized", function () {
|
|
const rawLines = ["var 𐐔𐐯𐑅𐐨𐑉𐐯𐐻, deseret;"].join("\n");
|
|
|
|
expect(
|
|
JSON.stringify(
|
|
codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 0, column: null } },
|
|
{
|
|
linesAbove: 1,
|
|
linesBelow: 1,
|
|
forceColor: true,
|
|
},
|
|
),
|
|
),
|
|
).toEqual(
|
|
JSON.stringify(
|
|
reset(
|
|
" " +
|
|
gutter(" 1 |") +
|
|
" " +
|
|
cyan("var") +
|
|
" " +
|
|
yellow("𐐔𐐯𐑅𐐨𐑉𐐯𐐻") +
|
|
yellow(",") +
|
|
" " +
|
|
"deseret" +
|
|
yellow(";"),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("when colors are not supported", () => {
|
|
stubColorSupport(false);
|
|
|
|
test("opts.highlightCode", function () {
|
|
const rawLines = "console.log('babel')";
|
|
const result = codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 1, column: 8 } },
|
|
{ highlightCode: true },
|
|
);
|
|
const stripped = stripVTControlCharacters(result);
|
|
expect(result).toBe(stripped);
|
|
expect(stripped).toEqual(
|
|
["> 1 | console.log('babel')", " | ^"].join("\n"),
|
|
);
|
|
});
|
|
|
|
test("opts.forceColor", function () {
|
|
const rawLines = ["", "", "", ""].join("\n");
|
|
expect(
|
|
codeFrameColumns(
|
|
rawLines,
|
|
{ start: { line: 3, column: null } },
|
|
{
|
|
linesAbove: 1,
|
|
linesBelow: 1,
|
|
forceColor: true,
|
|
},
|
|
),
|
|
).toEqual(
|
|
reset(
|
|
[
|
|
" " + gutter(" 2 |"),
|
|
marker(">") + gutter(" 3 |"),
|
|
" " + gutter(" 4 |"),
|
|
].join("\n"),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
});
|