Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
960ab6d
chore: update all dependencies
romhml May 10, 2026
9522b41
chore: remove vue-component-meta fork
romhml May 10, 2026
d72eea6
refactor: adapt to new meta parser
romhml May 16, 2026
cfba90a
Merge branch 'main' of github.com:romhml/compodium into chore/update-…
romhml May 16, 2026
3468a23
fix: dirty typecheck
romhml May 16, 2026
d0c156e
chore: up
romhml May 16, 2026
576d8c7
chore: up
romhml May 16, 2026
4667f71
test: skip broken tests
romhml May 16, 2026
795a75b
fix(core): listed dependencies
romhml May 16, 2026
da0e594
chore: up
romhml May 16, 2026
be90593
fix: downgrade vue-component-meta
romhml May 16, 2026
46819e1
fix(core): example meta override
romhml May 26, 2026
0319056
Merge branch 'main' of github.com:romhml/compodium into chore/update-…
romhml May 26, 2026
f039307
chore: up
romhml May 26, 2026
6771ccc
fix(core): override component default props with example's
romhml Jul 5, 2026
f64aa84
chore: update deps
romhml Jul 5, 2026
9c285cf
Merge branch 'main' of github.com:romhml/compodium into chore/update-…
romhml Jul 5, 2026
a12213e
fix: typechecks
romhml Jul 5, 2026
6d0a884
fix(nuxt): invalid tsconfig path
romhml Jul 5, 2026
ab31219
fix(core): downgrade component meta
romhml Jul 10, 2026
7d931aa
Revert "fix(core): downgrade component meta"
romhml Jul 10, 2026
5f4e0bf
fix(core): resolve Vue component meta through virtual imports
romhml Jul 10, 2026
77a949f
fix(compodium): resolve Nuxt UI component metadata via generated app …
romhml Jul 10, 2026
3191357
chore(nuxt): explicitly add pathe to dependencies
romhml Jul 10, 2026
cc0caf5
fix(vue): ui type resolution
romhml Jul 13, 2026
669c4b4
fix(core): optimize deps warning
romhml Jul 13, 2026
7682071
chore: increase test timeout
romhml Jul 13, 2026
9e5c20e
perf(core): skip inference for native types
romhml Jul 13, 2026
4b4b4a6
chore: up
romhml Jul 13, 2026
7df1a43
chore: dedupe
romhml Jul 13, 2026
484bb85
chore: typecheck
romhml Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: pnpm typecheck

- name: Test
run: pnpm test --testTimeout 5000 --hookTimeout 20000
run: pnpm test --testTimeout 20000 --hookTimeout 20000

- name: Bump version
run: pnpm jiti ./scripts/bump.ts 0.0.0-${{ github.sha }}
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ TODO.md
# Generated dirs
dist

# Generated Vue playground declarations
playgrounds/vue/auto-imports.d.ts
playgrounds/vue/components.d.ts

# Nuxt
.nuxt
.output
Expand Down Expand Up @@ -56,3 +60,5 @@ coverage
Network Trash Folder
Temporary Items
.apdisk

