Skip to content

Commit 8247ef8

Browse files
committed
fix: resolve vue.js cache bug and add e2e tests
1 parent 8b383df commit 8247ef8

File tree

4 files changed

+203
-11
lines changed

4 files changed

+203
-11
lines changed

README.md

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414

1515
## Features
1616

17-
* Faster [TypeScript](https://github.com/Microsoft/TypeScript) type checking and [ESLint](https://eslint.org/) linting (each on a separate process) 🏎
18-
* Support for modern TypeScript features like [project references](https://www.typescriptlang.org/docs/handbook/project-references.html) and [incremental mode](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#faster-subsequent-builds-with-the---incremental-flag)
19-
* Support for [Yarn PnP](https://classic.yarnpkg.com/en/docs/pnp/) 🧶
20-
* Nice errors reporting with the [code frame](https://babeljs.io/docs/en/next/babel-code-frame.html) formatter 🌈
17+
* Speeds up [TypeScript](https://github.com/Microsoft/TypeScript) type checking and [ESLint](https://eslint.org/) linting (by moving each to a separate process) 🏎
18+
* Supports modern TypeScript features like [project references](https://www.typescriptlang.org/docs/handbook/project-references.html) and [incremental mode](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#faster-subsequent-builds-with-the---incremental-flag)
19+
* Supports [Yarn PnP](https://classic.yarnpkg.com/en/docs/pnp/) 🧶
20+
* Supports [Vue Single File Component](https://vuejs.org/v2/guide/single-file-components.html) ✅ 
21+
* Displays nice error messages with the [code frame](https://babeljs.io/docs/en/next/babel-code-frame.html) formatter 🌈
2122

2223
## Installation
2324

@@ -194,6 +195,10 @@ Options for the issues filtering (`issues` option object).
194195
| `exclude` | `object` or `function` or `array` | `undefined` | Same as `include` but issues that match this predicate will be excluded. |
195196

196197
## Yarn PnP
198+
To enable Yarn PnP support, follow these steps:
199+
200+
<details>
201+
<summary>Expand Yarn PnP set up instruction</summary>
197202

198203
To enable Yarn PnP, you have to install [`ts-pnp`](https://github.com/arcanis/ts-pnp) and [`pnp-webpack-plugin`](https://github.com/arcanis/pnp-webpack-plugin) package:
199204

@@ -243,6 +248,120 @@ module.exports = {
243248
]
244249
};
245250
```
251+
</details>
252+
253+
## Vue.js
254+
255+
⚠️ There are additional **constraints** regarding Vue.js Single File Component support: ⚠️
256+
* It requires **TypeScript >= 3.8.0** and `"importsNotUsedAsValues": "preserve"` option in the `tsconfig.json` (it's a limitation of the `transpileOnly` mode from `ts-loader`)
257+
* It doesn't work with the `build` mode (project references)
258+
259+
To enable Vue.js support, follow these steps:
260+
261+
<details>
262+
<summary>Expand Vue.js set up instruction</summary>
263+
264+
1. Ensure you have all required packages installed:
265+
```sh
266+
# with npm
267+
npm install --save vue vue-class-component
268+
npm install --save-dev vue-loader ts-loader css-loader vue-template-compiler
269+
270+
# with yarn
271+
yarn add vue vue-class-component
272+
yarn add --dev vue-loader ts-loader css-loader vue-template-compiler
273+
```
274+
275+
2. Add `tsconfig.json` configuration:
276+
```json
277+
{
278+
"compilerOptions": {
279+
"experimentalDecorators": true,
280+
"jsx": "preserve",
281+
"target": "ES5",
282+
"lib": ["ES6", "DOM"],
283+
"baseUrl": ".",
284+
"paths": {
285+
"@/*": ["src/*"],
286+
"~/*": ["src/*"]
287+
},
288+
"sourceMap": true,
289+
"importsNotUsedAsValues": "preserve"
290+
},
291+
"include": [
292+
"src/**/*.ts",
293+
"src/**/*.vue"
294+
],
295+
"exclude": [
296+
"node_modules"
297+
]
298+
}
299+
```
300+
301+
3. Add `webpack.config.js` configuration:
302+
```js
303+
const path = require('path');
304+
const VueLoaderPlugin = require('vue-loader/lib/plugin');
305+
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
306+
307+
module.exports = {
308+
entry: './src/index.ts',
309+
output: {
310+
filename: 'index.js',
311+
path: path.resolve(__dirname, 'dist'),
312+
},
313+
module: {
314+
rules: [
315+
{
316+
test: /\.vue$/,
317+
loader: 'vue-loader'
318+
},
319+
{
320+
test: /\.ts$/,
321+
loader: 'ts-loader',
322+
exclude: /node_modules/,
323+
options: {
324+
appendTsSuffixTo: [/\.vue$/],
325+
transpileOnly: true
326+
}
327+
},
328+
{
329+
test: /\.css$/,
330+
loader: 'css-loader'
331+
},
332+
],
333+
},
334+
resolve: {
335+
extensions: ['.ts', '.js', '.vue', '.json'],
336+
alias: {
337+
'@': path.resolve(__dirname, './src'),
338+
'~': path.resolve(__dirname, './src'),
339+
}
340+
},
341+
plugins: [
342+
new VueLoaderPlugin(),
343+
new ForkTsCheckerWebpackPlugin({
344+
typescript: {
345+
extensions: {
346+
vue: true
347+
}
348+
}
349+
})
350+
]
351+
};
352+
```
353+
354+
4. Add `src/types/vue.d.ts` file to shim `.vue` modules:
355+
```typescript
356+
declare module "*.vue" {
357+
import Vue from "vue";
358+
export default Vue;
359+
}
360+
```
361+
362+
5. If you are working in VSCode, you can get the [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) extension to complete the developer workflow.
363+
364+
</details>
246365

247366
## Type-Only modules watching
248367

src/typescript-reporter/extension/TypeScriptEmbeddedExtension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ function createTypeScriptEmbeddedExtension({
122122
return host.watchFile(
123123
embeddedFileName,
124124
(innerFileName: string, eventKind: ts.FileWatcherEventKind) => {
125-
embeddedSourceCache.delete(innerFileName);
125+
embeddedSourceCache.delete(embeddedFileName);
126126
return callback(fileName, eventKind);
127127
},
128128
poolingInterval

test/e2e/TypeScriptVueExtension.spec.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ describe('TypeScript Vue Extension', () => {
2222
await sandbox.cleanup();
2323
});
2424

25-
it.each([{ async: true, webpack: '^4.0.0', typescript: '2.7.1', tsloader: '^5.0.0' }])(
25+
it.each([{ async: false, typescript: '^3.8.0', tsloader: '^7.0.0' }])(
2626
'reports semantic error for %p',
27-
async ({ async, webpack, typescript, tsloader }) => {
27+
async ({ async, typescript, tsloader }) => {
2828
await sandbox.load([
2929
await readFixture(join(__dirname, 'fixtures/environment/typescript-vue.fixture'), {
3030
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION: JSON.stringify(
3131
FORK_TS_CHECKER_WEBPACK_PLUGIN_VERSION
3232
),
3333
TS_LOADER_VERSION: JSON.stringify(tsloader),
3434
TYPESCRIPT_VERSION: JSON.stringify(typescript),
35-
WEBPACK_VERSION: JSON.stringify(webpack),
35+
WEBPACK_VERSION: JSON.stringify('^4.0.0'),
3636
WEBPACK_CLI_VERSION: JSON.stringify(WEBPACK_CLI_VERSION),
3737
WEBPACK_DEV_SERVER_VERSION: JSON.stringify(WEBPACK_DEV_SERVER_VERSION),
3838
ASYNC: JSON.stringify(async),
@@ -44,11 +44,72 @@ describe('TypeScript Vue Extension', () => {
4444
sandbox.spawn('npm run webpack-dev-server'),
4545
async
4646
);
47+
let errors: string[] = [];
4748

4849
// first compilation is successful
4950
await driver.waitForNoErrors();
5051

51-
// TODO: it seems that single-file components are broken on the ts-loader/typescript side
52+
// let's modify user model file
53+
await sandbox.patch(
54+
'src/component/LoggedIn.vue',
55+
"import User, { getUserName } from '@/model/User';",
56+
"import User from '@/model/User';"
57+
);
58+
59+
// next compilation should have missing function error
60+
errors = await driver.waitForErrors();
61+
expect(errors).toEqual([
62+
[
63+
'ERROR in src/component/LoggedIn.vue 28:24-35',
64+
"TS2304: Cannot find name 'getUserName'.",
65+
' 26 | ',
66+
' 27 | get userName() {',
67+
" > 28 | return this.user ? getUserName(this.user) : '';",
68+
' | ^^^^^^^^^^^',
69+
' 29 | }',
70+
' 30 | ',
71+
' 31 | async logout() {',
72+
].join('\n'),
73+
]);
74+
75+
// let's fix it
76+
await sandbox.patch(
77+
'src/component/LoggedIn.vue',
78+
"return this.user ? getUserName(this.user) : '';",
79+
"return this.user ? `${this.user.firstName} ${this.user.lastName}` : '';"
80+
);
81+
82+
await driver.waitForNoErrors();
83+
84+
// let's modify user model file again
85+
await sandbox.patch('src/model/User.ts', ' firstName?: string;\n', '');
86+
87+
// not we should have an error about missing firstName property
88+
errors = await driver.waitForErrors();
89+
expect(errors).toEqual([
90+
[
91+
'ERROR in src/component/LoggedIn.vue 28:37-46',
92+
"TS2339: Property 'firstName' does not exist on type 'User'.",
93+
' 26 | ',
94+
' 27 | get userName() {',
95+
" > 28 | return this.user ? `${this.user.firstName} ${this.user.lastName}` : '';",
96+
' | ^^^^^^^^^',
97+
' 29 | }',
98+
' 30 | ',
99+
' 31 | async logout() {',
100+
].join('\n'),
101+
[
102+
'ERROR in src/model/User.ts 11:16-25',
103+
"TS2339: Property 'firstName' does not exist on type 'User'.",
104+
' 9 | ',
105+
' 10 | function getUserName(user: User): string {',
106+
' > 11 | return [user.firstName, user.lastName]',
107+
' | ^^^^^^^^^',
108+
' 12 | .filter(name => name !== undefined)',
109+
" 13 | .join(' ');",
110+
' 14 | }',
111+
].join('\n'),
112+
]);
52113
}
53114
);
54115
});

test/e2e/fixtures/environment/typescript-vue.fixture

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"@/*": ["src/*"],
3737
"~/*": ["src/*"]
3838
},
39-
"sourceMap": true
39+
"sourceMap": true,
40+
"importsNotUsedAsValues": "preserve"
4041
},
4142
"include": [
4243
"src/**/*.ts",
@@ -69,9 +70,14 @@ module.exports = {
6970
loader: 'ts-loader',
7071
exclude: /node_modules/,
7172
options: {
72-
appendTsSuffixTo: [/\.vue$/]
73+
appendTsSuffixTo: [/\.vue$/],
74+
transpileOnly: true
7375
}
7476
},
77+
{
78+
test: /\.css$/,
79+
loader: 'css-loader'
80+
},
7581
],
7682
},
7783
resolve: {
@@ -96,3 +102,9 @@ module.exports = {
96102
})
97103
]
98104
};
105+
106+
/// src/vue-shim.d.ts
107+
declare module "*.vue" {
108+
import Vue from "vue";
109+
export default Vue;
110+
}

0 commit comments

Comments
 (0)