Skip to content

Commit 9bc9146

Browse files
authored
Merge pull request TypeStrong#92 from wonderful-panda/support-jsx-in-vue
Support jsx in vue
2 parents 840d0fd + 2355c40 commit 9bc9146

File tree

11 files changed

+222
-4
lines changed

11 files changed

+222
-4
lines changed

src/VueProgram.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ class VueProgram {
8585
const host = ts.createCompilerHost(programConfig.options);
8686
const realGetSourceFile = host.getSourceFile;
8787

88+
const getScriptKind = (lang: string) => {
89+
if (lang === "ts") {
90+
return ts.ScriptKind.TS;
91+
} else if (lang === "tsx") {
92+
return ts.ScriptKind.TSX;
93+
} else if (lang === "jsx") {
94+
return ts.ScriptKind.JSX;
95+
} else {
96+
// when lang is "js" or no lang specified
97+
return ts.ScriptKind.JS;
98+
}
99+
}
100+
88101
// We need a host that can parse Vue SFCs (single file components).
89102
host.getSourceFile = (filePath, languageVersion, onError) => {
90103
// first check if watcher is watching file - if not - check it's mtime
@@ -110,8 +123,21 @@ class VueProgram {
110123

111124
// get typescript contents from Vue file
112125
if (source && VueProgram.isVue(filePath)) {
113-
const parsed = vueParser.parse(source.text, 'script', { lang: ['ts', 'tsx', 'js', 'jsx'] });
114-
source = ts.createSourceFile(filePath, parsed, languageVersion, true);
126+
let parsed: string;
127+
let kind: ts.ScriptKind;
128+
for (const lang of ['ts', 'tsx', 'js', 'jsx']) {
129+
parsed = vueParser.parse(source.text, 'script', { lang: [lang], emptyExport: false });
130+
if (parsed) {
131+
kind = getScriptKind(lang);
132+
break;
133+
}
134+
}
135+
if (!parsed) {
136+
// when script tag has no lang, or no script tag given
137+
parsed = vueParser.parse(source.text, 'script');
138+
kind = ts.ScriptKind.JS;
139+
}
140+
source = ts.createSourceFile(filePath, parsed, languageVersion, true, kind);
115141
}
116142

117143
return source;

test/integration/vue.spec.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,91 @@ describe('[INTEGRATION] vue', function () {
147147
callback();
148148
});
149149
});
150+
151+
[
152+
'example-ts.vue',
153+
'example-tsx.vue',
154+
'example-js.vue',
155+
'example-jsx.vue',
156+
'example-nolang.vue'
157+
].forEach(fileName => {
158+
it('should be able to extract script from ' + fileName, function () {
159+
createCompiler({ vue: true, tsconfig: 'tsconfig-langs.json' });
160+
var sourceFilePath = path.resolve(compiler.context, 'src/langs/' + fileName)
161+
var source = checker.program.getSourceFile(sourceFilePath);
162+
expect(source).to.not.be.undefined;
163+
// remove padding lines
164+
var text = source.text.replace(/^\s*\/\/.*$\n/gm, '');
165+
expect(text.startsWith('/* OK */')).to.be.true;
166+
});
167+
});
168+
169+
function groupByFileName(errors) {
170+
var ret = {
171+
'example-ts.vue': [],
172+
'example-tsx.vue': [],
173+
'example-js.vue': [],
174+
'example-jsx.vue': [],
175+
'example-nolang.vue': []
176+
};
177+
for (var error of errors) {
178+
ret[path.basename(error.file)].push(error);
179+
}
180+
return ret;
181+
}
182+
183+
describe('should be able to compile *.vue with each lang', function () {
184+
var errors;
185+
before(function(callback) {
186+
createCompiler({ vue: true, tsconfig: 'tsconfig-langs.json' });
187+
compiler.run(function(error, stats) {
188+
errors = groupByFileName(stats.compilation.errors);
189+
callback();
190+
});
191+
});
192+
it("lang=ts", function() {
193+
expect(errors['example-ts.vue'].length).to.be.equal(0);
194+
})
195+
it("lang=tsx", function() {
196+
expect(errors['example-tsx.vue'].length).to.be.equal(0);
197+
});
198+
it("lang=js", function() {
199+
expect(errors['example-js.vue'].length).to.be.equal(0);
200+
});
201+
it("lang=jsx", function() {
202+
expect(errors['example-jsx.vue'].length).to.be.equal(0);
203+
});
204+
it("no lang", function() {
205+
expect(errors['example-nolang.vue'].length).to.be.equal(0);
206+
});
207+
});
208+
209+
describe('should be able to detect errors in *.vue', function () {
210+
var errors;
211+
before(function(callback) {
212+
// tsconfig-langs-strict.json === tsconfig-langs.json + noUnusedLocals
213+
createCompiler({ vue: true, tsconfig: 'tsconfig-langs-strict.json' });
214+
compiler.run(function(error, stats) {
215+
errors = groupByFileName(stats.compilation.errors);
216+
callback();
217+
});
218+
});
219+
it("lang=ts", function() {
220+
expect(errors['example-ts.vue'].length).to.be.equal(1);
221+
expect(errors['example-ts.vue'][0].rawMessage).to.match(/'a' is declared but/);
222+
})
223+
it("lang=tsx", function() {
224+
expect(errors['example-tsx.vue'].length).to.be.equal(1);
225+
expect(errors['example-tsx.vue'][0].rawMessage).to.match(/'a' is declared but/);
226+
});
227+
it("lang=js", function() {
228+
expect(errors['example-js.vue'].length).to.be.equal(0);
229+
});
230+
it("lang=jsx", function() {
231+
expect(errors['example-jsx.vue'].length).to.be.equal(0);
232+
});
233+
it("no lang", function() {
234+
expect(errors['example-nolang.vue'].length).to.be.equal(0);
235+
});
236+
});
150237
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<div>Example</div>
3+
</template>
4+
5+
<script lang="js">
6+
/* OK */
7+
import Vue from "vue";
8+
export default Vue.extend({
9+
name: "ExampleJs",
10+
methods: {
11+
foo() {
12+
const a = 1; // noUnusedLocals
13+
}
14+
}
15+
});
16+
</script>
17+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script lang="jsx">
2+
/* OK */
3+
import Vue from "vue";
4+
export default Vue.extend({
5+
name: "ExampleJsx",
6+
render() {
7+
const a = 1; // noUnusedLocals
8+
return <div>Example</div>;
9+
}
10+
});
11+
</script>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<div>Example</div>
3+
</template>
4+
5+
<script>
6+
/* OK */
7+
import Vue from "vue";
8+
export default Vue.extend({
9+
name: "ExampleNolang",
10+
methods: {
11+
foo() {
12+
const a = 1; // noUnusedLocals
13+
}
14+
}
15+
});
16+
</script>
17+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<div>{{ text }}</div>
3+
</template>
4+
5+
<script lang="ts">
6+
/* OK */
7+
import Vue from "vue";
8+
export default Vue.extend({
9+
name: "ExampleJs",
10+
computed: {
11+
text(): string {
12+
const a = 1; // noUnusedLocals
13+
return "Example";
14+
}
15+
}
16+
});
17+
</script>
18+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script lang="tsx">
2+
/* OK */
3+
import Vue, { VNode } from "vue";
4+
export default Vue.extend({
5+
name: "ExampleTsx",
6+
render(): VNode {
7+
const a = 1; // noUnusedLocals
8+
return <div>Example</div>;
9+
}
10+
});
11+
</script>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Vue from "vue";
2+
import ExampleTsx from "./example-tsx.vue";
3+
4+
new Vue({
5+
components: { ExampleTsx }
6+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"compilerOptions": {
3+
"jsx": "preserve",
4+
"noUnusedLocals": true
5+
},
6+
"include": [
7+
"src/langs/*.ts",
8+
"src/langs/*.vue"
9+
],
10+
"exclude": [
11+
"node_modules"
12+
]
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"jsx": "preserve"
4+
},
5+
"include": [
6+
"src/langs/*.ts",
7+
"src/langs/*.vue"
8+
],
9+
"exclude": [
10+
"node_modules"
11+
]
12+
}

0 commit comments

Comments
 (0)