Compare commits

...

15 Commits

Author SHA1 Message Date
Joaquín Sánchez
72ded1b5e7
Merge cf14552c5e into edc299a043 2024-11-20 06:35:52 -05:00
renovate[bot]
edc299a043
chore(deps): update all non-major dependencies (main) (#29995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 06:35:43 -05:00
renovate[bot]
ad3ab4d310
chore(deps): update all non-major dependencies (main) (#29986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 23:36:17 -05:00
Daniel Roe
cf14552c5e
Merge remote-tracking branch 'origin/main' into feat-add-auto-import-directives 2024-11-02 23:44:08 +00:00
Daniel Roe
93722c95e5
Merge remote-tracking branch 'origin/main' into feat-add-auto-import-directives 2024-10-25 09:50:01 +01:00
userquin
e1ffa41c9f chore: update lock file 2024-09-28 11:47:19 +02:00
userquin
2461ed6fb1 Merge branch 'refs/heads/main' into feat-add-auto-import-directives
# Conflicts:
#	pnpm-lock.yaml
2024-09-28 11:46:47 +02:00
userquin
01828a42f8 chore: bump unimport to 3.13.1 2024-09-28 11:45:47 +02:00
userquin
6012e158f4 docs: update directives 2024-09-27 22:27:39 +02:00
userquin
c9bdb8af7f chore: include missing vue preset types 2024-09-27 22:14:40 +02:00
userquin
7244fdf0b2 chore: update directives plugin comment 2024-09-27 22:03:41 +02:00
userquin
b164e4966f chore: don't mix imports 2024-09-27 22:02:00 +02:00
userquin
ae77a33069 chore: fix docs 2024-09-27 21:58:53 +02:00
userquin
03bf85a185 chore: fix watch comment 2024-09-27 21:55:50 +02:00
userquin
7ec3c04236 feat(nuxt,schema): add directives folder 2024-09-27 20:25:41 +02:00
9 changed files with 182 additions and 16 deletions

View File

@ -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 }}

View 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"}

View File

@ -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"
}, },

View 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()
},
}
})

View File

@ -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 }))

View File

@ -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',
], ],
}) })

View File

@ -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: {

View File

@ -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[]

View File

@ -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)