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

Support for view transition directives #832

Merged
merged 10 commits into from
Jul 19, 2023
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
7 changes: 7 additions & 0 deletions .changeset/young-turkeys-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astrojs/compiler': minor
---

Support for view transition directives

This adds support for `transition:animate` and `transition:name` which get passed into the new `renderTransition` runtime function.
30 changes: 20 additions & 10 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ func makeTransformOptions(options js.Value) transform.TransformOptions {
if jsBool(options.Get("resultScopedSlot")) {
scopedSlot = true
}
experimentalTransitions := false
if jsBool(options.Get("experimentalTransitions")) {
experimentalTransitions = true
}
transitionsAnimationURL := jsString(options.Get("transitionsAnimationURL"))
if transitionsAnimationURL == "" {
transitionsAnimationURL = "astro/components/viewtransitions.css"
}

var resolvePath any = options.Get("resolvePath")
var resolvePathFn func(string) string
Expand All @@ -117,16 +125,18 @@ func makeTransformOptions(options js.Value) transform.TransformOptions {
}

return transform.TransformOptions{
Filename: filename,
NormalizedFilename: normalizedFilename,
InternalURL: internalURL,
SourceMap: sourcemap,
AstroGlobalArgs: astroGlobalArgs,
Compact: compact,
ResolvePath: resolvePathFn,
PreprocessStyle: preprocessStyle,
ResultScopedSlot: scopedSlot,
ScopedStyleStrategy: scopedStyleStrategy,
Filename: filename,
NormalizedFilename: normalizedFilename,
InternalURL: internalURL,
SourceMap: sourcemap,
AstroGlobalArgs: astroGlobalArgs,
Compact: compact,
ResolvePath: resolvePathFn,
PreprocessStyle: preprocessStyle,
ResultScopedSlot: scopedSlot,
ScopedStyleStrategy: scopedStyleStrategy,
ExperimentalTransitions: experimentalTransitions,
TransitionsAnimationURL: transitionsAnimationURL,
}
}

