Skip to content
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/yellow-numbers-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': minor
---

Adds support for `Astro.self` (as accepted in the [Recursive Components RFC](https://github.com/withastro/rfcs/blob/main/active-rfcs/0000-recursive-components.md)).
49 changes: 49 additions & 0 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/norunners/vert"
astro "github.com/withastro/compiler/internal"
"github.com/withastro/compiler/internal/printer"
t "github.com/withastro/compiler/internal/t"
"github.com/withastro/compiler/internal/transform"
wasm_utils "github.com/withastro/compiler/internal_wasm/utils"
"golang.org/x/net/html/atom"
Expand All @@ -23,6 +24,7 @@ var done chan bool

func main() {
js.Global().Set("__astro_transform", Transform())
js.Global().Set("__astro_parse", Parse())
// This ensures that the WASM doesn't exit early
<-make(chan bool)
}
Expand All @@ -41,6 +43,19 @@ func jsBool(j js.Value) bool {
return j.Bool()
}

func makeParseOptions(options js.Value) t.ParseOptions {
position := true

pos := options.Get("position")
if !pos.IsNull() && !pos.IsUndefined() {
position = pos.Bool()
}

return t.ParseOptions{
Position: position,
}
}

func makeTransformOptions(options js.Value, hash string) transform.TransformOptions {
filename := jsString(options.Get("sourcefile"))
if filename == "" {
Expand Down Expand Up @@ -107,6 +122,10 @@ type HoistedScript struct {
Type string `js:"type"`
}

type ParseResult struct {
AST string `js:"ast"`
}

type TransformResult struct {
Code string `js:"code"`
Map string `js:"map"`
Expand All @@ -133,6 +152,36 @@ func preprocessStyle(i int, style *astro.Node, transformOptions transform.Transf
style.FirstChild.Data = str
}

func Parse() interface{} {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
source := jsString(args[0])
parseOptions := makeParseOptions(js.Value(args[1]))

var doc *astro.Node
nodes, err := astro.ParseFragment(strings.NewReader(source), &astro.Node{
Type: astro.ElementNode,
Data: atom.Template.String(),
DataAtom: atom.Template,
})
if err != nil {
fmt.Println(err)
}
doc = &astro.Node{
Type: astro.DocumentNode,
}
for i := 0; i < len(nodes); i++ {
n := nodes[i]
doc.AppendChild(n)
}

result := printer.PrintToJSON(source, doc, parseOptions)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very similar to the Transform function other than the way we print. Instead of generating a JS module from our AST, we have a new printer that generates stringified JSON.


return vert.ValueOf(ParseResult{
AST: string(result.Output),
})
})
}

func Transform() interface{} {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
source := jsString(args[0])
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ require (
golang.org/x/net v0.0.0-20210716203947-853a461950ff
)

require github.com/iancoleman/strcase v0.2.0 // indirect

replace github.com/norunners/vert => github.com/natemoo-re/vert v0.0.0-natemoo-re.7
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/natemoo-re/vert v0.0.0-natemoo-re.7 h1:nhfKslS16o2Uruqt8Bwv8ZFYUuf+PW9iC2M5HI/Bs6U=
Expand Down
25 changes: 25 additions & 0 deletions internal/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package astro

import a "golang.org/x/net/html/atom"

// Section 12.2.4.2 of the HTML5 specification says "The following elements
// have varying levels of special parsing rules".
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
Expand Down Expand Up @@ -109,3 +111,26 @@ func isSpecialElement(element *Node) bool {
}
return false
}

var knownDirectiveMap = map[string]bool{
"client:load": true,
"client:idle": true,
"client:visible": true,
"client:only": true,
"class:list": true,
"set:text": true,
"set:html": true,
}

func IsKnownDirective(element *Node, attr *Attribute) bool {
if knownDirectiveMap[attr.Key] {
return true
}
if element.DataAtom == a.Script {
return attr.Key == "hoist"
}
if element.DataAtom == a.Style {
return attr.Key == "global"
}
return false
}
23 changes: 23 additions & 0 deletions internal/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@ const (
ExpressionNode
)

func (t NodeType) String() string {
switch t {
case ErrorNode:
return "error"
case TextNode:
return "text"
case DocumentNode:
return "root"
case ElementNode:
return "element"
case CommentNode:
return "comment"
case DoctypeNode:
return "doctype"
case FrontmatterNode:
return "frontmatter"
case ExpressionNode:
return "expression"
default:
return ""
}
}

// Used as an Attribute Key to mark implicit nodes
const ImplicitNodeMarker = "\x00implicit"

Expand Down
1 change: 1 addition & 0 deletions internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ func (p *parser) addFrontmatter(empty bool) {
}
if empty {
p.frontmatterState = FrontmatterClosed
p.fm.Attr = append(p.fm.Attr, Attribute{Key: ImplicitNodeMarker, Type: EmptyAttribute})
} else {
p.frontmatterState = FrontmatterOpen
p.oe = append(p.oe, p.fm)
Expand Down
12 changes: 4 additions & 8 deletions internal/printer/print-to-js.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}

p.printReturnClose()
// TODO: use proper component name
p.printFuncSuffix("$$Component")
p.printFuncSuffix(opts.opts)
return
}

Expand Down Expand Up @@ -149,8 +148,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
// 3. The metadata object
p.printComponentMetadata(n.Parent, opts.opts, []byte(c.Data))

// TODO: use the proper component name
p.printFuncPrelude("$$Component")
p.printFuncPrelude(opts.opts)
} else {
importStatements := c.Data[0:renderBodyStart]
content := c.Data[renderBodyStart:]
Expand All @@ -176,8 +174,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}
}

// TODO: use the proper component name
p.printFuncPrelude("$$Component")
p.printFuncPrelude(opts.opts)
if len(c.Loc) > 0 {
p.addSourceMapping(loc.Loc{Start: c.Loc[0].Start + renderBodyStart})
}
Expand Down Expand Up @@ -225,8 +222,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.printTopLevelAstro(opts.opts)

// Render func prelude. Will only run for the first non-frontmatter node
// TODO: use the proper component name
p.printFuncPrelude("$$Component")
p.printFuncPrelude(opts.opts)
// This just ensures a newline
p.println("")

Expand Down
Loading