Skip to content

Question: Usage with vue-eslint-parser possible? #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
alappe opened this issue Jan 2, 2020 · 22 comments
Closed

Question: Usage with vue-eslint-parser possible? #27

alappe opened this issue Jan 2, 2020 · 22 comments

Comments

@alappe
Copy link

alappe commented Jan 2, 2020

Thanks for your efforts regarding style and coffeescript!

I was very happy to see that AST support landed in a coffeescript release and wanted to see if I can integrate your plugins for prettier and eslint into our projects.

But for our vue projects, I'm running into the issue that those single file components seem to need a separate parser themselves – is there a way to combine it with your parser?

Regards
Andreas

@helixbass
Copy link
Owner

Hi, you're welcome!

I'm not familiar with the setup for vue apps (or how generally to use "multiple custom parsers" on a project with ESLint), but I'm happy to look into how that works with vue-eslint-parser

Looking at the docs, it seems like maybe you could just specify this in your ESLint config:

{
  "parser": "vue-eslint-parser",
  "parserOptions": {
     "parser": "eslint-plugin-coffee"
  }
}

Could you try that (after installing eslint-plugin-coffee + its required eslint and coffeescript dependencies per the README) and see if it "just works"?

@alappe
Copy link
Author

alappe commented Jan 2, 2020

Yes, that one I tried. It results in

