9

Migrating xterm.js from TSLint to ESLint

 3 years ago
source link: https://www.growingwiththeweb.com/2020/03/migrating-xtermjs-from-tslint-to-eslint.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Automatic migration#

I started the migration by installing eslint and the TS plugin:

yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

Then used the tslint-to-eslint-config utility to automatically convert most rules:

npx tslint-to-eslint-config

This gave a basic .eslintrc to use as a starting point that used to tslint plugin for rules that could not be migrated, namely tslint-consistent-codestyle.

Project references aren’t supported#

This is the first issue I hit, after trying to run eslint (eslint -c .eslintrc.js --ext .ts src/ addons/) there were many errors about files not being within the tsconfig.

/home/daimms/dev/Tyriar/xterm.js/addons/xterm-addon-webgl/src/renderLayer/Types.ts
  0:0  error  Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: addons/xterm-addon-webgl/src/renderLayer/Types.ts.
The file must be included in at least one of the projects provided

This wasn’t true, it’s just the root tsconfig.json of xterm.js is a “solution” file that only points to other files, and this is yet to be supported. The fix for this right now is to enumerate all tsconfig projects as an array on the project property.

Before:

"parserOptions": {
  "project": "tsconfig.all.json",
  "sourceType": "module"
},

After:

"parserOptions": {
  "project": [
    "src/tsconfig.json",
    "src/browser/tsconfig.json",
    "src/common/tsconfig.json",
    "test/api/tsconfig.json",
    "test/benchmark/tsconfig.json",
    "addons/xterm-addon-attach/src/tsconfig.json",
    "addons/xterm-addon-fit/src/tsconfig.json",
    "addons/xterm-addon-search/src/tsconfig.json",
    "addons/xterm-addon-unicode11/src/tsconfig.json",
    "addons/xterm-addon-web-links/src/tsconfig.json",
    "addons/xterm-addon-webgl/src/tsconfig.json",
    "addons/xterm-addon-serialize/src/tsconfig.json",
    "addons/xterm-addon-serialize/benchmark/tsconfig.json"
  ],
  "sourceType": "module"
},

This is a shame that it’s needed for now as this list needs to include all transitive dependencies as well. Needing to reference the internal xterm-addon-serialize/benchmark project at the top level is something we explicitly wanted to avoid. There is advice to create a separate tsconfig.json just for eslint and use includes to include all your files in the v2 release but when I tried that Node ran out of memory.

Failing migrated rules#

spaced-comment: TypeScript triple slash references not working#

"spaced-comment": "error",

Error

10:1   error  Expected space or tab after '//' in comment                              spaced-comment
/// <reference lib="dom"/>

The fix was to add / as an exception to the rule:

"spaced-comment": [
    "error",
    "always",
    { "markers": ["/"] }
],

@typescript-eslint/array-type: Error on ReadonlyArray#

"@typescript-eslint/array-type": "error",

Error

583:23  error  Array type using 'Array<IMarker>' is forbidden. Use 'IMarker[]' instead  @typescript-eslint/array-type
readonly markers: ReadonlyArray<IMarker>;

The fix I went with was to just require the generic way for readonly arrays only by changing the rule:

"@typescript-eslint/array-type": [
  "error",
  {
    "default": "array-simple",
    "readonly": "generic"
  }
]

Alternatively all references of ReadonlyArray<T> could be changed to readonly T[].

@typescript-eslint/member-delimiter-style: Semicolons causing problems#

"@typescript-eslint/member-delimiter-style": [
    "error",
    {
        "multiline": {
            "delimiter": "semi",
            "requireLast": true
        },
        "singleline": {
            "delimiter": "semi",
            "requireLast": false
        }
    }
]

Error

641:33  error  Expected a semicolon  @typescript-eslint/member-delimiter-style
onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;

The fix was to require the use of commas instead of semi-colons in single-line types:

"@typescript-eslint/member-delimiter-style": [
    "error",
    {
        "multiline": {
            "delimiter": "semi",
            "requireLast": true
        },
        "singleline": {
            "delimiter": "comma",
            "requireLast": false
        }
    }
]

@typescript-eslint/quotes: String must use singlequote#

"@typescript-eslint/quotes": [
    "error",
    "single"
]

Error

36:23  error  Strings must use singlequote  @typescript-eslint/quotes
await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);

The fix was to allow this use of backticks even when not concatenating strings:

"@typescript-eslint/quotes": [
    "error",
    "single",
    { "allowTemplateLiterals": true }
],

Typings are not included in the project so eslint complains#

Error

