From b9171dde4df4154a1cc1bf208c822a042778da3d Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 12 Dec 2021 11:54:26 +0800 Subject: [PATCH] refactor(plugin-vue): resolve vue/compiler-sfc from project root BREAKING CHANGE: now requires vue@^3.2.13 as peer dep --- packages/plugin-vue/package.json | 4 +- packages/plugin-vue/src/compiler.ts | 29 +++++++------ packages/plugin-vue/src/handleHotUpdate.ts | 2 +- packages/plugin-vue/src/index.ts | 42 +++++++++++-------- packages/plugin-vue/src/main.ts | 5 +-- packages/plugin-vue/src/script.ts | 5 +-- packages/plugin-vue/src/style.ts | 5 +-- packages/plugin-vue/src/template.ts | 7 ++-- .../plugin-vue/src/utils/descriptorCache.ts | 7 ++-- packages/plugin-vue/src/utils/error.ts | 2 +- 10 files changed, 57 insertions(+), 51 deletions(-) diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 2d828644..07dde9cd 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -31,12 +31,12 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-vue#readme", "peerDependencies": { - "vite": "^2.5.10" + "vite": "^2.5.10", + "vue": "^3.2.13" }, "devDependencies": { "@rollup/pluginutils": "^4.1.1", "@types/hash-sum": "^1.0.0", - "@vue/compiler-sfc": "^3.2.23", "debug": "^4.3.2", "hash-sum": "^2.0.0", "rollup": "^2.59.0", diff --git a/packages/plugin-vue/src/compiler.ts b/packages/plugin-vue/src/compiler.ts index e7ece1df..59350659 100644 --- a/packages/plugin-vue/src/compiler.ts +++ b/packages/plugin-vue/src/compiler.ts @@ -1,25 +1,30 @@ // extend the descriptor so we can store the scopeId on it -declare module '@vue/compiler-sfc' { +declare module 'vue/compiler-sfc' { interface SFCDescriptor { id: string } } -import * as _compiler from '@vue/compiler-sfc' +import * as _compiler from 'vue/compiler-sfc' -export let compiler: typeof _compiler +export function resolveCompiler(root: string): typeof _compiler { + // resolve from project root first, then fallback to peer dep (if any) + const compiler = + tryRequire('vue/compiler-sfc', root) || tryRequire('vue/compiler-sfc') -try { - // Vue 3.2.13+ ships the SFC compiler directly under the `vue` package - // making it no longer necessary to have @vue/compiler-sfc separately installed. - compiler = require('vue/compiler-sfc') -} catch (e) { - try { - compiler = require('@vue/compiler-sfc') - } catch (e) { + if (!compiler) { throw new Error( - `@vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc ` + + `Failed to resolve vue/compiler-sfc.\n` + + `@vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc ` + `to be present in the dependency tree.` ) } + + return compiler +} + +function tryRequire(id: string, from?: string) { + try { + return from ? require(require.resolve(id, { paths: [from] })) : require(id) + } catch (e) {} } diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 4f472114..d93e647a 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -1,5 +1,5 @@ import _debug from 'debug' -import { SFCBlock, SFCDescriptor } from '@vue/compiler-sfc' +import { SFCBlock, SFCDescriptor } from 'vue/compiler-sfc' import { createDescriptor, getDescriptor, diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 9862a9da..4df81155 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -6,8 +6,9 @@ import { SFCScriptCompileOptions, SFCStyleCompileOptions, SFCTemplateCompileOptions -} from '@vue/compiler-sfc' -import { compiler } from './compiler' +} from 'vue/compiler-sfc' +import * as _compiler from 'vue/compiler-sfc' +import { resolveCompiler } from './compiler' import { parseVueRequest } from './utils/query' import { getDescriptor, getSrcDescriptor } from './utils/descriptorCache' import { getResolvedScript } from './script' @@ -25,7 +26,7 @@ export interface Options { isProduction?: boolean - // options to pass on to @vue/compiler-sfc + // options to pass on to vue/compiler-sfc script?: Partial template?: Partial style?: Partial @@ -60,9 +61,15 @@ export interface Options { * @deprecated the plugin now auto-detects whether it's being invoked for ssr. */ ssr?: boolean + + /** + * Use custom compiler-sfc instance. Can be used to force a specific version. + */ + compiler?: typeof _compiler } export interface ResolvedOptions extends Options { + compiler: typeof _compiler root: string sourceMap: boolean devServer?: ViteDevServer @@ -90,9 +97,6 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { ? createFilter(/\.(j|t)sx?$/, /node_modules/) : createFilter(refTransform) - // compat for older versions - const canUseRefTransform = typeof compiler.shouldTransformRef === 'function' - let options: ResolvedOptions = { isProduction: process.env.NODE_ENV === 'production', ...rawOptions, @@ -101,7 +105,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { customElement, refTransform, root: process.cwd(), - sourceMap: true + sourceMap: true, + compiler: null as any // to be set in configResolved } // Temporal handling for 2.7 breaking change @@ -122,7 +127,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { return handleHotUpdate(ctx, options) }, - config(config) { + config() { return { define: { __VUE_OPTIONS_API__: true, @@ -139,7 +144,8 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { ...options, root: config.root, sourceMap: config.command === 'build' ? !!config.build.sourcemap : true, - isProduction: config.isProduction + isProduction: config.isProduction, + compiler: options.compiler || resolveCompiler(config.root) } }, @@ -198,15 +204,15 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { return } if (!filter(filename) && !query.vue) { - if (!query.vue && refTransformFilter(filename)) { - if (!canUseRefTransform) { - this.warn('refTransform requires @vue/compiler-sfc@^3.2.5.') - } else if (compiler.shouldTransformRef(code)) { - return compiler.transformRef(code, { - filename, - sourceMap: true - }) - } + if ( + !query.vue && + refTransformFilter(filename) && + options.compiler.shouldTransformRef(code) + ) { + return options.compiler.transformRef(code, { + filename, + sourceMap: true + }) } return } diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index 9df5ba44..11dad79c 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -1,7 +1,6 @@ import qs from 'querystring' import path from 'path' -import { SFCBlock, SFCDescriptor } from '@vue/compiler-sfc' -import { compiler } from './compiler' +import { SFCBlock, SFCDescriptor } from 'vue/compiler-sfc' import { ResolvedOptions } from '.' import { createDescriptor, @@ -266,7 +265,7 @@ async function genScriptCode( // If the script is js/ts and has no external src, it can be directly placed // in the main module. if ((!script.lang || script.lang === 'ts') && !script.src) { - scriptCode = compiler.rewriteDefault( + scriptCode = options.compiler.rewriteDefault( script.content, '_sfc_main', script.lang === 'ts' ? ['typescript'] : undefined diff --git a/packages/plugin-vue/src/script.ts b/packages/plugin-vue/src/script.ts index bda618c4..aa4f9a7a 100644 --- a/packages/plugin-vue/src/script.ts +++ b/packages/plugin-vue/src/script.ts @@ -1,7 +1,6 @@ -import { SFCDescriptor, SFCScriptBlock } from '@vue/compiler-sfc' +import { SFCDescriptor, SFCScriptBlock } from 'vue/compiler-sfc' import { ResolvedOptions } from '.' import { resolveTemplateCompilerOptions } from './template' -import { compiler } from './compiler' // ssr and non ssr builds would output different script content const clientCache = new WeakMap() @@ -49,7 +48,7 @@ export function resolveScript( let resolved: SFCScriptBlock | null = null - resolved = compiler.compileScript(descriptor, { + resolved = options.compiler.compileScript(descriptor, { ...options.script, id: descriptor.id, isProd: options.isProduction, diff --git a/packages/plugin-vue/src/style.ts b/packages/plugin-vue/src/style.ts index fd71cd9d..c27e1659 100644 --- a/packages/plugin-vue/src/style.ts +++ b/packages/plugin-vue/src/style.ts @@ -1,7 +1,6 @@ -import { SFCDescriptor } from '@vue/compiler-sfc' +import { SFCDescriptor } from 'vue/compiler-sfc' import { TransformPluginContext } from 'rollup' import { ResolvedOptions } from '.' -import { compiler } from './compiler' // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export async function transformStyle( @@ -14,7 +13,7 @@ export async function transformStyle( const block = descriptor.styles[index] // vite already handles pre-processors and CSS module so this is only // applying SFC-specific transforms like scoped mode and CSS vars rewrite (v-bind(var)) - const result = await compiler.compileStyleAsync({ + const result = await options.compiler.compileStyleAsync({ ...options.style, filename: descriptor.filename, id: `data-v-${descriptor.id}`, diff --git a/packages/plugin-vue/src/template.ts b/packages/plugin-vue/src/template.ts index 25da5420..9ee386b1 100644 --- a/packages/plugin-vue/src/template.ts +++ b/packages/plugin-vue/src/template.ts @@ -10,7 +10,6 @@ import { PluginContext, TransformPluginContext } from 'rollup' import { ResolvedOptions } from '.' import { getResolvedScript } from './script' import { createRollupError } from './utils/error' -import { compiler } from './compiler' // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export async function transformTemplateAsModule( @@ -70,7 +69,7 @@ export function compile( ssr: boolean ) { const filename = descriptor.filename - const result = compiler.compileTemplate({ + const result = options.compiler.compileTemplate({ ...resolveTemplateCompilerOptions(descriptor, options, ssr)!, source: code }) @@ -111,10 +110,10 @@ export function resolveTemplateCompilerOptions( const { id, filename, cssVars } = descriptor let transformAssetUrls = options.template?.transformAssetUrls - // @vue/compiler-sfc/dist/compiler-sfc.d.ts should export `AssetURLOptions` + // compiler-sfc should export `AssetURLOptions` let assetUrlOptions //: AssetURLOptions | undefined if (options.devServer) { - // during dev, inject vite base so that @vue/compiler-sfc can transform + // during dev, inject vite base so that compiler-sfc can transform // relative paths directly to absolute paths without incurring an extra import // request if (filename.startsWith(options.root)) { diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index 432b5f8f..ab6d7773 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -2,11 +2,10 @@ import fs from 'fs' import path from 'path' import slash from 'slash' import hash from 'hash-sum' -import { CompilerError, SFCDescriptor } from '@vue/compiler-sfc' +import { CompilerError, SFCDescriptor } from 'vue/compiler-sfc' import { ResolvedOptions, VueQuery } from '..' -import { compiler } from '../compiler' -// node_modules/@vue/compiler-sfc/dist/compiler-sfc.d.ts SFCParseResult should be exported so it can be re-used +// compiler-sfc should be exported so it can be re-used export interface SFCParseResult { descriptor: SFCDescriptor errors: Array @@ -18,7 +17,7 @@ const prevCache = new Map() export function createDescriptor( filename: string, source: string, - { root, isProduction, sourceMap }: ResolvedOptions + { root, isProduction, sourceMap, compiler }: ResolvedOptions ): SFCParseResult { const { descriptor, errors } = compiler.parse(source, { filename, diff --git a/packages/plugin-vue/src/utils/error.ts b/packages/plugin-vue/src/utils/error.ts index 8c5e3f5d..1f3e48d7 100644 --- a/packages/plugin-vue/src/utils/error.ts +++ b/packages/plugin-vue/src/utils/error.ts @@ -1,4 +1,4 @@ -import { CompilerError } from '@vue/compiler-sfc' +import { CompilerError } from 'vue/compiler-sfc' import { RollupError } from 'rollup' export function createRollupError(