Nuxt/packages/kit/src/components.ts

76 lines
2.7 KiB
TypeScript

import { kebabCase, pascalCase } from 'scule'
import type { Component, ComponentsDir } from '@nuxt/schema'
import { useNuxt } from './context'
import { assertNuxtCompatibility } from './compatibility'
import { logger } from './logger'
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export async function addComponentsDir (dir: ComponentsDir, opts: { prepend?: boolean } = {}) {
const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
dir.priority ||= 0
nuxt.hook('components:dirs', (dirs) => { dirs[opts.prepend ? 'unshift' : 'push'](dir) })
}
export type AddComponentOptions = { name: string, filePath: string } & Partial<Exclude<Component,
'shortPath' | 'async' | 'level' | 'import' | 'asyncImport'
>>
/**
* Register a component by its name and filePath.
*
* Requires Nuxt 2.13+
*/
export async function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
if (!opts.mode) {
const [, mode = 'all'] = opts.filePath.match(/\.(server|client)(\.\w+)*$/) || []
opts.mode = mode as 'all' | 'client' | 'server'
}
// Apply defaults
const component: Component = {
export: opts.export || 'default',
chunkName: 'components/' + kebabCase(opts.name),
global: opts.global ?? false,
kebabName: kebabCase(opts.name || ''),
pascalName: pascalCase(opts.name || ''),
prefetch: false,
preload: false,
mode: 'all',
shortPath: opts.filePath,
priority: 0,
meta: {},
...opts
}
nuxt.hook('components:extend', (components: Component[]) => {
const existingComponentIndex = components.findIndex(c => (c.pascalName === component.pascalName || c.kebabName === component.kebabName) && c.mode === component.mode)
if (existingComponentIndex !== -1) {
const existingComponent = components[existingComponentIndex]
const existingPriority = existingComponent.priority ?? 0
const newPriority = component.priority ?? 0
if (newPriority < existingPriority) { return }
// We override where new component priority is equal or higher
// but we warn if they are equal.
if (newPriority === existingPriority) {
const name = existingComponent.pascalName || existingComponent.kebabName
logger.warn(`Overriding ${name} component. You can specify a \`priority\` option when calling \`addComponent\` to avoid this warning.`)
}
components.splice(existingComponentIndex, 1, component)
} else {
components.push(component)
}
})
}