mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
Compare commits
13 Commits
72ded1b5e7
...
9df517bdf3
Author | SHA1 | Date | |
---|---|---|---|
|
9df517bdf3 | ||
|
cf14552c5e | ||
|
93722c95e5 | ||
|
e1ffa41c9f | ||
|
2461ed6fb1 | ||
|
01828a42f8 | ||
|
6012e158f4 | ||
|
c9bdb8af7f | ||
|
7244fdf0b2 | ||
|
b164e4966f | ||
|
ae77a33069 | ||
|
03bf85a185 | ||
|
7ec3c04236 |
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 { existsSync } from 'node:fs'
|
||||||
import { addBuildPlugin, addTemplate, addTypeTemplate, defineNuxtModule, isIgnored, logger, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
|
import { addBuildPlugin, addTemplate, addTypeTemplate, defineNuxtModule, isIgnored, logger, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
|
||||||
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
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 { createUnimport, scanDirExports, toExports } from 'unimport'
|
||||||
import type { ImportPresetWithDeprecation, ImportsOptions, ResolvedNuxtTemplate } from 'nuxt/schema'
|
import type { ImportPresetWithDeprecation, ImportsOptions, ResolvedNuxtTemplate } from 'nuxt/schema'
|
||||||
import escapeRE from 'escape-string-regexp'
|
import escapeRE from 'escape-string-regexp'
|
||||||
@ -10,6 +10,7 @@ import { lookupNodeModuleSubpath, parseNodeModulePath } from 'mlly'
|
|||||||
import { isDirectory } from '../utils'
|
import { isDirectory } from '../utils'
|
||||||
import { TransformPlugin } from './transform'
|
import { TransformPlugin } from './transform'
|
||||||
import { defaultPresets } from './presets'
|
import { defaultPresets } from './presets'
|
||||||
|
import { DirectivesPlugin } from './directives'
|
||||||
|
|
||||||
export default defineNuxtModule<Partial<ImportsOptions>>({
|
export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||||
meta: {
|
meta: {
|
||||||
@ -41,14 +42,65 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
|||||||
// Filter disabled sources
|
// Filter disabled sources
|
||||||
// options.sources = options.sources.filter(source => source.disabled !== true)
|
// 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
|
// Create a context to share state between module internals
|
||||||
const ctx = createUnimport({
|
const ctx = createUnimport({
|
||||||
injectAtEnd: true,
|
injectAtEnd: true,
|
||||||
...options,
|
...rest,
|
||||||
addons: {
|
addons,
|
||||||
vueTemplate: options.autoImport,
|
|
||||||
...options.addons,
|
|
||||||
},
|
|
||||||
presets,
|
presets,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -66,6 +118,7 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
|||||||
}
|
}
|
||||||
composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
|
composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
|
||||||
composablesDirs.push(resolve(layer.config.srcDir, 'utils'))
|
composablesDirs.push(resolve(layer.config.srcDir, 'utils'))
|
||||||
|
composablesDirs.push(resolve(layer.config.srcDir, 'directives'))
|
||||||
|
|
||||||
if (isNuxtV4) {
|
if (isNuxtV4) {
|
||||||
composablesDirs.push(resolve(layer.config.rootDir, 'shared', 'utils'))
|
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')
|
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
|
// Transform to inject imports in production mode
|
||||||
addBuildPlugin(TransformPlugin({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
addBuildPlugin(TransformPlugin({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||||
|
|
||||||
|
@ -242,6 +242,8 @@ const vueTypesPreset = defineUnimportPreset({
|
|||||||
'Component',
|
'Component',
|
||||||
'ComponentPublicInstance',
|
'ComponentPublicInstance',
|
||||||
'ComputedRef',
|
'ComputedRef',
|
||||||
|
'DirectiveBinding',
|
||||||
|
'ExtractDefaultPropTypes',
|
||||||
'ExtractPropTypes',
|
'ExtractPropTypes',
|
||||||
'ExtractPublicPropTypes',
|
'ExtractPublicPropTypes',
|
||||||
'InjectionKey',
|
'InjectionKey',
|
||||||
@ -250,6 +252,7 @@ const vueTypesPreset = defineUnimportPreset({
|
|||||||
'MaybeRef',
|
'MaybeRef',
|
||||||
'MaybeRefOrGetter',
|
'MaybeRefOrGetter',
|
||||||
'VNode',
|
'VNode',
|
||||||
|
'WritableComputedRef',
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export default defineUntypedSchema({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of custom directories that will be auto-imported.
|
* 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
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
* imports: {
|
* imports: {
|
||||||
|
@ -11,7 +11,7 @@ export interface ImportsOptions extends UnimportOptions {
|
|||||||
/**
|
/**
|
||||||
* Directories to scan for auto imports.
|
* Directories to scan for auto imports.
|
||||||
* @see https://nuxt.com/docs/guide/directory-structure/composables#how-files-are-scanned
|
* @see https://nuxt.com/docs/guide/directory-structure/composables#how-files-are-scanned
|
||||||
* @default ['./composables', './utils']
|
* @default ['./composables', './utils', './directives']
|
||||||
*/
|
*/
|
||||||
dirs?: string[]
|
dirs?: string[]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user