Expand Down
10 changes: 6 additions & 4 deletions internal/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ type HydratedComponentMetadata struct {
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
// "svg" is short for "http://www.w3.org/2000/svg".
type Node struct {
Fragment bool
CustomElement bool
Component bool
Expression bool
Fragment bool
CustomElement bool
Component bool
Expression bool
Transition bool
TransitionScope string

Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node

Expand Down
25 changes: 24 additions & 1 deletion internal/printer/print-to-js.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"

. "github.com/withastro/compiler/internal"
astro "github.com/withastro/compiler/internal"
"github.com/withastro/compiler/internal/handler"
"github.com/withastro/compiler/internal/js_scanner"
"github.com/withastro/compiler/internal/loc"
Expand Down Expand Up @@ -110,6 +111,10 @@ func emptyTextNodeWithoutSiblings(n *Node) bool {
func render1(p *printer, n *Node, opts RenderOptions) {
depth := opts.depth

if opts.opts.ExperimentalTransitions && n.Transition {
p.needsTransitionCSS = true
}

// Root of the document, print all children
if n.Type == DocumentNode {
p.printInternalImports(p.opts.InternalURL, &opts)
Expand All @@ -129,7 +134,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}

p.printReturnClose()
p.printFuncSuffix(opts.opts)
p.printFuncSuffix(opts.opts, n)
return
}

Expand Down Expand Up @@ -429,10 +434,28 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}
p.print(`]`)
} else {
if transform.HasAttr(n, transform.TRANSITION_ANIMATE) || transform.HasAttr(n, transform.TRANSITION_NAME) {
animationName := ""
if transform.HasAttr(n, transform.TRANSITION_ANIMATE) {
animationName = transform.GetAttr(n, transform.TRANSITION_ANIMATE).Val
}
transitionName := ""
if transform.HasAttr(n, transform.TRANSITION_NAME) {
transitionName = transform.GetAttr(n, transform.TRANSITION_NAME).Val
}

n.Attr = append(n.Attr, astro.Attribute{
Key: "data-astro-transition-scope",
Val: fmt.Sprintf(`%s(%s, "%s", "%s", "%s")`, RENDER_TRANSITION, RESULT, n.TransitionScope, animationName, transitionName),
Type: astro.ExpressionAttribute,
})
}

for _, a := range n.Attr {
if transform.IsImplicitNodeMarker(a) || a.Key == "is:inline" {
continue
}

if a.Key == "slot" {
if n.Parent.Component || n.Parent.Expression {
continue
Expand Down
24 changes: 19 additions & 5 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type printer struct {
hasFuncPrelude bool
hasInternalImports bool
hasCSSImports bool
needsTransitionCSS bool
}

var TEMPLATE_TAG = "$$render"
Expand All @@ -40,6 +41,7 @@ var UNESCAPE_HTML = "$$unescapeHTML"
var RENDER_SLOT = "$$renderSlot"
var MERGE_SLOTS = "$$mergeSlots"
var ADD_ATTRIBUTE = "$$addAttribute"
var RENDER_TRANSITION = "$$renderTransition"
var SPREAD_ATTRIBUTES = "$$spreadAttributes"
var DEFINE_STYLE_VARS = "$$defineStyleVars"
var DEFINE_SCRIPT_VARS = "$$defineScriptVars"
Expand Down Expand Up @@ -116,6 +118,10 @@ func (p *printer) printInternalImports(importSpecifier string, opts *RenderOptio
p.print("defineStyleVars as " + DEFINE_STYLE_VARS + ",\n ")
p.addNilSourceMapping()
p.print("defineScriptVars as " + DEFINE_SCRIPT_VARS + ",\n ")
if opts.opts.ExperimentalTransitions {
p.addNilSourceMapping()
p.print("renderTransition as " + RENDER_TRANSITION + ",\n ")
}

// Only needed if using fallback `resolvePath` as it calls `$$metadata.resolvePath`
if opts.opts.ResolvePath == nil {
Expand All @@ -141,6 +147,10 @@ func (p *printer) printCSSImports(cssLen int) {
p.print(fmt.Sprintf("import \"%s?astro&type=style&index=%v&lang.css\";", p.opts.Filename, i))
i++
}
if p.needsTransitionCSS {
p.addNilSourceMapping()
p.print(fmt.Sprintf(`import "%s";`, p.opts.TransitionsAnimationURL))
}
p.print("\n")
p.hasCSSImports = true
}
Expand Down Expand Up @@ -250,15 +260,19 @@ func (p *printer) printFuncPrelude(opts transform.TransformOptions) {
p.hasFuncPrelude = true
}

func (p *printer) printFuncSuffix(opts transform.TransformOptions) {
func (p *printer) printFuncSuffix(opts transform.TransformOptions, n *astro.Node) {
componentName := getComponentName(opts.Filename)
p.addNilSourceMapping()
filenameArg := "undefined"
propagationArg := "undefined"
if len(opts.Filename) > 0 {
escapedFilename := strings.ReplaceAll(opts.Filename, "'", "\\'")
p.println(fmt.Sprintf("}, '%s');", escapedFilename))
} else {
p.println("});")
filenameArg = fmt.Sprintf("'%s'", escapedFilename)
}
if n.Transition {
propagationArg = "'self'"
}
p.println(fmt.Sprintf("}, %s, %s);", filenameArg, propagationArg))
bluwy marked this conversation as resolved.
Show resolved Hide resolved
p.println(fmt.Sprintf("export default %s;", componentName))
}

Expand Down Expand Up @@ -323,7 +337,7 @@ func (p *printer) printAttributesToObject(n *astro.Node) {
}

func (p *printer) printAttribute(attr astro.Attribute, n *astro.Node) {
if attr.Key == "define:vars" || attr.Key == "set:text" || attr.Key == "set:html" || attr.Key == "is:raw" {
if attr.Key == "define:vars" || attr.Key == "set:text" || attr.Key == "set:html" || attr.Key == "is:raw" || attr.Key == "transition:animate" || attr.Key == "transition:name" {
return
}

Expand Down
4 changes: 2 additions & 2 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Astro = $$result.createAstro($$Astro, $$props, %s);
Astro.self = $$Component;%s`, CREATE_COMPONENT, SLOTS, SLOTS, "\n\n")
var RETURN = fmt.Sprintf("return %s%s", TEMPLATE_TAG, BACKTICK)
var SUFFIX = fmt.Sprintf("%s;", BACKTICK) + `
});
}, undefined, undefined);
export default $$Component;`
var CREATE_ASTRO_CALL = "const $$Astro = $$createAstro('https://astro.build');\nconst Astro = $$Astro;"
var RENDER_HEAD_RESULT = "${$$renderHead($$result)}"
Expand All @@ -48,7 +48,7 @@ var NON_WHITESPACE_CHARS = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS
func suffixWithFilename(filename string) string {

return fmt.Sprintf("%s;", BACKTICK) + fmt.Sprintf(`
}, '%s');
}, '%s', undefined);
export default $$Component;`, filename)
}

Expand Down
33 changes: 22 additions & 11 deletions internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,41 @@ import (
a "golang.org/x/net/html/atom"
)

const TRANSITION_ANIMATE = "transition:animate"
const TRANSITION_NAME = "transition:name"

type TransformOptions struct {
Scope string
Filename string
NormalizedFilename string
InternalURL string
SourceMap string
AstroGlobalArgs string
ScopedStyleStrategy string
Compact bool
ResultScopedSlot bool
ResolvePath func(string) string
PreprocessStyle interface{}
Scope string
Filename string
NormalizedFilename string
InternalURL string
SourceMap string
AstroGlobalArgs string
ScopedStyleStrategy string
Compact bool
ResultScopedSlot bool
ExperimentalTransitions bool
TransitionsAnimationURL string
ResolvePath func(string) string
PreprocessStyle interface{}
}

func Transform(doc *astro.Node, opts TransformOptions, h *handler.Handler) *astro.Node {
shouldScope := len(doc.Styles) > 0 && ScopeStyle(doc.Styles, opts)
definedVars := GetDefineVars(doc.Styles)
didAddDefinedVars := false
i := 0
walk(doc, func(n *astro.Node) {
i++
ExtractScript(doc, n, &opts, h)
AddComponentProps(doc, n, &opts)
if shouldScope {
ScopeElement(n, opts)
}
if opts.ExperimentalTransitions && (HasAttr(n, TRANSITION_ANIMATE) || HasAttr(n, TRANSITION_NAME)) {
doc.Transition = true
n.TransitionScope = astro.HashString(fmt.Sprintf("%s-%v", opts.Scope, i))
}
if len(definedVars) > 0 {
didAdd := AddDefineVars(n, definedVars)
if !didAddDefinedVars {
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 @@ -53,6 +53,8 @@ export interface TransformOptions {
* @deprecated "as" has been removed and no longer has any effect!
*/
as?: 'document' | 'fragment';
experimentalTransitions?: boolean;
transitionsAnimationURL?: string;
resolvePath?: (specifier: string) => Promise<string>;
preprocessStyle?: (content: string, attrs: Record<string, string>) => null | Promise<PreprocessorResult | PreprocessorError>;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/test/basic/trailing-newline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ test.before(async () => {
});

test('does not add trailing newline to rendered output', () => {
assert.match(result.code, `}\`;\n}, '<stdin>');`, 'Does not include a trailing newline in the render function');
assert.match(result.code, `}\`;\n}, '<stdin>', undefined);`, 'Does not include a trailing newline in the render function');
});

test.run();
Loading