/home/daimms/dev/Tyriar/xterm.js/addons/xterm-addon-attach/typings/xterm-addon-attach.d.ts
  0:0  error  Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: addons/xterm-addon-attach/typings/xterm-addon-attach.d.ts.
The file must be included in at least one of the projects provided

Unforatunately I couldn’t figure out how to get these files covered so I ignored them in the .eslintrc:

"ignorePatterns": "**/typings/*.d.ts"

Manually migrating tslint-consistent-codestyle#

Most tslint-consistent-codestyle rules didn’t end up working as pointed out by this error:

Could not find implementations for the following rules specified in the configuration:
    naming-convention
    no-else-after-return
    prefer-const-enum
Try upgrading TSLint and/or ensuring that you have all necessary custom rules installed.
If TSLint was recently upgraded, you may have old rules configured which need to be cleaned up.

Each of these needed to be migrated manually. Additionally I wanted to remove tslint all together so there were some other rules as well.

no-else-after-return#

There’s an almost drop in replacement for this built in:

no-else-return: [
  "error",
  { allowElseIf: false }
]

prefer-const-enum#

This is currently a proposal so it could not be migrated.

naming-convention#

This was a big one as there was a lot to the rules I had set up. This doesn’t migrate automatically since it’s a tslint plugin but luckily there is the naming-convention builtin for naming that is roughly equivalent.

Before:

"naming-convention": [
    true,
    { "type": "default", "format": "camelCase", "leadingUnderscore": "forbid" },
    { "type": "type", "format": "PascalCase" },
    { "type": "class", "format": "PascalCase" },
    { "type": "property", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"] },
    { "type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require" },
    { "type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require" },
    { "type": "variable", "modifiers": ["const"], "format": [ "camelCase", "UPPER_CASE"] },
    { "type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I" },
    { "type": "interface", "prefix": "I" }
],

After:

"@typescript-eslint/naming-convention": [
    "error",
    { "selector": "default", "format": ["camelCase"] },
    // variableLike
    { "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
    { "selector": "variable", "filter": "^I.+Service$", "format": ["PascalCase"], "prefix": ["I"] },
    // memberLike
    { "selector": "memberLike", "modifiers": ["private"], "format": ["camelCase"], "leadingUnderscore": "require" },
    { "selector": "memberLike", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require" },
    { "selector": "enumMember", "format": ["UPPER_CASE"] },
    // typeLike
    { "selector": "typeLike", "format": ["PascalCase"] },
    { "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] },
],

typedef#

Before:

"typedef": [
    true,
    "call-signature",
    "parameter"
],

After:

"@typescript-eslint/explicit-function-return-type": [
    "error",
    {
       "allowExpressions": true
    }
]

whitespace#

Before:

"whitespace": [
    true,
    "check-branch",
    "check-decl",
    "check-module",
    "check-operator",
    "check-rest-spread",
    "check-separator",
    "check-type",
    "check-type-operator",
    "check-preblock"
]

After:

Most of the this was accomplished by adding a few rules, the main exception is that <T>this was no longer allowed due to the keyword-spacing rule, so I changed those to this as T.

"keyword-spacing": "error",
"no-irregular-whitespace": "error",
"no-trailing-spaces": "error",
"@typescript-eslint/type-annotation-spacing": "error",

New coverage#

A bunch of formatting errors are now being caught that weren’t before, not sure why many of them weren’t working before.

@typescript-eslint/semi#

Error

/home/daimms/dev/Tyriar/xterm.js/addons/xterm-addon-webgl/src/WebglRenderer.ts
  84:1  error  Expected indentation of 6 spaces but found 8  @typescript-eslint/indent
    if (!this._gl) {
        throw new Error('WebGL2 not supported ' + this._gl);
    }

prefer-const#

Error

/home/daimms/dev/Tyriar/xterm.js/src/Terminal.ts
  669:7   error  'pos' is never reassigned. Use 'const' instead  prefer-const
      let pos;

      // get mouse coordinates
      pos = self._mouseService.getRawByteCoords(ev, self.screenElement, self.cols, self.rows);

@typescript-eslint/indent#

90:1   error  Expected indentation of 2 spaces but found 3                  @typescript-eslint/indent
  /**
   * Triggers the onBinary event in the public API.
   * @param data The data that is being emitted.
   */
   triggerBinaryEvent(data: string): void;

@typescript-eslint/member-delimiter-style#

There should be no space before the :

Error

641:33  error  Expected a semicolon  @typescript-eslint/member-delimiter-style
hook(params: IParams) : void {}

Share this page#

More posts tagged TypeScript#


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK