refactor!: migrate to unimport (#3386)

This commit is contained in:
Anthony Fu 2022-03-11 16:09:11 +08:00 committed by GitHub
parent c942465f79
commit cff2f37cc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 310 additions and 479 deletions

View File

@ -1,35 +1,29 @@
import { installModule, useNuxt } from '@nuxt/kit'
import * as CompositionApi from '@vue/composition-api'
import type { Preset } from 'unimport'
import autoImports from '../../nuxt3/src/auto-imports/module'
import { vuePreset, commonPresets, appPreset } from '../../nuxt3/src/auto-imports/presets'
const UnsupportedImports = new Set(['useAsyncData', 'useFetch'])
const CapiHelpers = new Set(Object.keys(CompositionApi))
const ImportRewrites = {
vue: '@vue/composition-api'
}
export function setupAutoImports () {
const nuxt = useNuxt()
nuxt.hook('autoImports:extend', (autoImports) => {
for (const autoImport of autoImports) {
// Rewrite imports
if (autoImport.from in ImportRewrites) {
autoImport.from = ImportRewrites[autoImport.from]
}
// Disable unsupported imports
if (UnsupportedImports.has(autoImport.name)) {
autoImport.disabled = true
}
if (autoImport.from === '@vue/composition-api' && !CapiHelpers.has(autoImport.name)) {
autoImport.disabled = true
}
const bridgePresets: Preset[] = [
...commonPresets,
{
from: '#app',
imports: [
...appPreset.imports.filter(i => !UnsupportedImports.has(i as string)),
'useNuxt2Meta'
]
},
{
from: '@vue/composition-api',
imports: vuePreset.imports.filter(i => CapiHelpers.has(i as string))
}
]
// Add bridge-only auto-imports
autoImports.push({ name: 'useNuxt2Meta', as: 'useNuxt2Meta', from: '#app' })
})
nuxt.hook('modules:done', () => installModule(autoImports))
nuxt.hook('modules:done', () => installModule(autoImports, { presets: bridgePresets }))
}

View File

@ -1,30 +0,0 @@
import * as CompositionApi from '@vue/composition-api'
import { expect, describe, it } from 'vitest'
import { Nuxt3AutoImports } from '../../nuxt3/src/auto-imports/imports'
const excludedVueHelpers = [
'EffectScope',
'createApp',
'createRef',
'default',
'del',
'isRaw',
'set',
'useCSSModule',
'version',
'warn',
'watchPostEffect',
'watchSyncEffect'
]
describe('auto-imports:vue', () => {
for (const name of Object.keys(CompositionApi)) {
if (excludedVueHelpers.includes(name)) {
continue
}
it(`should register ${name} globally`, () => {
expect(Nuxt3AutoImports.find(a => a.from === 'vue').names).to.include(name)
})
}
})

View File

@ -29,6 +29,7 @@
"scule": "^0.2.1",
"semver": "^7.3.5",
"unctx": "^1.0.2",
"unimport": "0.0.8",
"untyped": "^0.4.2"
},
"devDependencies": {

View File

@ -1,14 +1,12 @@
import type { AutoImport } from '../../schema/src/types/imports'
import { Import } from 'unimport'
import { useNuxt } from './context'
import { assertNuxtCompatibility } from './compatibility'
export function addAutoImport (_autoImports: AutoImport | AutoImport[]) {
export function addAutoImport (imports: Import | Import[]) {
assertNuxtCompatibility({ bridge: true })
useNuxt().hook('autoImports:extend', (autoImports: AutoImport[]) => {
for (const composable of (Array.isArray(_autoImports) ? _autoImports : [_autoImports])) {
autoImports.push(composable)
}
useNuxt().hook('autoImports:extend', (autoImports) => {
autoImports.push(...(Array.isArray(imports) ? imports : [imports]))
})
}

View File

@ -58,6 +58,7 @@
"pathe": "^0.2.0",
"scule": "^0.2.1",
"ufo": "^0.7.11",
"unimport": "0.0.8",
"unplugin": "^0.4.0",
"untyped": "^0.4.2",
"vue": "^3.2.31",

View File

@ -2,44 +2,45 @@ import { promises as fsp, existsSync } from 'fs'
import { parse as parsePath } from 'pathe'
import { findExports } from 'mlly'
import { camelCase } from 'scule'
import { AutoImport } from '@nuxt/schema'
import { resolveFiles } from '@nuxt/kit'
import { filterInPlace } from './utils'
import { Unimport } from 'unimport'
export async function scanForComposables (dir: string | string[], autoImports: AutoImport[]) {
export async function scanForComposables (dir: string | string[], ctx: Unimport) {
const performScan = async (entry: string) => {
const files = await resolveFiles(entry, [
'*.{ts,js,mjs,cjs,mts,cts}',
'*/index.{ts,js,mjs,cjs,mts,cts}'
])
await Promise.all(
files.map(async (path) => {
// Remove original entries from the same import (for build watcher)
filterInPlace(autoImports, i => i.from !== path)
await ctx.modifyDynamicImports(async (dynamicImports) => {
await Promise.all(
files.map(async (path) => {
// Remove original entries from the same import (for build watcher)
filterInPlace(dynamicImports, i => i.from !== path)
const code = await fsp.readFile(path, 'utf-8')
const exports = findExports(code)
const defaultExport = exports.find(i => i.type === 'default')
const code = await fsp.readFile(path, 'utf-8')
const exports = findExports(code)
const defaultExport = exports.find(i => i.type === 'default')
if (defaultExport) {
let name = parsePath(path).name
if (name === 'index') {
name = parsePath(path.split('/').slice(0, -1).join('/')).name
}
autoImports.push({ name: 'default', as: camelCase(name), from: path })
}
for (const exp of exports) {
if (exp.type === 'named') {
for (const name of exp.names) {
autoImports.push({ name, as: name, from: path })
if (defaultExport) {
let name = parsePath(path).name
if (name === 'index') {
name = parsePath(path.split('/').slice(0, -1).join('/')).name
}
} else if (exp.type === 'declaration') {
autoImports.push({ name: exp.name, as: exp.name, from: path })
dynamicImports.push({ name: 'default', as: camelCase(name), from: path })
}
}
})
)
for (const exp of exports) {
if (exp.type === 'named') {
for (const name of exp.names) {
dynamicImports.push({ name, as: name, from: path })
}
} else if (exp.type === 'declaration') {
dynamicImports.push({ name: exp.name, as: exp.name, from: path })
}
}
})
)
})
}
for (const entry of Array.isArray(dir) ? dir : [dir]) {
@ -48,3 +49,12 @@ export async function scanForComposables (dir: string | string[], autoImports: A
await performScan(entry)
}
}
function filterInPlace<T> (arr: T[], predicate: (v: T) => any) {
let i = arr.length
while (i--) {
if (!predicate(arr[i])) {
arr.splice(i, 1)
}
}
}

View File

@ -1,49 +0,0 @@
import type { AutoImport } from '@nuxt/schema'
import escapeRE from 'escape-string-regexp'
export interface AutoImportContext {
autoImports: AutoImport[]
matchRE: RegExp
transform: {
exclude: RegExp[]
}
map: Map<string, AutoImport>
}
export function createAutoImportContext (opts): AutoImportContext {
return {
autoImports: [],
map: new Map(),
matchRE: /__never__/,
transform: {
exclude: opts.transform.exclude || [/node_modules/]
}
}
}
export function updateAutoImportContext (ctx: AutoImportContext) {
// Detect duplicates
const usedNames = new Set()
for (const autoImport of ctx.autoImports) {
if (usedNames.has(autoImport.as)) {
autoImport.disabled = true
console.warn(`Disabling duplicate auto import '${autoImport.as}' (imported from '${autoImport.from}')`)
} else {
usedNames.add(autoImport.as)
}
}
// Filter out disabled auto imports
ctx.autoImports = ctx.autoImports.filter(i => i.disabled !== true)
// Create regex
ctx.matchRE = new RegExp(`\\b(${ctx.autoImports.map(i => escapeRE(i.as)).join('|')})\\b`, 'g')
// Create map
ctx.map.clear()
for (const autoImport of ctx.autoImports) {
ctx.map.set(autoImport.as, autoImport)
}
return ctx
}

View File

@ -1,113 +0,0 @@
import type { AutoImportSource } from '@nuxt/schema'
export const Nuxt3AutoImports: AutoImportSource[] = [
// #app
{
from: '#app',
names: [
'useAsyncData',
'useLazyAsyncData',
'defineNuxtComponent',
'useNuxtApp',
'defineNuxtPlugin',
'useRuntimeConfig',
'useState',
'useFetch',
'useLazyFetch',
'useCookie',
'useRequestHeaders',
'useRouter',
'useRoute',
'defineNuxtRouteMiddleware',
'navigateTo',
'abortNavigation',
'addRouteMiddleware'
]
},
// #meta
{
from: '#meta',
names: [
'useMeta'
]
},
// vue-demi (mocked)
{
from: 'vue-demi',
names: [
'isVue2',
'isVue3'
]
},
// vue
{
from: 'vue',
names: [
// <script setup>
'withCtx',
'withDirectives',
'withKeys',
'withMemo',
'withModifiers',
'withScopeId',
// Lifecycle
'onActivated',
'onBeforeMount',
'onBeforeUnmount',
'onBeforeUpdate',
'onDeactivated',
'onErrorCaptured',
'onMounted',
'onRenderTracked',
'onRenderTriggered',
'onServerPrefetch',
'onUnmounted',
'onUpdated',
// Reactivity
'computed',
'customRef',
'isProxy',
'isReactive',
'isReadonly',
'isRef',
'markRaw',
'proxyRefs',
'reactive',
'readonly',
'ref',
'shallowReactive',
'shallowReadonly',
'shallowRef',
'toRaw',
'toRef',
'toRefs',
'triggerRef',
'unref',
'watch',
'watchEffect',
'isShallow',
// effect
'effect',
'effectScope',
'getCurrentScope',
'onScopeDispose',
// Component
'defineComponent',
'defineAsyncComponent',
'getCurrentInstance',
'h',
'inject',
'nextTick',
'provide',
'useAttrs',
'useCssModule',
'useCssVars',
'useSlots',
'useTransitionState'
] as Array<keyof typeof import('vue')>
}
]

View File

@ -1,21 +1,20 @@
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, resolveAlias, addPluginTemplate, useNuxt } from '@nuxt/kit'
import type { AutoImportsOptions } from '@nuxt/schema'
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, resolveAlias, useNuxt, addPluginTemplate, logger } from '@nuxt/kit'
import { isAbsolute, join, relative, resolve, normalize } from 'pathe'
import { genDynamicImport } from 'knitwork'
import { createUnimport, Import, toImports, Unimport } from 'unimport'
import { AutoImportsOptions, ImportPresetWithDeperection } from '@nuxt/schema'
import { TransformPlugin } from './transform'
import { Nuxt3AutoImports } from './imports'
import { defaultPresets } from './presets'
import { scanForComposables } from './composables'
import { toExports, toImports } from './utils'
import { AutoImportContext, createAutoImportContext, updateAutoImportContext } from './context'
export default defineNuxtModule<AutoImportsOptions>({
export default defineNuxtModule<Partial<AutoImportsOptions>>({
meta: {
name: 'auto-imports',
configKey: 'autoImports'
},
defaults: {
sources: Nuxt3AutoImports,
presets: defaultPresets,
global: false,
imports: [],
dirs: [],
transform: {
exclude: undefined
@ -23,13 +22,23 @@ export default defineNuxtModule<AutoImportsOptions>({
},
async setup (options, nuxt) {
// Allow modules extending sources
await nuxt.callHook('autoImports:sources', options.sources)
await nuxt.callHook('autoImports:sources', options.presets as ImportPresetWithDeperection[])
options.presets.forEach((i: ImportPresetWithDeperection) => {
if (typeof i !== 'string' && i.names && !i.imports) {
i.imports = i.names
logger.warn('auto-imports: presets.names is deprecated, use presets.imports instead')
}
})
// Filter disabled sources
options.sources = options.sources.filter(source => source.disabled !== true)
// options.sources = options.sources.filter(source => source.disabled !== true)
// Create a context to share state between module internals
const ctx = createAutoImportContext(options)
const ctx = createUnimport({
presets: defaultPresets,
imports: options.imports
})
// composables/ dirs
let composablesDirs = [
@ -51,7 +60,7 @@ export default defineNuxtModule<AutoImportsOptions>({
// Support for importing from '#imports'
addTemplate({
filename: 'imports.mjs',
getContents: () => toExports(ctx.autoImports)
getContents: () => ctx.toExports()
})
nuxt.options.alias['#imports'] = join(nuxt.options.buildDir, 'imports')
@ -62,30 +71,25 @@ export default defineNuxtModule<AutoImportsOptions>({
addPluginTemplate({
filename: 'auto-imports.mjs',
getContents: () => {
const imports = toImports(ctx.autoImports)
const globalThisSet = ctx.autoImports.map(i => `globalThis.${i.as} = ${i.as};`).join('\n')
return `${imports}\n\n${globalThisSet}\n\nexport default () => {};`
const imports = ctx.getImports()
const importStatement = toImports(imports)
const globalThisSet = imports.map(i => `globalThis.${i.as} = ${i.as};`).join('\n')
return `${importStatement}\n\n${globalThisSet}\n\nexport default () => {};`
}
})
} else {
// Transform to inject imports in production mode
addVitePlugin(TransformPlugin.vite(ctx))
addWebpackPlugin(TransformPlugin.webpack(ctx))
addVitePlugin(TransformPlugin.vite({ ctx, options }))
addWebpackPlugin(TransformPlugin.webpack({ ctx, options }))
}
const regenerateAutoImports = async () => {
// Resolve autoimports from sources
ctx.autoImports = options.sources.flatMap(source => source.names.map(
importName => typeof importName === 'string'
? { name: importName, as: importName, from: source.from }
: { name: importName.name, as: importName.as || importName.name, from: source.from }
))
// Scan composables/
await scanForComposables(composablesDirs, ctx.autoImports)
await scanForComposables(composablesDirs, ctx)
// Allow modules extending
await nuxt.callHook('autoImports:extend', ctx.autoImports)
// Update context
updateAutoImportContext(ctx)
await ctx.modifyDynamicImports(async (imports) => {
await nuxt.callHook('autoImports:extend', imports)
})
}
await regenerateAutoImports()
@ -113,37 +117,34 @@ export default defineNuxtModule<AutoImportsOptions>({
}
})
function addDeclarationTemplates (ctx: AutoImportContext) {
function addDeclarationTemplates (ctx: Unimport) {
const nuxt = useNuxt()
// Remove file extension for benefit of TypeScript
const stripExtension = (path: string) => path.replace(/\.[a-z]+$/, '')
const resolved = {}
const r = (id: string) => {
if (resolved[id]) { return resolved[id] }
let path = resolveAlias(id)
const r = ({ from }: Import) => {
if (resolved[from]) {
return resolved[from]
}
let path = resolveAlias(from)
if (isAbsolute(path)) {
path = relative(join(nuxt.options.buildDir, 'types'), path)
}
path = stripExtension(path)
resolved[id] = path
resolved[from] = path
return path
}
addTemplate({
filename: 'imports.d.ts',
getContents: () => toExports(ctx.autoImports.map(i => ({ ...i, from: stripExtension(i.from) })))
getContents: () => ctx.toExports()
})
addTemplate({
filename: 'types/auto-imports.d.ts',
getContents: () => `// Generated by auto imports
declare global {
${ctx.autoImports.map(i => ` const ${i.as}: typeof ${genDynamicImport(r(i.from), { wrapper: false })}['${i.name}']`).join('\n')}
}
export {}
`
getContents: () => '// Generated by auto imports\n' + ctx.generateTypeDecarations(r)
})
}

View File

@ -0,0 +1,120 @@
import { defineUnimportPreset } from 'unimport'
export const commonPresets = [
// #meta
defineUnimportPreset({
from: '#meta',
imports: [
'useMeta'
]
}),
// vue-demi (mocked)
defineUnimportPreset({
from: 'vue-demi',
imports: [
'isVue2',
'isVue3'
]
})
]
export const appPreset = defineUnimportPreset({
from: '#app',
imports: [
'useAsyncData',
'useLazyAsyncData',
'defineNuxtComponent',
'useNuxtApp',
'defineNuxtPlugin',
'useRuntimeConfig',
'useState',
'useFetch',
'useLazyFetch',
'useCookie',
'useRequestHeaders',
'useRouter',
'useRoute',
'defineNuxtRouteMiddleware',
'navigateTo',
'abortNavigation',
'addRouteMiddleware'
]
})
// vue
export const vuePreset = defineUnimportPreset({
from: 'vue',
imports: [
// <script setup>
'withCtx',
'withDirectives',
'withKeys',
'withMemo',
'withModifiers',
'withScopeId',
// Lifecycle
'onActivated',
'onBeforeMount',
'onBeforeUnmount',
'onBeforeUpdate',
'onDeactivated',
'onErrorCaptured',
'onMounted',
'onRenderTracked',
'onRenderTriggered',
'onServerPrefetch',
'onUnmounted',
'onUpdated',
// Reactivity
'computed',
'customRef',
'isProxy',
'isReactive',
'isReadonly',
'isRef',
'markRaw',
'proxyRefs',
'reactive',
'readonly',
'ref',
'shallowReactive',
'shallowReadonly',
'shallowRef',
'toRaw',
'toRef',
'toRefs',
'triggerRef',
'unref',
'watch',
'watchEffect',
'isShallow',
// effect
'effect',
'effectScope',
'getCurrentScope',
'onScopeDispose',
// Component
'defineComponent',
'defineAsyncComponent',
'getCurrentInstance',
'h',
'inject',
'nextTick',
'provide',
'useAttrs',
'useCssModule',
'useCssVars',
'useSlots',
'useTransitionState'
] as Array<keyof typeof import('vue')>
})
export const defaultPresets = [
...commonPresets,
appPreset,
vuePreset
]

View File

@ -1,37 +1,9 @@
import { createUnplugin } from 'unplugin'
import { parseQuery, parseURL } from 'ufo'
import { toImports } from './utils'
import { AutoImportContext } from './context'
import { Unimport } from 'unimport'
import { AutoImportsOptions } from '@nuxt/schema'
const excludeRE = [
// imported from other module
/\bimport\s*([\s\S]+?)\s*from\b/g,
// defined as function
/\bfunction\s*([\w_$]+?)\s*\(/g,
// defined as local variable
/\b(?:const|let|var)\s+?(\[[\s\S]*?\]|\{[\s\S]*?\}|[\s\S]+?)\s*?[=;\n]/g
]
const importAsRE = /^.*\sas\s+/
const separatorRE = /[,[\]{}\n]/g
const multilineCommentsRE = /\/\*\s(.|[\r\n])*?\*\//gm
const singlelineCommentsRE = /\/\/\s.*$/gm
const templateLiteralRE = /\$\{(.*)\}/g
const quotesRE = [
/(["'])((?:\\\1|(?!\1)|.|\r)*?)\1/gm,
/([`])((?:\\\1|(?!\1)|.|\n|\r)*?)\1/gm
]
function stripCommentsAndStrings (code: string) {
return code
.replace(multilineCommentsRE, '')
.replace(singlelineCommentsRE, '')
.replace(templateLiteralRE, '` + $1 + `')
.replace(quotesRE[0], '""')
.replace(quotesRE[1], '``')
}
export const TransformPlugin = createUnplugin((ctx: AutoImportContext) => {
export const TransformPlugin = createUnplugin(({ ctx, options }: {ctx: Unimport, options: Partial<AutoImportsOptions> }) => {
return {
name: 'nuxt:auto-imports-transform',
enforce: 'post',
@ -39,8 +11,10 @@ export const TransformPlugin = createUnplugin((ctx: AutoImportContext) => {
const { pathname, search } = parseURL(id)
const { type, macro } = parseQuery(search)
const exclude = options.transform?.exclude || [/[\\/]node_modules[\\/]/]
// Exclude node_modules by default
if (ctx.transform.exclude.some(pattern => id.match(pattern))) {
if (exclude.some(pattern => id.match(pattern))) {
return false
}
@ -57,35 +31,15 @@ export const TransformPlugin = createUnplugin((ctx: AutoImportContext) => {
return true
}
},
transform (code) {
// strip comments so we don't match on them
const stripped = stripCommentsAndStrings(code)
// find all possible injection
const matched = new Set(Array.from(stripped.matchAll(ctx.matchRE)).map(i => i[1]))
// remove those already defined
for (const regex of excludeRE) {
Array.from(stripped.matchAll(regex))
.flatMap(i => [
...(i[1]?.split(separatorRE) || []),
...(i[2]?.split(separatorRE) || [])
])
.map(i => i.replace(importAsRE, '').trim())
.forEach(i => matched.delete(i))
async transform (_code, id) {
const { code, s } = await ctx.injectImports(_code)
if (code === _code) {
return
}
if (!matched.size) {
return null
return {
code,
map: s.generateMap({ source: id, includeContent: true })
}
// For webpack4/bridge support
const isCJSContext = stripped.includes('require(')
const matchedImports = Array.from(matched).map(name => ctx.map.get(name)).filter(Boolean)
const imports = toImports(matchedImports, isCJSContext)
return imports + code
}
}
})

View File

@ -1,47 +0,0 @@
import type { AutoImport } from '@nuxt/schema'
import { genExport, genImport, genString } from 'knitwork'
export function toImportModuleMap (autoImports: AutoImport[], isCJS = false) {
const aliasKeyword = isCJS ? ' : ' : ' as '
const map: Record<string, Set<string>> = {}
for (const autoImport of autoImports) {
if (!map[autoImport.from]) {
map[autoImport.from] = new Set()
}
map[autoImport.from].add(
autoImport.name === autoImport.as
? autoImport.name
: autoImport.name + aliasKeyword + autoImport.as
)
}
return map
}
export function toImports (autoImports: AutoImport[], isCJS = false) {
const map = toImportModuleMap(autoImports, isCJS)
if (isCJS) {
return Object.entries(map)
.map(([name, imports]) => `const { ${Array.from(imports).join(', ')} } = require(${genString(name)});`)
.join('\n')
} else {
return Object.entries(map)
.map(([name, imports]) => genImport(name, Array.from(imports)))
.join('\n')
}
}
export function toExports (autoImports: AutoImport[]) {
const map = toImportModuleMap(autoImports, false)
return Object.entries(map)
.map(([name, imports]) => genExport(name, Array.from(imports)))
.join('\n')
}
export function filterInPlace<T> (arr: T[], predicate: (v: T) => any) {
let i = arr.length
while (i--) {
if (!predicate(arr[i])) {
arr.splice(i, 1)
}
}
}

View File

@ -1,35 +1,32 @@
import { readFileSync } from 'fs'
import type { AutoImport } from '@nuxt/schema'
import { expect, describe, it } from 'vitest'
import { join } from 'pathe'
import { createCommonJS, findExports } from 'mlly'
import * as VueFunctions from 'vue'
import { AutoImportContext, updateAutoImportContext } from '../src/auto-imports/context'
import { createUnimport, Import } from 'unimport'
import { TransformPlugin } from '../src/auto-imports/transform'
import { Nuxt3AutoImports } from '../src/auto-imports/imports'
import { defaultPresets } from '../src/auto-imports/presets'
describe('auto-imports:transform', () => {
const autoImports: AutoImport[] = [
const imports: Import[] = [
{ name: 'ref', as: 'ref', from: 'vue' },
{ name: 'computed', as: 'computed', from: 'bar' },
{ name: 'foo', as: 'foo', from: 'excluded' }
]
const ctx = {
autoImports,
map: new Map(),
transform: {
exclude: [/excluded/]
}
} as AutoImportContext
updateAutoImportContext(ctx)
const ctx = createUnimport({
imports
})
const transformPlugin = TransformPlugin.raw(ctx, { framework: 'rollup' })
const transform = (code: string) => transformPlugin.transform.call({ error: null, warn: null }, code, '')
const transformPlugin = TransformPlugin.raw({ ctx, options: { transform: { exclude: [/node_modules/] } } }, { framework: 'rollup' })
const transform = async (source: string) => {
const { code } = await transformPlugin.transform.call({ error: null, warn: null }, source, '') || { code: null }
return code
}
it('should correct inject', async () => {
expect(await transform('const a = ref(0)')).to.equal('import { ref } from "vue";const a = ref(0)')
expect(await transform('import { computed as ref } from "foo"; const a = ref(0)')).to.include('import { computed } from "bar";')
expect(await transform('const a = ref(0)')).toMatchInlineSnapshot('"import { ref } from \'vue\';const a = ref(0)"')
expect(await transform('import { computed as ref } from "foo"; const a = ref(0)')).to.toMatchInlineSnapshot('"import { computed } from \'bar\';import { computed as ref } from \\"foo\\"; const a = ref(0)"')
})
it('should ignore existing imported', async () => {
@ -43,11 +40,14 @@ describe('auto-imports:transform', () => {
it('should ignore comments', async () => {
const result = await transform('// import { computed } from "foo"\n;const a = computed(0)')
expect(result).to.equal('import { computed } from "bar";// import { computed } from "foo"\n;const a = computed(0)')
expect(result).toMatchInlineSnapshot(`
"import { computed } from 'bar';// import { computed } from \\"foo\\"
;const a = computed(0)"
`)
})
it('should exclude files from transform', () => {
expect(transformPlugin.transformInclude.call({ error: null, warn: null }, 'excluded')).to.equal(false)
it('should exclude files from transform', async () => {
expect(await transform('excluded')).toEqual(null)
})
})
@ -64,7 +64,7 @@ describe('auto-imports:nuxt3', () => {
continue
}
it(`should register ${name} globally`, () => {
expect(Nuxt3AutoImports.find(a => a.from === '#app').names).to.include(name)
expect(defaultPresets.find(a => a.from === '#app').imports).to.include(name)
})
}
} catch (e) {
@ -175,7 +175,7 @@ describe('auto-imports:vue', () => {
continue
}
it(`should register ${name} globally`, () => {
expect(Nuxt3AutoImports.find(a => a.from === 'vue').names).toContain(name)
expect(defaultPresets.find(a => a.from === 'vue').imports).toContain(name)
})
}
})

View File

@ -27,7 +27,8 @@
"postcss-import-resolver": "^2.0.0",
"scule": "^0.2.1",
"std-env": "^3.0.1",
"ufo": "^0.7.11"
"ufo": "^0.7.11",
"unimport": "0.0.5"
},
"engines": {
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"

View File

@ -4,7 +4,7 @@ import type { Compiler, Configuration, Stats } from 'webpack'
import type { TSConfig } from 'pkg-types'
import type { ModuleContainer } from './module'
import type { NuxtTemplate, Nuxt, NuxtApp } from './nuxt'
import type { AutoImport, AutoImportSource } from './imports'
import type { Preset as ImportPreset, Import } from 'unimport'
import type { NuxtConfig, NuxtOptions } from './config'
import type { Component, ComponentsDir, ScanDir, ComponentsOptions } from './components'
import { NuxtCompatibility, NuxtCompatibilityIssues } from '..'
@ -52,6 +52,13 @@ export type NuxtLayout = {
file: string
}
export interface ImportPresetWithDeperection extends ImportPreset {
/**
* @deprecated renamed to `imports`
*/
names?: string[]
}
export interface NuxtHooks {
// Kit
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
@ -66,8 +73,8 @@ export interface NuxtHooks {
'pages:layouts:extend': (layouts: NuxtLayout[]) => HookResult
// Auto imports
'autoImports:sources': (autoImportSources: AutoImportSource[]) => HookResult
'autoImports:extend': (autoImports: AutoImport[]) => HookResult
'autoImports:sources': (presets: ImportPresetWithDeperection[]) => HookResult
'autoImports:extend': (imports: Import[]) => HookResult
'autoImports:dirs': (dirs: string[]) => HookResult
// Components

View File

@ -1,66 +1,9 @@
export type IdentifierMap = Record<string, string>
export type Identifiers = [string, string][]
import { UnimportOptions } from 'unimport'
export interface AutoImport {
/**
* Export name to be imported
*
*/
name: string
/**
* Import as this name
*/
as: string
/**
* Module specifier to import from
*/
from: string
/**
* Disable auto import
*/
disabled?: boolean
}
export interface AutoImportSource {
/**
* Exports from module for auto-import
*
*/
names: (string | { name: string, as?: string })[]
/**
* Module specifier to import from
*/
from: string
/**
* Disable auto import source
*/
disabled?: boolean
}
export interface AutoImportsOptions {
/**
* Auto import sources
*/
sources?: AutoImportSource[]
/**
* [experimental] Use globalThis injection instead of transform for development
*/
global?: boolean
/**
* Additional directories to scan composables from
*
* By default <rootDir>/composables is added
*/
export interface AutoImportsOptions extends UnimportOptions {
dirs?: string[]
transform: {
/**
* Exclusion patterns for transforming files
*
* By default [/node_modules/] will be used
*/
global?: boolean,
transform?: {
exclude?: RegExp[]
}
}

View File

@ -2986,6 +2986,7 @@ __metadata:
semver: ^7.3.5
unbuild: latest
unctx: ^1.0.2
unimport: 0.0.8
untyped: ^0.4.2
languageName: unknown
linkType: soft
@ -3148,6 +3149,7 @@ __metadata:
std-env: ^3.0.1
ufo: ^0.7.11
unbuild: latest
unimport: 0.0.5
languageName: unknown
linkType: soft
@ -13829,6 +13831,15 @@ __metadata:
languageName: node
linkType: hard
"magic-string@npm:^0.26.0":
version: 0.26.0
resolution: "magic-string@npm:0.26.0"
dependencies:
sourcemap-codec: ^1.4.8
checksum: 2065de79cdbb76fe1b073244d51a2094cabf317e95a4ce385d7ddbe1d1f16b864c0f2a1ae514872251e64edc44bf56c8bb6a7dbe5eff9c9c236f877bcdfec9f9
languageName: node
linkType: hard
"magic-string@npm:^0.26.1":
version: 0.26.1
resolution: "magic-string@npm:0.26.1"
@ -15496,6 +15507,7 @@ __metadata:
scule: ^0.2.1
ufo: ^0.7.11
unbuild: latest
unimport: 0.0.8
unplugin: ^0.4.0
untyped: ^0.4.2
vue: ^3.2.31
@ -21184,6 +21196,34 @@ __metadata:
languageName: node
linkType: hard
"unimport@npm:0.0.5":
version: 0.0.5
resolution: "unimport@npm:0.0.5"
dependencies:
"@rollup/pluginutils": ^4.1.2
escape-string-regexp: ^5.0.0
local-pkg: ^0.4.1
magic-string: ^0.26.0
mlly: ^0.4.3
unplugin: ^0.3.3
checksum: 6514320ff1771f699dc6c69b7a7086fe6a62ca1ece7a4877798e89964bf3b9b2450631266a49dfa5785ba8939e4f30517333e4639ecf44e97077fb3128a946d5
languageName: node
linkType: hard
"unimport@npm:0.0.8":
version: 0.0.8
resolution: "unimport@npm:0.0.8"
dependencies:
"@rollup/pluginutils": ^4.1.2
escape-string-regexp: ^5.0.0
local-pkg: ^0.4.1
magic-string: ^0.26.0
mlly: ^0.4.3
unplugin: ^0.3.3
checksum: ac6e2ef860b274c28093117c23071aaa35f484f88ff23cd25a4f4d114c24e72969f46f729b26023546ffdc1016ef42e5fc399d14389c0c0374f4a74b17ff1955
languageName: node
linkType: hard
"union-value@npm:^1.0.0":
version: 1.0.1
resolution: "union-value@npm:1.0.1"
@ -21303,7 +21343,7 @@ __metadata:
languageName: node
linkType: hard
"unplugin@npm:^0.3.0, unplugin@npm:^0.3.2":
"unplugin@npm:^0.3.0, unplugin@npm:^0.3.2, unplugin@npm:^0.3.3":
version: 0.3.3
resolution: "unplugin@npm:0.3.3"
dependencies: