Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Support linting vue/html file #2099

Closed
nrip-monotype opened this issue Jan 22, 2017 · 35 comments
Closed

Support linting vue/html file #2099

nrip-monotype opened this issue Jan 22, 2017 · 35 comments

Comments

@nrip-monotype
Copy link

nrip-monotype commented Jan 22, 2017

Feature Request

  • TSLint version: 4.3.1
  • TypeScript version: 2.1.5
  • Running TSLint via: Node.js API

TypeScript code being linted

<template>
  <q-layout>
    <div slot="header" class="toolbar">
      <q-toolbar-title :padding="0">
        Quasar Framework v{{quasarVersion}}
      </q-toolbar-title>
    </div>

    <!--
      Replace following "div" with
      "<router-view class="layout-view">" component
      if using subRoutes
    -->
    <div class="layout-view">
      <div class="logo-container non-selectable no-pointer-events">
        <div class="logo" :style="position">
          <img src="~assets/quasar-logo.png">
          <p class="caption text-center">
            <span class="desktop-only">Move your mouse!!!.</span>
            <span class="touch-only">Touch screen and move.</span>
          </p>
        </div>
      </div>
    </div>
  </q-layout>
</template>

<script lang="ts">
  import * as Quasar from 'quasar';
  import { Utils } from 'quasar';
  import * as Vue from 'vue';
  import Component from 'vue-class-component';

  const moveForce = 30;
  const rotateForce = 40;

  @Component({
  })
  export default class Index extends Vue {
    rotateX: number;
    rotateY: number;
    moveY: number;
    moveX: number;
    quasarVersion: string;
   ...  
 }
</script>

<style lang="stylus">
....
</style>

with tslint.json configuration:

{
  "rules": {
    "class-name": true,
    "curly": true,
    "eofline": false,
    "expr" : true,
    "forin": true,
    "indent": [true, "spaces"],
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [true, 140],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": true,
    "no-eval": true,
    "no-string-literal": false,
    "no-switch-case-fall-through": true,
    "no-trailing-comma": true,
    "no-trailing-whitespace": true,
    "no-unused-expression": false,
    "no-unused-variable": true,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "one-line": [true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "quotemark": [true, "single"],
    "radix": false,
    "semicolon": [false],
    "triple-equals": [true, "allow-null-check"],
    "variable-name": false,
    "whitespace": [true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ]
  }
}

Actual behavior

Tries to lint the whole file and fails.

Expected behavior

check for code inside script tags only, similar to eslint.

@romandrahan
Copy link

romandrahan commented Apr 7, 2017

@nrip-monotype, you can use TSLint with Vue single file components by configuring vue-loader. For example in webpack 2 I'm using it like this (take a look at the loaders option of the vue-loader):

module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.ts$/,
                loader: 'tslint-loader',
                exclude: /(node_modules)/,
                options: {
                    configFile: 'tslint.json'
                }
            },
            {
                test: /\.ts$/,
                exclude: /node_modules|vue\/src/,
                loader: 'ts-loader',
                options: {
                    appendTsSuffixTo: [/\.vue$/],
                    transpileOnly: true,
                    isolatedModules: true
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        ts: 'ts-loader!tslint-loader'
                    }
                }
            },
        }
    }
}

@lucastheisen
Copy link

lucastheisen commented Apr 13, 2017

@romandragan, your suggestion works, but seems to have issues with no-consecutive-blank-lines. It seems as if the markup elements are replaced by empty lines, for example:

<template>
   <span class='hello'>hello world</span>
</tempalte>
<script lang="ts">
    return {};
</script>
<style>
    .hello { background-color: pink }
</style>

Is seen by tslint as:





    return {};




If i set "no-consecutive-blank-lines": [true, 3] it fails, but "no-consecutive-blank-lines": [true, 4] succeeds... Any idea how got get around this? (short of another plugin that strips leading and trailing whitespace...)

@romandrahan
Copy link

@lucastheisen, same issue for me 😞 Trying to find a solution...

@jklmli
Copy link

jklmli commented Apr 25, 2017

@romandragan's setup worked for me, but something to note is that the typeCheck flag for tslint-loader doesn't work in vue-loader. You can still use it normally outside of vue-loader.

