mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
Merge cf14552c5e
into edc299a043
This commit is contained in:
commit
72ded1b5e7
45
docs/2.guide/2.directory-structure/1.directives.md
Normal file
45
docs/2.guide/2.directory-structure/1.directives.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: "directives"
|
||||
head.title: "directives/"
|
||||
description: "The directives/ directory is where you put all your Vue directives."
|
||||
navigation.icon: i-ph-folder
|
||||
---
|
||||
|
||||
Nuxt automatically imports any directive in this directory (along with directives that are registered by any modules you may be using).
|
||||
|
||||
```bash [Directory Structure]
|
||||
| directives/
|
||||
--| awesome.ts
|
||||
--| focus.ts
|
||||
```
|
||||
|
||||
```html [app.vue]
|
||||
<template>
|
||||
<div v-awesome v-focus>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Directives
|
||||
|
||||
You must use always named exports for your directives.
|
||||
|
||||
If you have SSR enabled, you must use object notation in your directives. Otherwise, Vue will throw an error about missing `getSSRProps`.
|
||||
|
||||
```ts
|
||||
function mounted (el: HTMLElement, binding: TouchDirectiveBinding) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// other lifecycle hooks
|
||||
|
||||
export const Focus = {
|
||||
mounted
|
||||
// ...
|
||||
}
|
||||
|
||||
// DONT' USE export default here
|
||||
export default Focus
|
||||
```
|
||||
|
||||
:link-example{to="/docs/examples/features/auto-imports"}
|
63
packages/nuxt/src/imports/directives.ts
Normal file
63
packages/nuxt/src/imports/directives.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import type { AddonsOptions, Import } from 'unimport'
|
||||
import { createUnimport } from 'unimport'
|
||||
import type { ImportPresetWithDeprecation } from 'nuxt/schema'
|
||||
import MagicString from 'magic-string'
|
||||
import { isVue } from '../core/utils'
|
||||
|
||||
export const DirectivesPlugin = ({
|
||||
addons,
|
||||
dirs,
|
||||
imports,
|
||||
presets,
|
||||
}: {
|
||||
addons: AddonsOptions
|
||||
dirs: string[]
|
||||
imports: Import[]
|
||||
presets: ImportPresetWithDeprecation[]
|
||||
}) => createUnplugin(() => {
|
||||
const useImports: Import[] = []
|
||||
function visit (i: Import) {
|
||||
if (i.meta?.vueDirective === true) {
|
||||
useImports.push(i)
|
||||
}
|
||||
}
|
||||
imports?.forEach(visit)
|
||||
presets?.forEach((preset) => {
|
||||
if (preset && 'imports' in preset) {
|
||||
const imports = preset.imports as Import[]
|
||||
imports.forEach(visit)
|
||||
}
|
||||
})
|
||||
|
||||
const ctx = createUnimport({
|
||||
dirs,
|
||||
imports: useImports,
|
||||
addons,
|
||||
})
|
||||
|
||||
return {
|
||||
name: 'nuxt:directives-transform',
|
||||
enforce: 'post',
|
||||
transformInclude (id) {
|
||||
return isVue(id, { type: ['script', 'template'] })
|
||||
},
|
||||
async transform (code, id) {
|
||||
const s = new MagicString(code)
|
||||
|
||||
await ctx.injectImports(s, id, {
|
||||
autoImport: false,
|
||||
})
|
||||
|
||||
if (!s.hasChanged()) { return }
|
||||
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: s.generateMap(),
|
||||
}
|
||||
},
|
||||
async buildStart () {
|
||||
await ctx.init()
|
||||
},
|
||||
}
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { addBuildPlugin, addTemplate, addTypeTemplate, defineNuxtModule, isIgnored, logger, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
|
||||
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
||||
import type { Import, Unimport } from 'unimport'
|
||||
import type { AddonVueDirectivesOptions, AddonsOptions, Import, Unimport } from 'unimport'
|
||||
import { createUnimport, scanDirExports, toExports } from 'unimport'
|
||||
import type { ImportPresetWithDeprecation, ImportsOptions, ResolvedNuxtTemplate } from 'nuxt/schema'
|
||||
import escapeRE from 'escape-string-regexp'
|
||||
@ -10,6 +10,7 @@ import { lookupNodeModuleSubpath, parseNodeModulePath } from 'mlly'
|
||||
import { isDirectory } from '../utils'
|
||||
import { TransformPlugin } from './transform'
|
||||
import { defaultPresets } from './presets'
|
||||
import { DirectivesPlugin } from './directives'
|
||||
|
||||
export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
meta: {
|
||||
@ -41,14 +42,65 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
// Filter disabled sources
|
||||
// options.sources = options.sources.filter(source => source.disabled !== true)
|
||||
|
||||
let directivesDir: string[] = []
|
||||
if (options.scan) {
|
||||
for (const layer of nuxt.options._layers) {
|
||||
// Layer disabled scanning for itself
|
||||
if (layer.config?.imports?.scan === false) {
|
||||
continue
|
||||
}
|
||||
directivesDir.push(resolve(layer.config.srcDir, 'directives'))
|
||||
}
|
||||
|
||||
directivesDir = directivesDir.map(dir => normalize(dir))
|
||||
|
||||
// Restart nuxt when directives directories are added/removed
|
||||
nuxt.hook('builder:watch', (event, relativePath) => {
|
||||
if (!['addDir', 'unlinkDir'].includes(event)) { return }
|
||||
|
||||
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||
if (directivesDir.includes(path)) {
|
||||
logger.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
|
||||
return nuxt.callHook('restart')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// We need to enable vueDirectives when:
|
||||
// - vueDirectives is explicitly set to true or callback enabled
|
||||
// - autoImport is enabled: allow use directives from presets
|
||||
// We also need to resolve the directives from any layer
|
||||
function isDirectiveFactory (): true | AddonVueDirectivesOptions | undefined {
|
||||
if (!options.scan && !options.autoImport) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (!options.scan) {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
isDirective: (normalizeImportFrom: string) => {
|
||||
return directivesDir.some(dir => normalizeImportFrom.startsWith(dir))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const { addons: inlineAddons, ...rest } = options
|
||||
|
||||
const addons: AddonsOptions = {
|
||||
addons: inlineAddons && Array.isArray(inlineAddons)
|
||||
? [...inlineAddons]
|
||||
: [],
|
||||
vueDirectives: isDirectiveFactory(),
|
||||
vueTemplate: options.autoImport,
|
||||
}
|
||||
|
||||
// Create a context to share state between module internals
|
||||
const ctx = createUnimport({
|
||||
injectAtEnd: true,
|
||||
...options,
|
||||
addons: {
|
||||
vueTemplate: options.autoImport,
|
||||
...options.addons,
|
||||
},
|
||||
...rest,
|
||||
addons,
|
||||
presets,
|
||||
})
|
||||
|
||||
@ -66,6 +118,7 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
}
|
||||
composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
|
||||
composablesDirs.push(resolve(layer.config.srcDir, 'utils'))
|
||||
composablesDirs.push(resolve(layer.config.srcDir, 'directives'))
|
||||
|
||||
if (isNuxtV4) {
|
||||
composablesDirs.push(resolve(layer.config.rootDir, 'shared', 'utils'))
|
||||
@ -102,6 +155,8 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
})
|
||||
nuxt.options.alias['#imports'] = join(nuxt.options.buildDir, 'imports')
|
||||
|
||||
// Auto import directives
|
||||
addBuildPlugin(DirectivesPlugin({ addons, dirs: directivesDir, imports: options.imports ?? [], presets }))
|
||||
// Transform to inject imports in production mode
|
||||
addBuildPlugin(TransformPlugin({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||
|
||||
|
@ -242,6 +242,8 @@ const vueTypesPreset = defineUnimportPreset({
|
||||
'Component',
|
||||
'ComponentPublicInstance',
|
||||
'ComputedRef',
|
||||
'DirectiveBinding',
|
||||
'ExtractDefaultPropTypes',
|
||||
'ExtractPropTypes',
|
||||
'ExtractPublicPropTypes',
|
||||
'InjectionKey',
|
||||
@ -250,6 +252,7 @@ const vueTypesPreset = defineUnimportPreset({
|
||||
'MaybeRef',
|
||||
'MaybeRefOrGetter',
|
||||
'VNode',
|
||||
'WritableComputedRef',
|
||||
],
|
||||
})
|
||||
|
||||
|
@ -31,7 +31,7 @@ export default defineUntypedSchema({
|
||||
|
||||
/**
|
||||
* An array of custom directories that will be auto-imported.
|
||||
* Note that this option will not override the default directories (~/composables, ~/utils).
|
||||
* Note that this option will not override the default directories (~/composables, ~/utils, ~/directives).
|
||||
* @example
|
||||
* ```js
|
||||
* imports: {
|
||||
|
@ -11,7 +11,7 @@ export interface ImportsOptions extends UnimportOptions {
|
||||
/**
|
||||
* Directories to scan for auto imports.
|
||||
* @see https://nuxt.com/docs/guide/directory-structure/composables#how-files-are-scanned
|
||||
* @default ['./composables', './utils']
|
||||
* @default ['./composables', './utils', './directives']
|
||||
*/
|
||||
dirs?: string[]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user