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 }}
|
||||
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'
|
||||
with:
|
||||
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",
|
||||
"eslint": "9.15.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",
|
||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||
"happy-dom": "15.11.6",
|
||||
@ -118,7 +118,7 @@
|
||||
"vue-router": "4.4.5",
|
||||
"vue-tsc": "2.1.10"
|
||||
},
|
||||
"packageManager": "pnpm@9.13.2",
|
||||
"packageManager": "pnpm@9.14.1",
|
||||
"engines": {
|
||||
"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 { 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[]
|
||||
|
||||
|
@ -114,8 +114,8 @@ importers:
|
||||
specifier: 3.3.0
|
||||
version: 3.3.0
|
||||
eslint-plugin-perfectionist:
|
||||
specifier: 4.0.2
|
||||
version: 4.0.2(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
||||
specifier: 4.0.3
|
||||
version: 4.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)
|
||||
eslint-typegen:
|
||||
specifier: 0.3.2
|
||||
version: 0.3.2(eslint@9.15.0(jiti@2.4.0))
|
||||
@ -4373,8 +4373,8 @@ packages:
|
||||
resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
|
||||
engines: {node: '>=5.0.0'}
|
||||
|
||||
eslint-plugin-perfectionist@4.0.2:
|
||||
resolution: {integrity: sha512-zWdgyg2SdHqhp/P9d9vKwo5qD9is28xMAGzBslHqkZz5mVIikjyz1qvuJ4yS7Wrsf4KlbGorORefb4Kbe7Puzg==}
|
||||
eslint-plugin-perfectionist@4.0.3:
|
||||
resolution: {integrity: sha512-CyafnreF6boy4lf1XaF72U8NbkwrfjU/mOf1y6doaDMS9zGXhUU1DSk+ZPf/rVwCf1PL1m+rhHqFs+IcB8kDmA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
eslint: '>=8.0.0'
|
||||
@ -11519,7 +11519,7 @@ snapshots:
|
||||
|
||||
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:
|
||||
'@typescript-eslint/types': 8.15.0
|
||||
'@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