diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts
index 9e58d751d..a481ddcf5 100644
--- a/packages/compiler-core/src/codegen.ts
+++ b/packages/compiler-core/src/codegen.ts
@@ -215,9 +215,10 @@ export function generate(
}
// binding optimizations
- const optimizeSources = options.bindingMetadata
- ? `, $props, $setup, $data, $options`
- : ``
+ const optimizeSources =
+ options.bindingMetadata && !options.inline
+ ? `, $props, $setup, $data, $options`
+ : ``
// enter render function
if (!ssr) {
if (isSetupInlined) {
diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts
index f72bd16bb..0a6cc8ef0 100644
--- a/packages/compiler-core/src/transforms/transformExpression.ts
+++ b/packages/compiler-core/src/transforms/transformExpression.ts
@@ -113,17 +113,16 @@ export function processExpression(
// it gets correct type
return `__props.${raw}`
}
- }
-
- if (type === BindingTypes.CONST) {
- // setup const binding in non-inline mode
- return `$setup.${raw}`
- } else if (type) {
- return `$${type}.${raw}`
} else {
- // fallback to ctx
- return `_ctx.${raw}`
+ if (type === BindingTypes.CONST) {
+ // setup const binding in non-inline mode
+ return `$setup.${raw}`
+ } else if (type) {
+ return `$${type}.${raw}`
+ }
}
+ // fallback to ctx
+ return `_ctx.${raw}`
}
// fast path if expression is a simple identifier.
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
index baac4d581..907a47c8d 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
@@ -1,62 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`SFC compile \n` +
- ``
- ).content
- )
- })
-
- test('\n` +
- ``
- ).content
- )
- })
-
- test('\n` +
- ``
- ).content
- )
- })
-
- test('w/ \n` +
- ``
- ).content
- )
- })
- })
-
describe('async/await detection', () => {
function assertAwaitDetection(code: string, shouldAsync = true) {
const { content } = compile(``)
- expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup()`)
+ expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
}
test('expression statement', () => {
diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts
index ce8374703..a1b3b21ee 100644
--- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts
+++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts
@@ -9,27 +9,27 @@ import {
} from '../src/compileStyle'
import path from 'path'
-describe('SFC scoped CSS', () => {
- function compileScoped(
- source: string,
- options?: Partial
- ): string {
- const res = compileStyle({
- source,
- filename: 'test.css',
- id: 'test',
- scoped: true,
- ...options
+export function compileScoped(
+ source: string,
+ options?: Partial
+): string {
+ const res = compileStyle({
+ source,
+ filename: 'test.css',
+ id: 'test',
+ scoped: true,
+ ...options
+ })
+ if (res.errors.length) {
+ res.errors.forEach(err => {
+ console.error(err)
})
- if (res.errors.length) {
- res.errors.forEach(err => {
- console.error(err)
- })
- expect(res.errors.length).toBe(0)
- }
- return res.code
+ expect(res.errors.length).toBe(0)
}
+ return res.code
+}
+describe('SFC scoped CSS', () => {
test('simple selectors', () => {
expect(compileScoped(`h1 { color: red; }`)).toMatch(
`h1[test] { color: red;`
@@ -266,27 +266,6 @@ describe('SFC scoped CSS', () => {
).toHaveBeenWarned()
})
})
-
- describe('`
+ ).content
+ )
+ })
+
+ test('\n` +
+ ``
+ ).content
+ )
+ })
+
+ test('\n` + ``
+ ).content
+ )
+ })
+
+ test('w/ \n` +
+ ``
+ ).content
+ )
+ })
+ })
+
+ test('generating correct code for nested paths', () => {
+ const { content } = compileSFCScript(
+ `\n` +
+ ``
+ )
+ expect(content).toMatch(`_useCssVars(_ctx => ({
+ color: (_ctx.color),
+ font_size: (_ctx.font.size)
+})`)
+ assertCode(content)
+ })
+
+ test('w/ \n` +
+ ``
+ )
+ // should handle:
+ // 1. local const bindings
+ // 2. local potential ref bindings
+ // 3. props bindings (analyzed)
+ expect(content).toMatch(`_useCssVars(_ctx => ({
+ color: (color),
+ size: (_unref(size)),
+ foo: (__props.foo)
+})`)
+ expect(content).toMatch(
+ `import { useCssVars as _useCssVars, unref as _unref } from 'vue'`
+ )
+ assertCode(content)
+ })
+
+ test('should rewrite CSS vars in scoped mode', () => {
+ const { code } = compileStyle({
+ source: `.foo {
+ color: var(--v-bind:color);
+ font-size: var(--:font.size);
+ }`,
+ filename: 'test.css',
+ id: 'data-v-test'
+ })
+ expect(code).toMatchInlineSnapshot(`
+ ".foo {
+ color: var(--test-color);
+ font-size: var(--test-font_size);
+ }"
+ `)
+ })
+})
diff --git a/packages/compiler-sfc/__tests__/utils.ts b/packages/compiler-sfc/__tests__/utils.ts
new file mode 100644
index 000000000..ae6861193
--- /dev/null
+++ b/packages/compiler-sfc/__tests__/utils.ts
@@ -0,0 +1,28 @@
+import { parse, SFCScriptCompileOptions, compileScript } from '../src'
+import { parse as babelParse } from '@babel/parser'
+import { babelParserDefaultPlugins } from '@vue/shared'
+
+export function compileSFCScript(
+ src: string,
+ options?: Partial
+) {
+ const { descriptor } = parse(src)
+ return compileScript(descriptor, {
+ ...options,
+ id: 'xxxxxxxx'
+ })
+}
+
+export function assertCode(code: string) {
+ // parse the generated code to make sure it is valid
+ try {
+ babelParse(code, {
+ sourceType: 'module',
+ plugins: [...babelParserDefaultPlugins, 'typescript']
+ })
+ } catch (e) {
+ console.log(code)
+ throw e
+ }
+ expect(code).toMatchSnapshot()
+}
diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts
index a049d2059..522277db2 100644
--- a/packages/compiler-sfc/src/compileScript.ts
+++ b/packages/compiler-sfc/src/compileScript.ts
@@ -1,5 +1,5 @@
import MagicString from 'magic-string'
-import { BindingMetadata, BindingTypes } from '@vue/compiler-core'
+import { BindingMetadata, BindingTypes, UNREF } from '@vue/compiler-core'
import { SFCDescriptor, SFCScriptBlock } from './parse'
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
import { babelParserDefaultPlugins, generateCodeFrame } from '@vue/shared'
@@ -26,14 +26,20 @@ import { walk } from 'estree-walker'
import { RawSourceMap } from 'source-map'
import {
CSS_VARS_HELPER,
+ parseCssVars,
genCssVarsCode,
injectCssVarsCalls
-} from './genCssVars'
+} from './cssVars'
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
const DEFINE_OPTIONS = 'defineOptions'
export interface SFCScriptCompileOptions {
+ /**
+ * Scope ID for prefixing injected CSS varialbes.
+ * This must be consistent with the `id` passed to `compileStyle`.
+ */
+ id: string
/**
* https://babeljs.io/docs/en/babel-parser#plugins
*/
@@ -52,7 +58,7 @@ export interface SFCScriptCompileOptions {
* from being hot-reloaded separately from component state.
*/
inlineTemplate?: boolean
- templateOptions?: SFCTemplateCompileOptions
+ templateOptions?: Partial
}
const hasWarned: Record = {}
@@ -71,19 +77,33 @@ function warnOnce(msg: string) {
*/
export function compileScript(
sfc: SFCDescriptor,
- options: SFCScriptCompileOptions = {}
+ options: SFCScriptCompileOptions
): SFCScriptBlock {
- const { script, scriptSetup, styles, source, filename } = sfc
+ const { script, scriptSetup, source, filename } = sfc
if (__DEV__ && !__TEST__ && scriptSetup) {
warnOnce(
`