Skip to content

Commit

Permalink
feat(experimental): support ref transform for sfc normal <script>
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Aug 23, 2021
1 parent f173cf0 commit 06051c4
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 74 deletions.
12 changes: 10 additions & 2 deletions packages/compiler-core/src/babelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
Node,
Function,
ObjectProperty,
BlockStatement
BlockStatement,
Program
} from '@babel/types'
import { walk } from 'estree-walker'

Expand Down Expand Up @@ -149,16 +150,23 @@ export function walkFunctionParams(
}

export function walkBlockDeclarations(
block: BlockStatement,
block: BlockStatement | Program,
onIdent: (node: Identifier) => void
) {
for (const stmt of block.body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
for (const id of extractIdentifiers(decl.id)) {
onIdent(id)
}
}
} else if (
stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration'
) {
if (stmt.declare || !stmt.id) continue
onIdent(stmt.id)
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`sfc ref transform $ unwrapping 1`] = `
"import { ref, shallowRef } from 'vue'
export default {
setup(__props, { expose }) {
expose()
let foo = (ref())
let a = (ref(1))
let b = (shallowRef({
count: 0
}))
let c = () => {}
let d
return { foo, a, b, c, d, ref, shallowRef }
}
}"
`;

exports[`sfc ref transform $ref & $shallowRef declarations 1`] = `
"import { ref as _ref, shallowRef as _shallowRef } from 'vue'
export default {
setup(__props, { expose }) {
expose()
let foo = _ref()
let a = _ref(1)
let b = _shallowRef({
count: 0
})
let c = () => {}
let d
return { foo, a, b, c, d }
}
}"
`;

