From a862a67c804c6b345c3956464dd38c06bcb960b1 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 6 May 2022 19:11:34 +0800 Subject: [PATCH] feat(auto-import): custom include option (#4834) --- packages/nuxt/src/auto-imports/transform.ts | 6 ++ packages/nuxt/src/components/loader.ts | 107 +++++++++++--------- packages/schema/src/types/components.ts | 5 + packages/schema/src/types/imports.ts | 1 + 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/packages/nuxt/src/auto-imports/transform.ts b/packages/nuxt/src/auto-imports/transform.ts index 370bf6e239..a184cef0dc 100644 --- a/packages/nuxt/src/auto-imports/transform.ts +++ b/packages/nuxt/src/auto-imports/transform.ts @@ -13,12 +13,18 @@ export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: {ctx const { type, macro } = parseQuery(search) const exclude = options.transform?.exclude || [/[\\/]node_modules[\\/]/] + const include = options.transform?.include || [] // Exclude node_modules by default if (exclude.some(pattern => id.match(pattern))) { return false } + // Custom includes + if (include.some(pattern => id.match(pattern))) { + return true + } + // vue files if ( pathname.endsWith('.vue') && diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 7d1adf4f21..b874ff9c26 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -1,7 +1,7 @@ import { pathToFileURL } from 'node:url' import { createUnplugin } from 'unplugin' import { parseQuery, parseURL } from 'ufo' -import { Component } from '@nuxt/schema' +import { Component, ComponentsOptions } from '@nuxt/schema' import { genDynamicImport, genImport } from 'knitwork' import MagicString from 'magic-string' import { pascalCase } from 'scule' @@ -10,61 +10,74 @@ interface LoaderOptions { getComponents(): Component[] mode: 'server' | 'client' sourcemap?: boolean + transform?: ComponentsOptions['transform'] } -export const loaderPlugin = createUnplugin((options: LoaderOptions) => ({ - name: 'nuxt:components-loader', - enforce: 'post', - transformInclude (id) { - const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) - const query = parseQuery(search) - // we only transform render functions - // from `type=template` (in Webpack) and bare `.vue` file (in Vite) - return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !search) - }, - transform (code, id) { - const components = options.getComponents() +export const loaderPlugin = createUnplugin((options: LoaderOptions) => { + const exclude = options.transform?.exclude || [] + const include = options.transform?.include || [] - let num = 0 - const imports = new Set() - const map = new Map() - const s = new MagicString(code) - - // replace `_resolveComponent("...")` to direct import - s.replace(/(?<=[ (])_?resolveComponent\(["'](lazy-|Lazy)?([^'"]*?)["']\)/g, (full, lazy, name) => { - const component = findComponent(components, name, options.mode) - if (component) { - const identifier = map.get(component) || `__nuxt_component_${num++}` - map.set(component, identifier) - const isClientOnly = component.mode === 'client' - if (isClientOnly) { - imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }])) - } - if (lazy) { - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) - imports.add(`const ${identifier}_lazy = __defineAsyncComponent(${genDynamicImport(component.filePath)})`) - return isClientOnly ? `createClientOnly(${identifier}_lazy)` : `${identifier}_lazy` - } else { - imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }])) - return isClientOnly ? `createClientOnly(${identifier})` : identifier - } + return { + name: 'nuxt:components-loader', + enforce: 'post', + transformInclude (id) { + if (exclude.some(pattern => id.match(pattern))) { + return false + } + if (include.some(pattern => id.match(pattern))) { + return true } - // no matched - return full - }) - if (imports.size) { - s.prepend([...imports, ''].join('\n')) - } + const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) + const query = parseQuery(search) + // we only transform render functions + // from `type=template` (in Webpack) and bare `.vue` file (in Vite) + return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !search) + }, + transform (code, id) { + const components = options.getComponents() - if (s.hasChanged()) { - return { - code: s.toString(), - map: options.sourcemap && s.generateMap({ source: id, includeContent: true }) + let num = 0 + const imports = new Set() + const map = new Map() + const s = new MagicString(code) + + // replace `_resolveComponent("...")` to direct import + s.replace(/(?<=[ (])_?resolveComponent\(["'](lazy-|Lazy)?([^'"]*?)["']\)/g, (full, lazy, name) => { + const component = findComponent(components, name, options.mode) + if (component) { + const identifier = map.get(component) || `__nuxt_component_${num++}` + map.set(component, identifier) + const isClientOnly = component.mode === 'client' + if (isClientOnly) { + imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }])) + } + if (lazy) { + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) + imports.add(`const ${identifier}_lazy = __defineAsyncComponent(${genDynamicImport(component.filePath)})`) + return isClientOnly ? `createClientOnly(${identifier}_lazy)` : `${identifier}_lazy` + } else { + imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }])) + return isClientOnly ? `createClientOnly(${identifier})` : identifier + } + } + // no matched + return full + }) + + if (imports.size) { + s.prepend([...imports, ''].join('\n')) + } + + if (s.hasChanged()) { + return { + code: s.toString(), + map: options.sourcemap && s.generateMap({ source: id, includeContent: true }) + } } } } -})) +}) function findComponent (components: Component[], name: string, mode: LoaderOptions['mode']) { const id = pascalCase(name).replace(/["']/g, '') diff --git a/packages/schema/src/types/components.ts b/packages/schema/src/types/components.ts index 57e3abe792..a4218bb645 100644 --- a/packages/schema/src/types/components.ts +++ b/packages/schema/src/types/components.ts @@ -105,4 +105,9 @@ export interface ComponentsOptions { */ global?: boolean loader?: boolean + + transform?: { + exclude?: RegExp[] + include?: RegExp[] + } } diff --git a/packages/schema/src/types/imports.ts b/packages/schema/src/types/imports.ts index 71f5635a6d..666cf9fe77 100644 --- a/packages/schema/src/types/imports.ts +++ b/packages/schema/src/types/imports.ts @@ -5,5 +5,6 @@ export interface AutoImportsOptions extends UnimportOptions { global?: boolean, transform?: { exclude?: RegExp[] + include?: RegExp[] } }