From 316d7afc0c84e51359938a12ebe1b09ca34ea8bd Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 19 Sep 2021 16:26:59 -0400 Subject: [PATCH] fix(plugin-vue): generate tree-shakable code --- packages/plugin-vue/src/helper.ts | 10 +++++++ packages/plugin-vue/src/index.ts | 11 ++++++- packages/plugin-vue/src/main.ts | 49 ++++++++++++++++++------------- 3 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 packages/plugin-vue/src/helper.ts diff --git a/packages/plugin-vue/src/helper.ts b/packages/plugin-vue/src/helper.ts new file mode 100644 index 00000000000000..3b1dbd3407a5fe --- /dev/null +++ b/packages/plugin-vue/src/helper.ts @@ -0,0 +1,10 @@ +export const EXPORT_HELPER_ID = 'plugin-vue:export-helper' + +export const helperCode = ` +export default (sfc, props) => { + for (const [key, val] of props) { + sfc[key] = val + } + return sfc +} +` diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index c1504cc781d17a..f42fad7130fff6 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -25,6 +25,7 @@ import { transformMain } from './main' import { handleHotUpdate } from './handleHotUpdate' import { transformTemplateAsModule } from './template' import { transformStyle } from './style' +import { EXPORT_HELPER_ID, helperCode } from './helper' // extend the descriptor so we can store the scopeId on it declare module '@vue/compiler-sfc' { @@ -156,7 +157,11 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { options.devServer = server }, - async resolveId(id, importer) { + async resolveId(id) { + // component export helper + if (id === EXPORT_HELPER_ID) { + return id + } // serve sub-part requests (*?vue) as virtual modules if (parseVueRequest(id).query.vue) { return id @@ -164,6 +169,10 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { }, load(id, ssr = !!options.ssr) { + if (id === EXPORT_HELPER_ID) { + return helperCode + } + const { filename, query } = parseVueRequest(id) // select corresponding block for sub-part virtual modules if (query.vue) { diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index f8e4b622fc4c6d..9a6e64312b74d5 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -15,6 +15,7 @@ import { isOnlyTemplateChanged, isEqualBlock } from './handleHotUpdate' import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map' import { createRollupError } from './utils/error' import { transformWithEsbuild } from 'vite' +import { EXPORT_HELPER_ID } from './helper' // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export async function transformMain( @@ -39,6 +40,7 @@ export async function transformMain( } // feature information + const attachedProps: [string, string][] = [] const hasScoped = descriptor.styles.some((s) => s.scoped) // script @@ -70,11 +72,10 @@ export async function transformMain( )) } - let renderReplace = '' if (hasTemplateImport) { - renderReplace = ssr - ? `_sfc_main.ssrRender = _sfc_ssrRender` - : `_sfc_main.render = _sfc_render` + attachedProps.push( + ssr ? ['ssrRender', '_sfc_ssrRender'] : ['render', '_sfc_render'] + ) } else { // #2128 // User may empty the template but we didn't provide rerender function before @@ -82,9 +83,7 @@ export async function transformMain( prevDescriptor && !isEqualBlock(descriptor.template, prevDescriptor.template) ) { - renderReplace = ssr - ? `_sfc_main.ssrRender = () => {}` - : `_sfc_main.render = () => {}` + attachedProps.push([ssr ? 'ssrRender' : 'render', '() => {}']) } } @@ -92,7 +91,8 @@ export async function transformMain( const stylesCode = await genStyleCode( descriptor, pluginContext, - asCustomElement + asCustomElement, + attachedProps ) // custom blocks @@ -102,17 +102,14 @@ export async function transformMain( scriptCode, templateCode, stylesCode, - customBlocksCode, - renderReplace + customBlocksCode ] if (hasScoped) { - output.push( - `_sfc_main.__scopeId = ${JSON.stringify(`data-v-${descriptor.id}`)}` - ) + attachedProps.push([`__scopeId`, JSON.stringify(`data-v-${descriptor.id}`)]) } if (devServer && !isProduction) { // expose filename during serve for devtools to pickup - output.push(`_sfc_main.__file = ${JSON.stringify(filename)}`) + attachedProps.push([`__file`, JSON.stringify(filename)]) } // HMR @@ -185,7 +182,16 @@ export async function transformMain( resolvedMap.sourcesContent = templateMap.sourcesContent } - output.push(`export default _sfc_main`) + if (!attachedProps.length) { + output.push(`export default _sfc_main`) + } else { + output.push( + `import _export_sfc from '${EXPORT_HELPER_ID}'`, + `export default /*#__PURE__*/_export_sfc(_sfc_main, [${attachedProps + .map(([key, val]) => `['${key}',${val}]`) + .join(',')}])` + ) + } // handle TS transpilation let resolvedCode = output.join('\n') @@ -290,7 +296,8 @@ async function genScriptCode( async function genStyleCode( descriptor: SFCDescriptor, pluginContext: PluginContext, - asCustomElement: boolean + asCustomElement: boolean, + attachedProps: [string, string][] ) { let stylesCode = `` let hasCSSModules = false @@ -315,7 +322,8 @@ async function genStyleCode( ) } if (!hasCSSModules) { - stylesCode += `\nconst cssModules = _sfc_main.__cssModules = {}` + stylesCode += `\nconst cssModules = {}` + attachedProps.push([`__cssModules`, `cssModules`]) hasCSSModules = true } stylesCode += genCSSModulesCode(i, styleRequest, style.module) @@ -331,9 +339,10 @@ async function genStyleCode( // TODO SSR critical CSS collection } if (asCustomElement) { - stylesCode += `\n_sfc_main.styles = [${descriptor.styles - .map((_, i) => `_style_${i}`) - .join(',')}]` + attachedProps.push([ + `styles`, + `[${descriptor.styles.map((_, i) => `_style_${i}`).join(',')}]` + ]) } } return stylesCode