Question origin

The CLI tool I’m developing, WCLI, is written in Node +typescript, which references a lot of modules.

Plenty of import utils from ‘.. /.. /.. /.. /.. /utils’ too much hassle and too gross, so begins the TS path alias Tram pit tour.

Ts path alias Settings

The ts path alias Settings are well established in the community and are documented on the official website. The following content will refer directly to the description and Settings on the official website.

Path mapping configuration on the TS official website

Sometimes modules are not placed directly under baseUrl. Fully “jquery” module to import, for example, in the runtime may be interpreted as node_modules/jquery/dist/jquery. Slim. Min. Js. The loader uses the mapping configuration to map module names to runtime files, see RequireJs Documentation and SystemJS Documentation.

The TypeScript compiler supports such declarative mapping by using “paths” in the tsconfig.json file. Here is an example of how to specify paths in jquery.

{
  "compilerOptions": {
    "baseUrl": ".".// This must be specified if "paths" is.
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // the mapping is relative to "baseUrl"}}}Copy the code

Note that Paths is parsed relative to “baseUrl”. If baseUrl is set to a value other than “.”, such as the tsconfig.json directory, then the mapping must be changed accordingly. If you set baseUrl:./ SRC in the above example, jquery should map to.. / node_modules/jquery/dist/jquery.

Lesson representative Summary

  1. BaseUrl: Used to set the root path of the following path.
  2. Path: Used to set the corresponding path mapping. The path in the array corresponds to the path of baseUrl.
"baseUrl": "."."paths": {
  "@srcTypes/*": ["src/types/*"]."@constants/*": ["src/constants/*"]."@commands/*": ["src/commands/*"]."@utils/*": ["src/utils/*"]}Copy the code

After configuration, it can be used in the project.

import XXX from "@utils/getPluginFile";
import { xx } from "@utils/checktype";
import { xx } from "@utils/createContext";
Copy the code

The pit of TSC

The path mapping is done, but the code found after performing TSC packaging error: after compiling with TSC, the mapped path will not be processed, resulting in compiled code can not find modules.

For example, import XXX from ‘@utils/getPluginFile’ should be const XXX = require(‘@utils/getPluginFile’) The result is that the module cannot be found.

The solution

Depending on whether to use Webpack packaging, please choose

  1. If the project is packaged via Webpack, you can leave the path substitution to the Webpack alias.
module.exports = {
  / /...
  resolve: {
    alias: {
      @utils: path.resolve(__dirname, 'src/utils/')}}};Copy the code
  1. The problem is that my WCLI is not packaged with Webpack, but is compiled into JS using TSC and executed on Node. Later found on the community provided a library –module-alias. After configuring tsConfig, it can be realized in two simple steps. inpackage.jsonConfigure the path to the Webpack Alias and call the reference method in the entry file.
// Aliases
"_moduleAliases": {
  "@root"      : ".".// Application's root
  "@deep"      : "src/some/very/deep/directory/or/file"."@my_module" : "lib/some-file.js"."something"  : "src/foo".// Or without @. Actually, it could be any string
}

// Call from the top of the entry file
require('module-alias/register')
Copy the code

For additional uses, you can go to the official website, which is the jump link.

The pit of eslint

According to the above, TSC makes packaged code work as well. However, there is a new catch: ESLint does not recognize path aliases, although it can jump and reference correctly. Sad ah! Ma Fei! .

The easiest and most violent way to do this would be to simply remove the plugin or disable the associated ESLint rules, but eslint-plugin-import 30+ rules is a collection of ES6 best practices from the JS community, and disabling this rule would be the next best thing. I finally found the perfect esLint plugin to solve this problem -eslint-import-resolver-typescript – on a big bloke’s blog.

eslint-import-resolver-typescript

The name eslint-import-resolver-typescript is very relevant to this issue. As you can see from the README project, the lib can find the correct.ts and.tsx files in the TypeScript project with eslint-plugin-import, and also recognize the path configuration of tsconfig.json (path alias 2). Even projects such as Monorepo with multiple projects in a Git repository are supported.

It is also easy to use in esLint’s “import/resolver”: just point to the path of tsconfig where path is currently configured, and esLint will automatically recognize it and not report errors.

{
  "plugins": ["import"]."rules": {
    "import/no-unresolved": "error"
  },
  "settings": {
    "import/parsers": {
      // Use TypeScript parser
      "@typescript-eslint/parser": [".ts".".tsx"]},"import/resolver": {
      // The default root directory is tsconfig.json
      "typescript": {
        // Read the type definition from 
      @types
        "alwaysTryTypes": true,},/ / using the specified path tsconfig. Json, < root > / path/to/folder/tsconfig json
      "typescript": {
        "directory": "./path/to/folder"
      },

      // monorepos of this kind multi-tsconfig.json

      // You can use matching patterns like glob
      "typescript": {
        "directory": "./packages/*/tsconfig.json"
      },

      // Or array
      "typescript": {
        "directory": [
          "./packages/module-a/tsconfig.json"."./packages/module-b/tsconfig.json"]},// Can also be mixed
      "typescript": {
        "directory": [
          "./packages/*/tsconfig.json"."./other-packages/*/tsconfig.json"]}}}}Copy the code

The last

Post my eslintrc configuration for reference:

module.exports = {
  'parser': '@typescript-eslint/parser'.// Define a parser for ESLint
  'extends': ['airbnb-base'.'plugin:@typescript-eslint/recommended'].// Define a subspecification for file inheritance
  'plugins': ['@typescript-eslint'].// Defines the plugins that the ESLint file depends on
  'env': {                          // Specify the environment in which the code is run
    'browser': false.'node': true
  }
  'settings': {
    // Resolve path reference ts file error
    'import/resolver': {
      'node': {
        'extensions': ['.js'.'.jsx'.'.ts'.'.tsx']},// Fix a bug where the path alias under tsconfig causes the ESLint plugin to fail to resolve
      'typescript': {
        'alwaysTryTypes': true}}}},Copy the code

reference

ESLint checks TypeScript for the “Unable to resolve path to module ‘XXX'” error