diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts index 39f39dc38..fb3bd3451 100644 --- a/packages/astro/src/compiler/codegen/index.ts +++ b/packages/astro/src/compiler/codegen/index.ts @@ -309,6 +309,7 @@ interface CodegenState { markers: { insideMarkdown: boolean | Record; }; + declarations: Set; exportStatements: Set; importStatements: Set; customElementCandidates: Map; @@ -375,6 +376,9 @@ function compileModule(ast: Ast, module: Script, state: CodegenState, compileOpt break; } case 'FunctionDeclaration': { + if (node.id?.name) { + state.declarations.add(node.id?.name); + } break; } case 'ImportDeclaration': { @@ -384,8 +388,14 @@ function compileModule(ast: Ast, module: Script, state: CodegenState, compileOpt } case 'VariableDeclaration': { for (const declaration of node.declarations) { - // only select Astro.fetchContent() calls here. this utility filters those out for us. - if (!isFetchContent(declaration)) continue; + // only select Astro.fetchContent() calls for more processing, + // otherwise just push name to declarations + if (!isFetchContent(declaration)) { + if (declaration.id.type === 'Identifier') { + state.declarations.add(declaration.id.name); + } + continue; + } // remove node body.splice(i, 1); @@ -687,7 +697,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile const [componentNamespace] = componentName.split('.'); componentInfo = components.get(componentNamespace); } - if (!componentInfo && !isCustomElementTag(componentName)) { + if (state.declarations.has(componentName) && !componentInfo && !isCustomElementTag(componentName)) { if (hydrationAttributes.method) { throw new Error( `Unable to hydrate "${componentName}" because it is statically defined in the frontmatter script. Hydration directives may only be used on imported components.` @@ -705,6 +715,8 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile paren++; buffers[curr] += `h(${componentName}, ${attributes ? generateAttributes(attributes) : 'null'}`; return; + } else if (!state.declarations.has(componentName) && !componentInfo && !isCustomElementTag(componentName)) { + throw new Error(`Unable to render "${componentName}" because it is undefined\n ${state.filename}`) } if (componentName === 'Markdown') { const { $scope } = attributes ?? {}; @@ -870,6 +882,7 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co markers: { insideMarkdown: false, }, + declarations: new Set(), importStatements: new Set(), exportStatements: new Set(), customElementCandidates: new Map(), diff --git a/packages/astro/test/astro-basic.test.js b/packages/astro/test/astro-basic.test.js index c2051b3eb..38f62085e 100644 --- a/packages/astro/test/astro-basic.test.js +++ b/packages/astro/test/astro-basic.test.js @@ -95,12 +95,4 @@ Basics('Allows spread attributes with TypeScript (#521)', async ({ runtime }) => assert.equal($('#spread-ts').attr('c'), '2'); }); -Basics('Allows Components defined in frontmatter', async ({ runtime }) => { - const result = await runtime.load('/frontmatter-component'); - const html = result.contents; - const $ = doc(html); - - assert.equal($('h1').length, 1); -}); - Basics.run(); diff --git a/packages/astro/test/astro-components.test.js b/packages/astro/test/astro-components.test.js index 4f6fc05ab..aa5630584 100644 --- a/packages/astro/test/astro-components.test.js +++ b/packages/astro/test/astro-components.test.js @@ -26,4 +26,18 @@ Components('Astro components are able to render framework components', async ({ assert.not.type($svelte, 'undefined', 'Renders Svelte component'); }); + +Components('Allows Components defined in frontmatter', async ({ runtime }) => { + const result = await runtime.load('/frontmatter-component'); + const html = result.contents; + const $ = doc(html); + + assert.equal($('h1').length, 1); +}); + +Components('Still throws an error for undefined components', async ({ runtime }) => { + const result = await runtime.load('/undefined-component'); + assert.equal(result.statusCode, 500); +}); + Components.run(); diff --git a/packages/astro/test/fixtures/astro-basic/src/pages/frontmatter-component.astro b/packages/astro/test/fixtures/astro-components/src/pages/frontmatter-component.astro similarity index 100% rename from packages/astro/test/fixtures/astro-basic/src/pages/frontmatter-component.astro rename to packages/astro/test/fixtures/astro-components/src/pages/frontmatter-component.astro diff --git a/packages/astro/test/fixtures/astro-components/src/pages/undefined-component.astro b/packages/astro/test/fixtures/astro-components/src/pages/undefined-component.astro new file mode 100644 index 000000000..bdf9424bc --- /dev/null +++ b/packages/astro/test/fixtures/astro-components/src/pages/undefined-component.astro @@ -0,0 +1,11 @@ +--- +function FnComponent() { + const lame = 'ugh'; + return

Hey

+} +const Defined = 'h1' +--- + + +Hello world! +