…/src/components/styleguide/Gauge.vue
  0:0  error  Parsing error: unexpected (

✖ 1 problem (1 error, 0 warnings)

So I was unsure if it works with multiple parsers at all. I also tried switching both parsers, resulting in a similar message unexpected =.

@helixbass
Copy link
Owner

helixbass commented Jan 3, 2020

Ok,

Here is an example Vue project that I set up: https://github.com/helixbass/vue-coffee-eslint-example

The issue causing the Parsing error: unexpected ( is that vue-eslint-parser wraps template expressions in 0(...), which doesn't parse in Coffeescript

So I can open an issue with vue-eslint-parser to see if they can update to a Coffeescript-compatible wrapper

In the meantime, using Yarn selective dependency resolutions, you can swap in a forked version of vue-eslint-parser that uses [...] instead of 0(...):

In your package.json:

"resolutions": {
  "eslint-plugin-vue/vue-eslint-parser": "github:helixbass/vue-eslint-parser#0fff0dd1"
},

and then yarn install

Still it won't report ESLint errors in .coffee files in the yarn serve dev server, since Vue's cli-plugin-eslint hardcodes file extensions that don't include .coffee

So to work around that you can use a forked version of @vue/cli-plugin-eslint:

yarn add --dev github:helixbass/vue-cli#vue-cli-plugin-eslint-v4.1.2-dev-400-gitpkg

At that point it is successfully reporting ESLint errors for me both in the yarn serve dev server and when running yarn lint on the command line

I saw that some Vue ESLint rules may not work correctly out-of-the-box with Coffeescript (eg "vue/require-render-return"), so I disabled it manually

It should be possible to add support for overrides for these incompatible Vue rules to this plugin (opened #29)

Also I'm curious should you be able to use Coffeescript syntax inside Vue template expressions? I tried eg {{ someImplicitCall withArgs }} and it failed to parse

@GeoffreyBooth
Copy link

Also I'm curious should you be able to use Coffeescript syntax inside Vue template expressions? I tried eg {{ someImplicitCall withArgs }} and it failed to parse

I don't think so. I've been working with Vue for a few years, and it supports CoffeeScript in .vue files via script lang="coffee" just fine, but within template expressions I think it's only plain JavaScript: https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions. I've never heard of TypeScript in there either, basically if you want anything at all complicated you write it in a helper that the template calls out to.

@alappe
Copy link
Author

alappe commented Jan 3, 2020

Bam, thanks so much @helixbass! I will try to set it up as you explained, switching to yarn for now.

GeoffreyBooth is right—vue only supports javascript in the template section. A weird mix I personally don't enjoy at all, but the team decided 🤷‍♂️

@alappe
Copy link
Author

alappe commented Jan 3, 2020

Ok, much of the codebase seems to work, generating a lot of useful errors to fix.

I do see those two patterns:

0:0 error Parsing error: reserved word 'void', (--debug brings:

eslint:linter [stdin]:1:1: error: reserved word 'void'
void function($event){hook.uri = initialHook.name}

) with this line of the source: <MyComponent @clear="hook.uri = initialHook.name" />

and

0:0 error Parsing error: reserved word 'let' (--debug brings:

eslint:linter [stdin]:1:5: error: reserved word 'let'
for(let hook in hooks);

) with this line of the source: <li v-for="hook in hooks">…

This is the output of vue-eslint-parser, again producing output which is not compatible with CoffeeScript as well, right?

@GeoffreyBooth
Copy link

Those seem like CoffeeScript compiler errors.

@helixbass
Copy link
Owner

I don't think so. I've been working with Vue for a few years, and it supports CoffeeScript in .vue files via script lang="coffee" just fine, but within template expressions I think it's only plain JavaScript

Interesting. In that case vue-eslint-parser's behavior of using the specified parser (in this case eslint-plugin-coffee ie parsing as Coffeescript) for template expressions seems incorrect (ie it should fall back to a known JS parser for template expressions)?

This is the output of vue-eslint-parser, again producing output which is not compatible with CoffeeScript as well, right?

Looks that way. I can look more into it and keep extending the forked vue-eslint-parser dependency

But per the first part of this comment, it would seem like here also vue-eslint-parser should just be using a JS parser (rather than the specified parser) for its converted pseudo-JavaScript from eg v-for (ie you also can't use Coffeescript inside a v-for or an @clear)?

Those seem like CoffeeScript compiler errors

Yup if it's the same issue as the template expressions being wrapped with 0(...), then the sequence is:

  1. vue-eslint-parser finds a "nested expression" (eg a template expression, a v-for or an @clear)
  2. vue-eslint-parser does some "wrapping" of that expression eg hook in hooks -> for(let hook in hooks); in order to make it a complete expression that can be parsed
  3. vue-eslint-parser hands off that wrapped expression to eslint-plugin-coffee's AST parser
  4. eslint-plugin-coffee's AST parser asks the Coffeescript compiler to generate AST for the wrapped expression

So the Coffeescript compiler would be failing to parse the expressions that are wrapped in non-Coffeescript compatible wrappers

But what I'm now saying here is that 3. should instead hand off to a known JS AST parser (not the parser you specified)

I guess it hasn't been an issue because the only custom parsers that people have been using (babel-eslint, Typescript) are JS-compatible, but it seems like the wrong thing to do conceptually

@helixbass
Copy link
Owner

Ok yup looks like those issues are all resolved by making vue-eslint-parser use ESLint's "native" JS parser (espree) for all expressions

@alappe can you try updating your vue-eslint-parser dependency resolution in package.json to:

"resolutions": {
  "eslint-plugin-vue/vue-eslint-parser": "github:helixbass/vue-eslint-parser#936ba67b"
}

and run yarn install?

For me this is working in the example project, it's not choking on @clear/v-for and it's correctly using Coffeescript parser when linting .coffee files and <script lang="coffee"> blocks

@alappe
Copy link
Author

alappe commented Jan 5, 2020

@helixbass Yes, this looks great now. Some Expected to return a value in "diff" computed property, but I can disable vue/return-in-computed-property for that, as CoffeeScript always returns.

Thank you very much for your help!

@joallard
Copy link

Hey folks! I'm trying to make this work, but I'm getting confused between a lot of instructions.

I modified some rules to quash some non-errors:

    "rules": {
      "coffee/id-length": "off",
      "vue/require-render-return": "off",
      "vue/no-shared-component-data": "off",
      "vue/return-in-computed-property": "off"
    }

Now I'm getting this very curious one:

error: 'h' is not defined (no-undef) at src/main.js:7:16:
  5 |
  6 | new Vue({
> 7 |   render: h => h(App),
    |                ^
  8 | }).$mount('#app')
  9 |

Any idea what that is?

@helixbass
Copy link
Owner

@joallard I'm not super familiar with vue but I'm guessing it's trying to parse your src/main.js as Coffeescript rather than JS (since in Coffeescript that would parse as h(=> h(App)), at which point the no-undef error makes sense)

In the example Vue project that I linked above it looks like I used only .coffee, no .js files

So it your project has a mixture of .coffee and .js files, that's probably a separate thing to figure out how to instruct the ESLint parser to use the appropriate parser/config for each

I can probably take a deeper look at this over the weekend, but in the meantime you may want to look at #33, where we were able to use overrides to specify different ESLint parsers/config for JS vs Coffeescript (in a hybrid JS/Coffeescript project)

@joallard
Copy link

Thanks so much! I'm taking a break from this feature at the moment, but I'll check it out when I come back!

@GeoffreyBooth
Copy link

The code you pasted is both valid JS and valid CoffeeScript, so it's unclear which you intended it to be and which it's getting parsed as. Since it's in a file named main.js I assume it's JavaScript, but you have eslint checking it and applying the CoffeeScript rules to it?

Could you rename the file to main.coffee and convert this file's code into CoffeeScript? Remember that parentheses are required for arrow functions in CoffeeScript, so (h) => h App:

new Vue(
  render: (h) => h App
).$mount '#app'

I'm leaving out some optional parentheses and braces here so that this only parses as CoffeeScript, in case that causes an error to be thrown by something parsing this as JavaScript and therefore you get another clue to what's wrong.

@MentalGear
Copy link

MentalGear commented May 14, 2020

Great to see there're still so many people dedicated to Coffescript!
I'm also trying to get the babel linter working with the coffeescript linter, are there an progresses made by you @joallard or @alappe ?
Also thanks @helixbass for your packages to keep coffeescipt supported with popular frameworks!

Maybe we can also transform the custom patched version of

  • eslint-plugin-vue/vue-eslint-parser and
  • @vue/cli-plugin-eslint

into 2 custom packages that pull in the original dependecies as data sources and run a script after installation that patches the 0() with [], respective the custom eslint parser, for that we can always have the lastest version of those packages automatically coffescript eslint patched.
It's just an idea, but would this be doable as a npm package ? (I thought I ask beforehand, as I don't have much experience with custom npm packages)

@joallard
Copy link

Thanks for following up @MentalGear! I haven't been very reactive on this because that part took a lot of energy for me to set up. I managed to get something running, but it's still very messy and with a project on top. I'll try to see if I can share the base of my project that works.

@MentalGear
Copy link

MentalGear commented May 31, 2020

Turns out parser:babel-eslint is only needed, as per their docs, if you use non-standard features (not present in the last released ES specs yet). Coffescript already transpiles to ES6, so we should be alright without the babel eslint parser, even if you use babel.

I added this as a comment to my parser option:

    "parserOptions": {
      "parser": "eslint-plugin-coffee",
      "comment": "parser:babel-eslint not needed as coffeescript transpiles to ES6 already, and babel eslint would only be needed if non standard-set features are used (as per their docs)"
    },

@joallard
Copy link

So. I was able to re-do the steps, making a commit each step of the way. Here it is:
https://github.com/joallard/project-vue-coffee-pug/commits/master

Looks like nothing will work without:

  "resolutions": {
    "eslint-plugin-vue/vue-eslint-parser": "github:helixbass/vue-eslint-parser#936ba67b"
  }

@MentalGear
Copy link

Yes, that's because the package author had to use a customized version of eslint-parser to make it work. I was also confused by it, and hence, after investigation, I documented the changes made to package.json for others to easily understand within the readme file, and made a pull request. Hower, @helixbass would still have to approve it.
helixbass/vue-coffee-eslint-example@68d94b3?short_path=04c6e90#diff-04c6e90faac2675aa89e2176d2eec7d8

@joallard
Copy link

Tagging PR vuejs/vue-eslint-parser#62

@helixbass
Copy link
Owner

Ok ya to summarize the current status:

  1. The forked version of vue-eslint-parser is necessary because vue-eslint-parser incorrectly delegates parsing of template expressions to parserOptions.parser (eg the Coffeescript parser) rather than always using a JS parser. So you have to use yarn (because npm doesn't support selective dependency resolutions) and add to your package.json:
"resolutions": {
  "eslint-plugin-vue/vue-eslint-parser": "github:helixbass/vue-eslint-parser#936ba67b"
}
  1. The forked version of @vue/cli-plugin-eslint is necessary because that package hardcodes the file extensions targeted by ESLint, so this adds support for .coffee:
yarn add --dev github:helixbass/vue-cli#vue-cli-plugin-eslint-v4.1.2-dev-400-gitpkg
  1. Per @joallard's initial comment above, it seems likely that there's currently no way to support ESLint + a "hybrid" of .js + .coffee files in a single Vue project, due to the fact that the parserOptions.parser expected by vue-eslint-parser is "singular" (rather than eg a map from extensions/script lang's to ESLint custom parsers)

So then as far as moving forward, I opened vuejs/vue-eslint-parser#62 to try and get # 1 merged. And just opened vuejs/vue-cli#5581 for # 2 so we can see if that can also be merged

For # 3, the next step might be to further fork vue-eslint-parser and support something like:

parserOptions:
  parser:
    js: 'espree'
    coffee: 'eslint-plugin-coffee'

And I could correspondingly try and update the example repo to demonstrate configuration for a hybrid JS/Coffeescript project (I think there are still open questions around how to specify different ESLint configurations (eg different Coffeescript-specific rules) for JS vs Coffeescript)

Beyond that, for solid Vue support we'd want to identify eslint-plugin-vue rules that don't work with Coffeescript "out of the box" and add Coffeescript-compatible overrides (probably to this repo), that's tracked by #29

I think it'd be good to add a "Usage with Vue" section to the README documenting this setup + status, opened #41 (@MentalGear I think this project's README is a more important place to document this than in the example repo - that being said, I'm also not opposed to the example repo also ultimately being "self-documenting" with a useful README, just not highest priority)

@helixbass
Copy link
Owner

Turns out parser:babel-eslint is only needed, as per their docs, if you use non-standard features (not present in the last released ES specs yet). Coffescript already transpiles to ES6, so we should be alright without the babel eslint parser, even if you use babel

@MentalGear if I'm following, I think your mental model is incorrect here - eslint-plugin-coffee doesn't use the transpiled JS from the Coffeescript compiler at all, it runs directly against a Coffeescript-specific AST. So the version of JS emitted by the Coffeescript compiler should not be relevant to ESLint config

I'm also trying to get the babel linter working with the coffeescript linter

What was your initial use case here? Trying to get a Vue project that includes both JS and Coffeescript files working with ESLint?

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

No branches or pull requests

5 participants