diff --git a/packages/core/src/plugins/meta/checker.ts b/packages/core/src/plugins/meta/checker.ts index bd23db82..0ede676a 100644 --- a/packages/core/src/plugins/meta/checker.ts +++ b/packages/core/src/plugins/meta/checker.ts @@ -1,10 +1,10 @@ import { existsSync } from 'node:fs' import { basename } from 'node:path' -import { createCheckerByJson } from 'vue-component-meta' import type { CompodiumMeta, ComponentsDir } from '../../types' import { inferPropTypes } from './infer' -type MetaChecker = ReturnType +type CreateMetaChecker = (typeof import('vue-component-meta'))['createCheckerByJson'] +type MetaChecker = ReturnType const checkerOptions = { forceUseTs: true, @@ -18,8 +18,9 @@ const checkerOptions = { } } -export function createChecker(dirs: ComponentsDir[], rootDir = process.cwd(), tsconfigPath?: string) { - const metaChecker = createMetaChecker(dirs, rootDir, tsconfigPath) +export async function createChecker(dirs: ComponentsDir[], rootDir = process.cwd(), tsconfigPath?: string) { + const { createCheckerByJson } = await import('vue-component-meta') + const metaChecker = createMetaChecker(createCheckerByJson, dirs, rootDir, tsconfigPath) const checker = { ...metaChecker, @@ -40,7 +41,7 @@ export function createChecker(dirs: ComponentsDir[], rootDir = process.cwd(), ts return checker } -function createMetaChecker(dirs: ComponentsDir[], rootDir: string, tsconfigPath?: string): MetaChecker { +function createMetaChecker(createCheckerByJson: CreateMetaChecker, dirs: ComponentsDir[], rootDir: string, tsconfigPath?: string): MetaChecker { const tsconfig = `${rootDir}/tsconfig.json` const tsconfigToExtend = tsconfigPath && existsSync(tsconfigPath) ? tsconfigPath : tsconfig diff --git a/packages/core/src/plugins/meta/index.ts b/packages/core/src/plugins/meta/index.ts index e6e71969..96adad20 100644 --- a/packages/core/src/plugins/meta/index.ts +++ b/packages/core/src/plugins/meta/index.ts @@ -38,12 +38,23 @@ export function metaPlugin(options: PluginOptions): VitePlugin { collections = resolveCollections(options, viteConfig) }, - configureServer(server) { + async configureServer(server) { const checkerDirs = collections.flatMap(c => [ ...c.dirs, ...c.exampleDirs ]) - const checker = createChecker(checkerDirs, rootDir, options.tsconfigPath) + let checker: Awaited> + + try { + checker = await createChecker(checkerDirs, rootDir, options.tsconfigPath) + } catch { + server.config.logger.warn('[Compodium] Unable to parse component props. TypeScript 7 is not supported; use TypeScript 6 instead.') + server.middlewares.use('/__compodium__/api/meta', (_req, res) => { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Metadata unavailable' })) + }) + return + } server.middlewares.use('/__compodium__/api/meta', async (req, res) => { try { @@ -68,7 +79,7 @@ export function metaPlugin(options: PluginOptions): VitePlugin { res.end(JSON.stringify(meta)) } catch { res.statusCode = 500 - res.end(JSON.stringify({ error: 'Failed to fetch metadata' })) + res.end(JSON.stringify({ error: 'Metadata unavailable' })) } }) @@ -88,7 +99,7 @@ export function metaPlugin(options: PluginOptions): VitePlugin { } }) - watcher.on('add', async () => { + watcher.on('add', () => { checker.reload() }) diff --git a/packages/nuxt/test/fixtures/meta-checker-failure/app/app.vue b/packages/nuxt/test/fixtures/meta-checker-failure/app/app.vue new file mode 100644 index 00000000..ffbd5449 --- /dev/null +++ b/packages/nuxt/test/fixtures/meta-checker-failure/app/app.vue @@ -0,0 +1,3 @@ + diff --git a/packages/nuxt/test/fixtures/meta-checker-failure/app/components/ExampleComponent.vue b/packages/nuxt/test/fixtures/meta-checker-failure/app/components/ExampleComponent.vue new file mode 100644 index 00000000..bac6c1d4 --- /dev/null +++ b/packages/nuxt/test/fixtures/meta-checker-failure/app/components/ExampleComponent.vue @@ -0,0 +1,7 @@ + + + diff --git a/packages/nuxt/test/fixtures/meta-checker-failure/nuxt.config.ts b/packages/nuxt/test/fixtures/meta-checker-failure/nuxt.config.ts new file mode 100644 index 00000000..7546d0ef --- /dev/null +++ b/packages/nuxt/test/fixtures/meta-checker-failure/nuxt.config.ts @@ -0,0 +1,8 @@ +import { fileURLToPath } from 'node:url' + +export default defineNuxtConfig({ + modules: ['../../../src/module'], + alias: { + 'vue-component-meta': fileURLToPath(new URL('./vue-component-meta.ts', import.meta.url)) + } +}) diff --git a/packages/nuxt/test/fixtures/meta-checker-failure/package.json b/packages/nuxt/test/fixtures/meta-checker-failure/package.json new file mode 100644 index 00000000..804188e7 --- /dev/null +++ b/packages/nuxt/test/fixtures/meta-checker-failure/package.json @@ -0,0 +1,5 @@ +{ + "private": true, + "name": "meta-checker-failure", + "type": "module" +} diff --git a/packages/nuxt/test/fixtures/meta-checker-failure/vue-component-meta.ts b/packages/nuxt/test/fixtures/meta-checker-failure/vue-component-meta.ts new file mode 100644 index 00000000..e199839b --- /dev/null +++ b/packages/nuxt/test/fixtures/meta-checker-failure/vue-component-meta.ts @@ -0,0 +1,3 @@ +export function createCheckerByJson(): never { + throw new Error('Unsupported TypeScript version') +} diff --git a/packages/nuxt/test/meta-checker-failure.nuxt.test.ts b/packages/nuxt/test/meta-checker-failure.nuxt.test.ts new file mode 100644 index 00000000..481747ca --- /dev/null +++ b/packages/nuxt/test/meta-checker-failure.nuxt.test.ts @@ -0,0 +1,25 @@ +import { fetch, setup } from '@nuxt/test-utils/e2e' +import { join } from 'pathe' +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' + +describe('metadata checker failure', async () => { + const rootDir = fileURLToPath(new URL('./fixtures/meta-checker-failure', import.meta.url)) + + await setup({ + rootDir, + dev: true, + port: 4546, + setupTimeout: 30000 + }) + + it('returns an error when component metadata initialization fails', async () => { + const query = new URLSearchParams({ + component: join(rootDir, 'app/components/ExampleComponent.vue') + }) + const response = await fetch(`/__compodium__/api/meta?${query}`) + + expect(response.status).toBe(500) + expect(await response.json()).toEqual({ error: 'Metadata unavailable' }) + }) +})