Skip to content

Commit 7f2287c

Browse files
kiroushiFelixGraflmiller1990
authored
feat: support <script setup> in Vue 2.7 (supersedes #483) (#489)
* feat: support <script setup> in Vue 2.7 * refactor: remove unnecessary bindingMetadata * refactor: remove unnecessary refTransform * feat: add setup to generateSourceMap * chore: update Vue dependencies * fix: remove inline sourceMap in vue2 setup scripts * fix: update 2.x snapshot * fix: use appropriate package according to vue version * fix: rebase typo * fix: make script setup & script blocks work together * Update test.js.snap * fixing snapshots Co-authored-by: Felix Graf <[email protected]> Co-authored-by: Lachlan Miller <[email protected]>
1 parent 1cd403d commit 7f2287c

File tree

15 files changed

+216
-60
lines changed

15 files changed

+216
-60
lines changed

e2e/2.x/babel-in-package/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
},
99
"dependencies": {
1010
"source-map": "0.5.6",
11-
"vue": "^2.5.21",
12-
"vue-template-compiler": "^2.5.21"
11+
"vue": "^2.7.7",
12+
"vue-template-compiler": "^2.7.7"
1313
},
1414
"devDependencies": {
1515
"@babel/core": "^7.9.0",

e2e/2.x/basic/__snapshots__/test.js.snap

+2-24
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`generates source maps for .vue files 1`] = `
44
{
55
"file": "./components/Basic.vue",
6-
"mappings": ";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;eAEe;AACbA,MAAI,EAAE,OADO;AAEbC,UAAQ,EAAE;AACRC,kBAAc,EAAE,SAASA,cAAT,GAA0B;AACxC,aAAO;AACLC,WAAG,EAAE,KAAKC,OADL;AAELC,YAAI,EAAE,CAAC,KAAKD,OAFP;AAGLE,cAAM,EAAE,KAAKF;AAHR,OAAP;AAKD;AAPO,GAFG;AAWbG,MAAI,EAAE,SAASA,IAAT,GAAgB;AACpB,WAAO;AACLC,SAAG,EAAE,4BADA;AAELJ,aAAO,EAAE;AAFJ,KAAP;AAID,GAhBY;AAiBbK,SAAO,EAAE;AACPC,eAAW,EAAE,SAASA,WAAT,GAAuB;AAClC,WAAKN,OAAL,GAAe,CAAC,KAAKA,OAArB;AACD;AAHM;AAjBI,C",
6+
"mappings": ";;;;;;eACe;AACbA,MAAI,EAAE,OADO;AAEbC,UAAQ,EAAE;AACRC,kBAAc,EAAE,SAASA,cAAT,GAA0B;AACxC,aAAO;AACLC,WAAG,EAAE,KAAKC,OADL;AAELC,YAAI,EAAE,CAAC,KAAKD,OAFP;AAGLE,cAAM,EAAE,KAAKF;AAHR,OAAP;AAKD;AAPO,GAFG;AAWbG,MAAI,EAAE,SAASA,IAAT,GAAgB;AACpB,WAAO;AACLC,SAAG,EAAE,4BADA;AAELJ,aAAO,EAAE;AAFJ,KAAP;AAID,GAhBY;AAiBbK,SAAO,EAAE;AACPC,eAAW,EAAE,SAASA,WAAT,GAAuB;AAClC,WAAKN,OAAL,GAAe,CAAC,KAAKA,OAArB;AACD;AAHM;AAjBI,C",
77
"names": [
88
"name",
99
"computed",
@@ -21,29 +21,7 @@ exports[`generates source maps for .vue files 1`] = `
2121
"Basic.vue",
2222
],
2323
"sourcesContent": [
24-
"//
25-
//
26-
//
27-
//
28-
//
29-
//
30-
//
31-
//
32-
//
33-
//
34-
//
35-
//
36-
//
37-
//
38-
//
39-
//
40-
//
41-
//
42-
//
43-
//
44-
//
45-
//
46-
24+
"
4725
export default {
4826
name: 'basic',
4927
computed: {
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<div>
3+
<button @click="increase">Count: {{ num }}</button>
4+
<Basic />
5+
<span>{{ msg }}</span>
6+
</div>
7+
</template>
8+
9+
<script lang="ts" setup>
10+
import { ref } from 'vue'
11+
import Basic from './Basic.vue'
12+
13+
const num = ref(5)
14+
const greet = () => console.log('greet')
15+
const increase = () => {
16+
greet()
17+
num.value++
18+
}
19+
const msg = 'hello world'
20+
</script>

e2e/2.x/basic/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"test": "jest --no-cache --coverage test.js"
88
},
99
"dependencies": {
10-
"vue": "^2.5.21",
11-
"vue-template-compiler": "^2.5.21"
10+
"vue": "^2.7.7",
11+
"vue-template-compiler": "^2.7.7"
1212
},
1313
"devDependencies": {
1414
"@babel/core": "^7.9.0",

e2e/2.x/basic/test.js

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import PugRelative from './components/PugRelativeExtends.vue'
2020
import Jsx from './components/Jsx.vue'
2121
import Constructor from './components/Constructor.vue'
2222
import { compileStyle } from '@vue/component-compiler-utils'
23+
import ScriptSetup from './components/ScriptSetup'
2324
jest.mock('@vue/component-compiler-utils', () => ({
2425
...jest.requireActual('@vue/component-compiler-utils'),
2526
compileStyle: jest.fn(() => ({ errors: [], code: '' }))
@@ -156,6 +157,12 @@ test('processes SFC with no template', () => {
156157
expect(wrapper.element.tagName).toBe('SECTION')
157158
})
158159

160+
test('processes SFC with <script setup>', () => {
161+
const wrapper = mount(ScriptSetup)
162+
expect(wrapper.html()).toContain('Count: 5')
163+
expect(wrapper.html()).toContain('Welcome to Your Vue.js App')
164+
})
165+
159166
test('should pass properly "styleOptions" into "preprocessOptions"', () => {
160167
const filePath = resolve(__dirname, './components/Basic.vue')
161168
const fileString = readFileSync(filePath, { encoding: 'utf8' })

e2e/2.x/custom-transformers/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"test": "jest --no-cache --coverage test.js"
88
},
99
"dependencies": {
10-
"vue": "^2.5.21",
11-
"vue-template-compiler": "^2.5.21"
10+
"vue": "^2.7.7",
11+
"vue-template-compiler": "^2.7.7"
1212
},
1313
"devDependencies": {
1414
"@babel/core": "^7.9.0",

e2e/2.x/sass-importer/entry/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"test": "jest --no-cache --coverage test.js"
88
},
99
"dependencies": {
10-
"vue": "^2.5.21",
11-
"vue-template-compiler": "^2.5.21",
10+
"vue": "^2.7.7",
11+
"vue-template-compiler": "^2.7.7",
1212
"vue2-sass-importer-lib": "file:../lib",
1313
"vue2-sass-importer-sass-lib": "file:../sass-lib-v2"
1414
},

e2e/2.x/sass-importer/lib/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
"vue2-sass-importer-sass-lib": "file:../sass-lib-v1"
1515
},
1616
"peerDependencies": {
17-
"vue": "^2.5.21"
17+
"vue": "^2.7.7"
1818
}
1919
}

e2e/2.x/style/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"test": "node setup.js && jest --no-cache test.js"
88
},
99
"dependencies": {
10-
"vue": "^2.5.21",
11-
"vue-template-compiler": "^2.5.21"
10+
"vue": "^2.7.7",
11+
"vue-template-compiler": "^2.7.7"
1212
},
1313
"devDependencies": {
1414
"@babel/core": "^7.9.0",

packages/vue2-jest/lib/generate-code.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function addToSourceMap(node, result) {
1818

1919
module.exports = function generateCode(
2020
scriptResult,
21+
scriptSetupResult,
2122
templateResult,
2223
stylesResult,
2324
customBlocksResult,
@@ -26,8 +27,9 @@ module.exports = function generateCode(
2627
) {
2728
var node = new SourceNode(null, null)
2829

29-
if (scriptResult) {
30-
addToSourceMap(node, scriptResult)
30+
if (scriptResult || scriptSetupResult) {
31+
scriptResult && addToSourceMap(node, scriptResult)
32+
scriptSetupResult && addToSourceMap(node, scriptSetupResult)
3133
} else {
3234
node.add(
3335
`Object.defineProperty(exports, "__esModule", {\n` +

packages/vue2-jest/lib/map-lines.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const { SourceMapGenerator, SourceMapConsumer } = require('source-map')
2+
3+
// based on @vue/compiler-sfc mapLines
4+
module.exports = function mapLines(oldMap, newMap) {
5+
if (!oldMap) return newMap
6+
if (!newMap) return oldMap
7+
8+
const oldMapConsumer = new SourceMapConsumer(oldMap)
9+
const newMapConsumer = new SourceMapConsumer(newMap)
10+
const mergedMapGenerator = new SourceMapGenerator()
11+
12+
newMapConsumer.eachMapping(m => {
13+
if (m.originalLine == null) {
14+
return
15+
}
16+
17+
const origPosInOldMap = oldMapConsumer.originalPositionFor({
18+
line: m.originalLine,
19+
column: m.originalColumn
20+
})
21+
22+
if (origPosInOldMap.source == null) {
23+
return
24+
}
25+
26+
mergedMapGenerator.addMapping({
27+
generated: {
28+
line: m.generatedLine,
29+
column: m.generatedColumn
30+
},
31+
original: {
32+
line: origPosInOldMap.line, // map line
33+
// use current column, since the oldMap produced by @vue/compiler-sfc
34+
// does not
35+
column: m.originalColumn
36+
},
37+
source: origPosInOldMap.source,
38+
name: origPosInOldMap.name
39+
})
40+
})
41+
42+
// source-map's type definition is incomplete
43+
const generator = mergedMapGenerator
44+
oldMapConsumer.sources.forEach(sourceFile => {
45+
generator._sources.add(sourceFile)
46+
const sourceContent = oldMapConsumer.sourceContentFor(sourceFile)
47+
if (sourceContent != null) {
48+
mergedMapGenerator.setSourceContent(sourceFile, sourceContent)
49+
}
50+
})
51+
52+
generator._sourceRoot = oldMap.sourceRoot
53+
generator._file = oldMap.file
54+
return generator.toJSON()
55+
}

packages/vue2-jest/lib/process-custom-blocks.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const { getVueJestConfig, getCustomTransformer } = require('./utils')
2-
const vueOptionsNamespace = require('./constants').vueOptionsNamespace
32

43
function applyTransformer(
54
transformer,
@@ -17,7 +16,7 @@ function groupByType(acc, block) {
1716
return acc
1817
}
1918

20-
module.exports = function(allBlocks, filename, config) {
19+
module.exports = function(allBlocks, filename, componentNamespace, config) {
2120
const blocksByType = allBlocks.reduce(groupByType, {})
2221
const code = []
2322
for (const [type, blocks] of Object.entries(blocksByType)) {
@@ -29,7 +28,7 @@ module.exports = function(allBlocks, filename, config) {
2928
const codeStr = applyTransformer(
3029
transformer,
3130
blocks,
32-
vueOptionsNamespace,
31+
componentNamespace,
3332
filename,
3433
config
3534
)

packages/vue2-jest/lib/process.js

+60-6
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,19 @@ const stripInlineSourceMap = require('./utils').stripInlineSourceMap
88
const getCustomTransformer = require('./utils').getCustomTransformer
99
const loadSrc = require('./utils').loadSrc
1010
const babelTransformer = require('babel-jest').default
11-
const compilerUtils = require('@vue/component-compiler-utils')
1211
const generateCode = require('./generate-code')
12+
const mapLines = require('./map-lines')
13+
const vueComponentNamespace = require('./constants').vueComponentNamespace
14+
15+
let isVue27 = false
16+
let compilerUtils
17+
18+
try {
19+
compilerUtils = require('vue/compiler-sfc')
20+
isVue27 = true
21+
} catch (e) {
22+
compilerUtils = require('@vue/component-compiler-utils')
23+
}
1324

1425
function resolveTransformer(lang = 'js', vueJestConfig) {
1526
const transformer = getCustomTransformer(vueJestConfig['transform'], lang)
@@ -42,7 +53,33 @@ function processScript(scriptPart, filePath, config) {
4253
return result
4354
}
4455

45-
function processTemplate(template, filename, config) {
56+
function processScriptSetup(descriptor, filePath, config) {
57+
if (!descriptor.scriptSetup) {
58+
return null
59+
}
60+
const vueJestConfig = getVueJestConfig(config)
61+
const content = compilerUtils.compileScript(descriptor, {
62+
id: filePath,
63+
reactivityTransform: true,
64+
...vueJestConfig.compilerOptions
65+
})
66+
const contentMap = mapLines(descriptor.scriptSetup.map, content.map)
67+
68+
const transformer = resolveTransformer(
69+
descriptor.scriptSetup.lang,
70+
vueJestConfig
71+
)
72+
73+
const result = transformer.process(content.content, filePath, config)
74+
result.code = stripInlineSourceMap(result.code)
75+
result.map = mapLines(contentMap, result.map)
76+
77+
return result
78+
}
79+
80+
function processTemplate(descriptor, filename, config) {
81+
const { template, scriptSetup } = descriptor
82+
4683
if (!template) {
4784
return null
4885
}
@@ -53,6 +90,16 @@ function processTemplate(template, filename, config) {
5390
template.content = loadSrc(template.src, filename)
5491
}
5592

93+
let bindings
94+
if (isVue27 && scriptSetup) {
95+
const scriptSetupResult = compilerUtils.compileScript(descriptor, {
96+
id: filename,
97+
reactivityTransform: true,
98+
...vueJestConfig.compilerOptions
99+
})
100+
bindings = scriptSetupResult.bindings
101+
}
102+
56103
const userTemplateCompilerOptions = vueJestConfig.templateCompiler || {}
57104
const result = compilerUtils.compileTemplate({
58105
source: template.content,
@@ -63,9 +110,10 @@ function processTemplate(template, filename, config) {
63110
preprocessOptions: vueJestConfig[template.lang],
64111
...userTemplateCompilerOptions,
65112
compilerOptions: {
66-
optimize: false,
113+
...(!isVue27 ? { optimize: false } : {}),
67114
...userTemplateCompilerOptions.compilerOptions
68-
}
115+
},
116+
...(isVue27 ? { bindings } : {})
69117
})
70118

71119
logResultErrors(result)
@@ -91,16 +139,21 @@ function processStyle(styles, filename, config) {
91139
module.exports = function(src, filename, config) {
92140
const descriptor = compilerUtils.parse({
93141
source: src,
94-
compiler: VueTemplateCompiler,
142+
compiler: isVue27 ? undefined : VueTemplateCompiler,
95143
filename
96144
})
97145

98-
const templateResult = processTemplate(descriptor.template, filename, config)
146+
const componentNamespace =
147+
getVueJestConfig(config)['componentNamespace'] || vueComponentNamespace
148+
149+
const templateResult = processTemplate(descriptor, filename, config)
99150
const scriptResult = processScript(descriptor.script, filename, config)
151+
const scriptSetupResult = processScriptSetup(descriptor, filename, config)
100152
const stylesResult = processStyle(descriptor.styles, filename, config)
101153
const customBlocksResult = processCustomBlocks(
102154
descriptor.customBlocks,
103155
filename,
156+
componentNamespace,
104157
config
105158
)
106159

@@ -114,6 +167,7 @@ module.exports = function(src, filename, config) {
114167

115168
const output = generateCode(
116169
scriptResult,
170+
scriptSetupResult,
117171
templateResult,
118172
stylesResult,
119173
customBlocksResult,

packages/vue2-jest/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
"jest": "29.x",
3434
"semantic-release": "^15.13.2",
3535
"typescript": "^4.6.4",
36-
"vue": "^2.4.2",
37-
"vue-template-compiler": "^2.4.2"
36+
"vue": "^2.7.7",
37+
"vue-template-compiler": "^2.7.7"
3838
},
3939
"peerDependencies": {
4040
"@babel/core": "7.x",

0 commit comments

Comments
 (0)