Skip to content

Commit

Permalink
feat(tsx): Return interesting ranges for TSX output (#931)
Browse files Browse the repository at this point in the history
* feat(tsx): Return interesting ranges for TSX output

* test: add test

* chore: changeset

* fix: wait a minute, this is not JavaScript!

* test: add test for no frontmatter

* test: update new tests
  • Loading branch information
Princesseuh authored Jan 2, 2024
1 parent fe2f0c8 commit 9ff6342
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/fair-windows-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': minor
---

Return generated frontmatter and body ranges in TSX output
2 changes: 2 additions & 0 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -269,6 +270,7 @@ func ConvertToTSX() any {
Code: code,
Map: sourcemapString,
Diagnostics: h.Diagnostics(),
Ranges: result.TSXRanges,
}).Value
})
}
Expand Down
5 changes: 5 additions & 0 deletions internal/loc/loc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
24 changes: 24 additions & 0 deletions internal/printer/print-to-tsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 `;`.
Expand All @@ -123,11 +130,18 @@ func renderTsx(p *printer, n *Node) {
// We always need to start the body with `<Fragment>`
p.addNilSourceMapping()
p.print("<Fragment>\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("<Fragment>\n")

startLoc = len(p.output)

hasChildren = true
}
renderTsx(p, c)
Expand All @@ -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 `</Fragment>` if we printed a body
if hasChildren {
p.print("</Fragment>\n")
Expand Down Expand Up @@ -176,6 +195,7 @@ declare const Astro: Readonly<import('astro').AstroGlobal<%s, typeof %s`, propsI

if n.Type == FrontmatterNode {
p.addSourceMapping(loc.Loc{Start: 0})
frontmatterStart := len(p.output)
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == TextNode {
if len(c.Loc) > 0 {
Expand All @@ -192,6 +212,10 @@ declare const Astro: Readonly<import('astro').AstroGlobal<%s, typeof %s`, propsI
p.addSourceMapping(loc.Loc{Start: n.FirstChild.Loc[0].Start + len(n.FirstChild.Data) + 3})
p.println("")
}
p.setTSXFrontmatterRange(loc.TSXRange{
Start: frontmatterStart,
End: len(p.output),
})
return
}

Expand Down
13 changes: 13 additions & 0 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
type PrintResult struct {
Output []byte
SourceMapChunk sourcemap.Chunk
// Optional, used only for TSX output
TSXRanges TSXRanges
}

type printer struct {
Expand All @@ -31,6 +33,9 @@ type printer struct {
hasInternalImports bool
hasCSSImports bool
needsTransitionCSS bool

// Optional, used only for TSX output
ranges TSXRanges
}

var TEMPLATE_TAG = "$$render"
Expand Down Expand Up @@ -68,6 +73,14 @@ func (p *printer) println(text string) {
p.print(text + "\n")
}

func (p *printer) setTSXFrontmatterRange(frontmatterRange loc.TSXRange) {
p.ranges.Frontmatter = frontmatterRange
}

func (p *printer) setTSXBodyRange(componentRange loc.TSXRange) {
p.ranges.Body = componentRange
}

func (p *printer) printTextWithSourcemap(text string, l loc.Loc) {
start := l.Start
for pos, c := range text {
Expand Down
10 changes: 10 additions & 0 deletions packages/compiler/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ export interface TSXResult {
code: string;
map: SourceMap;
diagnostics: DiagnosticMessage[];
metaRanges: {
frontmatter: {
start: number;
end: number;
};
body: {
start: number;
end: number;
};
};
}

export interface ParseResult {
Expand Down
32 changes: 32 additions & 0 deletions packages/compiler/test/tsx/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,36 @@ export default function __AstroComponent_(_props: Record<string, any>): any {}\n
assert.snapshot(code, output, `expected code to match snapshot`);
});

test('return ranges', async () => {
const input = `---\nconsole.log("Hello!")\n---\n\n<div></div>`;
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 = `<div></div>`;
const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' });

assert.equal(metaRanges, {
frontmatter: {
start: 31,
end: 31,
},
body: {
start: 42,
end: 54,
},
});
});

test.run();
9 changes: 5 additions & 4 deletions packages/compiler/test/tsx/escape.ts
Original file line number Diff line number Diff line change
@@ -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 = `<!-- {<div>Not JSX!<div/>}-->`;
const output = `<Fragment>
const output = `${TSXPrefix}<Fragment>
{/** \\\\{<div>Not JSX!<div/>\\\\}*/}
</Fragment>
export default function __AstroComponent_(_props: Record<string, any>): any {}\n`;
Expand All @@ -14,7 +15,7 @@ export default function __AstroComponent_(_props: Record<string, any>): any {}\n

test('always inserts space before comment', async () => {
const input = `<!--/<div>Error?<div/>-->`;
const output = `<Fragment>
const output = `${TSXPrefix}<Fragment>
{/** /<div>Error?<div/>*/}
</Fragment>
export default function __AstroComponent_(_props: Record<string, any>): any {}\n`;
Expand All @@ -24,7 +25,7 @@ export default function __AstroComponent_(_props: Record<string, any>): any {}\n

test('simple escapes star slashes (*/)', async () => {
const input = `<!--*/<div>Evil comment<div/>-->`;
const output = `<Fragment>
const output = `${TSXPrefix}<Fragment>
{/** *\\/<div>Evil comment<div/>*/}
</Fragment>
export default function __AstroComponent_(_props: Record<string, any>): any {}\n`;
Expand All @@ -34,7 +35,7 @@ export default function __AstroComponent_(_props: Record<string, any>): any {}\n

test('multiple escapes star slashes (*/)', async () => {
const input = `<!--***/*/**/*/*/*/<div>Even more evil comment<div/>-->`;
const output = `<Fragment>
const output = `${TSXPrefix}<Fragment>
{/** ***\\/*\\/**\\/*\\/*\\/*\\/<div>Even more evil comment<div/>*/}
</Fragment>
export default function __AstroComponent_(_props: Record<string, any>): any {}\n`;
Expand Down

0 comments on commit 9ff6342

Please sign in to comment.