Skip to content
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

Add support for TypeScript and document usage with Flow #326

Merged
merged 33 commits into from
Jun 24, 2018
Merged

Add support for TypeScript and document usage with Flow #326

merged 33 commits into from
Jun 24, 2018

Conversation

jorgegonzalez
Copy link
Contributor

@jorgegonzalez jorgegonzalez commented May 17, 2018

Fixes #315

Allows TypeScript and Flow files to be parsed by XO by setting "flow": true or "typescript": true in the config.

I think the error messages can be improved a bit. And I'm not 100% sure if that's where this particular block of code should go, but it works.

Also there is probably a better way to get the devDependencies list.

@pvdlg
Copy link
Contributor

pvdlg commented May 18, 2018

Instead of looking into the dev dependencies could we do something like that?

  • Add ts and tsx to the default extension list we parse
  • For files with a .ts or .tsx extension automatically set the parser to typescript-eslint-parser
  • Add a paragraph to the documentation explaining that in order to use flow, the parser must must be set to babel-parser (as we don't have a way to detect if a file is written in Flow or regular JS and set the parser automatically)

That would avoid adding the typescript and flow config options which are not really useful. That would also make things works out of the box for users (at least in the case of typescript).

@jorgegonzalez
Copy link
Contributor Author

I agree.

@jorgegonzalez
Copy link
Contributor Author

XO will show these errors if the proper dependencies are not installed:

image

image

image

index.js Outdated
@@ -102,6 +102,10 @@ module.exports.lintFiles = (patterns, opts) => {
});
}