docs/.data
1 change: 1 addition & 0 deletions .nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
setups.@nuxt/test-utils="4.0.3"
18 changes: 9 additions & 9 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
"typecheck": "nuxt typecheck"
},
"dependencies": {
"@iconify-json/lucide": "^1.2.71",
"@iconify-json/simple-icons": "^1.2.56",
"@iconify-json/vscode-icons": "^1.2.33",
"@nuxt/content": "^3.7.1",
"@nuxt/image": "^1.11.0",
"@nuxthub/core": "0.9.0",
"better-sqlite3": "^12.4.1",
"@iconify-json/lucide": "^1.2.116",
"@iconify-json/simple-icons": "^1.2.89",
"@iconify-json/vscode-icons": "^1.2.64",
"@nuxt/content": "^3.15.0",
"@nuxt/image": "^2.0.0",
"@nuxthub/core": "0.10.7",
"better-sqlite3": "^12.11.1",
"nuxt": "catalog:",
"nuxt-llms": "0.1.3",
"nuxt-og-image": "^5.1.12"
"nuxt-llms": "0.2.0",
"nuxt-og-image": "^6.7.2"
}
}
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@
"bump": "jiti ./scripts/bump.ts"
},
"devDependencies": {
"@nuxt/eslint-config": "^1.9.0",
"@nuxt/test-utils": "^3.20.1",
"@types/node": "^22.18.12",
"@types/supertest": "^6.0.3",
"@nuxt/eslint-config": "^1.15.2",
"@nuxt/test-utils": "^4.0.3",
"@types/node": "^25.8.0",
"@types/supertest": "^7.2.0",
"changelogen": "^0.6.2",
"eslint": "^9.38.0",
"happy-dom": "^17.4.4",
"supertest": "^7.1.4",
"typescript": "^5.9.2",
"eslint": "^10.4.0",
"happy-dom": "^20.9.0",
"supertest": "^7.2.2",
"typescript": "^6.0.3",
"unbuild": "^3.6.1",
"vite": "^7.1.12",
"vitest": "^3.2.4",
"wrangler": "^4.45.0"
"vite": "8.0.16",
"vitest": "^4.1.6",
"wrangler": "^4.92.0"
},
"resolutions": {
"@compodium/core": "workspace:^",
"@compodium/examples": "workspace:^",
"@compodium/meta": "workspace:^",
"@compodium/nuxt": "workspace:^",
"@compodium/vue": "workspace:^"
"@compodium/vue": "workspace:^",
"vite": "8.0.16"
},
"pnpm": {
"onlyBuiltDependencies": [
Expand Down
28 changes: 17 additions & 11 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
},
"./*": "./dist/*"
Expand All @@ -33,7 +34,8 @@
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"vite": ">=6",
"typescript": ">=5.8",
"vite": ">=7",
"vue": ">=3"
},
"peerDependenciesMeta": {
Expand All @@ -43,18 +45,22 @@
},
"dependencies": {
"@compodium/examples": "workspace:^",
"@compodium/meta": "workspace:^",
"@vueuse/core": "^13.9.0",
"chokidar": "^4.0.3",
"hookable": "^5.5.3",
"mlly": "^1.8.0",
"@vue/compiler-sfc": "^3.5.39",
"@vue/language-core": "^3.3.6",
"@vueuse/core": "^14.3.0",
"chokidar": "^5.0.0",
"defu": "^6.1.7",
"hookable": "^6.1.1",
"mlly": "^1.8.2",
"oxc-parser": "^0.138.0",
"pathe": "^2.0.3",
"scule": "^1.3.0",
"sirv": "^3.0.2",
"tinyglobby": "^0.2.15",
"ufo": "^1.6.1",
"unplugin": "^2.3.10",
"unplugin-ast": "^0.15.3",
"zod": "^4.1.12"
"tinyglobby": "^0.2.17",
"ufo": "^1.6.4",
"unplugin": "^3.3.0",
"unplugin-ast": "^0.17.2",
"vue-component-meta": "3.3.6",
"zod": "^4.4.3"
}
}
31 changes: 20 additions & 11 deletions packages/core/src/plugins/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { watch } from 'chokidar'
import type { VitePlugin } from 'unplugin'
import { resolve } from 'pathe'
import { joinURL } from 'ufo'
import type { ResolvedConfig } from 'vite'
import type { Component } from 'vue'
import { parseCompodiumMeta } from './meta/compodium-meta'

