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
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.
25 changes: 15 additions & 10 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ func makeTransformOptions(options js.Value) transform.TransformOptions {
if jsBool(options.Get("resultScopedSlot")) {
scopedSlot = true
}
experimentalTransitions := false
if jsBool(options.Get("experimentalTransitions")) {
experimentalTransitions = true
}

var resolvePath any = options.Get("resolvePath")
var resolvePathFn func(string) string
Expand All @@ -117,16 +121,17 @@ 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,
}
}

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(`import "astro/components/viewtransitions.css";`)
bluwy marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

I didn't clearly comment this yesterday, I was also wondering if we should create a new option for this import path, similar to how we have the internalURL option.

Not blocking as this is experimental and we can change again if we want.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah, I can add that, good call.

Copy link
Member

Choose a reason for hiding this comment

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

Awesome. Do we also want to prefix with "experimental"? (just in case)

}
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
32 changes: 21 additions & 11 deletions internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,40 @@ 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
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
1 change: 1 addition & 0 deletions packages/compiler/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface TransformOptions {
* @deprecated "as" has been removed and no longer has any effect!
*/
as?: 'document' | 'fragment';
experimentalTransitions?: boolean;
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