Skip to content

Commit

Permalink
Merge pull request #1304 from bradennapier/feature/typescript-export-…
Browse files Browse the repository at this point in the history
…type

support export type named exports from typescript
  • Loading branch information
ljharb committed Apr 10, 2019
2 parents 3aefa79 + 49af9d8 commit 28dd614
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 103 deletions.
14 changes: 8 additions & 6 deletions README.md
Expand Up @@ -148,7 +148,7 @@ rules:

You may use the following shortcut or assemble your own config using the granular settings described below.

Make sure you have installed the [`@typescript-eslint/parser`] which is used in the following configuration. Unfortunately NPM does not allow to list optional peer dependencies.
Make sure you have installed [`@typescript-eslint/parser`] which is used in the following configuration. Unfortunately NPM does not allow to list optional peer dependencies.

```yaml
extends:
Expand Down Expand Up @@ -344,14 +344,16 @@ directly using webpack, for example:
# .eslintrc.yml
settings:
import/parsers:
typescript-eslint-parser: [ .ts, .tsx ]
@typescript-eslint/parser: [ .ts, .tsx ]
```

In this case, [`typescript-eslint-parser`](https://github.com/eslint/typescript-eslint-parser) must be installed and require-able from
the running `eslint` module's location (i.e., install it as a peer of ESLint).
In this case, [`@typescript-eslint/parser`](https://www.npmjs.com/package/@typescript-eslint/parser)
must be installed and require-able from the running `eslint` module's location
(i.e., install it as a peer of ESLint).

This is currently only tested with `typescript-eslint-parser` but should theoretically
work with any moderately ESTree-compliant parser.
This is currently only tested with `@typescript-eslint/parser` (and its predecessor,
`typescript-eslint-parser`) but should theoretically work with any moderately
ESTree-compliant parser.

It's difficult to say how well various plugin features will be supported, too,
depending on how far down the rabbit hole goes. Submit an issue if you find strange
Expand Down
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -44,6 +44,7 @@
},
"homepage": "https://github.com/benmosher/eslint-plugin-import",
"devDependencies": {
"@typescript-eslint/parser": "^1.5.0",
"babel-eslint": "^8.2.6",
"babel-plugin-istanbul": "^4.1.6",
"babel-preset-es2015-argon": "latest",
Expand All @@ -67,9 +68,10 @@
"nyc": "^11.9.0",
"redux": "^3.7.2",
"rimraf": "^2.6.3",
"semver": "^6.0.0",
"sinon": "^2.4.1",
"typescript": "^3.2.2",
"typescript-eslint-parser": "^21.0.2"
"typescript-eslint-parser": "^22.0.0"
},
"peerDependencies": {
"eslint": "2.x - 5.x"
Expand Down
1 change: 1 addition & 0 deletions src/ExportMap.js
Expand Up @@ -465,6 +465,7 @@ ExportMap.parse = function (path, content, context) {
case 'TypeAlias': // flowtype with babel-eslint parser
case 'InterfaceDeclaration':
case 'TSEnumDeclaration':
case 'TSTypeAliasDeclaration':
case 'TSInterfaceDeclaration':
case 'TSAbstractClassDeclaration':
case 'TSModuleDeclaration':
Expand Down
6 changes: 6 additions & 0 deletions tests/dep-time-travel.sh
Expand Up @@ -4,6 +4,12 @@

npm install --no-save eslint@$ESLINT_VERSION --ignore-scripts || true

# completely remove the new typescript parser for ESLint < v5
if [[ "$ESLINT_VERSION" -lt "5" ]]; then
echo "Removing @typescript-eslint/parser..."
npm uninstall @typescript-eslint/parser
fi

# use these alternate typescript dependencies for ESLint < v4
if [[ "$ESLINT_VERSION" -lt "4" ]]; then
echo "Downgrading babel-eslint..."
Expand Down
6 changes: 6 additions & 0 deletions tests/src/core/getExports.js
@@ -1,4 +1,6 @@
import { expect } from 'chai'
import semver from 'semver'
import { linter } from 'eslint'
import ExportMap from '../../../src/ExportMap'

import * as fs from 'fs'
Expand Down Expand Up @@ -317,6 +319,10 @@ describe('ExportMap', function () {
['array form', { 'typescript-eslint-parser': ['.ts', '.tsx'] }],
]

if (semver.satisfies(linter.version, '>5.0.0')) {
configs.push(['array form', { '@typescript-eslint/parser': ['.ts', '.tsx'] }])
}

configs.forEach(([description, parserConfig]) => {

describe(description, function () {
Expand Down
197 changes: 103 additions & 94 deletions tests/src/rules/named.js
@@ -1,5 +1,6 @@
import { test, SYNTAX_CASES } from '../utils'
import { RuleTester } from 'eslint'
import { RuleTester, linter } from 'eslint'
import semver from 'semver'

import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'

Expand Down Expand Up @@ -254,99 +255,107 @@ ruleTester.run('named (export *)', rule, {
})


context("Typescript", function () {
context('Typescript', function () {
// Typescript
ruleTester.run("named", rule, {
valid: [
test({
code: 'import { MyType } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { Foo } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { Bar } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { getFoo } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { MyEnum } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: `
import { MyModule } from "./typescript"
MyModule.ModuleFunction()
`,
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: `
import { MyNamespace } from "./typescript"
MyNamespace.NSModule.NSModuleFunction()
`,
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
],

invalid: [
test({
code: 'import { MissingType } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
errors: [{
message: "MissingType not found in './typescript'",
type: 'Identifier',
}],
}),
test({
code: 'import { NotExported } from "./typescript"',
parser: 'typescript-eslint-parser',
settings: {
'import/parsers': { 'typescript-eslint-parser': ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
errors: [{
message: "NotExported not found in './typescript'",
type: 'Identifier',
}],
}),
]
const parsers = ['typescript-eslint-parser']

if (semver.satisfies(linter.version, '>5.0.0')) {
parsers.push('@typescript-eslint/parser')
}

parsers.forEach((parser) => {
ruleTester.run('named', rule, {
valid: [
test({
code: 'import { MyType } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { Foo } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { Bar } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { getFoo } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: 'import { MyEnum } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: `
import { MyModule } from "./typescript"
MyModule.ModuleFunction()
`,
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
test({
code: `
import { MyNamespace } from "./typescript"
MyNamespace.NSModule.NSModuleFunction()
`,
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
}),
],

invalid: [
test({
code: 'import { MissingType } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
errors: [{
message: "MissingType not found in './typescript'",
type: 'Identifier',
}],
}),
test({
code: 'import { NotExported } from "./typescript"',
parser: parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
errors: [{
message: "NotExported not found in './typescript'",
type: 'Identifier',
}],
}),
],
})
})
})
20 changes: 18 additions & 2 deletions tests/src/rules/order.js
@@ -1,4 +1,4 @@
import { test } from '../utils'
import { test, testVersion } from '../utils'

import { RuleTester } from 'eslint'

Expand Down Expand Up @@ -1276,5 +1276,21 @@ ruleTester.run('order', rule, {
message: '`fs` import should occur before import of `async`',
}],
}),
],
// fix incorrect order with @typescript-eslint/parser
testVersion('>5.0.0', {
code: `
var async = require('async');
var fs = require('fs');
`,
output: `
var fs = require('fs');
var async = require('async');
`,
parser: '@typescript-eslint/parser',
errors: [{
ruleId: 'order',
message: '`fs` import should occur before import of `async`',
}],
}),
].filter((t) => !!t),
})
6 changes: 6 additions & 0 deletions tests/src/utils.js
@@ -1,4 +1,6 @@
import path from 'path'
import { linter } from 'eslint'
import semver from 'semver'

// warms up the module cache. this import takes a while (>500ms)
import 'babel-eslint'
Expand All @@ -9,6 +11,10 @@ export function testFilePath(relativePath) {

export const FILENAME = testFilePath('foo.js')

export function testVersion(specifier, t) {
return semver.satisfies(linter.version) && test(t)
}

export function test(t) {
return Object.assign({
filename: FILENAME,
Expand Down

0 comments on commit 28dd614

Please sign in to comment.