mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
Compare commits
15 Commits
9df517bdf3
...
72ded1b5e7
Author | SHA1 | Date | |
---|---|---|---|
|
72ded1b5e7 | ||
|
edc299a043 | ||
|
ad3ab4d310 | ||
|
cf14552c5e | ||
|
93722c95e5 | ||
|
e1ffa41c9f | ||
|
2461ed6fb1 | ||
|
01828a42f8 | ||
|
6012e158f4 | ||
|
c9bdb8af7f | ||
|
7244fdf0b2 | ||
|
b164e4966f | ||
|
ae77a33069 | ||
|
03bf85a185 | ||
|
7ec3c04236 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -248,7 +248,7 @@ jobs:
|
|||||||
TEST_PAYLOAD: ${{ matrix.payload }}
|
TEST_PAYLOAD: ${{ matrix.payload }}
|
||||||
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || matrix.payload == 'js' || runner.os == 'Windows' }}
|
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || matrix.payload == 'js' || runner.os == 'Windows' }}
|
||||||
|
|
||||||
- uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a # v5.0.2
|
- uses: codecov/codecov-action@985343d70564a82044c1b7fcb84c2fa05405c1a2 # v5.0.4
|
||||||
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
|
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
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"}
|
@ -91,7 +91,7 @@
|
|||||||
"devalue": "5.1.1",
|
"devalue": "5.1.1",
|
||||||
"eslint": "9.15.0",
|
"eslint": "9.15.0",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "4.0.2",
|
"eslint-plugin-perfectionist": "4.0.3",
|
||||||
"eslint-typegen": "0.3.2",
|
"eslint-typegen": "0.3.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"happy-dom": "15.11.6",
|
"happy-dom": "15.11.6",
|
||||||
@ -118,7 +118,7 @@
|
|||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-tsc": "2.1.10"
|
"vue-tsc": "2.1.10"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.13.2",
|
"packageManager": "pnpm@9.14.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.10.0 || >=18.0.0"
|
"node": "^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
|
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[]
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ importers:
|
|||||||
specifier: 3.3.0
|
specifier: 3.3.0
|
||||||
version: 3.3.0
|
version: 3.3.0
|
||||||
eslint-plugin-perfectionist:
|
eslint-plugin-perfectionist:
|
||||||
specifier: 4.0.2
|
specifier: 4.0.3
|
||||||
version: 4.0.2(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
version: 4.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
||||||
eslint-typegen:
|
eslint-typegen:
|
||||||
specifier: 0.3.2
|
specifier: 0.3.2
|
||||||
version: 0.3.2(eslint@9.15.0(jiti@2.4.0))
|
version: 0.3.2(eslint@9.15.0(jiti@2.4.0))
|
||||||
@ -4373,8 +4373,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
|
resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
|
||||||
engines: {node: '>=5.0.0'}
|
engines: {node: '>=5.0.0'}
|
||||||
|
|
||||||
eslint-plugin-perfectionist@4.0.2:
|
eslint-plugin-perfectionist@4.0.3:
|
||||||
resolution: {integrity: sha512-zWdgyg2SdHqhp/P9d9vKwo5qD9is28xMAGzBslHqkZz5mVIikjyz1qvuJ4yS7Wrsf4KlbGorORefb4Kbe7Puzg==}
|
resolution: {integrity: sha512-CyafnreF6boy4lf1XaF72U8NbkwrfjU/mOf1y6doaDMS9zGXhUU1DSk+ZPf/rVwCf1PL1m+rhHqFs+IcB8kDmA==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=8.0.0'
|
eslint: '>=8.0.0'
|
||||||
@ -11519,7 +11519,7 @@ snapshots:
|
|||||||
|
|
||||||
eslint-plugin-no-only-tests@3.3.0: {}
|
eslint-plugin-no-only-tests@3.3.0: {}
|
||||||
|
|
||||||
eslint-plugin-perfectionist@4.0.2(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3):
|
eslint-plugin-perfectionist@4.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.15.0
|
'@typescript-eslint/types': 8.15.0
|
||||||
'@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
'@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
||||||
|
Loading…
Reference in New Issue
Block a user