Skip to content

Commit

Permalink
feat: support component-level compilerOptions when using runtime co…
Browse files Browse the repository at this point in the history
…mpiler

- The `delimiters` component option is deprecated.
  Use `compilerOptions.delimiters` instead.
  • Loading branch information
yyx990803 committed Apr 28, 2021
1 parent e486254 commit ce0bbe0
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 7 deletions.
24 changes: 19 additions & 5 deletions packages/runtime-core/src/component.ts
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion packages/runtime-core/src/componentOptions.ts
Expand Up @@ -150,6 +150,9 @@ export interface ComponentOptionsBase<
expose?: string[]
serverPrefetch?(): Promise<any>

// Runtime compiler only -----------------------------------------------------
compilerOptions?: RuntimeCompilerOptions

// Internal ------------------------------------------------------------------

/**
Expand Down Expand Up @@ -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 = {},
Expand Down Expand Up @@ -446,7 +459,10 @@ interface LegacyOptions<
renderTriggered?: DebuggerHook
errorCaptured?: ErrorCapturedHook

// runtime compile only
/**
* runtime compile only
* @deprecated use `compilerOptions.delimiters` instead.
*/
delimiters?: [string, string]

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/index.ts
Expand Up @@ -179,7 +179,8 @@ export {
ComponentOptionsBase,
RenderFunction,
MethodOptions,
ComputedOptions
ComputedOptions,
RuntimeCompilerOptions
} from './componentOptions'
export { EmitsOptions, ObjectEmitsOptions } from './componentEmits'
export {
Expand Down
104 changes: 104 additions & 0 deletions packages/vue/__tests__/runtimeCompilerOptions.spec.ts
@@ -0,0 +1,104 @@
import { createApp } from 'vue'

describe('config.compilerOptions', () => {
test('isCustomElement', () => {
const app = createApp({
template: `<foo/>`
})
app.config.compilerOptions.isCustomElement = (tag: string) => tag === 'foo'
const root = document.createElement('div')
app.mount(root)
expect(root.innerHTML).toBe('<foo></foo>')
})

test('comments', () => {
const app = createApp({
template: `<div/><!--test--><div/>`
})
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('<div></div><!--test--><div></div>')
__DEV__ = true
})

test('whitespace', () => {
const app = createApp({
template: `<div><span/>\n <span/></div>`
})
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: `<foo/>`,
compilerOptions: {
isCustomElement: (tag: string) => tag === 'foo'
}
})
const root = document.createElement('div')
app.mount(root)
expect(root.innerHTML).toBe('<foo></foo>')
})

test('comments', () => {
const app = createApp({
template: `<div/><!--test--><div/>`,
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('<div></div><!--test--><div></div>')
__DEV__ = true
})

test('whitespace', () => {
const app = createApp({
template: `<div><span/>\n <span/></div>`,
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')
})
})

0 comments on commit ce0bbe0

Please sign in to comment.