diff --git a/packages/babel-code-frame/README.md b/packages/babel-code-frame/README.md index 7ef4d3f9df..818b78a0ed 100644 --- a/packages/babel-code-frame/README.md +++ b/packages/babel-code-frame/README.md @@ -68,6 +68,7 @@ console.log(result); Toggles syntax highlighting the code as JavaScript for terminals. + ### `linesAbove` `number`, defaults to `2`. @@ -86,6 +87,21 @@ Adjust the number of lines to show below the error. Enable this to forcibly syntax highlight the code as JavaScript (for non-terminals); overrides `highlightCode`. +### `message` + +`string`, otherwise nothing + +Pass in a string to be displayed inline (if possible) next to the highlighted +location in the code. If it can't be positioned inline, it will be placed above +the code frame. + +``` +1 | class Foo { +> 2 | constructor() + | ^ Missing { +3 | }; +``` + ## Upgrading from prior versions Prior to version 7, the only API exposed by this module was for a single line and optional column pointer. The old API will now log a deprecation warning. diff --git a/packages/babel-code-frame/src/index.js b/packages/babel-code-frame/src/index.js index d43420b005..c8da91a5ae 100644 --- a/packages/babel-code-frame/src/index.js +++ b/packages/babel-code-frame/src/index.js @@ -32,6 +32,7 @@ function getDefs(chalk) { invalid: chalk.white.bgRed.bold, gutter: chalk.grey, marker: chalk.red.bold, + message: chalk.red.bold, }; } @@ -200,16 +201,18 @@ export function codeFrameColumns( const lines = rawLines.split(NEWLINE); const { start, end, markerLines } = getMarkerLines(loc, lines, opts); + const hasColumns = loc.start && typeof loc.start.column === "number"; const numberMaxWidth = String(end).length; - const frame = lines + let frame = lines .slice(start, end) .map((line, index) => { const number = start + 1 + index; const paddedNumber = ` ${number}`.slice(-numberMaxWidth); const gutter = ` ${paddedNumber} | `; const hasMarker = markerLines[number]; + const lastMarkerLine = !markerLines[number + 1]; if (hasMarker) { let markerLine = ""; if (Array.isArray(hasMarker)) { @@ -224,6 +227,10 @@ export function codeFrameColumns( markerSpacing, maybeHighlight(defs.marker, "^").repeat(numberOfMarkers), ].join(""); + + if (lastMarkerLine && opts.message) { + markerLine += " " + maybeHighlight(defs.message, opts.message); + } } return [ maybeHighlight(defs.marker, ">"), @@ -237,6 +244,10 @@ export function codeFrameColumns( }) .join("\n"); + if (opts.message && !hasColumns) { + frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`; + } + if (highlighted) { return chalk.reset(frame); } else { diff --git a/packages/babel-code-frame/test/index.js b/packages/babel-code-frame/test/index.js index 89a6001057..7f097ce773 100644 --- a/packages/babel-code-frame/test/index.js +++ b/packages/babel-code-frame/test/index.js @@ -299,4 +299,101 @@ describe("@babel/code-frame", function() { ].join("\n"), ); }); + + it("opts.message", function() { + const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); + assert.equal( + codeFrameColumns( + rawLines, + { start: { line: 2, column: 16 } }, + { + message: "Missing {", + }, + ), + [ + " 1 | class Foo {", + "> 2 | constructor()", + " | ^ Missing {", + " 3 | };", + ].join("\n"), + ); + }); + + it("opts.message without column", function() { + const rawLines = ["class Foo {", " constructor()", "};"].join("\n"); + assert.equal( + codeFrameColumns( + rawLines, + { start: { line: 2 } }, + { + message: "Missing {", + }, + ), + [ + " Missing {", + " 1 | class Foo {", + "> 2 | constructor()", + " 3 | };", + ].join("\n"), + ); + }); + + it("opts.message with multiple lines", function() { + const rawLines = [ + "class Foo {", + " constructor() {", + " console.log(arguments);", + " }", + "};", + ].join("\n"); + assert.equal( + codeFrameColumns( + rawLines, + { + start: { line: 2, column: 17 }, + end: { line: 4, column: 3 }, + }, + { + message: "something about the constructor body", + }, + ), + [ + " 1 | class Foo {", + "> 2 | constructor() {", + " | ^", + "> 3 | console.log(arguments);", + " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^", + "> 4 | }", + " | ^^^ something about the constructor body", + " 5 | };", + ].join("\n"), + ); + }); + + it("opts.message with multiple lines without columns", function() { + const rawLines = [ + "class Foo {", + " constructor() {", + " console.log(arguments);", + " }", + "};", + ].join("\n"); + assert.equal( + codeFrameColumns( + rawLines, + { start: { line: 2 }, end: { line: 4 } }, + { + message: "something about the constructor body", + }, + ), + [ + " something about the constructor body", + " 1 | class Foo {", + "> 2 | constructor() {", + "> 3 | console.log(arguments);", + "> 4 | }", + " 5 | };", + ].join("\n"), + ); + }); });