diff --git a/.changeset/yellow-numbers-leave.md b/.changeset/yellow-numbers-leave.md new file mode 100644 index 000000000..3079a7bc3 --- /dev/null +++ b/.changeset/yellow-numbers-leave.md @@ -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)). diff --git a/go.mod b/go.mod index 97342ec73..00a259e1b 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index ac607f966..4530539b0 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/printer/print-to-js.go b/internal/printer/print-to-js.go index c06abe00c..d439db4b2 100644 --- a/internal/printer/print-to-js.go +++ b/internal/printer/print-to-js.go @@ -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 } @@ -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:] @@ -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}) } @@ -223,8 +220,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("") diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 729ba462e..5a04b48ef 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -142,18 +142,21 @@ func (p *printer) printDefineVars(n *astro.Node) { } } -func (p *printer) printFuncPrelude(componentName string) { +func (p *printer) printFuncPrelude(opts transform.TransformOptions) { if p.hasFuncPrelude { return } + componentName := getComponentName(opts.Pathname) p.addNilSourceMapping() p.println("\n//@ts-ignore") p.println(fmt.Sprintf("const %s = %s(async (%s, $$props, %s) => {", componentName, CREATE_COMPONENT, RESULT, SLOTS)) p.println(fmt.Sprintf("const Astro = %s.createAstro($$Astro, $$props, %s);", RESULT, SLOTS)) + p.println(fmt.Sprintf("Astro.self = %s;", componentName)) p.hasFuncPrelude = true } -func (p *printer) printFuncSuffix(componentName string) { +func (p *printer) printFuncSuffix(opts transform.TransformOptions) { + componentName := getComponentName(opts.Pathname) p.addNilSourceMapping() p.println("});") p.println(fmt.Sprintf("export default %s;", componentName)) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index e4a839d09..fa2a2a87d 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -28,7 +28,8 @@ var INTERNAL_IMPORTS = fmt.Sprintf("import {\n %s\n} from \"%s\";\n", strings.J }, ",\n "), "http://localhost:3000/") var PRELUDE = fmt.Sprintf(`//@ts-ignore const $$Component = %s(async ($$result, $$props, %s) => { -const Astro = $$result.createAstro($$Astro, $$props, %s);%s`, CREATE_COMPONENT, SLOTS, SLOTS, "\n") +const Astro = $$result.createAstro($$Astro, $$props, %s); +Astro.self = $$Component;%s`, CREATE_COMPONENT, SLOTS, SLOTS, "\n") var RETURN = fmt.Sprintf("return %s%s", TEMPLATE_TAG, BACKTICK) var SUFFIX = fmt.Sprintf("%s;", BACKTICK) + ` }); diff --git a/internal/printer/utils.go b/internal/printer/utils.go index c1304c033..6c4c05d30 100644 --- a/internal/printer/utils.go +++ b/internal/printer/utils.go @@ -3,6 +3,8 @@ package printer import ( "regexp" "strings" + + "github.com/iancoleman/strcase" ) func escapeText(src string) string { @@ -13,6 +15,22 @@ func escapeText(src string) string { ) } +func getComponentName(pathname string) string { + if len(pathname) == 0 { + return "$$Component" + } + parts := strings.Split(pathname, "/") + part := parts[len(parts)-1] + if len(part) == 0 { + return "$$Component" + } + basename := strcase.ToCamel(strings.Split(part, ".")[0]) + if basename == "Astro" { + return "$$Component" + } + return strings.Join([]string{"$$", basename}, "") +} + func escapeExistingEscapes(src string) string { return strings.Replace(src, "\\", "\\\\", -1) } diff --git a/lib/compiler/deno/astro.wasm b/lib/compiler/deno/astro.wasm index a8cceeb54..016b0fc55 100755 Binary files a/lib/compiler/deno/astro.wasm and b/lib/compiler/deno/astro.wasm differ diff --git a/lib/compiler/test/component-name.test.mjs b/lib/compiler/test/component-name.test.mjs new file mode 100644 index 000000000..025debb31 --- /dev/null +++ b/lib/compiler/test/component-name.test.mjs @@ -0,0 +1,16 @@ +/* eslint-disable no-console */ + +import { transform } from '@astrojs/compiler'; + +async function run() { + const result = await transform(`