Skip to content

Commit

Permalink
Add client hydration to component metadata (#220)
Browse files Browse the repository at this point in the history
* Add client hydration to component metadata

* rename

* Adds the changeset

* Allow passing in the pathname too
  • Loading branch information
matthewp authored Dec 28, 2021
1 parent 5f99055 commit 43cbac3
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-gifts-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': minor
---

Adds metadata on hydration directives used by the component
9 changes: 8 additions & 1 deletion cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ func makeTransformOptions(options js.Value, hash string) transform.TransformOpti
filename = "<stdin>"
}

pathname := jsString(options.Get("pathname"))
if pathname == "" {
pathname = "<stdin>"
}

as := jsString(options.Get("as"))
if as == "" {
as = "document"
Expand Down Expand Up @@ -83,6 +88,7 @@ func makeTransformOptions(options js.Value, hash string) transform.TransformOpti
As: as,
Scope: hash,
Filename: filename,
Pathname: pathname,
InternalURL: internalURL,
SourceMap: sourcemap,
Site: site,
Expand Down Expand Up @@ -153,7 +159,8 @@ func Transform() interface{} {
fmt.Println(err)
}
doc = &astro.Node{
Type: astro.DocumentNode,
Type: astro.DocumentNode,
HydrationDirectives: make(map[string]bool),
}
for i := 0; i < len(nodes); i++ {
n := nodes[i]
Expand Down
1 change: 1 addition & 0 deletions internal/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type Node struct {
Styles, Scripts []*Node
HydratedComponents []*Node
ClientOnlyComponents []*Node
HydrationDirectives map[string]bool

Type NodeType
DataAtom atom.Atom
Expand Down
3 changes: 2 additions & 1 deletion internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2799,7 +2799,8 @@ func ParseWithOptions(r io.Reader, opts ...ParseOption) (*Node, error) {
p := &parser{
tokenizer: NewTokenizer(r),
doc: &Node{
Type: DocumentNode,
Type: DocumentNode,
HydrationDirectives: make(map[string]bool),
},
scripting: true,
framesetOK: true,
Expand Down
42 changes: 25 additions & 17 deletions internal/printer/print-to-js.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ func PrintToJSFragment(sourcetext string, n *Node, cssLen int, opts transform.Tr
}

type RenderOptions struct {
isRoot bool
isExpression bool
depth int
cssLen int
staticExtraction bool
isRoot bool
isExpression bool
depth int
cssLen int
opts transform.TransformOptions
}

type ExtractedStatement struct {
Expand All @@ -73,11 +73,11 @@ type ExtractedStatement struct {

func printToJs(p *printer, n *Node, cssLen int, opts transform.TransformOptions) PrintResult {
render1(p, n, RenderOptions{
cssLen: cssLen,
isRoot: true,
isExpression: false,
depth: 0,
staticExtraction: opts.StaticExtraction,
cssLen: cssLen,
isRoot: true,
isExpression: false,
depth: 0,
opts: opts,
})

return PrintResult{
Expand All @@ -92,7 +92,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
// Root of the document, print all children
if n.Type == DocumentNode {
p.printInternalImports(p.opts.InternalURL)
if opts.staticExtraction {
if opts.opts.StaticExtraction {
p.printCSSImports(opts.cssLen)
}

Expand All @@ -101,6 +101,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: false,
depth: depth + 1,
opts: opts.opts,
})
}

Expand All @@ -115,7 +116,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == TextNode {
p.printInternalImports(p.opts.InternalURL)
if opts.staticExtraction {
if opts.opts.StaticExtraction {
p.printCSSImports(opts.cssLen)
}

Expand Down Expand Up @@ -146,7 +147,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.print(strings.TrimSpace(c.Data))

// 3. The metadata object
p.printComponentMetadata(n.Parent, []byte(c.Data))
p.printComponentMetadata(n.Parent, opts.opts, []byte(c.Data))

// TODO: use the proper component name
p.printFuncPrelude("$$Component")
Expand All @@ -165,7 +166,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.println(strings.TrimSpace(importStatements))

// 1. Component imports, if any exist.
p.printComponentMetadata(n.Parent, []byte(importStatements))
p.printComponentMetadata(n.Parent, opts.opts, []byte(importStatements))
// 2. Top-level Astro global.
p.printTopLevelAstro()

Expand All @@ -185,7 +186,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {

// Print empty just to ensure a newline
p.println("")
if !opts.staticExtraction && len(n.Parent.Styles) > 0 {
if !opts.opts.StaticExtraction && len(n.Parent.Styles) > 0 {
p.println("const STYLES = [")
for _, style := range n.Parent.Styles {
p.printStyleOrScript(style)
Expand All @@ -211,13 +212,14 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: true,
depth: depth + 1,
opts: opts.opts,
})
p.addSourceMapping(loc.Loc{Start: n.Loc[1].Start - 3})
}
}
return
} else if !p.hasFuncPrelude {
p.printComponentMetadata(n.Parent, []byte{})
p.printComponentMetadata(n.Parent, opts.opts, []byte{})
p.printTopLevelAstro()

// Render func prelude. Will only run for the first non-frontmatter node
Expand All @@ -227,7 +229,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.println("")

// If we haven't printed the funcPrelude but we do have Styles/Scripts, we need to print them!
if !opts.staticExtraction && len(n.Parent.Styles) > 0 {
if !opts.opts.StaticExtraction && len(n.Parent.Styles) > 0 {
p.println("const STYLES = [")
for _, style := range n.Parent.Styles {
p.printStyleOrScript(style)
Expand Down Expand Up @@ -320,6 +322,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: true,
depth: depth + 1,
opts: opts.opts,
})
if c.NextSibling == nil || c.NextSibling.Type == TextNode {
p.printTemplateLiteralClose()
Expand Down Expand Up @@ -443,6 +446,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
render1(p, c, RenderOptions{
isRoot: false,
depth: depth + 1,
opts: opts.opts,
})
}
}
Expand Down Expand Up @@ -474,6 +478,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: opts.isExpression,
depth: depth + 1,
opts: opts.opts,
})
}
p.printTemplateLiteralClose()
Expand Down Expand Up @@ -515,6 +520,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: opts.isExpression,
depth: depth + 1,
opts: opts.opts,
})
}
p.printTemplateLiteralClose()
Expand All @@ -529,6 +535,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: opts.isExpression,
depth: depth + 1,
opts: opts.opts,
})
}
p.printTemplateLiteralClose()
Expand All @@ -538,6 +545,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
isRoot: false,
isExpression: opts.isExpression,
depth: depth + 1,
opts: opts.opts,
})
}
}
Expand Down
21 changes: 18 additions & 3 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func (p *printer) printTopLevelAstro() {
p.println(fmt.Sprintf("const $$Astro = %s(import.meta.url, '%s', '%s');\nconst Astro = $$Astro;", CREATE_ASTRO, p.opts.Site, p.opts.ProjectRoot))
}

