From ce0bbe053abaf8ba18de8baf535e175048596ee5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 28 Apr 2021 11:35:59 -0400 Subject: [PATCH] feat: support component-level `compilerOptions` when using runtime compiler - The `delimiters` component option is deprecated. Use `compilerOptions.delimiters` instead. --- packages/runtime-core/src/component.ts | 24 +++- packages/runtime-core/src/componentOptions.ts | 18 ++- packages/runtime-core/src/index.ts | 3 +- .../__tests__/runtimeCompilerOptions.spec.ts | 104 ++++++++++++++++++ 4 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 packages/vue/__tests__/runtimeCompilerOptions.spec.ts diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index ee817d6ca..54cf487bb 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -578,6 +578,13 @@ function setupStatefulComponent( validateDirectiveName(names[i]) } } + if (Component.compilerOptions && isRuntimeOnly()) { + warn( + `"compilerOptions" is only supported when using a build of Vue that ` + + `includes the runtime compiler. Since you are using a runtime-only ` + + `build, the options should be passed via your build tool config instead.` + ) + } } // 0. create render proxy property access cache instance.accessCache = Object.create(null) @@ -728,12 +735,19 @@ export function finishComponentSetup( startMeasure(instance, `compile`) } const { isCustomElement, compilerOptions } = instance.appContext.config + const { + delimiters, + compilerOptions: componentCompilerOptions + } = Component const finalCompilerOptions: CompilerOptions = extend( - { - isCustomElement: isCustomElement || NO, - delimiters: Component.delimiters - }, - compilerOptions + extend( + { + isCustomElement, + delimiters + }, + compilerOptions + ), + componentCompilerOptions ) if (__COMPAT__) { // pass runtime compat config into the compiler diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 4222d063e..366ac379d 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -150,6 +150,9 @@ export interface ComponentOptionsBase< expose?: string[] serverPrefetch?(): Promise + // Runtime compiler only ----------------------------------------------------- + compilerOptions?: RuntimeCompilerOptions + // Internal ------------------------------------------------------------------ /** @@ -203,6 +206,16 @@ export interface ComponentOptionsBase< __defaults?: Defaults } +/** + * Subset of compiler options that makes sense for the runtime. + */ +export interface RuntimeCompilerOptions { + isCustomElement?: (tag: string) => boolean + whitespace?: 'preserve' | 'condense' + comments?: boolean + delimiters?: [string, string] +} + export type ComponentOptionsWithoutProps< Props = {}, RawBindings = {}, @@ -446,7 +459,10 @@ interface LegacyOptions< renderTriggered?: DebuggerHook errorCaptured?: ErrorCapturedHook - // runtime compile only + /** + * runtime compile only + * @deprecated use `compilerOptions.delimiters` instead. + */ delimiters?: [string, string] /** diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 3b4a16565..b4b635453 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -179,7 +179,8 @@ export { ComponentOptionsBase, RenderFunction, MethodOptions, - ComputedOptions + ComputedOptions, + RuntimeCompilerOptions } from './componentOptions' export { EmitsOptions, ObjectEmitsOptions } from './componentEmits' export { diff --git a/packages/vue/__tests__/runtimeCompilerOptions.spec.ts b/packages/vue/__tests__/runtimeCompilerOptions.spec.ts new file mode 100644 index 000000000..3222462fe --- /dev/null +++ b/packages/vue/__tests__/runtimeCompilerOptions.spec.ts @@ -0,0 +1,104 @@ +import { createApp } from 'vue' + +describe('config.compilerOptions', () => { + test('isCustomElement', () => { + const app = createApp({ + template: `` + }) + app.config.compilerOptions.isCustomElement = (tag: string) => tag === 'foo' + const root = document.createElement('div') + app.mount(root) + expect(root.innerHTML).toBe('') + }) + + test('comments', () => { + const app = createApp({ + template: `
` + }) + app.config.compilerOptions.comments = true + // the comments option is only relevant in production mode + __DEV__ = false + const root = document.createElement('div') + app.mount(root) + expect(root.innerHTML).toBe('
') + __DEV__ = true + }) + + test('whitespace', () => { + const app = createApp({ + template: `
\n
` + }) + app.config.compilerOptions.whitespace = 'preserve' + const root = document.createElement('div') + app.mount(root) + expect(root.firstChild!.childNodes.length).toBe(3) + expect(root.firstChild!.childNodes[1].nodeType).toBe(Node.TEXT_NODE) + }) + + test('delimiters', () => { + const app = createApp({ + data: () => ({ foo: 'hi' }), + template: `[[ foo ]]` + }) + app.config.compilerOptions.delimiters = [`[[`, `]]`] + const root = document.createElement('div') + app.mount(root) + expect(root.textContent).toBe('hi') + }) +}) + +describe('per-component compilerOptions', () => { + test('isCustomElement', () => { + const app = createApp({ + template: ``, + compilerOptions: { + isCustomElement: (tag: string) => tag === 'foo' + } + }) + const root = document.createElement('div') + app.mount(root) + expect(root.innerHTML).toBe('') + }) + + test('comments', () => { + const app = createApp({ + template: `
`, + compilerOptions: { + comments: true + } + }) + app.config.compilerOptions.comments = false + // the comments option is only relevant in production mode + __DEV__ = false + const root = document.createElement('div') + app.mount(root) + expect(root.innerHTML).toBe('
') + __DEV__ = true + }) + + test('whitespace', () => { + const app = createApp({ + template: `
\n
`, + compilerOptions: { + whitespace: 'preserve' + } + }) + const root = document.createElement('div') + app.mount(root) + expect(root.firstChild!.childNodes.length).toBe(3) + expect(root.firstChild!.childNodes[1].nodeType).toBe(Node.TEXT_NODE) + }) + + test('delimiters', () => { + const app = createApp({ + data: () => ({ foo: 'hi' }), + template: `[[ foo ]]`, + compilerOptions: { + delimiters: [`[[`, `]]`] + } + }) + const root = document.createElement('div') + app.mount(root) + expect(root.textContent).toBe('hi') + }) +})