module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.ts$/,
                loader: 'tslint-loader',
                exclude: /(node_modules)/,
                options: {
                    configFile: 'tslint.json'
                }
            },
            {
                test: /\.ts$/,
                exclude: /node_modules|vue\/src/,
                loader: 'ts-loader',
                options: {
                    appendTsSuffixTo: [/\.vue$/],
                    transpileOnly: true,
                    isolatedModules: true,
                    typeCheck: true // This is ok.
                }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        ts: 'ts-loader!tslint-loader' // Can't append `?typeCheck` here.
                    }
                }
            },
        }
    }
}

Trying to use type-checked rules in vue-loader yields an error like this:

ERROR in ./app.ts
(10,29): error TS2307: Cannot find module './components/sidebar.vue'.

ERROR in ./~/ts-loader!./~/tslint-loader?formatter=verbose&typeCheck!./~/vue-loader/lib/selector.js?type=script&index=0!./components/sidebar.vue
Module build failed: Error:
Invalid source file: /absolute/path/to/sidebar.vue. Ensure that the files supplied to lint have a .ts, .tsx, .js or .jsx extension.

@jklmli
Copy link

jklmli commented Apr 25, 2017

I'm also getting the same no-consecutive-blank-lines false positive as @lucastheisen and @romandragan.

@pixel1234561
Copy link

pixel1234561 commented Apr 30, 2017

I'm getting this ERROR in Entry module not found: Error: Can't resolve 'ts-loader!tslint-loader' when using this config:

            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: {
                        ts: 'ts-loader!tslint-loader'
                    }
                }
            },

@romandrahan
Copy link

@aromancev, what version of webpack are you using? Do you have ts-loader and tslint-loader npm modules installed?

@pixel1234561
Copy link

pixel1234561 commented May 1, 2017

@romandragan, here is the npm config im using:

"tslint": "^5.1.0",
"ts-loader": "^2.0.3",
"webpack": "^2.4.1",

And this is the webpack config:

      {
        test: /\.tsx?$/,
        enforce: 'pre',
        loader: 'tslint-loader'
      },
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          appendTsSuffixTo: [/\.vue$/]
        }
      },

ts-loader and tslint-loader both installed and work fine since i get correct lint from *.ts files.

@romandrahan
Copy link

As a workaround we can separate TypeScript logic from .vue file and remove tslint-loader from vue loaders. Then, no-consecutive-blank-lines disappears.

@manisenkov
Copy link

@romandragan this works, but it would be nice to lint code inside <script> tag as well: this is needed for editor support and it's much easier to run tslint command separately, rather then through whole webpack flow.

@galenyuan
Copy link

@adidahiya any plan for this feature?
or any plan for support plugin system in tslint like eslint plugins?

@prograhammer
Copy link

prograhammer commented Dec 4, 2017

If you are using Webpack, I've got .vue files working and linting at typechecker level with no no-consecutive-blank-lines issue in fork-ts-checker-webpack-plugin. You can see the PR here (and npm install from a test branch and use it right now, see top of PR thread): TypeStrong/fork-ts-checker-webpack-plugin#77

Also, if you are using VSCode editor, check out the TSLint Vue extension.

@Toilal
Copy link

Toilal commented Dec 20, 2017

I have created a pull request that implements a plugin feature in tslint, and wrote the first plugin able to parse "vue" single file components.

#3596
https://github.com/Toilal/tslint-plugin-vue

This is a work in progress, but feel free to try. You can add the plugin with npm install git+https://https://github.com/Toilal/tslint-plugin-vue and enable it by adding plugins: 'vue' into tslint.json. Currently it doesn't work with -p/--project option, you have to use -c/--config option.

@NicoCevallos
Copy link

Hi guys, another alternative solution, for now, is moving the script tag at the beginning of the file and add the /* tslint:disable:no-consecutive-blank-lines */ in the last line to don't take into account this rule.
Check the image below
image

@w1ndy
Copy link

w1ndy commented Jan 19, 2018

Vue-loader can actually extract JavaScript or TypeScript from vue files, so parsing vue files is not a problem for webpack. However, TSLint still throws "Invalid source file" because file names are ended with ".vue" if typeCheck is enabled...