exports[`sfc ref transform usage in normal <script> 1`] = `
"import { ref as _ref } from 'vue'
export default {
setup() {
let count = _ref(0)
const inc = () => count.value++
return ({ count })
}
}
"
`;
exports[`sfc ref transform usage with normal <script> + <script setup> 1`] = `
"import { ref as _ref } from 'vue'
let a = _ref(0)
let c = _ref(0)
export default {
setup(__props, { expose }) {
expose()
let b = _ref(0)
let c = 0
function change() {
a.value++
b.value++
c++
}
return { a, c, b, change }
}
}"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { compileSFCScript as compile, assertCode } from './utils'

// this file only tests integration with SFC - main test case for the ref
// transform can be found in <root>/packages/ref-transform/__tests__
describe('<script setup> ref sugar', () => {
function compileWithRefSugar(src: string) {
describe('sfc ref transform', () => {
function compileWithRefTransform(src: string) {
return compile(src, { refSugar: true })
}

test('$ unwrapping', () => {
const { content, bindings } = compileWithRefSugar(`<script setup>
const { content, bindings } = compileWithRefTransform(`<script setup>
import { ref, shallowRef } from 'vue'
let foo = $(ref())
let a = $(ref(1))
Expand Down Expand Up @@ -46,7 +46,7 @@ describe('<script setup> ref sugar', () => {
})

test('$ref & $shallowRef declarations', () => {
const { content, bindings } = compileWithRefSugar(`<script setup>
const { content, bindings } = compileWithRefTransform(`<script setup>
let foo = $ref()
let a = $ref(1)
let b = $shallowRef({
Expand Down Expand Up @@ -81,6 +81,58 @@ describe('<script setup> ref sugar', () => {
})
})

test('usage in normal <script>', () => {
const { content } = compileWithRefTransform(`<script>
export default {
setup() {
let count = $ref(0)
const inc = () => count++
return $$({ count })
}
}
</script>`)
expect(content).not.toMatch(`$ref(0)`)
expect(content).toMatch(`import { ref as _ref } from 'vue'`)
expect(content).toMatch(`let count = _ref(0)`)
expect(content).toMatch(`count.value++`)
expect(content).toMatch(`return ({ count })`)
assertCode(content)
})

test('usage with normal <script> + <script setup>', () => {
const { content, bindings } = compileWithRefTransform(`<script>
let a = $ref(0)
let c = $ref(0)
</script>
<script setup>
let b = $ref(0)
let c = 0
function change() {
a++
b++
c++
}
</script>`)
// should dedupe helper imports
expect(content).toMatch(`import { ref as _ref } from 'vue'`)

expect(content).toMatch(`let a = _ref(0)`)
expect(content).toMatch(`let b = _ref(0)`)

// root level ref binding declared in <script> should be inherited in <script setup>
expect(content).toMatch(`a.value++`)
expect(content).toMatch(`b.value++`)
// c shadowed
expect(content).toMatch(`c++`)
assertCode(content)
expect(bindings).toStrictEqual({
a: BindingTypes.SETUP_REF,
b: BindingTypes.SETUP_REF,
c: BindingTypes.SETUP_REF,
change: BindingTypes.SETUP_CONST
})
})

describe('errors', () => {
test('defineProps/Emit() referencing ref declarations', () => {
expect(() =>
Expand Down
57 changes: 49 additions & 8 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,34 @@ export function compileScript(
return script
}
try {
const scriptAst = _parse(script.content, {
let content = script.content
let map = script.map
const scriptAst = _parse(content, {
plugins,
sourceType: 'module'
}).program.body
const bindings = analyzeScriptBindings(scriptAst)
let content = script.content
}).program
const bindings = analyzeScriptBindings(scriptAst.body)
if (enableRefTransform && shouldTransformRef(content)) {
const s = new MagicString(source)
const startOffset = script.loc.start.offset
const endOffset = script.loc.end.offset
const { importedHelpers } = transformRefAST(scriptAst, s, startOffset)
if (importedHelpers.length) {
s.prepend(
`import { ${importedHelpers
.map(h => `${h} as _${h}`)
.join(', ')} } from 'vue'\n`
)
}
s.remove(0, startOffset)
s.remove(endOffset, source.length)
content = s.toString()
map = s.generateMap({
source: filename,
hires: true,
includeContent: true
}) as unknown as RawSourceMap
}
if (cssVars.length) {
content = rewriteDefault(content, `__default__`, plugins)
content += genNormalScriptCssVarsCode(
Expand All @@ -184,8 +206,9 @@ export function compileScript(
return {
...script,
content,
map,
bindings,
scriptAst
scriptAst: scriptAst.body
}
} catch (e) {
// silently fallback if parse fails since user may be using custom
Expand Down Expand Up @@ -629,6 +652,23 @@ export function compileScript(
walkDeclaration(node, setupBindings, userImportAlias)
}
}

// apply ref transform
if (enableRefTransform && shouldTransformRef(script.content)) {
warnExperimental(
`ref sugar`,
`https://github.com/vuejs/rfcs/discussions/369`
)
const { rootVars, importedHelpers } = transformRefAST(
scriptAst,
s,
scriptStartOffset!
)
refBindings = rootVars
for (const h of importedHelpers) {
helperImports.add(h)
}
}
}

// 2. parse <script setup> and walk over top level statements
Expand Down Expand Up @@ -862,17 +902,18 @@ export function compileScript(
}

// 3. Apply ref sugar transform
if (enableRefTransform && shouldTransformRef(source)) {
if (enableRefTransform && shouldTransformRef(scriptSetup.content)) {
warnExperimental(
`ref sugar`,
`https://github.com/vuejs/rfcs/discussions/369`
)
const { rootVars, importedHelpers } = transformRefAST(
scriptSetupAst,
s,
startOffset
startOffset,
refBindings
)
refBindings = rootVars
refBindings = refBindings ? [...refBindings, ...rootVars] : rootVars
for (const h of importedHelpers) {
helperImports.add(h)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,21 @@ exports[`nested scopes 1`] = `
b.value++ // outer b
c++ // outer c
let bar = _ref(0)
bar.value++ // outer bar
function foo({ a }) {
a++ // inner a
b.value++ // inner b
let c = _ref(0)
c.value++ // inner c
let d = _ref(0)
const bar = (c) => {
function bar(c) {
c++ // nested c
d.value++ // nested d
}
bar() // inner bar
if (true) {
let a = _ref(0)
Expand Down
Loading

0 comments on commit 06051c4

Please sign in to comment.