feat(components): build time sync loader (#383)

This commit is contained in:
Anthony Fu 2021-07-28 20:11:32 +08:00 committed by GitHub
parent bb757045ec
commit 8e5e229031
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 3 deletions

View File

@ -1,5 +1,6 @@
<template> <template>
<div> <div>
<hello-world /> <hello-world />
<nuxt3/>
</div> </div>
</template> </template>

View File

@ -0,0 +1,5 @@
<template>
<b style="color: #00C58E">
From Nuxt 3
</b>
</template>

View File

@ -1,4 +1,5 @@
import { defineNuxtConfig } from '@nuxt/kit' import { defineNuxtConfig } from '@nuxt/kit'
export default defineNuxtConfig({ export default defineNuxtConfig({
vite: false
}) })

View File

@ -16,6 +16,7 @@
"globby": "^11.0.4", "globby": "^11.0.4",
"scule": "^0.2.1", "scule": "^0.2.1",
"ufo": "^0.7.7", "ufo": "^0.7.7",
"unplugin": "^0.0.5",
"upath": "^2.0.1" "upath": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -0,0 +1,47 @@
import { createUnplugin } from 'unplugin'
import { parseQuery, parseURL } from 'ufo'
import { Component } from './types'
interface LoaderOptions {
getComponents(): Component[]
}
export const loaderPlugin = createUnplugin((options: LoaderOptions) => ({
name: 'nuxt-components-loader',
enforce: 'post',
transformInclude (id) {
const { pathname, search } = parseURL(id)
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' || !search)
},
transform (code) {
return transform(code, options.getComponents())
}
}))
function findComponent (components: Component[], name:string) {
return components.find(({ pascalName, kebabName }) => [pascalName, kebabName].includes(name))
}
function transform (content: string, components: Component[]) {
let num = 0
let imports = ''
const map = new Map<Component, string>()
// replace `_resolveComponent("...")` to direct import
const newContent = content.replace(/ _resolveComponent\("(.*?)"\)/g, (full, name) => {
const component = findComponent(components, name)
if (component) {
const identifier = map.get(component) || `__nuxt_component_${num++}`
map.set(component, identifier)
imports += `import ${identifier} from "${component.filePath}";`
return ` ${identifier}`
}
// no matched
return full
})
return `${imports}\n${newContent}`
}

View File

@ -1,8 +1,9 @@
import fs from 'fs' import fs from 'fs'
import { defineNuxtModule, resolveAlias } from '@nuxt/kit' import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
import { resolve } from 'upath' import { resolve } from 'upath'
import { scanComponents } from './scan' import { scanComponents } from './scan'
import type { ComponentsDir } from './types' import type { Component, ComponentsDir } from './types'
import { loaderPlugin } from './loader'
const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string' const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string'
const isDirectory = (p: string) => { try { return fs.statSync(p).isDirectory() } catch (_e) { return false } } const isDirectory = (p: string) => { try { return fs.statSync(p).isDirectory() } catch (_e) { return false } }
@ -14,6 +15,7 @@ export default defineNuxtModule({
}, },
setup (options, nuxt) { setup (options, nuxt) {
let componentDirs = [] let componentDirs = []
let components: Component[] = []
// Resolve dirs // Resolve dirs
nuxt.hook('app:resolve', async () => { nuxt.hook('app:resolve', async () => {
@ -56,7 +58,7 @@ export default defineNuxtModule({
// Scan components and add to plugin // Scan components and add to plugin
nuxt.hook('app:templates', async (app) => { nuxt.hook('app:templates', async (app) => {
const components = await scanComponents(componentDirs, nuxt.options.srcDir!) components = await scanComponents(componentDirs, nuxt.options.srcDir!)
await nuxt.callHook('components:extend', components) await nuxt.callHook('components:extend', components)
if (!components.length) { if (!components.length) {
return return
@ -87,5 +89,11 @@ export default defineNuxtModule({
await nuxt.callHook('builder:generateApp') await nuxt.callHook('builder:generateApp')
} }
}) })
if (!nuxt.options.dev) {
const options = { getComponents: () => components }
addWebpackPlugin(loaderPlugin.webpack(options))
addVitePlugin(loaderPlugin.vite(options))
}
} }
}) })

View File

@ -1413,6 +1413,7 @@ __metadata:
scule: ^0.2.1 scule: ^0.2.1
ufo: ^0.7.7 ufo: ^0.7.7
unbuild: ^0.4.0 unbuild: ^0.4.0
unplugin: ^0.0.5
upath: ^2.0.1 upath: ^2.0.1
peerDependencies: peerDependencies:
vue: 3.1.5 vue: 3.1.5
@ -11955,6 +11956,26 @@ typescript@^4.3.5:
languageName: node languageName: node
linkType: hard linkType: hard
"unplugin@npm:^0.0.5":
version: 0.0.5
resolution: "unplugin@npm:0.0.5"
dependencies:
webpack-virtual-modules: ^0.4.3
peerDependencies:
rollup: ^2.50.0
vite: ^2.3.0
webpack: ^5.0.0
peerDependenciesMeta:
rollup:
optional: true
vite:
optional: true
webpack:
optional: true
checksum: 37f66f585365a9d7c26c03cc971329391839b13505942548be48ad834d7d5c68576e4defce92f9845bddd154c93340f8a1773337fce797f1cac1a1a34d6aac26
languageName: node
linkType: hard
"unstorage@npm:^0.2.3": "unstorage@npm:^0.2.3":
version: 0.2.3 version: 0.2.3
resolution: "unstorage@npm:0.2.3" resolution: "unstorage@npm:0.2.3"