Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
/frameworkPublic archive
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/nuxt/src/auto-imports/transform.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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') &&
Expand Down
105 changes: 59 additions & 46 deletions packages/nuxt/src/components/loader.ts
Original file line numberDiff line numberDiff line change
@@ -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'
Expand All@@ -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<string>()
const map = new Map<Component, string>()
const s = new MagicString(code)
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
}

// 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
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()

let num = 0
const imports = new Set<string>()
const map = new Map<Component, string>()
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
})
// no matched
return full
})

if (imports.size) {
s.prepend([...imports, ''].join('\n'))
}
if (imports.size) {
s.prepend([...imports, ''].join('\n'))
}

if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
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, '')
Expand Down
5 changes: 5 additions & 0 deletions packages/schema/src/types/components.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -105,4 +105,9 @@ export interface ComponentsOptions {
*/
global?: boolean
loader?: boolean

transform?: {
exclude?: RegExp[]
include?: RegExp[]
}
}
1 change: 1 addition & 0 deletions packages/schema/src/types/imports.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,5 +5,6 @@ export interface AutoImportsOptions extends UnimportOptions {
global?: boolean,
transform?: {
exclude?: RegExp[]
include?: RegExp[]
}
}