In addition, we can make TSLint stop complaining consecutive blank lines by allowing consective blank lines at the beginning or the end of vue files, because vue-loader does not delete lines irrelevant to TypeScript but clear them instead to maintain correct line numbers.

@ajafff
Copy link
Contributor

ajafff commented Jan 19, 2018

I'm currently working on a linter that could solve these problems without the need for webpack: https://github.com/ajafff/wotan
This is currently a proof of concept and may or may not be integrated into TSLint. It's a complete rewrite from scratch and pretty much incompatible with TSLint.

It has the ability to transform files using a processor (similar to ESLint processors). A vue processor extracts the TypeScript code from the SFC, the linter only lints the code, then the processor maps the errors to the correct location in the original file. This even works with type checking.
You can see a sample output of linting a vue file here:
https://github.com/ajafff/wotan/blob/master/baselines/integration/processors/vue/default/hello.vue.lint#L29

It also allows automatic fixing. Same file as above automatically fixed: https://github.com/ajafff/wotan/blob/master/baselines/integration/processors/vue/default/hello.vue#L28

Once I have fixed fimbullinter/wotan#32 I will publish a release so you can try it on your real world code.
Note that the vue processor is not part of the main project but I'll make sure I publish a separate package for the processor.

@ffxsam
Copy link

ffxsam commented Jan 27, 2018

Does anyone know how I would set up tsconfig.json so that command-line tslint will lint the TS portion of my Vue files? It works fine in VS Code, but I'd like the command line to work as well.

@ajafff
Copy link
Contributor

ajafff commented Feb 1, 2018

I took a bit longer than I thought, but here it is: https://www.npmjs.com/package/@fimbul/wotan

You can even use the linter runtime (@fimbul/wotan) to execute TSLint rules. You only need @fimbul/heimdall, see https://github.com/fimbullinter/wotan/tree/master/packages/heimdall#readme

To lint Vue files, use @fimbul/ve (no typo, there is no 'u' in 've'): https://github.com/fimbullinter/wotan/tree/master/packages/ve#readme

@Hankszhang
Copy link

Is there a schedule when will tslint support linting vue/html file? @Toilal

@ajafff
Copy link
Contributor

ajafff commented Feb 25, 2018

I'm back with even greater news for everyone on this thread:

The latest version of wotan can execute TSLint rules according to your tslint.json and adds support for processors. That means you don't have to change your config and can start linting your Vue files now:

  1. Install
    yarn add -D @fimbul/wotan @fimbul/ve @fimbul/valtyr
    # or
    npm install --save-dev @fimbul/wotan @fimbul/ve @fimbul/valtyr
  2. Configure
    Add a new file .fimbullinter.yaml in the root directory of your project and add the following content:
    modules: "@fimbul/valtyr"
    valtyr:
      overrides:
        - files: "*.vue"
          processor: "@fimbul/ve"
  3. Run
    See examples below and adapt for your own use. Read the docs for more information about available CLI arguments.
    wotan # finds tsconfig.json and lints the whole project with type information according to your tslint.json
    wotan 'src/**/*.vue' -f verbose # lint all Vue files, use TSLint's verbose formatter
    wotan -p tsconfig.json -c tslint.json --fix # lint the whole project with tslint.json and fix failures
  4. Further Reading
    Wotan - CLI and configuration: https://github.com/fimbullinter/wotan/tree/master/packages/wotan#readme
    Valtýr - plugin for TSLint rules and formatters - "the TSLint runtime that's better than TSLint": https://github.com/fimbullinter/wotan/tree/master/packages/valtyr#readme
    Vé - processor for Vue single file components: https://github.com/fimbullinter/wotan/tree/master/packages/ve#readme
    Fimbullinter - why you should start using this project now: https://github.com/fimbullinter/wotan#readme
  5. Like, Share and Subscribe ⭐

@wujekbogdan
Copy link

@romandragan

you can use TSLint with Vue single file components by configuring vue-loader.

Thanks for the solution. But it's just a Webpack specific solution. It would be great if there was a generic solution that's not dependant on a build tool so that we could tslint Vue files compiled with Fusebox (or any other build tool).

@Assassyn
Copy link

Another solution for webpack.

