diff --git a/.changeset/happy-fireants-occur.md b/.changeset/happy-fireants-occur.md new file mode 100644 index 000000000000..40ffcf4c6cf7 --- /dev/null +++ b/.changeset/happy-fireants-occur.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Improve error handling when an Astro component is rendered manually diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 0b74ca6752a5..5a2b75cabd2f 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -415,6 +415,20 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati }, hint: 'Mutable values declared at runtime are not supported. Please make sure to use exactly `export const prerender = true`.', }, + /** + * @docs + * @message + * **Example error messages:**
+ * InvalidComponentArgs: Invalid arguments passed to component. + * @description + * Astro components cannot be rendered manually via a function call, such as `Component()` or `{items.map(Component)}`. Prefer the component syntax `` or `{items.map(item => )}`. + */ + InvalidComponentArgs: { + title: 'Invalid component arguments.', + code: 3020, + message: (name: string) => `Invalid arguments passed to${name ? ` <${name}>` : ''} component.`, + hint: 'Astro components cannot be rendered directly via function call, such as `Component()` or `{items.map(Component)}`.', + }, // Vite Errors - 4xxx UnknownViteError: { title: 'Unknown Vite Error.', diff --git a/packages/astro/src/runtime/server/astro-component.ts b/packages/astro/src/runtime/server/astro-component.ts index 933a69a9a67a..975be7054acb 100644 --- a/packages/astro/src/runtime/server/astro-component.ts +++ b/packages/astro/src/runtime/server/astro-component.ts @@ -1,11 +1,28 @@ import type { PropagationHint } from '../../@types/astro'; import type { AstroComponentFactory } from './render/index.js'; +import { AstroError, AstroErrorData } from '../../core/errors/index.js'; -function baseCreateComponent(cb: AstroComponentFactory, moduleId?: string) { +function validateArgs(args: unknown[]): args is Parameters { + if (args.length !== 3) return false; + if (!args[0] ||typeof args[0] !== 'object') return false; + return true; +} +function baseCreateComponent(cb: AstroComponentFactory, moduleId?: string): AstroComponentFactory { + const name = moduleId?.split('/').pop()?.replace('.astro', '') ?? '' + const fn = (...args: Parameters) => { + if (!validateArgs(args)) { + throw new AstroError({ + ...AstroErrorData.InvalidComponentArgs, + message: AstroErrorData.InvalidComponentArgs.message(name), + }); + } + return cb(...args); + } + Object.defineProperty(fn, 'name', { value: name, writable: false }); // Add a flag to this callback to mark it as an Astro component - cb.isAstroComponentFactory = true; - cb.moduleId = moduleId; - return cb; + fn.isAstroComponentFactory = true; + fn.moduleId = moduleId; + return fn; } interface CreateComponentOptions {