Skip to content

Commit

Permalink
Merge pull request #2066 from vuejs/ver-specific-eslint
Browse files Browse the repository at this point in the history
Vue 2/3 eslint rules. Fix #2015
  • Loading branch information
octref committed Jul 29, 2020
2 parents 8ba2b9a + 6846b50 commit 5730ead
Show file tree
Hide file tree
Showing 20 changed files with 435 additions and 262 deletions.
18 changes: 18 additions & 0 deletions .vscode/launch.json
Expand Up @@ -99,5 +99,23 @@
"smartStep": true,
"skipFiles": ["<node_internals>/**"]
},
{
"name": "E2E Test (Vue3)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/dist/test/vue3",
"--user-data-dir=${workspaceFolder}/test/vue3/data-dir",
"--disable-extensions",
"${workspaceFolder}/test/vue3/fixture"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/dist/test/**/*.js"],
"smartStep": true,
"skipFiles": ["<node_internals>/**"]
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

### 0.26.0

- Load different `eslint-plugin-vue` rulesets depending on workspace vue version. #2015.
- Remove leading empty line in diagnostic errors. #2067.
- `"vetur.completion.tagCasing": "initial"` causes double tag completion. #2053

Expand Down
27 changes: 3 additions & 24 deletions docs/linting-error.md
Expand Up @@ -14,35 +14,14 @@ You can selectively turn error checking off by `vetur.validation.[template/style

## Linting

Install [ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) for the best linting experience. Vetur's template linting is only for quick start and does not support rule configuration.
Vetur bundles [`eslint-plugin-vue`](https://eslint.vuejs.org) for template error checking. By default, Vetur loads the [`vue/essential`](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention-for-vue-js-2-x) ruleset for Vue 2 projects and [`vue3-essential`](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention-for-vue-js-3-x) ruleset for Vue 3 projects.

After you installed [ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), add `vue` to `eslint.validate` in VS Code config:

```json
{
"eslint.validate": [
"javascript",
"javascriptreact",
"vue"
]
}
```

When configured correctly, ESLint should work for both `<template>` and `<script>`.

#### Linting for `<template>`

Vetur bundles a version of [`eslint-plugin-vue`](https://eslint.vuejs.org/) for linting `<template>` section. Linting configuration is based on eslint-plugin-vue's [essential rule set](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention).

This linting is not configurable and based on a fixed version of `eslint-plugin-vue`. If you would like to configure the template linting rules:

To configure linting rules:
If you want to config ESLint rules, do the following:

- Turn off Vetur's template validation with `vetur.validation.template: false`
- Make sure you have the [ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). The errors will come from ESLint plugin, not Vetur.
- `yarn add -D eslint eslint-plugin-vue` in your workspace root
- Set ESLint rules in `.eslintrc`. An example:

- Set ESLint rules in `.eslintrc`. For example:
```json
{
"extends": [
Expand Down
4 changes: 2 additions & 2 deletions server/package.json
Expand Up @@ -25,8 +25,8 @@
"@starptech/prettyhtml": "^0.10.0",
"bootstrap-vue-helper-json": "^1.1.1",
"element-helper-json": "^2.0.6",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^6.2.1",
"eslint": "^7.5.0",
"eslint-plugin-vue": "^7.0.0-beta.0",
"gridsome-helper-json": "^1.0.3",
"js-beautify": "^1.10.0",
"lodash": "^4.17.4",
Expand Down
5 changes: 0 additions & 5 deletions server/src/embeddedSupport/util.ts

This file was deleted.

7 changes: 5 additions & 2 deletions server/src/modes/template/htmlMode.ts
Expand Up @@ -16,9 +16,10 @@ import { findDefinition } from './services/htmlDefinition';
import { getTagProviderSettings, IHTMLTagProvider, CompletionConfiguration } from './tagProviders';
import { getEnabledTagProviders } from './tagProviders';
import { DocumentContext } from '../../types';
import { VLSFormatConfig, VLSConfig, VLSFullConfig } from '../../config';
import { VLSFormatConfig, VLSFullConfig } from '../../config';
import { VueInfoService } from '../../services/vueInfoService';
import { getComponentInfoTagProvider } from './tagProviders/componentInfoTagProvider';
import { VueVersion } from '../../services/typescriptService/vueVersion';

export class HTMLMode implements LanguageMode {
private tagProviderSettings: CompletionConfiguration;
Expand All @@ -27,11 +28,12 @@ export class HTMLMode implements LanguageMode {

private config: any = {};

private lintEngine = createLintEngine();
private lintEngine: any;

constructor(
documentRegions: LanguageModelCache<VueDocumentRegions>,
workspacePath: string | undefined,
vueVersion: VueVersion,
private vueDocuments: LanguageModelCache<HTMLDocument>,
private vueInfoService?: VueInfoService
) {
Expand All @@ -40,6 +42,7 @@ export class HTMLMode implements LanguageMode {
this.embeddedDocuments = getLanguageModelCache<TextDocument>(10, 60, document =>
documentRegions.refreshAndGet(document).getSingleLanguageDocument('vue-html')
);
this.lintEngine = createLintEngine(vueVersion);
}

getId() {
Expand Down
6 changes: 4 additions & 2 deletions server/src/modes/template/index.ts
Expand Up @@ -17,6 +17,7 @@ import { VueInterpolationMode } from './interpolationMode';
import { IServiceHost } from '../../services/typescriptService/serviceHost';
import { T_TypeScript } from '../../services/dependencyService';
import { HTMLDocument, parseHTMLDocument } from './parser/htmlParser';
import { inferVueVersion } from '../../services/typescriptService/vueVersion';

type DocumentRegionCache = LanguageModelCache<VueDocumentRegions>;

Expand All @@ -28,11 +29,12 @@ export class VueHTMLMode implements LanguageMode {
tsModule: T_TypeScript,
serviceHost: IServiceHost,
documentRegions: DocumentRegionCache,
workspacePath: string | undefined,
workspacePath: string,
vueInfoService?: VueInfoService
) {
const vueDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => parseHTMLDocument(document));
this.htmlMode = new HTMLMode(documentRegions, workspacePath, vueDocuments, vueInfoService);
const vueVersion = inferVueVersion(tsModule, workspacePath);
this.htmlMode = new HTMLMode(documentRegions, workspacePath, vueVersion, vueDocuments, vueInfoService);
this.vueInterpolationMode = new VueInterpolationMode(tsModule, serviceHost, vueDocuments);
}
getId() {
Expand Down
16 changes: 12 additions & 4 deletions server/src/modes/template/services/htmlValidation.ts
Expand Up @@ -2,6 +2,7 @@ import { CLIEngine, Linter } from 'eslint';
import { configs } from 'eslint-plugin-vue';
import { TextDocument, Diagnostic, Range, DiagnosticSeverity } from 'vscode-languageserver-types';
import { resolve } from 'path';
import { VueVersion } from '../../../services/typescriptService/vueVersion';

function toDiagnostic(error: Linter.LintMessage): Diagnostic {
const line = error.line - 1;
Expand All @@ -28,13 +29,20 @@ export function doESLintValidation(document: TextDocument, engine: CLIEngine): D
return report.results[0] ? report.results[0].messages.map(toDiagnostic) : [];
}

export function createLintEngine() {
export function createLintEngine(vueVersion: VueVersion) {
const SERVER_ROOT = resolve(__dirname, '../../../../');
return new CLIEngine({

const basicConfig = {
useEslintrc: false,
// So ESLint can find the bundled eslint-plugin-vue
cwd: SERVER_ROOT,
...configs.base,
...configs.essential
...configs.base
};

const versionSpecificConfig = vueVersion === VueVersion.V30 ? configs['vue3-essential'] : configs.essential;

return new CLIEngine({
...basicConfig,
...versionSpecificConfig
});
}
45 changes: 1 addition & 44 deletions server/src/services/typescriptService/serviceHost.ts
Expand Up @@ -15,6 +15,7 @@ import { isVirtualVueTemplateFile, isVueFile } from './util';
import { logger } from '../../log';
import { ModuleResolutionCache } from './moduleResolutionCache';
import { globalScope } from './transformTemplate';
import { inferVueVersion, VueVersion } from './vueVersion';

const NEWLINE = process.platform === 'win32' ? '\r\n' : '\n';

Expand Down Expand Up @@ -466,50 +467,6 @@ function getScriptKind(tsModule: T_TypeScript, langId: string): ts.ScriptKind {
: tsModule.ScriptKind.JS;
}

enum VueVersion {
VPre25,
V25,
V30
}

function floatVersionToEnum(v: number) {
if (v < 2.5) {
return VueVersion.VPre25;
} else if (v < 3.0) {
return VueVersion.V25;
} else {
return VueVersion.V30;
}
}

function inferVueVersion(tsModule: T_TypeScript, workspacePath: string): VueVersion {
const packageJSONPath = tsModule.findConfigFile(workspacePath, tsModule.sys.fileExists, 'package.json');
try {
const packageJSON = packageJSONPath && JSON.parse(tsModule.sys.readFile(packageJSONPath)!);
const vueDependencyVersion = packageJSON.dependencies.vue || packageJSON.devDependencies.vue;

if (vueDependencyVersion) {
// use a sloppy method to infer version, to reduce dep on semver or so
const vueDep = vueDependencyVersion.match(/\d+\.\d+/)[0];
const sloppyVersion = parseFloat(vueDep);
return floatVersionToEnum(sloppyVersion);
}

const nodeModulesVuePackagePath = tsModule.findConfigFile(
path.resolve(workspacePath, 'node_modules/vue'),
tsModule.sys.fileExists,
'package.json'
);
const nodeModulesVuePackageJSON =
nodeModulesVuePackagePath && JSON.parse(tsModule.sys.readFile(nodeModulesVuePackagePath)!);
const nodeModulesVueVersion = parseFloat(nodeModulesVuePackageJSON.version.match(/\d+\.\d+/)[0]);

return floatVersionToEnum(nodeModulesVueVersion);
} catch (e) {
return VueVersion.VPre25;
}
}

function getParsedConfig(tsModule: T_TypeScript, workspacePath: string) {
const configFilename =
tsModule.findConfigFile(workspacePath, tsModule.sys.fileExists, 'tsconfig.json') ||
Expand Down
46 changes: 46 additions & 0 deletions server/src/services/typescriptService/vueVersion.ts
@@ -0,0 +1,46 @@
import * as path from 'path';
import { T_TypeScript } from '../dependencyService';

export enum VueVersion {
VPre25,
V25,
V30
}

function floatVersionToEnum(v: number) {
if (v < 2.5) {
return VueVersion.VPre25;
} else if (v < 3.0) {
return VueVersion.V25;
} else {
return VueVersion.V30;
}
}

export function inferVueVersion(tsModule: T_TypeScript, workspacePath: string): VueVersion {
const packageJSONPath = tsModule.findConfigFile(workspacePath, tsModule.sys.fileExists, 'package.json');
try {
const packageJSON = packageJSONPath && JSON.parse(tsModule.sys.readFile(packageJSONPath)!);
const vueDependencyVersion = packageJSON.dependencies.vue || packageJSON.devDependencies.vue;

if (vueDependencyVersion) {
// use a sloppy method to infer version, to reduce dep on semver or so
const vueDep = vueDependencyVersion.match(/\d+\.\d+/)[0];
const sloppyVersion = parseFloat(vueDep);
return floatVersionToEnum(sloppyVersion);
}

const nodeModulesVuePackagePath = tsModule.findConfigFile(
path.resolve(workspacePath, 'node_modules/vue'),
tsModule.sys.fileExists,
'package.json'
);
const nodeModulesVuePackageJSON =
nodeModulesVuePackagePath && JSON.parse(tsModule.sys.readFile(nodeModulesVuePackagePath)!);
const nodeModulesVueVersion = parseFloat(nodeModulesVuePackageJSON.version.match(/\d+\.\d+/)[0]);

return floatVersionToEnum(nodeModulesVueVersion);
} catch (e) {
return VueVersion.VPre25;
}
}

0 comments on commit 5730ead

Please sign in to comment.