if (paths.filter(path => path.includes('.ts')).length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use path.extname here instead of includes. I guess it should also includes .tsx files as well.

@pvdlg
Copy link
Contributor

pvdlg commented May 18, 2018

Ultimately we might consider using TSLint rather (or in combination of) to lint .ts and .tsx files but that would be another issue/PR/discussion.

@jorgegonzalez
Copy link
Contributor Author

jorgegonzalez commented May 18, 2018

There is a benefit in applying ESLint rules to TypeScript though, as TSLint is missing many of the nicer ESLint plugins (like eslint-plugin-unicorn), and it can be a little wonky in my experience.

@pvdlg
Copy link
Contributor

pvdlg commented May 18, 2018

Indeed. That would probably be a combination of both.

@jorgegonzalez
Copy link
Contributor Author

jorgegonzalez commented May 21, 2018

Hmmm getting an import/no-unresolved error for .ts files. But only if the extension is excluded in the import:

import test from './test'; //=> error
import test from './test.ts'; //=> no error

readme.md Outdated
To enable parsing of Flow-typed files, install `babel-eslint` and add it to the config:

```
npm i -D babel-eslint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use long-hand for documentation: npm install --save-dev babel-eslint

@pvdlg
Copy link
Contributor

pvdlg commented May 21, 2018

Hmmm getting an import/no-unresolved error for .ts files. But only if the extension is excluded in the import

Sounds like something to report to eslint-plugin-import project.

@sindresorhus
Copy link
Member

I think you need to set the import/parser option too.

readme.md Outdated
}
```

### TypeScript
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Place TypeScript first since it's more popular.

@jorgegonzalez
Copy link
Contributor Author

Oh cool, setting import/parsers seems like a better solution than what I had done previously.

@@ -25,7 +25,17 @@ module.exports = {
'import/core-modules': [
'electron',
'atom'
]
],
'import/resolver': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be as follow?

import/parsers: {
    typescript-eslint-parser: [ .ts, .tsx ]
}

Also should we add that only for ts/tsx files similarly to parser = 'typescript-eslint-parser'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually still need this or the import/unresolved rule throws.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it should be done dynamically in the index.js only for ts/tsx files I imagine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's an issue unless someone is intentionally mixing .js and .ts files in a directory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the import/parsers and import/resolver in index.js so it will be enabled only for ts and tsx files? This way that would avoid creating additional load and add a dependency to typescript-eslint-parser for non TS file.

if (paths.filter(filePath => isTSFile(path.extname(filePath))).length > 0) {
  opts.parser = 'typescript-eslint-parser';
  opts['import/resolver'].node.extensions = ['.js', '.jsx', '.ts', 'tsx'];
  opts['import/parsers'].['typescript-eslint-parser'] = ['.ts', 'tsx'];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey dokey

index.js Outdated

if (paths.filter(filePath => isTSFile(path.extname(filePath))).length > 0) {
opts.parser = 'typescript-eslint-parser';
opts['settings'] = {
Copy link
Contributor

@pvdlg pvdlg May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct as with the current code the entire settings value will be overridden.
We want to add/merge only the last leaf (extensions and typescript-eslint-parser).
So if the user config already contains something like:

{
settings: {
  'import/resolver': {
    node: {
      someConf: 'someValue'
    }
  }
}
}

We want to end up with:

{
settings: {
  'import/resolver': {
    node: {
      extensions: ['.js', '.jsx', '.ts', '.tsx'],
      someConf: 'someValue'
    }
  }
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You right

index.js Outdated

if (paths.filter(filePath => isTSFile(path.extname(filePath))).length > 0) {
opts.parser = 'typescript-eslint-parser';
opts['settings'] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opts['settings'] should be opts.settings for consistency.

index.js Outdated
@@ -102,6 +102,20 @@ module.exports.lintFiles = (patterns, opts) => {
});
}

const isTSFile = ext => ext === '.ts' || ext === '.tsx';

if (paths.filter(filePath => isTSFile(path.extname(filePath))).length > 0) {
Copy link
Contributor

@pvdlg pvdlg May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of filter + .length > 0 you'll get better performances with find as it will stop on the first item found . rather than going through the whole list:

if (paths.find(filePath => isTSFile(path.extname(filePath))))

index.js Outdated
if (paths.find(filePath => isTSFile(path.extname(filePath)))) {
opts.parser = 'typescript-eslint-parser';

const extensions =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is probably a better way to do this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use mergeWith like in the rest of the codebase: https://github.com/xojs/xo/blob/master/lib/options-manager.js#L168

'no-use-extend-native',
'node',
'promise',
'typescript',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different than the scope of this PR. If we start using eslint-plugin-typescript this should be done in a different PR, that might potentially be released later when the plugin is stable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. It's pretty usable right now, just missing a lot of rules. And I think enabling additional TypeScript-specific rules is definitely in the scope of a PR titled "Add support for Flow and TypeScript".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first sentence of the readme is This is still in the very early stages, so please be patient.
So it's pretty clear it's not production ready and therefore not ready to added to XO.
That said XO is pretty configurable so nothing prevent you to configure the plugin plugin manually or to create a shareable config that includes it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Early stages" does not imply "unstable" or "not production ready". Recent npm stats indicate that users have confidence in its current usability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that we'll be blamed if something doesn't work. We will also constantly have to bump major to be able to upgrade as they seem to do breaking changes often. I think it's wise to require it to be added as a dependency for now and we can open an issue about including it by default in the future.

@jorgegonzalez
Copy link
Contributor Author

@pvdlg I really appreciate your help and all the code reviews!

@jorgegonzalez jorgegonzalez changed the title Add support for Flow and TypeScript Add support for TypeScript and document usage with Flow May 25, 2018
@sindresorhus
Copy link
Member

@jorgegonzalez I've made https://github.com/xojs/eslint-config-xo-typescript now. Can you mention it in the readme?

@jorgegonzalez
Copy link
Contributor Author

@sindresorhus Do you also want me to mention xojs/eslint-config-xo-flow?

@sindresorhus
Copy link
Member

Yes

@jorgegonzalez
Copy link
Contributor Author

Does anything else need to be done to merge this?

@pvdlg
Copy link
Contributor

pvdlg commented Jun 12, 2018

It seems we decided to go with the option of using eslint-config-xo-typescript and eslint-config-xo-flow. So the code changes in index.js should be removed.
The only code change to keep is to add .ts and .tsx to DEFAULT_EXTENSION.

The doc should also be modified to simply:

@sindresorhus sindresorhus merged commit 957b0d9 into xojs:master Jun 24, 2018
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

Successfully merging this pull request may close these issues.

None yet

3 participants