Skip to content

Commit

Permalink
Add server island metadata (#1008)
Browse files Browse the repository at this point in the history
* Add server island metadata

* Append servercomponent metadata

* Add localName to serverComponents output

* Add tests

* linting

* Updated changeset

* fix: ormatting

---------

Co-authored-by: Princesseuh <[email protected]>
  • Loading branch information
matthewp and Princesseuh authored Jul 16, 2024
1 parent 3e25858 commit 9fb8d5d
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changeset/yellow-cooks-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astrojs/compiler': minor
---

Adds `serverComponents` metadata

This adds a change necessary to support server islands. During transformation the compiler discovers `server:defer` directives and appends them to the `serverComponents` array. This is exported along with the other metadata so that it can be used inside of Astro.
13 changes: 13 additions & 0 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ type HoistedScript struct {

type HydratedComponent struct {
ExportName string `js:"exportName"`
LocalName string `js:"localName"`
Specifier string `js:"specifier"`
ResolvedPath string `js:"resolvedPath"`
}
Expand All @@ -208,6 +209,7 @@ type TransformResult struct {
Scripts []HoistedScript `js:"scripts"`
HydratedComponents []HydratedComponent `js:"hydratedComponents"`
ClientOnlyComponents []HydratedComponent `js:"clientOnlyComponents"`
ServerComponents []HydratedComponent `js:"serverComponents"`
ContainsHead bool `js:"containsHead"`
StyleError []string `js:"styleError"`
Propagation bool `js:"propagation"`
Expand Down Expand Up @@ -358,6 +360,7 @@ func Transform() any {
scripts := []HoistedScript{}
hydratedComponents := []HydratedComponent{}
clientOnlyComponents := []HydratedComponent{}
serverComponents := []HydratedComponent{}
css_result := printer.PrintCSS(source, doc, transformOptions)
for _, bytes := range css_result.Output {
css = append(css, string(bytes))
Expand Down Expand Up @@ -438,6 +441,15 @@ func Transform() any {
})
}

for _, c := range doc.ServerComponents {
serverComponents = append(serverComponents, HydratedComponent{
ExportName: c.ExportName,
LocalName: c.LocalName,
Specifier: c.Specifier,
ResolvedPath: c.ResolvedPath,
})
}

var value vert.Value
result := printer.PrintToJS(source, doc, len(css), transformOptions, h)
transformResult := &TransformResult{
Expand All @@ -446,6 +458,7 @@ func Transform() any {
Scripts: scripts,
HydratedComponents: hydratedComponents,
ClientOnlyComponents: clientOnlyComponents,
ServerComponents: serverComponents,
ContainsHead: doc.ContainsHead,
StyleError: styleError,
Propagation: doc.HeadPropagation,
Expand Down
2 changes: 2 additions & 0 deletions internal/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var scopeMarker = Node{Type: scopeMarkerNode}

type HydratedComponentMetadata struct {
ExportName string
LocalName string
Specifier string
ResolvedPath string
}
Expand Down Expand Up @@ -98,6 +99,7 @@ type Node struct {
ClientOnlyComponentNodes []*Node
ClientOnlyComponents []*HydratedComponentMetadata
HydrationDirectives map[string]bool
ServerComponents []*HydratedComponentMetadata
ContainsHead bool
HeadPropagation bool

Expand Down
33 changes: 33 additions & 0 deletions internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,39 @@ func AddComponentProps(doc *astro.Node, n *astro.Node, opts *TransformOptions) {
}

break
} else if strings.HasPrefix(attr.Key, "server:") {
parts := strings.Split(attr.Key, ":")
directive := parts[1]

hydrationAttr := astro.Attribute{
Key: "server:component-directive",
Val: directive,
}
n.Attr = append(n.Attr, hydrationAttr)

match := matchNodeToImportStatement(doc, n)
if match != nil {
doc.ServerComponents = append(doc.ServerComponents, &astro.HydratedComponentMetadata{
ExportName: match.ExportName,
LocalName: n.Data,
Specifier: match.Specifier,
ResolvedPath: ResolveIdForMatch(match.Specifier, opts),
})

pathAttr := astro.Attribute{
Key: "server:component-path",
Val: fmt.Sprintf(`"%s"`, ResolveIdForMatch(match.Specifier, opts)),
Type: astro.ExpressionAttribute,
}
n.Attr = append(n.Attr, pathAttr)

exportAttr := astro.Attribute{
Key: "server:component-export",
Val: fmt.Sprintf(`"%s"`, match.ExportName),
Type: astro.ExpressionAttribute,
}
n.Attr = append(n.Attr, exportAttr)
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export type HoistedScript = { type: string } & (

export interface HydratedComponent {
exportName: string;
localName: string;
specifier: string;
resolvedPath: string;
}
Expand All @@ -109,6 +110,7 @@ export interface TransformResult {
scripts: HoistedScript[];
hydratedComponents: HydratedComponent[];
clientOnlyComponents: HydratedComponent[];
serverComponents: HydratedComponent[];
containsHead: boolean;
propagation: boolean;
}
Expand Down
45 changes: 45 additions & 0 deletions packages/compiler/test/server-islands/meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { fileURLToPath } from 'node:url';
import { transform } from '@astrojs/compiler';
import { test } from 'uvu';
import * as assert from 'uvu/assert';

const FIXTURE = `
---
import Avatar from './Avatar.astro';
import {Other} from './Other.astro';
---
<Avatar server:defer />
<Other server:defer />
`;

let result: Awaited<ReturnType<typeof transform>>;
test.before(async () => {
result = await transform(FIXTURE, {
resolvePath: async (s: string) => {
const out = new URL(s, import.meta.url);
return fileURLToPath(out);
},
});
});

test('component metadata added', () => {
assert.equal(result.serverComponents.length, 2);
});

test('path resolved to the filename', () => {
const m = result.serverComponents[0];
assert.ok(m.specifier !== m.resolvedPath);
});

test('localName is the name used in the template', () => {
assert.equal(result.serverComponents[0].localName, 'Avatar');
assert.equal(result.serverComponents[1].localName, 'Other');
});

test('exportName is the export name of the imported module', () => {
assert.equal(result.serverComponents[0].exportName, 'default');
assert.equal(result.serverComponents[1].exportName, 'Other');
});

test.run();

0 comments on commit 9fb8d5d

Please sign in to comment.