I have created a simple loader which trims all whitespaces from a "file" provided by vue-loader and then add white space for a linter.

webpack.config.js:

test:/\.vue$/, loader: 'vue-loader', options: { loaders: { 'ts': [ 'vue-ts-loader', 'tslint-loader', path.resolve('./path/to/remove-whitespace-ts-loader.js') ],....

and then remove-whitespace-ts-loader:
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Szymon Sasin */ module.exports = function(source) { let result = source.replace(/^\s+|\s+$/g, '') result += '\r\n' return result; }
Any help to how to improve the code formatting welcomed

@mignonat
Copy link

It works fine and permits not to modify sources, thanks

@smitt04
Copy link

smitt04 commented Apr 2, 2018

Just so I am on the same page, is this not going to be fixed unless it is done as a PR?

@bobbylight
Copy link

Besides no-consecutive-blank-lines not working in this setup (which should be work-aroundable with a custom loader), I'm just noting that, even with recent webpack and vue-loader versions, I can't get the typeCheck: true working for *.vue files because tslint is unhappy with the extension (even with appendTsSuffixTo). This demo project gives a simple example to play around with.

@luxueyan
Copy link

mark

@nicokoenig
Copy link

Some more news here? 🤙

@zhanba
Copy link

zhanba commented Nov 23, 2018

If someone want to use cli to lint your .vue file, you can try vue-tslint. It's not perfect, but it works...

@jhanschoo
Copy link
Contributor

jhanschoo commented Nov 25, 2018

Note that @romandragan 's solution is now not best-practice (not sure if it was so previously). Instead, add tslint-loader to your use in \.ts$ rules like you would for usual typescript files; this is now vue-loader handles template files in general, delegating the different sections to your Webpack configuration for those respective extensions. The plugin component of vue-loader will extract the rules for \.ts$ and stream <script lang="ts"> blocks through these rules.

For those using Vue CLI, add a

config.module.rule('ts')
  .use('tslint-loader')
  .loader('tslint-loader');

statement to your chainWebpack option in your vue.config.js. For example,

vue.config.js

module.exports = {
  chainWebpack: (config) => {
    config.devtool('source-map');
    config.module.rule('ts')
      .use('tslint-loader')
      .loader('tslint-loader');
  },
  pluginOptions: {
    apollo: {
      enableMocks: true,
      enableEngine: true
    }
  }
}

@jacklp
Copy link

jacklp commented Feb 14, 2019

This worked for me

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = (baseConfig, env, defaultConfig) => {
  defaultConfig.resolve.extensions.push('.ts', '.tsx', '.vue', '.css', '.less', '.scss', '.sass', '.html')

  defaultConfig.module.rules.push( {
    test: /\.ts$/,
    exclude: /(node_modules)/,
    use: [
      {
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/]
          // transpileOnly: true,
        }
      },
      {
        loader: 'tslint-loader',
        options: {
          configFile: 'tslint.json',
          emitErrors: true,
        }
      }
    ]
  })

  defaultConfig.module.rules.push({ test: /\.less$/, loaders: [ 'style-loader', 'css-loader', 'less-loader' ] })
  defaultConfig.module.rules.push({ test: /\.scss$/, loaders: [ 'style-loader', 'css-loader', 'sass-loader' ] })
  defaultConfig.module.rules.push({ test: /\.styl$/, loader: 'style-loader!css-loader!stylus-loader' })

  defaultConfig.plugins.push(new ForkTsCheckerWebpackPlugin())

  return defaultConfig
}

@JoshuaKGoldberg
Copy link
Contributor

Per #4379, a Vue-specific workaround isn't going to be able to land in TSLint core:

Given the lack of discussion or concrete proposal here and the upcoming deprecation of TSLint in favor of ESLint (#4534), I'm going to go ahead and close this issue and related Vue-specific reports for housekeeping purposes.

If this is still an issue for you in typescript-eslint, I'd recommend filing an issue there. Best of luck!

@akoidan

This comment has been minimized.

@palantir palantir locked as off-topic and limited conversation to collaborators Oct 14, 2019
@adidahiya
Copy link
Contributor

@akoidan please bring up issues like that with the wotan project, not tslint

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests