From 699d7c56a7ea72fde5c3c0f205fbf59e8b9e0fe5 Mon Sep 17 00:00:00 2001 From: Farnabaz Date: Thu, 10 Jul 2025 14:41:22 +0200 Subject: [PATCH 1/2] feat: inherit component prop types in content collection --- package.json | 2 +- playground/components/PropsExample.vue | 70 ++++++++++++++++++++++++++ playground/components/TestD.vue | 22 ++++++++ playground/content.config.ts | 6 +++ pnpm-lock.yaml | 18 +++++-- src/utils/collection.ts | 3 +- src/utils/schema.ts | 34 ++++++++++++- src/utils/zod.ts | 10 +++- 8 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 playground/components/PropsExample.vue create mode 100644 playground/components/TestD.vue diff --git a/package.json b/package.json index ca0ccd490..fd6e9e09f 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "micromatch": "^4.0.8", "minimark": "^0.2.0", "minimatch": "^10.0.3", - "nuxt-component-meta": "^0.12.1", + "nuxt-component-meta": "https://pkg.pr.new/nuxt-component-meta@ff0edad", "nypm": "^0.6.0", "ohash": "^2.0.11", "pathe": "^2.0.3", diff --git a/playground/components/PropsExample.vue b/playground/components/PropsExample.vue new file mode 100644 index 000000000..4876b146e --- /dev/null +++ b/playground/components/PropsExample.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file diff --git a/playground/components/TestD.vue b/playground/components/TestD.vue new file mode 100644 index 000000000..77dad7a01 --- /dev/null +++ b/playground/components/TestD.vue @@ -0,0 +1,22 @@ + + + diff --git a/playground/content.config.ts b/playground/content.config.ts index 44b32fc2a..69ebe7b13 100644 --- a/playground/content.config.ts +++ b/playground/content.config.ts @@ -34,6 +34,7 @@ const content = defineCollection({ schema: z.object({ date: z.date(), rawbody: z.string(), + testd: z.component('components/TestD.vue'), }), }) @@ -69,6 +70,11 @@ const collections = { content, data, pages, + buttons: defineCollection({ + type: 'data', + source: 'testd/**', + schema: z.component('@nuxt/ui/components/Button.vue'), + }), contentV2: defineCollection({ type: 'page', source: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4e77de12..53fd080dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,8 +99,8 @@ importers: specifier: ^10.0.3 version: 10.0.3 nuxt-component-meta: - specifier: ^0.12.1 - version: 0.12.1(magicast@0.3.5)(vue-component-type-helpers@2.2.12) + specifier: https://pkg.pr.new/nuxt-component-meta@ff0edad + version: https://pkg.pr.new/nuxt-component-meta@ff0edad(magicast@0.3.5)(vue-component-type-helpers@2.2.12) nypm: specifier: ^0.6.0 version: 0.6.0 @@ -5043,6 +5043,10 @@ packages: engines: {node: '>=16.0.0'} hasBin: true + json-schema-to-zod@2.6.1: + resolution: {integrity: sha512-uiHmWH21h9FjKJkRBntfVGTLpYlCZ1n98D0izIlByqQLqpmkQpNTBtfbdP04Na6+43lgsvrShFh2uWLkQDKJuQ==} + hasBin: true + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -5817,8 +5821,9 @@ packages: engines: {node: ^16.10.0 || >=18.0.0} hasBin: true - nuxt-component-meta@0.12.1: - resolution: {integrity: sha512-w0ZgRCE9wGZLbmAnCUV2F4/Gjwy1J6L9FejrpTHSHM8Igrf2THwe809QOXbdV8g4+Elkz2MeC/34ZFyt05tKCg==} + nuxt-component-meta@https://pkg.pr.new/nuxt-component-meta@ff0edad: + resolution: {tarball: https://pkg.pr.new/nuxt-component-meta@ff0edad} + version: 0.12.1 hasBin: true nuxt-llms@0.1.3: @@ -13709,6 +13714,8 @@ snapshots: prettier: 3.6.2 tinyglobby: 0.2.14 + json-schema-to-zod@2.6.1: {} + json-schema-traverse@0.4.1: {} json-schema@0.4.0: {} @@ -14765,10 +14772,11 @@ snapshots: nuxi@3.25.1: {} - nuxt-component-meta@0.12.1(magicast@0.3.5)(vue-component-type-helpers@2.2.12): + nuxt-component-meta@https://pkg.pr.new/nuxt-component-meta@ff0edad(magicast@0.3.5)(vue-component-type-helpers@2.2.12): dependencies: '@nuxt/kit': 3.17.6(magicast@0.3.5) citty: 0.1.6 + json-schema-to-zod: 2.6.1 mlly: 1.7.4 ohash: 2.0.11 scule: 1.3.0 diff --git a/src/utils/collection.ts b/src/utils/collection.ts index ce5381f24..e82eabc3c 100644 --- a/src/utils/collection.ts +++ b/src/utils/collection.ts @@ -4,7 +4,7 @@ import type { Collection, ResolvedCollection, CollectionSource, DefinedCollectio import { getOrderedSchemaKeys, describeProperty, getCollectionFieldsTypes } from '../runtime/internal/schema' import type { Draft07, ParsedContentFile } from '../types' import { defineLocalSource, defineGitHubSource, defineBitbucketSource } from './source' -import { emptyStandardSchema, mergeStandardSchema, metaStandardSchema, pageStandardSchema } from './schema' +import { emptyStandardSchema, mergeStandardSchema, metaStandardSchema, pageStandardSchema, replaceComponentSchemas } from './schema' import { z, zodToStandardSchema } from './zod' import { logger } from './dev' @@ -17,6 +17,7 @@ export function defineCollection(collection: Collection>(obj: T) { return Object.values(obj) as [(typeof obj)[keyof T]] @@ -204,3 +207,32 @@ export function mergeStandardSchema(s1: Draft07, s2: Draft07): Draft07 { ), } } + +export function replaceComponentSchemas(property: T): T { + if (property.type !== 'object') { + return property + } + + // If the property has a _inherit property, replace it with the component's props schema + if (property.properties?._inherit) { + const nuxt = useNuxt() + let path = String((property.properties._inherit as Draft07DefinitionProperty).default) + try { + path = resolveModule(path) + } + catch { + } + + const meta = getComponentMeta(path, { rootDir: nuxt.options.rootDir }) + return propsToJsonSchema(meta.props) as T + } + + // Look for _inherit properties in nested objects + if (property.properties) { + Object.entries(property.properties).forEach(([key, value]) => { + property.properties![key] = replaceComponentSchemas(value as Draft07DefinitionProperty) as Draft07DefinitionProperty + }) + } + + return property +} diff --git a/src/utils/zod.ts b/src/utils/zod.ts index 481a2dc05..f2a478d88 100644 --- a/src/utils/zod.ts +++ b/src/utils/zod.ts @@ -30,7 +30,15 @@ export type SqlFieldType = 'VARCHAR' | 'INT' | 'BOOLEAN' | 'DATE' | 'TEXT' return this } -export const z = zod + +export const z = { + ...zod, + component(component: string) { + return zod.object({ + _inherit: zod.string().default(component) + }) + } +} // Function to get the underlying Zod type export function getUnderlyingType(zodType: ZodType): ZodType { From 101ed8d3354d9c23c61dbc6f08eb80b638a92031 Mon Sep 17 00:00:00 2001 From: Farnabaz Date: Thu, 10 Jul 2025 14:54:39 +0200 Subject: [PATCH 2/2] lint: fix --- playground/components/PropsExample.vue | 18 ++++++++++++------ playground/components/TestD.vue | 1 - src/utils/schema.ts | 1 + src/utils/zod.ts | 5 ++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/playground/components/PropsExample.vue b/playground/components/PropsExample.vue index 4876b146e..d13b4b319 100644 --- a/playground/components/PropsExample.vue +++ b/playground/components/PropsExample.vue @@ -6,13 +6,19 @@

Title (string): {{ title }}

Is Active (boolean): {{ isActive ? 'Yes' : 'No' }}

- -
+ +

This content is shown when isActive is true!

Current count multiplied by 2: {{ count * 2 }}

- -
+ +
{{ title }}
@@ -25,7 +31,7 @@ interface Props { isActive: boolean } -const props = defineProps() +efineProps() // You can also define props with default values like this: // const props = withDefaults(defineProps(), { @@ -67,4 +73,4 @@ const props = defineProps() .styled-title.active { color: #007bff; } - \ No newline at end of file + diff --git a/playground/components/TestD.vue b/playground/components/TestD.vue index 77dad7a01..63b2227a1 100644 --- a/playground/components/TestD.vue +++ b/playground/components/TestD.vue @@ -12,7 +12,6 @@ defineProps<{ */ bar?: number }>() -