Nuxt/packages/nuxt3/src/components/module.ts

109 lines
3.9 KiB
TypeScript
Raw Normal View History

import { statSync } from 'fs'
import { resolve, relative } from 'upath'
import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
import { distDir } from '../dirs'
import { scanComponents } from './scan'
import type { Component, ComponentsDir } from './types'
import { loaderPlugin } from './loader'
const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string'
const isDirectory = (p: string) => { try { return statSync(p).isDirectory() } catch (_e) { return false } }
export default defineNuxtModule({
name: 'components',
defaults: {
dirs: ['~/components']
},
setup (options, nuxt) {
let componentDirs = []
let components: Component[] = []
// Resolve dirs
nuxt.hook('app:resolve', async () => {
await nuxt.callHook('components:dirs', options.dirs)
componentDirs = options.dirs.filter(isPureObjectOrString).map((dir) => {
const dirOptions: ComponentsDir = typeof dir === 'object' ? dir : { path: dir }
const dirPath = resolveAlias(dirOptions.path, nuxt.options.alias)
const transpile = typeof dirOptions.transpile === 'boolean' ? dirOptions.transpile : 'auto'
const extensions = dirOptions.extensions || ['vue'] // TODO: nuxt extensions and strip leading dot
dirOptions.level = Number(dirOptions.level || 0)
const present = isDirectory(dirPath)
if (!present && dirOptions.path !== '~/components') {
// eslint-disable-next-line no-console
console.warn('Components directory not found: `' + dirPath + '`')
}
return {
...dirOptions,
// TODO: https://github.com/nuxt/framework/pull/251
enabled: true,
path: dirPath,
extensions,
pattern: dirOptions.pattern || `**/*.{${extensions.join(',')},}`,
ignore: [
'**/*.stories.{js,ts,jsx,tsx}', // ignore storybook files
'**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}', // ignore mixins
'**/*.d.ts', // .d.ts files
// TODO: support nuxt ignore patterns
...(dirOptions.ignore || [])
],
transpile: (transpile === 'auto' ? dirPath.includes('node_modules') : transpile)
}
}).filter(d => d.enabled)
nuxt.options.build!.transpile!.push(...componentDirs.filter(dir => dir.transpile).map(dir => dir.path))
})
// Scan components and add to plugin
nuxt.hook('app:templates', async (app) => {
components = await scanComponents(componentDirs, nuxt.options.srcDir!)
await nuxt.callHook('components:extend', components)
if (!components.length) {
return
}
app.templates.push({
filename: 'components.mjs',
2021-08-16 09:08:47 +00:00
src: resolve(distDir, 'components/runtime/components.tmpl.mjs'),
options: { components }
})
app.templates.push({
filename: 'components.d.ts',
write: true,
getContents: () => `// Generated by components discovery
declare module 'vue' {
export interface GlobalComponents {
${components.map(c => ` '${c.pascalName}': typeof import('${relative(nuxt.options.buildDir, c.filePath)}')['${c.export}']`).join(',\n')}
}\n}\n\nexport {}`
})
app.plugins.push({ src: '#build/components' })
})
nuxt.hook('prepare:types', ({ references }) => {
references.push({ path: resolve(nuxt.options.buildDir, 'components.d.ts') })
})
// Watch for changes
nuxt.hook('builder:watch', async (event, path) => {
if (!['add', 'unlink'].includes(event)) {
return
}
const fPath = resolve(nuxt.options.rootDir, path)
if (componentDirs.find(dir => fPath.startsWith(dir.path))) {
await nuxt.callHook('builder:generateApp')
}
})
if (!nuxt.options.dev) {
const options = { getComponents: () => components }
addWebpackPlugin(loaderPlugin.webpack(options))
addVitePlugin(loaderPlugin.vite(options))
}
}
})