feat: improve handling of undefined components (#650)

This commit is contained in:
Nate Moore 2021-07-09 17:02:39 -05:00 committed by GitHub
parent fb78b76cc6
commit 5077ff2e11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 11 deletions

View file

@ -309,6 +309,7 @@ interface CodegenState {
markers: {
insideMarkdown: boolean | Record<string, any>;
};
declarations: Set<string>;
exportStatements: Set<string>;
importStatements: Set<string>;
customElementCandidates: Map<string, string>;
@ -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(),

View file

@ -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();

View file

@ -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();

View file

@ -0,0 +1,11 @@
---
function FnComponent() {
const lame = 'ugh';
return <h2>Hey</h2>
}
const Defined = 'h1'
---
<FnComponent />
<Defined>Hello world!</Defined>
<Undefined />