├── .eslintignore ├── .prettierrc ├── .renovaterc ├── dummy.js ├── index.js └── package.json ├── src ├── .eslintrc └── index.ts ├── .prettierignore ├── tests ├── withPaths │ ├── tsImportee.ts │ ├── subfolder │ │ ├── tsImportee.ts │ │ └── tsxImportee.tsx │ ├── tsxImportee.tsx │ ├── .eslintrc.js │ ├── tsconfig.json │ └── index.ts ├── withoutPaths │ ├── tsImportee.ts │ ├── subfolder │ │ ├── tsImportee.ts │ │ ├── tsxImportee.tsx │ │ └── dtsImportee.d.ts │ ├── tsxImportee.tsx │ ├── .eslintrc.js │ ├── dtsImportee.d.ts │ ├── tsconfig.json │ └── index.ts ├── multipleTsconfigs │ ├── packages │ │ ├── module-a │ │ │ ├── tsImportee.ts │ │ │ ├── subfolder │ │ │ │ ├── tsImportee.ts │ │ │ │ └── tsxImportee.tsx │ │ │ ├── tsxImportee.tsx │ │ │ ├── tsconfig.json │ │ │ └── index.ts │ │ └── module-b │ │ │ ├── tsImportee.ts │ │ │ ├── subfolder │ │ │ ├── tsImportee.ts │ │ │ └── tsxImportee.tsx │ │ │ ├── tsxImportee.tsx │ │ │ ├── tsconfig.json │ │ │ └── index.ts │ └── .eslintrc.js └── baseEslintConfig.js ├── .huskyrc.js ├── .commitlintrc ├── .lintstagedrc.js ├── .gitignore ├── .remarkrc ├── .github ├── FUNDING.yml └── workflows │ └── nodejs.yml ├── .eslintrc ├── tsconfig.json ├── .editorconfig ├── scripts └── deploy.sh ├── package.json ├── CHANGELOG.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | CHANGELOG.md 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | "@1stg/prettier-config" 2 | -------------------------------------------------------------------------------- /.renovaterc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@1stg" 3 | } 4 | -------------------------------------------------------------------------------- /dummy.js/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'dummy' 2 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@1stg" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | lib 2 | node_modules 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /tests/withPaths/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /.huskyrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@1stg/husky-config') 2 | -------------------------------------------------------------------------------- /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@1stg" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@1stg/lint-staged') 2 | -------------------------------------------------------------------------------- /tests/withPaths/subfolder/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/withPaths/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/subfolder/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .type-coverage 2 | lib 3 | node_modules 4 | .*cache 5 | *.log 6 | -------------------------------------------------------------------------------- /.remarkrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@1stg/remark-config" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tests/withPaths/subfolder/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [alexgorbatchev, JounQin] 2 | open_collective: rxts 3 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/subfolder/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/subfolder/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/subfolder/tsImportee.ts: -------------------------------------------------------------------------------- 1 | export default 'yes' 2 | -------------------------------------------------------------------------------- /tests/withPaths/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../baseEslintConfig')(__dirname) 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../baseEslintConfig')(__dirname) 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/dtsImportee.d.ts: -------------------------------------------------------------------------------- 1 | declare const content: 'yes' 2 | 3 | export default content 4 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/subfolder/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/subfolder/tsxImportee.tsx: -------------------------------------------------------------------------------- 1 | export default 'React Component' 2 | -------------------------------------------------------------------------------- /tests/withoutPaths/subfolder/dtsImportee.d.ts: -------------------------------------------------------------------------------- 1 | declare const content: 'yes' 2 | 3 | export default content 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:prettier/recommended", 4 | "plugin:mdx/recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /dummy.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dummy.js", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/withoutPaths/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react" 4 | }, 5 | "files": ["index.ts", "tsImportee.ts", "tsxImportee.tsx"] 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@1stg/tsconfig/lib.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "outDir": "lib" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /tests/withPaths/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "react", 5 | "paths": { 6 | "folder/*": ["*"], 7 | "*": ["../../node_modules/*"] 8 | } 9 | }, 10 | "files": ["index.ts", "tsImportee.ts", "tsxImportee.tsx"] 11 | } 12 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "react", 5 | "paths": { 6 | "folder/*": ["*"], 7 | "*": ["../../../../node_modules/*"] 8 | } 9 | }, 10 | "files": ["index.ts", "tsImportee.ts", "tsxImportee.tsx"] 11 | } 12 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "react", 5 | "paths": { 6 | "folder/*": ["*"], 7 | "*": ["../../../../node_modules/*"] 8 | } 9 | }, 10 | "files": ["index.ts", "tsImportee.ts", "tsxImportee.tsx"] 11 | } 12 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const globPattern = './packages/*/tsconfig.json'; 4 | 5 | // in normal cases this is not needed because the __dirname would be the root 6 | const absoluteGlobPath = path.join(__dirname, globPattern); 7 | 8 | module.exports = require('../baseEslintConfig')(absoluteGlobPath); 9 | -------------------------------------------------------------------------------- /tests/withoutPaths/index.ts: -------------------------------------------------------------------------------- 1 | // import relative 2 | import './tsImportee' 3 | import './tsxImportee' 4 | import './dtsImportee' 5 | import './subfolder/dtsImportee' 6 | import './subfolder/tsImportee' 7 | import './subfolder/tsxImportee' 8 | 9 | // import from node_module 10 | import 'typescript' 11 | import 'dummy.js' 12 | 13 | // import from `@types/` 14 | import 'json5' 15 | 16 | // enable alwaysTryTypes 17 | import 'unist' 18 | -------------------------------------------------------------------------------- /tests/withPaths/index.ts: -------------------------------------------------------------------------------- 1 | // import relative 2 | import './tsImportee' 3 | import './tsxImportee' 4 | import './subfolder/tsImportee' 5 | import './subfolder/tsxImportee' 6 | 7 | // import using tsconfig.json path mapping 8 | import 'folder/tsImportee' 9 | import 'folder/tsxImportee' 10 | import 'folder/subfolder/tsImportee' 11 | import 'folder/subfolder/tsxImportee' 12 | 13 | // import from node_module 14 | import 'typescript' 15 | import 'dummy.js' 16 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-a/index.ts: -------------------------------------------------------------------------------- 1 | // import relative 2 | import './tsImportee' 3 | import './tsxImportee' 4 | import './subfolder/tsImportee' 5 | import './subfolder/tsxImportee' 6 | 7 | // import using tsconfig.json path mapping 8 | import 'folder/tsImportee' 9 | import 'folder/tsxImportee' 10 | import 'folder/subfolder/tsImportee' 11 | import 'folder/subfolder/tsxImportee' 12 | 13 | // import from node_module 14 | import 'typescript' 15 | import 'dummy.js' 16 | -------------------------------------------------------------------------------- /tests/multipleTsconfigs/packages/module-b/index.ts: -------------------------------------------------------------------------------- 1 | // import relative 2 | import './tsImportee' 3 | import './tsxImportee' 4 | import './subfolder/tsImportee' 5 | import './subfolder/tsxImportee' 6 | 7 | // import using tsconfig.json path mapping 8 | import 'folder/tsImportee' 9 | import 'folder/tsxImportee' 10 | import 'folder/subfolder/tsImportee' 11 | import 'folder/subfolder/tsxImportee' 12 | 13 | // import from node_module 14 | import 'typescript' 15 | import 'dummy.js' 16 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | git remote set-url origin "https://user:$GITHUB_TOKEN@github.com/$GH_REPO.git" 4 | npm set //registry.npmjs.org/:_authToken "$NPM_TOKEN" 5 | 6 | git fetch origin "$GH_BRANCH":"$GH_BRANCH" 7 | git checkout "$GH_BRANCH" 8 | 9 | PKG_VERSION=$(jq -r '.version' package.json) 10 | 11 | git fetch origin v"$PKG_VERSION" || { 12 | yarn global add standard-version 13 | standard-version -a --release-as "$PKG_VERSION" 14 | git push --follow-tags origin "$GH_BRANCH" 15 | npm publish 16 | } 17 | -------------------------------------------------------------------------------- /tests/baseEslintConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = directory => ({ 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/eslint-recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:import/errors', 8 | 'plugin:import/typescript', 9 | ], 10 | settings: { 11 | 'import/resolver': { 12 | ts: { 13 | directory, 14 | alwaysTryTypes: true, 15 | }, 16 | }, 17 | }, 18 | rules: { 19 | 'import/no-duplicates': 0, 20 | 'import/no-unresolved': 2, 21 | 'import/extensions': [ 22 | 2, 23 | 'ignorePackages', 24 | { 25 | js: 'never', 26 | jsx: 'never', 27 | ts: 'never', 28 | tsx: 'never', 29 | }, 30 | ], 31 | 'node/no-extraneous-import': 0, 32 | 'node/no-missing-import': 0, 33 | }, 34 | }) 35 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | default: 7 | strategy: 8 | matrix: 9 | node: [10, 12] 10 | os: [macOS-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: ${{ matrix.node }} 17 | - name: Setup yarn 18 | run: | 19 | curl -o- -L https://yarnpkg.com/install.sh | bash 20 | export PATH="$HOME/.yarn/bin:$PATH" 21 | - name: Install Dependencies 22 | run: yarn --frozen-lockfile 23 | 24 | - name: Build, Lint and Test 25 | run: yarn run-s build lint test type-coverage 26 | env: 27 | EFF_NO_LINK_RULES: 'true' 28 | PARSER_NO_WATCH: 'true' 29 | 30 | - name: Publish GitHub Release and npm Package 31 | if: matrix.os == 'macOS-latest' && github.event_name == 'push' && github.ref == 'refs/heads/master' 32 | run: bash scripts/deploy.sh 33 | env: 34 | CI: 'true' 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | GH_BRANCH: master 37 | GH_REPO: ${{ github.repository }} 38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-import-resolver-ts", 3 | "version": "0.5.0", 4 | "description": "TypeScript .ts .tsx module resolver for `eslint-plugin-import`.", 5 | "repository": "https://github.com/rx-ts/eslint-import-resolver-ts", 6 | "author": "Alex Gorbatchev ", 7 | "contributors": [ 8 | "JounQin " 9 | ], 10 | "license": "ISC", 11 | "engines": { 12 | "node": ">=4" 13 | }, 14 | "main": "lib/cjs", 15 | "module": "lib", 16 | "es2015": "lib/es2015", 17 | "fesm5": "lib/esm", 18 | "types": "lib", 19 | "files": [ 20 | "lib" 21 | ], 22 | "keywords": [ 23 | "typescript", 24 | "eslint", 25 | "import", 26 | "resolver", 27 | "plugin" 28 | ], 29 | "scripts": { 30 | "build": "run-p build:*", 31 | "build:r": "r", 32 | "build:ts": "tsc -b", 33 | "lint": "run-p lint:*", 34 | "lint:es": "cross-env PARSER_NO_WATCH=true eslint src --cache --ext md,js,ts -f friendly", 35 | "lint:tsc": "tsc --incremental false --noEmit", 36 | "postinstall": "yarn-deduplicate", 37 | "prepublishOnly": "yarn build", 38 | "pretest": "r", 39 | "test": "run-p test:*", 40 | "test:multipleTsconfigs": "eslint --ext ts,tsx tests/multipleTsconfigs", 41 | "test:withPaths": "eslint --ext ts,tsx tests/withPaths", 42 | "test:withoutPaths": "eslint --ext ts,tsx tests/withoutPaths", 43 | "type-coverage": "type-coverage --cache --detail --ignore-catch --strict --update" 44 | }, 45 | "peerDependencies": { 46 | "eslint": "*", 47 | "eslint-plugin-import": "*" 48 | }, 49 | "dependencies": { 50 | "debug": "^4.1.1", 51 | "glob": "^7.1.6", 52 | "is-glob": "^4.0.1", 53 | "resolve": "^1.17.0", 54 | "tsconfig-paths": "^3.9.0" 55 | }, 56 | "devDependencies": { 57 | "@1stg/lib-config": "^0.5.5", 58 | "@types/debug": "^4.1.5", 59 | "@types/glob": "^7.1.3", 60 | "@types/is-glob": "^4.0.1", 61 | "@types/node": "^14.0.19", 62 | "@types/resolve": "^1.17.1", 63 | "@types/unist": "^2.0.3", 64 | "dummy.js": "link:dummy.js", 65 | "npm-run-all": "^4.1.5", 66 | "react": "^16.13.1", 67 | "type-coverage": "^2.8.4", 68 | "typescript": "^3.9.6", 69 | "yarn-deduplicate": "^2.0.0" 70 | }, 71 | "resolutions": { 72 | "eslint-import-resolver-ts": "link:.", 73 | "prettier": "^2.0.5" 74 | }, 75 | "typeCoverage": { 76 | "atLeast": 98.62 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.5.0](https://github.com/rx-ts/eslint-import-resolver-ts/compare/v0.4.0...v0.5.0) (2020-07-08) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * add pretest script which is required ([1ffcd83](https://github.com/rx-ts/eslint-import-resolver-ts/commit/1ffcd834931ebc1f721543ed89d071a91fadb1ae)) 11 | * options could be null - close [#42](https://github.com/rx-ts/eslint-import-resolver-ts/issues/42) ([81db8eb](https://github.com/rx-ts/eslint-import-resolver-ts/commit/81db8eb0ae81af437e11b6341d8f237bc4bc4e39)) 12 | * typo ([#40](https://github.com/rx-ts/eslint-import-resolver-ts/issues/40)) ([585509e](https://github.com/rx-ts/eslint-import-resolver-ts/commit/585509e95f93adf8b7ef5839029c19c55edbe76e)) 13 | 14 | ## [2.0.0](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/compare/v1.1.1...v2.0.0) (2019-10-17) 15 | 16 | 17 | ### Features 18 | 19 | * add alwaysTryTypes option, add tests ([fe0aa6f](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/fe0aa6f0001904274c122d78cf0fd0757005b61f)) 20 | * replace glob with tiny-glob for faster speed, close [#12](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/issues/12) ([f436627](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/f436627deb910afb332d6de9764a13f05b231dab)) 21 | * replace glob with tiny-glob for faster speed, close [#12](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/issues/12) ([#13](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/issues/13)) ([5f87698](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/5f8769830abcbb87e67e5788d4bbcda7a5e632c7)) 22 | * resolve .ts/.tsx/.d.ts first, and then fallback to @types/* ([b11ede3](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/b11ede3c9fafbc548db011729fd64e958cde6e51)) 23 | * support scoped packages from DefinitelyTyped ([b4e72a5](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/b4e72a54966bda12ef70791f09df5cbe0f04b889)) 24 | * use types/typings/module first to use .d.ts whenever possible ([74de3d9](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/74de3d9fa2552b5d1b0eb799638a657c9af67887)) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * add pretest script which is required ([1ffcd83](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/1ffcd834931ebc1f721543ed89d071a91fadb1ae)) 30 | * **deps:** bump configurations, use resolutions to simplify tests ([5eb4874](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/5eb48749870f8bcc5ff246a39d15daf19d11af39)) 31 | * only check alwaysTryTypes if foundNodePath is null ([23e2e8c](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/commit/23e2e8cf71ee6c19da9f55e85b2ab34543d2a12e)) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-import-resolver-typescript 2 | 3 | [![GitHub Actions](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/workflows/Node%20CI/badge.svg)](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/actions) 4 | [![type-coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&prefix=%E2%89%A5&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Falexgorbatchev%2Feslint-import-resolver-typescript%2Fmaster%2Fpackage.json)](https://github.com/plantain-00/type-coverage) 5 | [![npm](https://img.shields.io/npm/v/eslint-import-resolver-typescript.svg)](https://www.npmjs.com/package/eslint-import-resolver-typescript) 6 | [![GitHub Release](https://img.shields.io/github/release/alexgorbatchev/eslint-import-resolver-typescript)](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/releases) 7 | 8 | [![David Peer](https://img.shields.io/david/peer/alexgorbatchev/eslint-import-resolver-typescript.svg)](https://david-dm.org/alexgorbatchev/eslint-import-resolver-typescript?type=peer) 9 | [![David](https://img.shields.io/david/alexgorbatchev/eslint-import-resolver-typescript.svg)](https://david-dm.org/alexgorbatchev/eslint-import-resolver-typescript) 10 | [![David Dev](https://img.shields.io/david/dev/alexgorbatchev/eslint-import-resolver-typescript.svg)](https://david-dm.org/alexgorbatchev/eslint-import-resolver-typescript?type=dev) 11 | 12 | [![Conventional Commits](https://img.shields.io/badge/conventional%20commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 13 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 14 | [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 15 | [![codechecks.io](https://raw.githubusercontent.com/codechecks/docs/master/images/badges/badge-default.svg?sanitize=true)](https://codechecks.io) 16 | 17 | This plugin adds TypeScript support to [`eslint-plugin-import`](https://www.npmjs.com/package/eslint-plugin-import). 18 | 19 | This means you can: 20 | 21 | - `import`/`require` files with extension `.ts`/`.tsx`! 22 | - Use [`paths`](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping) defined in `tsconfig.json`. 23 | - Prefer resolve `@types/*` definitions over plain `.js`. 24 | - Multiple tsconfigs support just like normal. 25 | 26 | ## TOC 27 | 28 | - [Notice](#notice) 29 | - [Installation](#installation) 30 | - [Configuration](#configuration) 31 | - [Contributing](#contributing) 32 | 33 | ## Notice 34 | 35 | After version 2.0.0, `.d.ts` will take higher priority then normal `.js` files on resolving `node_modules` packages in favor of `@types/*` definitions. 36 | 37 | If you're facing some problems on rules `import/default` or `import/named` from [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import), do not post any issue here, because they are just working exactly as [expected](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/issues/31#issuecomment-539751607) on our sides, take as reference or post a new issue to [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import) instead. 38 | 39 | ## Installation 40 | 41 | ```sh 42 | # npm 43 | npm i -D eslint-plugin-import @typescript-eslint/parser eslint-import-resolver-typescript 44 | 45 | # yarn 46 | yarn add -D eslint-plugin-import @typescript-eslint/parser eslint-import-resolver-typescript 47 | ``` 48 | 49 | ## Configuration 50 | 51 | Add the following to your `.eslintrc` config: 52 | 53 | ```jsonc 54 | { 55 | "plugins": ["import"], 56 | "rules": { 57 | // turn on errors for missing imports 58 | "import/no-unresolved": "error" 59 | }, 60 | "settings": { 61 | "import/parsers": { 62 | "@typescript-eslint/parser": [".ts", ".tsx"] 63 | }, 64 | "import/resolver": { 65 | // use /tsconfig.json 66 | "typescript": { 67 | "alwaysTryTypes": true // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist` 68 | }, 69 | 70 | // use /path/to/folder/tsconfig.json 71 | "typescript": { 72 | "directory": "path/to/folder" 73 | }, 74 | 75 | // Multiple tsconfigs (Useful for monorepos) 76 | 77 | // use a glob pattern 78 | "typescript": { 79 | "directory": "packages/*/tsconfig.json" 80 | }, 81 | 82 | // use an array 83 | "typescript": { 84 | "directory": [ 85 | "packages/module-a/tsconfig.json", 86 | "packages/module-b/tsconfig.json" 87 | ] 88 | }, 89 | 90 | // use an array of glob patterns 91 | "typescript": { 92 | "directory": [ 93 | "packages/*/tsconfig.json", 94 | "other-packages/*/tsconfig.json" 95 | ] 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | 102 | ## Contributing 103 | 104 | - Make sure your change is covered by a test import. 105 | - Make sure that `yarn test` passes without a failure. 106 | - Make sure that `yarn lint` passes without conflicts. 107 | - Make sure your code changes match our [type-coverage](https://github.com/plantain-00/type-coverage) settings: `yarn type-coverage`. 108 | 109 | We have [GitHub Actions](https://github.com/alexgorbatchev/eslint-import-resolver-typescript/actions) which will run the above commands on your PRs. 110 | 111 | If either fails, we won't be able to merge your PR until it's fixed. 112 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | import { 4 | ConfigLoaderSuccessResult, 5 | createMatchPath, 6 | loadConfig, 7 | ConfigLoaderResult, 8 | } from 'tsconfig-paths' 9 | import { sync as globSync } from 'glob' 10 | import isGlob from 'is-glob' 11 | import { isCore, sync } from 'resolve' 12 | import debug from 'debug' 13 | 14 | const log = debug('eslint-import-resolver-ts') 15 | 16 | const extensions = ['.ts', '.tsx', '.d.ts'].concat( 17 | // eslint-disable-next-line node/no-deprecated-api 18 | Object.keys(require.extensions), 19 | '.jsx', 20 | ) 21 | 22 | export const interfaceVersion = 2 23 | 24 | export interface TsResolverOptions { 25 | alwaysTryTypes?: boolean 26 | directory?: string | string[] 27 | } 28 | 29 | /** 30 | * @param {string} source the module to resolve; i.e './some-module' 31 | * @param {string} file the importing file's full path; i.e. '/usr/local/bin/file.js' 32 | */ 33 | export function resolve( 34 | source: string, 35 | file: string, 36 | options: TsResolverOptions | null, 37 | ): { 38 | found: boolean 39 | path?: string | null 40 | } { 41 | options = options || {} 42 | 43 | log('looking for:', source) 44 | 45 | // don't worry about core node modules 46 | if (isCore(source)) { 47 | log('matched core:', source) 48 | 49 | return { 50 | found: true, 51 | path: null, 52 | } 53 | } 54 | 55 | initMappers(options) 56 | const mappedPath = getMappedPath(source, file) 57 | if (mappedPath) { 58 | log('matched ts path:', mappedPath) 59 | } 60 | 61 | // note that even if we map the path, we still need to do a final resolve 62 | let foundNodePath: string | null | undefined 63 | try { 64 | foundNodePath = sync(mappedPath || source, { 65 | extensions, 66 | basedir: path.dirname(path.resolve(file)), 67 | packageFilter, 68 | }) 69 | } catch { 70 | foundNodePath = null 71 | } 72 | 73 | // naive attempt at @types/* resolution, 74 | // if path is neither absolute nor relative 75 | if ( 76 | (/\.jsx?$/.test(foundNodePath!) || 77 | (options.alwaysTryTypes && !foundNodePath)) && 78 | !/^@types[/\\]/.test(source) && 79 | !path.isAbsolute(source) && 80 | !source.startsWith('.') 81 | ) { 82 | const definitelyTyped = resolve( 83 | '@types' + path.sep + mangleScopedPackage(source), 84 | file, 85 | options, 86 | ) 87 | if (definitelyTyped.found) { 88 | return definitelyTyped 89 | } 90 | } 91 | 92 | if (foundNodePath) { 93 | log('matched node path:', foundNodePath) 94 | 95 | return { 96 | found: true, 97 | path: foundNodePath, 98 | } 99 | } 100 | 101 | log("didn't find ", source) 102 | 103 | return { 104 | found: false, 105 | } 106 | } 107 | 108 | function packageFilter(pkg: Record) { 109 | pkg.main = 110 | pkg.types || pkg.typings || pkg.module || pkg['jsnext:main'] || pkg.main 111 | return pkg 112 | } 113 | 114 | let mappers: 115 | | Array<(source: string, file: string) => string | undefined> 116 | | undefined 117 | 118 | /** 119 | * @param {string} source the module to resolve; i.e './some-module' 120 | * @param {string} file the importing file's full path; i.e. '/usr/local/bin/file.js' 121 | * @returns The mapped path of the module or undefined 122 | */ 123 | function getMappedPath(source: string, file: string) { 124 | const paths = mappers! 125 | .map(mapper => mapper(source, file)) 126 | .filter(path => !!path) 127 | 128 | if (paths.length > 1) { 129 | log('found multiple matching ts paths:', paths) 130 | } 131 | 132 | return paths[0] 133 | } 134 | 135 | function initMappers(options: TsResolverOptions) { 136 | if (mappers) { 137 | return 138 | } 139 | 140 | const isArrayOfStrings = (array?: string | string[]) => 141 | Array.isArray(array) && array.every(o => typeof o === 'string') 142 | 143 | const configPaths = 144 | typeof options.directory === 'string' 145 | ? [options.directory] 146 | : isArrayOfStrings(options.directory) 147 | ? options.directory 148 | : [process.cwd()] 149 | 150 | mappers = configPaths! 151 | // turn glob patterns into paths 152 | .reduce( 153 | (paths, path) => paths.concat(isGlob(path) ? globSync(path) : path), 154 | [], 155 | ) 156 | // eslint-disable-next-line unicorn/no-fn-reference-in-iterator 157 | .map(loadConfig) 158 | // eslint-disable-next-line unicorn/no-fn-reference-in-iterator 159 | .filter(isConfigLoaderSuccessResult) 160 | .map(configLoaderResult => { 161 | const matchPath = createMatchPath( 162 | configLoaderResult.absoluteBaseUrl, 163 | configLoaderResult.paths, 164 | ) 165 | 166 | return (source: string, file: string) => { 167 | // exclude files that are not part of the config base url 168 | if (!file.includes(configLoaderResult.absoluteBaseUrl)) { 169 | return 170 | } 171 | 172 | // look for files based on setup tsconfig "paths" 173 | return matchPath(source, undefined, undefined, extensions) 174 | } 175 | }) 176 | } 177 | 178 | function isConfigLoaderSuccessResult( 179 | configLoaderResult: ConfigLoaderResult, 180 | ): configLoaderResult is ConfigLoaderSuccessResult { 181 | if (configLoaderResult.resultType !== 'success') { 182 | // this can happen if the user has problems with their tsconfig 183 | // or if it's valid, but they don't have baseUrl set 184 | log('failed to init tsconfig-paths:', configLoaderResult.message) 185 | return false 186 | } 187 | return true 188 | } 189 | 190 | /** 191 | * For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. 192 | */ 193 | function mangleScopedPackage(moduleName: string) { 194 | if (moduleName.startsWith('@')) { 195 | const replaceSlash = moduleName.replace(path.sep, '__') 196 | if (replaceSlash !== moduleName) { 197 | return replaceSlash.slice(1) // Take off the "@" 198 | } 199 | } 200 | return moduleName 201 | } 202 | --------------------------------------------------------------------------------