export function resolveCollections(options: PluginOptions, viteConfig: ResolvedConfig): Collection[] {
export function resolveCollections(options: PluginOptions, viteConfig: any): Collection[] {
const rootDir = options.rootDir ?? viteConfig.root

const exampleDir = {
Expand Down Expand Up @@ -40,7 +41,7 @@ export function resolveCollections(options: PluginOptions, viteConfig: ResolvedC
prefix: collection.prefix
},
dirs: [{
path: collection.path,
path: resolve(rootDir, collection.path),
pattern: '**/*.{vue,tsx}',
ignore: collection.ignore,
prefix: collection.prefix
Expand Down Expand Up @@ -73,26 +74,34 @@ export function collectionsPlugin(options: PluginOptions): VitePlugin {
const components = await scanComponents(col.dirs)
const examples = await scanComponents([col.exampleDir])

const collectionComponents = components.flatMap((c) => {
const componentExamples = examples?.filter(e => e.pascalName.startsWith(`${c.pascalName}Example`)).map(e => ({
const collectionComponents: Component[] = []

for (const c of components) {
const componentMeta = await parseCompodiumMeta(c.filePath)

const componentExamples = await Promise.all(examples?.filter(e => e.pascalName.startsWith(`${c.pascalName}Example`)).map(async e => ({
...e,
isExample: true,
componentPath: c.filePath
}))
componentPath: c.filePath,
componentName: c.pascalName,
collectionName: col.name,
...(await parseCompodiumMeta(e.filePath) ?? componentMeta)
})))

const mainExample = componentExamples.find(e => e.pascalName === `${c.pascalName}Example`)
const component = mainExample ?? c

// Hides third party library components if no example can be found.
if (col.name !== 'Components' && !mainExample) return []
if (col.name !== 'Components' && !mainExample) continue

return [{
collectionComponents.push({
...componentMeta,
...component,
wrapperComponent: col.wrapperComponent,
docUrl: col.getDocUrl?.(c.pascalName),
examples: componentExamples.filter(e => e.pascalName !== mainExample?.pascalName)
}]
})
})
}

return {
...col,
Expand Down
129 changes: 93 additions & 36 deletions packages/core/src/plugins/meta/checker.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,36 @@
import { createCheckerByJson } from '@compodium/meta'
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'

export function createChecker(dirs: ComponentsDir[]) {
const rootDir = process.cwd()
const metaChecker = createCheckerByJson(
rootDir,
{
extends: `${rootDir}/tsconfig.json`,
compilerOptions: {
allowArbitraryExtensions: true // Fixes Nuxt UI component type resolution
},
include: [
'**/*',
...dirs?.map((dir: any) => {
const path = typeof dir === 'string' ? dir : (dir?.path || '')
const ext = path.split('.').pop()!
return ['vue', 'ts', 'tsx', 'js', 'jsx'].includes(ext) ? path : `${path}/**/*`
}) ?? []
],
exclude: ['**/*.vue.d.ts']
},
{
forceUseTs: true,
schema: {
ignore: [
'NuxtComponentMetaNames',
'RouteLocationRaw',
'RouteLocationPathRaw',
'RouteLocationNamedRaw'
]
}
}
)
type MetaChecker = ReturnType<typeof createCheckerByJson>

const checkerOptions = {
forceUseTs: true,
schema: {
ignore: [
'NuxtComponentMetaNames',
'RouteLocationRaw',
'RouteLocationPathRaw',
'RouteLocationNamedRaw'
]
}
}

export function createChecker(dirs: ComponentsDir[], rootDir = process.cwd(), tsconfigPath?: string) {
const metaChecker = createMetaChecker(dirs, rootDir, tsconfigPath)

const checker = {
...metaChecker,
getComponentMeta: (componentPath: string): CompodiumMeta => {
const meta = metaChecker.getComponentMeta(componentPath)
const meta = getComponentMeta(metaChecker, componentPath)
return {
props: meta.props
.filter((sch: any) => !sch.global)
.filter(sch => !(/^on[A-Z]/.test(sch.name))) // Filter out event emitters (onXxx pattern)
.filter((sch: any) => !(/^on[A-Z]/.test(sch.name))) // Filter out event emitters (onXxx pattern)
.map((sch: any) => stripeTypeScriptInternalTypesSchema(sch, true))
.map(inferPropTypes),

compodium: meta.compodium,
events: meta.events.map((sch: any) => sch.name)
// exposed: meta.exposed.map(sch => stripeTypeScriptInternalTypesSchema(sch, true)),
// slots: meta.slots.map(sch => stripeTypeScriptInternalTypesSchema(sch, true))
Expand All @@ -55,13 +40,85 @@ export function createChecker(dirs: ComponentsDir[]) {
return checker
}

function createMetaChecker(dirs: ComponentsDir[], rootDir: string, tsconfigPath?: string): MetaChecker {
const tsconfig = `${rootDir}/tsconfig.json`
const tsconfigToExtend = tsconfigPath && existsSync(tsconfigPath) ? tsconfigPath : tsconfig

// Keep a JSON wrapper so library components outside Nuxt's app tsconfig include set remain inspectable.
return createCheckerByJson(
rootDir,
{
...(existsSync(tsconfigToExtend) ? { extends: tsconfigToExtend } : {}),
compilerOptions: {
allowArbitraryExtensions: true // Fixes Nuxt UI component type resolution
},
include: [
'**/*',
...dirs?.map((dir: any) => {
const path = typeof dir === 'string' ? dir : (dir?.path || '')
const ext = path.split('.').pop()!
return ['vue', 'ts', 'tsx', 'js', 'jsx'].includes(ext) ? path : `${path}/**/*`
}) ?? []
]
},
checkerOptions
)
}

function getComponentMeta(metaChecker: MetaChecker, componentPath: string) {
if (!componentPath.endsWith('.vue')) {
return metaChecker.getComponentMeta(componentPath)
}

const metaEntryPath = `${componentPath}.compodium-meta.ts`
const componentImportPath = `./${basename(componentPath)}`
// vue-component-meta needs a named export to inspect virtual .vue component metadata reliably.
metaChecker.updateFile(metaEntryPath, `import Component from ${JSON.stringify(componentImportPath)}\nexport const CompodiumMetaComponent = Component\nexport default Component\n`)

return metaChecker.getComponentMeta(metaEntryPath, 'CompodiumMetaComponent')
}

export function isNativeBrowserType(typeName: string): boolean {
const nativeTypes = [
// HTML Elements
'HTMLElement', 'HTMLCanvasElement', 'HTMLDivElement', 'HTMLSpanElement',
'HTMLInputElement', 'HTMLButtonElement', 'HTMLFormElement', 'HTMLImageElement',
'HTMLAnchorElement', 'HTMLLinkElement', 'HTMLScriptElement', 'HTMLStyleElement',
'HTMLTableElement', 'HTMLIFrameElement', 'HTMLVideoElement', 'HTMLAudioElement',
'HTMLSelectElement', 'HTMLOptionElement', 'HTMLTextAreaElement', 'HTMLLabelElement',
'HTMLSlotElement',
// DOM
'Element', 'Document', 'Window', 'Node', 'NodeList', 'HTMLCollection',
'DOMTokenList', 'NamedNodeMap', 'DocumentFragment', 'ShadowRoot',
// Events
'Event', 'MouseEvent', 'KeyboardEvent', 'FocusEvent', 'InputEvent',
'EventTarget', 'EventListener',
// Canvas/WebGL
'CanvasRenderingContext2D', 'WebGLRenderingContext', 'WebGL2RenderingContext',
'ImageBitmap', 'OffscreenCanvas',
// Media
'MediaStream', 'MediaStreamTrack', 'MediaRecorder',
// Storage/Data
'Storage', 'SessionStorage', 'LocalStorage', 'DOMStringMap',
// Node.js types
'Buffer', 'Process', 'Stream'
]
return nativeTypes.includes(typeName)
}

export function stripeTypeScriptInternalTypesSchema(type: any, topLevel: boolean = true): any {
if (!type) {
return type
}

if (type?.name?.startsWith('__')) return false

// Check if this type's schema is a native browser/Node type
if (type.schema && typeof type.schema === 'object' && type.schema.kind === 'object'
&& typeof type.schema.type === 'string' && isNativeBrowserType(type.schema.type)) {
return false
}

if (!topLevel && type.declarations && type.declarations.find((d: any) => d.file.includes('node_modules/typescript') || d.file.includes('@vue/runtime-core'))) {
return false
}
Expand Down
Loading
Loading