Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tsx): Return interesting ranges for TSX output #931

Merged
merged 7 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading