diff --git a/.changeset/fair-windows-wave.md b/.changeset/fair-windows-wave.md new file mode 100644 index 000000000..f752d3be8 --- /dev/null +++ b/.changeset/fair-windows-wave.md @@ -0,0 +1,5 @@ +--- +'@astrojs/compiler': minor +--- + +Return generated frontmatter and body ranges in TSX output diff --git a/cmd/astro-wasm/astro-wasm.go b/cmd/astro-wasm/astro-wasm.go index dda3bdd00..ce652b850 100644 --- a/cmd/astro-wasm/astro-wasm.go +++ b/cmd/astro-wasm/astro-wasm.go @@ -173,6 +173,7 @@ type TSXResult struct { Code string `js:"code"` Map string `js:"map"` Diagnostics []loc.DiagnosticMessage `js:"diagnostics"` + Ranges printer.TSXRanges `js:"metaRanges"` } type TransformResult struct { @@ -269,6 +270,7 @@ func ConvertToTSX() any { Code: code, Map: sourcemapString, Diagnostics: h.Diagnostics(), + Ranges: result.TSXRanges, }).Value }) } diff --git a/internal/loc/loc.go b/internal/loc/loc.go index b2a6603a9..7168c32f9 100644 --- a/internal/loc/loc.go +++ b/internal/loc/loc.go @@ -20,6 +20,11 @@ type Span struct { Start, End int } +type TSXRange struct { + Start int `js:"start"` + End int `js:"end"` +} + // A NodeType is the type of a Node. type DiagnosticSeverity int diff --git a/internal/printer/print-to-tsx.go b/internal/printer/print-to-tsx.go index 93236c075..a3b5d9674 100644 --- a/internal/printer/print-to-tsx.go +++ b/internal/printer/print-to-tsx.go @@ -31,9 +31,15 @@ func PrintToTSX(sourcetext string, n *Node, opts transform.TransformOptions, h * return PrintResult{ Output: p.output, SourceMapChunk: p.builder.GenerateChunk(p.output), + TSXRanges: p.ranges, } } +type TSXRanges struct { + Frontmatter loc.TSXRange `js:"frontmatter"` + Body loc.TSXRange `js:"body"` +} + func isScript(p *astro.Node) bool { return p.DataAtom == atom.Script } @@ -106,6 +112,7 @@ func renderTsx(p *printer, n *Node) { props := js_scanner.GetPropsType(source) hasGetStaticPaths := js_scanner.HasGetStaticPaths(source) hasChildren := false + startLoc := len(p.output) for c := n.FirstChild; c != nil; c = c.NextSibling { // This checks for the first node that comes *after* the frontmatter // to ensure that the statement is properly closed with a `;`. @@ -123,11 +130,18 @@ func renderTsx(p *printer, n *Node) { // We always need to start the body with `` p.addNilSourceMapping() p.print("\n") + + // Update the start location of the body to the start of the first child + startLoc = len(p.output) + hasChildren = true } if c.PrevSibling == nil && c.Type != FrontmatterNode { p.addNilSourceMapping() p.print("\n") + + startLoc = len(p.output) + hasChildren = true } renderTsx(p, c) @@ -136,6 +150,11 @@ func renderTsx(p *printer, n *Node) { p.print("\n") p.addNilSourceMapping() + p.setTSXBodyRange(loc.TSXRange{ + Start: startLoc, + End: len(p.output), + }) + // Only close the body with `` if we printed a body if hasChildren { p.print("\n") @@ -176,6 +195,7 @@ declare const Astro: Readonly 0 { @@ -192,6 +212,10 @@ declare const Astro: Readonly): any {}\n assert.snapshot(code, output, `expected code to match snapshot`); }); +test('return ranges', async () => { + const input = `---\nconsole.log("Hello!")\n---\n\n
`; + const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); + + assert.equal(metaRanges, { + frontmatter: { + start: 31, + end: 55, + }, + body: { + start: 69, + end: 81, + }, + }); +}); + +test('return ranges - no frontmatter', async () => { + const input = `
`; + const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); + + assert.equal(metaRanges, { + frontmatter: { + start: 31, + end: 31, + }, + body: { + start: 42, + end: 54, + }, + }); +}); + test.run(); diff --git a/packages/compiler/test/tsx/escape.ts b/packages/compiler/test/tsx/escape.ts index e0702a739..d8d47f662 100644 --- a/packages/compiler/test/tsx/escape.ts +++ b/packages/compiler/test/tsx/escape.ts @@ -1,10 +1,11 @@ import { convertToTSX } from '@astrojs/compiler'; import { test } from 'uvu'; import * as assert from 'uvu/assert'; +import { TSXPrefix } from '../utils'; test('escapes braces in comment', async () => { const input = ``; - const output = ` + const output = `${TSXPrefix} {/** \\\\{
Not JSX!
\\\\}*/} export default function __AstroComponent_(_props: Record): any {}\n`; @@ -14,7 +15,7 @@ export default function __AstroComponent_(_props: Record): any {}\n test('always inserts space before comment', async () => { const input = ``; - const output = ` + const output = `${TSXPrefix} {/** /
Error?
*/} export default function __AstroComponent_(_props: Record): any {}\n`; @@ -24,7 +25,7 @@ export default function __AstroComponent_(_props: Record): any {}\n test('simple escapes star slashes (*/)', async () => { const input = ``; - const output = ` + const output = `${TSXPrefix} {/** *\\/
Evil comment
*/} export default function __AstroComponent_(_props: Record): any {}\n`; @@ -34,7 +35,7 @@ export default function __AstroComponent_(_props: Record): any {}\n test('multiple escapes star slashes (*/)', async () => { const input = ``; - const output = ` + const output = `${TSXPrefix} {/** ***\\/*\\/**\\/*\\/*\\/*\\/
Even more evil comment
*/} export default function __AstroComponent_(_props: Record): any {}\n`;