From 8e5e229031d157cf19131d4f692b702b7c9f233f Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Wed, 28 Jul 2021 20:11:32 +0800 Subject: [PATCH] feat(components): build time sync loader (#383) --- examples/with-components/app.vue | 1 + examples/with-components/components/Nuxt3.vue | 5 ++ examples/with-components/nuxt.config.ts | 1 + packages/components/package.json | 1 + packages/components/src/loader.ts | 47 +++++++++++++++++++ packages/components/src/module.ts | 14 ++++-- yarn.lock | 21 +++++++++ 7 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 examples/with-components/components/Nuxt3.vue create mode 100644 packages/components/src/loader.ts diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index 14eebea575..599adac638 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -1,5 +1,6 @@ diff --git a/examples/with-components/components/Nuxt3.vue b/examples/with-components/components/Nuxt3.vue new file mode 100644 index 0000000000..0faa5c7efe --- /dev/null +++ b/examples/with-components/components/Nuxt3.vue @@ -0,0 +1,5 @@ + diff --git a/examples/with-components/nuxt.config.ts b/examples/with-components/nuxt.config.ts index 854fc6bfc5..15046bd316 100644 --- a/examples/with-components/nuxt.config.ts +++ b/examples/with-components/nuxt.config.ts @@ -1,4 +1,5 @@ import { defineNuxtConfig } from '@nuxt/kit' export default defineNuxtConfig({ + vite: false }) diff --git a/packages/components/package.json b/packages/components/package.json index 6c6346a8a4..688f2b8c62 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -16,6 +16,7 @@ "globby": "^11.0.4", "scule": "^0.2.1", "ufo": "^0.7.7", + "unplugin": "^0.0.5", "upath": "^2.0.1" }, "devDependencies": { diff --git a/packages/components/src/loader.ts b/packages/components/src/loader.ts new file mode 100644 index 0000000000..4bbd454846 --- /dev/null +++ b/packages/components/src/loader.ts @@ -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() + + // 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}` +} diff --git a/packages/components/src/module.ts b/packages/components/src/module.ts index 94232c1860..5371302fa2 100644 --- a/packages/components/src/module.ts +++ b/packages/components/src/module.ts @@ -1,8 +1,9 @@ import fs from 'fs' -import { defineNuxtModule, resolveAlias } from '@nuxt/kit' +import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit' import { resolve } from 'upath' 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 isDirectory = (p: string) => { try { return fs.statSync(p).isDirectory() } catch (_e) { return false } } @@ -14,6 +15,7 @@ export default defineNuxtModule({ }, setup (options, nuxt) { let componentDirs = [] + let components: Component[] = [] // Resolve dirs nuxt.hook('app:resolve', async () => { @@ -56,7 +58,7 @@ export default defineNuxtModule({ // Scan components and add to plugin 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) if (!components.length) { return @@ -87,5 +89,11 @@ export default defineNuxtModule({ await nuxt.callHook('builder:generateApp') } }) + + if (!nuxt.options.dev) { + const options = { getComponents: () => components } + addWebpackPlugin(loaderPlugin.webpack(options)) + addVitePlugin(loaderPlugin.vite(options)) + } } }) diff --git a/yarn.lock b/yarn.lock index 6a7091b54d..df197e734c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1413,6 +1413,7 @@ __metadata: scule: ^0.2.1 ufo: ^0.7.7 unbuild: ^0.4.0 + unplugin: ^0.0.5 upath: ^2.0.1 peerDependencies: vue: 3.1.5 @@ -11955,6 +11956,26 @@ typescript@^4.3.5: languageName: node 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": version: 0.2.3 resolution: "unstorage@npm:0.2.3"