func (p *printer) printComponentMetadata(doc *astro.Node, source []byte) {
func (p *printer) printComponentMetadata(doc *astro.Node, opts transform.TransformOptions, source []byte) {
var specs []string
var asrts []string

Expand Down Expand Up @@ -363,7 +363,13 @@ func (p *printer) printComponentMetadata(doc *astro.Node, source []byte) {
}

// Call createMetadata
p.print(fmt.Sprintf("\nexport const $$metadata = %s(import.meta.url, { ", CREATE_METADATA))
patharg := opts.Pathname
if patharg == "" {
patharg = "import.meta.url"
} else {
patharg = fmt.Sprintf("\"%s\"", patharg)
}
p.print(fmt.Sprintf("\nexport const $$metadata = %s(%s, { ", CREATE_METADATA, patharg))

// Add modules
p.print("modules: [")
Expand Down Expand Up @@ -392,7 +398,16 @@ func (p *printer) printComponentMetadata(doc *astro.Node, source []byte) {
p.print(node.Data)
}
}
p.print("], hoisted: [")
p.print("], hydrationDirectives: new Set([")
i := 0
for directive := range doc.HydrationDirectives {
if i > 0 {
p.print(", ")
}
p.print(fmt.Sprintf("'%s'", directive))
i++
}
p.print("]), hoisted: [")
for i, node := range doc.Scripts {
if i > 0 {
p.print(", ")
Expand Down
54 changes: 39 additions & 15 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ type want struct {
}

type metadata struct {
hoisted []string
hydratedComponents []string
modules []string
hoisted []string
hydratedComponents []string
modules []string
hydrationDirectives []string
}

type testcase struct {
Expand Down Expand Up @@ -224,13 +225,16 @@ import Component from '../components';
</html>`,
want: want{
frontmatter: []string{"import Component from '../components';"},
metadata: metadata{
hydrationDirectives: []string{"only"},
},
// Specifically do NOT render any metadata here, we need to skip this import
code: `<html>
<head>
<title>Hello world</title>
</head>
<body>
${` + RENDER_COMPONENT + `($$result,'Component',null,{"client:only":true,"client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"default"})}
${` + RENDER_COMPONENT + `($$result,'Component',null,{"client:only":true,"client:component-hydration":"only","client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"default"})}
</body></html>`,
},
},
Expand All @@ -249,13 +253,16 @@ import { Component } from '../components';
</html>`,
want: want{
frontmatter: []string{"import { Component } from '../components';"},
metadata: metadata{
hydrationDirectives: []string{"only"},
},
// Specifically do NOT render any metadata here, we need to skip this import
code: `<html>
<head>
<title>Hello world</title>
</head>
<body>
${` + RENDER_COMPONENT + `($$result,'Component',null,{"client:only":true,"client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"Component"})}
${` + RENDER_COMPONENT + `($$result,'Component',null,{"client:only":true,"client:component-hydration":"only","client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"Component"})}
</body></html>`,
},
},
Expand All @@ -274,13 +281,16 @@ import * as components from '../components';
</html>`,
want: want{
frontmatter: []string{"import * as components from '../components';"},
metadata: metadata{
hydrationDirectives: []string{"only"},
},
// Specifically do NOT render any metadata here, we need to skip this import
code: `<html>
<head>
<title>Hello world</title>
</head>
<body>
${` + RENDER_COMPONENT + `($$result,'components.A',null,{"client:only":true,"client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"A"})}
${` + RENDER_COMPONENT + `($$result,'components.A',null,{"client:only":true,"client:component-hydration":"only","client:component-path":($$metadata.resolvePath("../components")),"client:component-export":"A"})}
</body></html>`,
},
},
Expand Down Expand Up @@ -598,8 +608,9 @@ import Counter from '../components/Counter.jsx'`,
// https://docs.astro.build/core-concepts/astro-components/`},
styles: []string{fmt.Sprintf(`{props:{"data-astro-id":"HMNNHVCQ"},children:%s:root{font-family:system-ui;padding:2em 0;}.counter{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));place-items:center;font-size:2em;margin-top:2em;}.children{display:grid;place-items:center;margin-bottom:2em;}%s}`, BACKTICK, BACKTICK)},
metadata: metadata{
modules: []string{`{ module: $$module1, specifier: '../components/Counter.jsx', assert: {} }`},
hydratedComponents: []string{`Counter`},
modules: []string{`{ module: $$module1, specifier: '../components/Counter.jsx', assert: {} }`},
hydratedComponents: []string{`Counter`},
hydrationDirectives: []string{"visible"},
},
code: `<html lang="en" class="astro-HMNNHVCQ">
<head>
Expand All @@ -610,7 +621,7 @@ import Counter from '../components/Counter.jsx'`,
</head>
<body>
<main class="astro-HMNNHVCQ">
${$$renderComponent($$result,'Counter',Counter,{...(someProps),"client:visible":true,"client:component-path":($$metadata.getPath(Counter)),"client:component-export":($$metadata.getExport(Counter)),"class":"astro-HMNNHVCQ"},{"default": () => $$render` + "`" + `<h1 class="astro-HMNNHVCQ">Hello React!</h1>` + "`" + `,})}
${$$renderComponent($$result,'Counter',Counter,{...(someProps),"client:visible":true,"client:component-hydration":"visible","client:component-path":($$metadata.getPath(Counter)),"client:component-export":($$metadata.getExport(Counter)),"class":"astro-HMNNHVCQ"},{"default": () => $$render` + "`" + `<h1 class="astro-HMNNHVCQ">Hello React!</h1>` + "`" + `,})}
</main>
</body></html>`,
},
Expand Down Expand Up @@ -775,11 +786,12 @@ import 'custom-element';`,
`{ module: $$module2, specifier: 'two', assert: {} }`,
`{ module: $$module3, specifier: 'custom-element', assert: {} }`,
},
hydratedComponents: []string{"'my-element'", "Two", "One"},
hydratedComponents: []string{"'my-element'", "Two", "One"},
hydrationDirectives: []string{"load"},
},
code: `${$$renderComponent($$result,'One',One,{"client:load":true,"client:component-path":($$metadata.getPath(One)),"client:component-export":($$metadata.getExport(One))})}
${$$renderComponent($$result,'Two',Two,{"client:load":true,"client:component-path":($$metadata.getPath(Two)),"client:component-export":($$metadata.getExport(Two))})}
${$$renderComponent($$result,'my-element','my-element',{"client:load":true,"client:component-path":($$metadata.getPath('my-element')),"client:component-export":($$metadata.getExport('my-element'))})}`,
code: `${$$renderComponent($$result,'One',One,{"client:load":true,"client:component-hydration":"load","client:component-path":($$metadata.getPath(One)),"client:component-export":($$metadata.getExport(One))})}
${$$renderComponent($$result,'Two',Two,{"client:load":true,"client:component-hydration":"load","client:component-path":($$metadata.getPath(Two)),"client:component-export":($$metadata.getExport(Two))})}
${$$renderComponent($$result,'my-element','my-element',{"client:load":true,"client:component-hydration":"load","client:component-path":($$metadata.getPath('my-element')),"client:component-export":($$metadata.getExport('my-element'))})}`,
},
},
{
Expand Down Expand Up @@ -1156,7 +1168,7 @@ const { product } = Astro.props;
${$$renderComponent($$result,'Header',Header,{})}
<div class="product-page">
<article>
${$$renderComponent($$result,'ProductPageContent',ProductPageContent,{"client:visible":true,"product":(product.node),"client:component-path":($$metadata.getPath(ProductPageContent)),"client:component-export":($$metadata.getExport(ProductPageContent))})}
${$$renderComponent($$result,'ProductPageContent',ProductPageContent,{"client:visible":true,"product":(product.node),"client:component-hydration":"visible","client:component-path":($$metadata.getPath(ProductPageContent)),"client:component-export":($$metadata.getExport(ProductPageContent))})}
</article>
</div>
${$$renderComponent($$result,'Footer',Footer,{})}
Expand Down Expand Up @@ -1187,7 +1199,8 @@ import ProductPageContent from '../../components/ProductPageContent.jsx';`,
`{ module: $$module2, specifier: '../../components/Footer.astro', assert: {} }`,
`{ module: $$module3, specifier: '../../components/ProductPageContent.jsx', assert: {} }`,
},
hydratedComponents: []string{`ProductPageContent`},
hydratedComponents: []string{`ProductPageContent`},
hydrationDirectives: []string{"visible"},
},
},
},
Expand Down Expand Up @@ -1408,6 +1421,17 @@ const items = ["Dog", "Cat", "Platipus"];
}
}
metadata += "]"
// directives
metadata += ", hydrationDirectives: new Set(["
if len(tt.want.hydrationDirectives) > 0 {
for i, c := range tt.want.hydrationDirectives {
if i > 0 {
metadata += ", "
}
metadata += fmt.Sprintf("'%s'", c)
}
}
metadata += "])"
// metadata.hoisted
metadata += ", hoisted: ["
if len(tt.want.metadata.hoisted) > 0 {
Expand Down
Loading

0 comments on commit 43cbac3

Please sign in to comment.