import chalk from "chalk"; import stripAnsi from "strip-ansi"; import codeFrame, { codeFrameColumns } from "../lib/index.js"; describe("@babel/code-frame", function () { test("basic usage", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect(codeFrame(rawLines, 2, 16)).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^", " 3 | };", ].join("\n"), ); }); test("optional column number", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect(codeFrame(rawLines, 2, null)).toEqual( [" 1 | class Foo {", "> 2 | constructor()", " 3 | };"].join("\n"), ); }); test("maximum context lines and padding", function () { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2)).toEqual( [ " 5 | * @param b Number", " 6 | * @returns Number", "> 7 | */", " | ^", " 8 |", " 9 | function sum(a, b) {", " 10 | return a + b", ].join("\n"), ); }); test("no unnecessary padding due to one-off errors", function () { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 6, 2)).toEqual( [ " 4 | * @param a Number", " 5 | * @param b Number", "> 6 | * @returns Number", " | ^", " 7 | */", " 8 |", " 9 | function sum(a, b) {", ].join("\n"), ); }); test("tabs", function () { const rawLines = [ "\tclass Foo {", "\t \t\t constructor\t(\t)", "\t};", ].join("\n"); expect(codeFrame(rawLines, 2, 25)).toEqual( [ " 1 | \tclass Foo {", "> 2 | \t \t\t constructor\t(\t)", " | \t \t\t \t \t ^", " 3 | \t};", ].join("\n"), ); }); test("opts.highlightCode", function () { const rawLines = "console.log('babel')"; const result = codeFrame(rawLines, 1, 9, { highlightCode: true }); const stripped = stripAnsi(result); expect(result.length).toBeGreaterThan(stripped.length); expect(stripped).toEqual( ["> 1 | console.log('babel')", " | ^"].join("\n"), ); }); 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: 1, }, end: { line: 3, column: 1, }, }, { highlightCode: true, message: "Message about things", }, ); const stripped = stripAnsi(result); expect(stripped).toEqual( // prettier-ignore [ "> 1 | function a(b, c) {", " | ^^^^^^^^^^^^^^^^^^", "> 2 | return b + c;", " | ^^^^^^^^^^^^^^^", "> 3 | }", " | ^ Message about things", ].join('\n'), ); }); test("opts.linesAbove", function () { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesAbove: 1 })).toEqual( [ " 6 | * @returns Number", "> 7 | */", " | ^", " 8 |", " 9 | function sum(a, b) {", " 10 | return a + b", ].join("\n"), ); }); test("opts.linesBelow", function () { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesBelow: 1 })).toEqual( [ " 5 | * @param b Number", " 6 | * @returns Number", "> 7 | */", " | ^", " 8 |", ].join("\n"), ); }); test("opts.linesAbove and opts.linesBelow", function () { const rawLines = [ "/**", " * Sums two numbers.", " *", " * @param a Number", " * @param b Number", " * @returns Number", " */", "", "function sum(a, b) {", " return a + b", "}", ].join("\n"); expect(codeFrame(rawLines, 7, 2, { linesAbove: 1, linesBelow: 1 })).toEqual( [" 6 | * @returns Number", "> 7 | */", " | ^", " 8 |"].join("\n"), ); }); test("opts.linesAbove no lines above", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 } }, { linesAbove: 0 }), ).toEqual( [ "> 2 | constructor() {", " 3 | console.log(arguments);", " 4 | }", " 5 | };", ].join("\n"), ); }); test("opts.linesBelow no lines below", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 } }, { linesBelow: 0 }), ).toEqual([" 1 | class Foo {", "> 2 | constructor() {"].join("\n")); }); test("opts.linesBelow single line", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 } }, { linesAbove: 0, linesBelow: 0 }, ), ).toEqual(["> 2 | constructor() {"].join("\n")); }); test("opts.forceColor", function () { const marker = chalk.red.bold; const gutter = chalk.grey; const rawLines = ["", "", "", ""].join("\n"); expect( codeFrame(rawLines, 3, null, { linesAbove: 1, linesBelow: 1, forceColor: true, }), ).toEqual( chalk.reset( [ " " + gutter(" 2 |"), marker(">") + gutter(" 3 |"), " " + gutter(" 4 |"), ].join("\n"), ), ); }); test("jsx", function () { const gutter = chalk.grey; const yellow = chalk.yellow; const rawLines = ["
"].join("\n"); expect( JSON.stringify( codeFrame(rawLines, 0, null, { linesAbove: 1, linesBelow: 1, forceColor: true, }), ), ).toEqual( JSON.stringify( chalk.reset( " " + gutter(" 1 |") + " " + yellow("<") + yellow("div") + " " + yellow("/") + yellow(">"), ), ), ); }); test("basic usage, new API", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 16 } }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^", " 3 | };", ].join("\n"), ); }); test("mark multiple columns", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 3 }, end: { line: 2, column: 16 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^^^^^^^^^^^^^", " 3 | };", ].join("\n"), ); }); test("mark multiple columns across lines", function () { const rawLines = ["class Foo {", " constructor() {", " }", "};"].join( "\n", ); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 17 }, end: { line: 3, column: 3 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | }", " | ^^^", " 4 | };", ].join("\n"), ); }); test("mark multiple columns across multiple lines", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2, column: 17 }, end: { line: 4, column: 3 }, }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | console.log(arguments);", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^", "> 4 | }", " | ^^^", " 5 | };", ].join("\n"), ); }); test("mark across multiple lines without columns", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns(rawLines, { start: { line: 2 }, end: { line: 4 } }), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", "> 3 | console.log(arguments);", "> 4 | }", " 5 | };", ].join("\n"), ); }); test("opts.message", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2, column: 16 } }, { message: "Missing {", }, ), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor()", " | ^ Missing {", " 3 | };", ].join("\n"), ); }); test("opts.message without column", function () { const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 } }, { message: "Missing {", }, ), ).toEqual( [ " Missing {", " 1 | class Foo {", "> 2 | constructor()", " 3 | };", ].join("\n"), ); }); test("opts.message with multiple lines", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2, column: 17 }, end: { line: 4, column: 3 }, }, { message: "something about the constructor body", }, ), ).toEqual( [ " 1 | class Foo {", "> 2 | constructor() {", " | ^", "> 3 | console.log(arguments);", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^", "> 4 | }", " | ^^^ something about the constructor body", " 5 | };", ].join("\n"), ); }); test("opts.message with multiple lines without columns", function () { const rawLines = [ "class Foo {", " constructor() {", " console.log(arguments);", " }", "};", ].join("\n"); expect( codeFrameColumns( rawLines, { start: { line: 2 }, end: { line: 4 } }, { message: "something about the constructor body", }, ), ).toEqual( [ " something about the constructor body", " 1 | class Foo {", "> 2 | constructor() {", "> 3 | console.log(arguments);", "> 4 | }", " 5 | };", ].join("\n"), ); }); });