angular-eslint/angular-eslint

Unable to convert from TSLint to ESLint with tslint-to-eslint-config 2.7.0

mkurte opened this issue · 34 comments

Hi guys,

in this issue I described that I am not able to run the command to migrate from tslint to eslint.

When I type ng g @angular-eslint/schematics:convert-tslint-to-eslint PROJECTNAME and check the defaults I get this error:

There was a critical error when trying to inspect your tslint.json:

env: node: No such file or directory

I'm running this command on a mac, in a multiple projects workspace.
My project is in the root folder, the tslint.json file too. Is there something I am missing?

If I type npx tslint --print-config {PATH_TO_YOUR_TSLINT_JSON_HERE} as suggested I get a new tslint.json config, but that one doesn't work too.

Many thanks in advance,
Matthias

My tslint.json looks like this:

{
  "rulesDirectory": [
    "node_modules/codelyzer",
    "node_modules/rxjs-tslint"
  ],
  "rules": {
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warning"
    },
    "eofline": true,
    "forin": true,
    "import-blacklist": [],
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      160
    ],
    "member-access": false,
    "member-ordering": [
      true,
      {
        "order": [
          "public-static-field",
          "public-instance-field",
          "private-static-field",
          "private-instance-field",
          "constructor",
          "public-static-method",
          "public-instance-method",
          "private-static-method",
          "private-instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": true,
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [true, "ignore-params", "ignore-properties"],
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": true,
    "no-unused-expression": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": true,
    "semicolon": [
      true,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],

    "directive-selector": [true, "attribute", "p1", "camelCase"],
    "component-selector": [true, "element", "p1", "kebab-case"],
    "no-inputs-metadata-property": true,
    "no-outputs-metadata-property": true,
    "no-host-metadata-property": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-lifecycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "rxjs-collapse-imports": true,
    "rxjs-pipeable-operators-only": true,
    "rxjs-no-static-observable-methods": true,
    "rxjs-proper-imports": true
  }
}

Update: Even if I create a new app with Angular CLI 11 I get the same error:

ng new my-app
cd my-app
ng add @angular-eslint/schematics
ng g @angular-eslint/schematics:convert-tslint-to-eslint --defaults

Result:

    INFO: We are now installing the "tslint-to-eslint-config" package into a tmp directory to aid with the conversion

    This may take a minute or two...


There was a critical error when trying to inspect your tslint.json:

env: node: No such file or directory

@mkurte are you replacing the part which says to put the path to your actual tslint.json i.e. the placeholder {PATH_TO_YOUR_TSLINT_JSON_HERE} - it's not clear from your description.

E.g. if your tslint.json were in the current directory you would run

npx tslint --print-config ./tslint.json

I am in the directory where my tslint.json lives and enter the command

ng g @angular-eslint/schematics:convert-tslint-to-eslint projectname

That leads to the error.

If I type npx tslint --print-config ./tslint.json I get

{
  "extends": [],
  "jsRules": {},
  "linterOptions": {},
  "rules": {
    "callable-types": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "class-name": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "comment-format": {
      "ruleArguments": [
        "check-space"
      ],
      "ruleSeverity": "error"
    },
    "curly": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "deprecation": {
      "ruleSeverity": "warning"
    },
    "eofline": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "forin": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "import-blacklist": {
      "ruleSeverity": "error"
    },
    "import-spacing": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "indent": {
      "ruleArguments": [
        "spaces"
      ],
      "ruleSeverity": "error"
    },
    "interface-over-type-literal": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "label-position": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "max-line-length": {
      "ruleArguments": [
        160
      ],
      "ruleSeverity": "error"
    },
    "member-access": {
      "ruleArguments": [],
      "ruleSeverity": "off"
    },
    "member-ordering": {
      "ruleArguments": [
        {
          "order": [
            "public-static-field",
            "public-instance-field",
            "private-static-field",
            "private-instance-field",
            "constructor",
            "public-static-method",
            "public-instance-method",
            "private-static-method",
            "private-instance-method"
          ]
        }
      ],
      "ruleSeverity": "error"
    },
    "no-arg": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-bitwise": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-console": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-construct": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-debugger": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-duplicate-variable": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-empty": {
      "ruleArguments": [],
      "ruleSeverity": "off"
    },
    "no-empty-interface": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-eval": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-inferrable-types": {
      "ruleArguments": [
        "ignore-params",
        "ignore-properties"
      ],
      "ruleSeverity": "error"
    },
    "no-shadowed-variable": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-string-literal": {
      "ruleArguments": [],
      "ruleSeverity": "off"
    },
    "no-string-throw": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-switch-case-fall-through": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-trailing-whitespace": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-unused-expression": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-var-keyword": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "object-literal-sort-keys": {
      "ruleArguments": [],
      "ruleSeverity": "off"
    },
    "one-line": {
      "ruleArguments": [
        "check-open-brace",
        "check-catch",
        "check-else",
        "check-whitespace"
      ],
      "ruleSeverity": "error"
    },
    "prefer-const": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "quotemark": {
      "ruleArguments": [
        "single"
      ],
      "ruleSeverity": "error"
    },
    "radix": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "semicolon": {
      "ruleArguments": [
        "always"
      ],
      "ruleSeverity": "error"
    },
    "triple-equals": {
      "ruleArguments": [
        "allow-null-check"
      ],
      "ruleSeverity": "error"
    },
    "typedef-whitespace": {
      "ruleArguments": [
        {
          "call-signature": "nospace",
          "index-signature": "nospace",
          "parameter": "nospace",
          "property-declaration": "nospace",
          "variable-declaration": "nospace"
        }
      ],
      "ruleSeverity": "error"
    },
    "unified-signatures": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "variable-name": {
      "ruleArguments": [],
      "ruleSeverity": "off"
    },
    "whitespace": {
      "ruleArguments": [
        "check-branch",
        "check-decl",
        "check-operator",
        "check-separator",
        "check-type"
      ],
      "ruleSeverity": "error"
    },
    "directive-selector": {
      "ruleArguments": [
        "attribute",
        "p1",
        "camelCase"
      ],
      "ruleSeverity": "error"
    },
    "component-selector": {
      "ruleArguments": [
        "element",
        "p1",
        "kebab-case"
      ],
      "ruleSeverity": "error"
    },
    "no-inputs-metadata-property": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-outputs-metadata-property": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-host-metadata-property": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-input-rename": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "no-output-rename": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "use-lifecycle-interface": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "use-pipe-transform-interface": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "component-class-suffix": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "directive-class-suffix": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "rxjs-collapse-imports": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "rxjs-pipeable-operators-only": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "rxjs-no-static-observable-methods": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    },
    "rxjs-proper-imports": {
      "ruleArguments": [],
      "ruleSeverity": "error"
    }
  },
  "rulesDirectory": [
    "/Users/matthiaskurte/src/planone/frontend/app/node_modules/codelyzer",
    "/Users/matthiaskurte/src/planone/frontend/app/node_modules/rxjs-tslint"
  ]
}

This is my directory structure:

.                                
├── README.md                    
├── angular.json                 
├── karma.global.conf.js         
├── ngsw-config.json             
├── package-lock.json            
├── package.json                 
├── projects                     
│   └── libs                     
├── run-tests.sh                 
├── server.ts                    
├── src                          
│   ├── app                      
│   ├── assets                   
│   ├── environments             
│   ├── i18n                     
│   ├── index.html               
│   ├── karma.conf.js            
│   ├── main.server.ts           
│   ├── main.ts                  
│   ├── polyfills.ts             
│   ├── styles                   
│   ├── test.ts                  
│   ├── tsconfig.app.json        
│   ├── tsconfig.server.json     
│   ├── tsconfig.spec.json       
│   └── typings.d.ts             
├── tsconfig.e2e.json            
├── tsconfig.json                
├── tsconfig.spec.json           
├── tslint.json                  
├── typedoc.json                 
└── webpack.server.config.js     

Thanks @mkurte that's useful.

What happens if you run:

node --version

and

npm --version

?

After a quick search env: node: No such file or directory seems to be coming up for folks (unrelated to angular-eslint) who have unusual/malformed nodejs installations, so the above might give us a clue on that...

I use nvm and have currently installed:

node -v
v12.18.0

npm -v
7.16.0

I have the same problem with a project containing several applications

Update: I have the impression that the problem is with the package tslint-to-eslint-config with the last version (not working with 2.7.0, working with 2.6.0)

@KyDenZ Yep, thanks a lot. Converting a tslint.json file to .eslintrs.js with tslint-to-eslint-config@2.6.0 works for me too. Should we close that issue here?

@mkurte We could fix it by fixing the version in the schematics package

My quick fix was to manually install tslint-to-eslint-config in version 2.6.0 as a dev dependency, migrate and then uninstall it again:

ng add @angular-elements/schematics
npm install --save-dev tslint-to-eslint-config@2.6.0
ng g @angular-eslint/schematics:convert-tslint-to-eslint myproject
npm uninstall --save-dev tslint-to-eslint-config

Yes we can do like that! Thank you

Thanks for looking into this more and reporting it on typescript-eslint/tslint-to-eslint-config#1159

I was mightily confused why 3 separate people were suddenly reporting installation related issues on the same day, but an issue/change in tslint-to-eslint-config explains that.

I'll wait to hear back from Josh on this before changing anything about angular-eslint, and direct people here in the meantime if anyone else runs into it

Hi folks, sorry for the breakage! 😰

I don't know why tslint-to-eslint-config@2.7.0 would suddenly break things. There are exactly two changes in it that could impact file reading in ng:

For now, I've released a 2.9.0 that reverts those two suspicious changes.

So that we can add back those features, is there a self-contained reproduction someone can post here for me to use locally? I'm not totally familiar with ng schematics, but going forward I'd like to add some kind of testing to tslint-to-eslint-config to make sure we don't break angular-eslint again.

Edit:

env: node No such file or directory

This makes me suspect typescript-eslint/tslint-to-eslint-config#1152 a bit more. FYI @AdamTReineke

Thanks so much @JoshuaKGoldberg!

@mkurte @KyDenZ @gs-scooter would you mind trying the conversion again without any workarounds to verify 2.9.0 is behaving as 2.6.0 used to?


@JoshuaKGoldberg the relevant bits of usage are that we dynamically install tslint-to-eslint-config into a tmp dir only when the user actually needs conversion:

execSync(`npm i -D tslint-to-eslint-config@${tslintToEslintConfigVersion}`, {

(The version is based on the devDeps version we use:

"tslint-to-eslint-config": "^2.4.0"
- I didn't pin it to avoid the need for angular-eslint to update every time there are improvements in tslint-to-eslint-config)

This allows us to avoid tslint ending up in people's node_modules (and the associated install warnings).

We then leverage the tslint that it brought in after installing it: https://github.com/angular-eslint/angular-eslint/blob/master/packages/schematics/src/convert-tslint-to-eslint/convert-to-eslint-config.ts#L75

...in order to use the node API of tslint-to-eslint-config

const summarizedConfiguration = await createESLintConfiguration(
etc

@JamesHenry I removed the package from my project, and reinstalled it, it works fine again

Thanks !

Same here - works like a charm! Thanks a lot for the quick fix!

Thanks for confirming @KyDenZ @mkurte please could I ask a favour in helping Josh and I get to the bottom of this?

Please code you create a simple js file (e.g. repro.js) with the following contents and then run it

repro.js

const { dirSync } = require(‘tmp’);
const { execSync } = require(‘child_process’);

const tempDir = dirSync().name;
execSync(`npm i -D tslint-to-eslint-config@2.7.0`, {
  cwd: tempDir,
  stdio: ‘ignore’,
});

console.log(
  require(require.resolve(‘tslint-to-eslint-config’, {
    paths: [tempDir],
  }))
);
node ./repro.js

Do you get a similar error to when you ran the angular-eslint schematic?

@mkurte @KyDenZ @gs-scooter would you mind trying the conversion again without any workarounds to verify 2.9.0 is behaving as 2.6.0 used to?

@JamesHenry - I can confirm that the conversion works as expected - Thanks!

@JamesHenry The normal way with only adding schematics and then converting works now as expected.

@mkurte Yes understood, but that's only because Josh reverted the recent changes - this isn't really a true solution. So we were hoping you could help us troubleshoot further by ignoring the schematics and running the snippet outlined above: #571 (comment)

If it fails in the same way that you were seeing via the schematics previously then we know there is something about your environment and the changes that went into v2.7.0 of tslint-to-eslint-config that do not mix.

Please could you give it a try?

Calling the above script gives me following output:

/Users/matthiaskurte/Desktop/test/repro.js:1
const { dirSync } = require(‘tmp’);


SyntaxError: Invalid or unexpected token
    at wrapSafe (internal/modules/cjs/loader.js:1054:16)
    at Module._compile (internal/modules/cjs/loader.js:1102:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

@mkurte those don’t look like single quotes in your string there, is that just a GitHub rendering issue or have you somehow ended up with a different Unicode character in there?

@JamesHenry the snippet you pasted has the odd quotes 😛. Here's a version with them fixed:

repro.js

const { dirSync } = require('tmp');
const { execSync } = require('child_process');

const tempDir = dirSync().name;
execSync(`npm i -D tslint-to-eslint-config@2.7.0`, {
  cwd: tempDir,
  stdio: 'ignore',
});

console.log(
  require(require.resolve('tslint-to-eslint-config', {
    paths: [tempDir],
  }))
);
node ./repro.js

What would be super duper helpful for me is if someone could post a simple reliable way to trigger the initial error by forcing tslint-to-eslint-config@2.7.0 to run in a project. I'm having a hard time getting that.

I tried a similar set of scripts to the suggestion from @mkurte earlier (thanks for this!):

ng new my-app
cd my-app
npm i tslint
ng add @angular-eslint/schematics
ng g @angular-eslint/schematics:convert-tslint-to-eslint

...with these manual steps:

  • Before the ng g, adding a tslint.json with basic contents
  • Changing the value of tslintToEslintConfigVersion in my-app/node_modules/@angular-eslint/schematics/dist/convert-tslint-to-eslint/convert-to-eslint-config.js to 2.7.0
  • Selecting N (no) for prompts in the schematic

No luck. Nothing crashes and the config is successfully created.

Hey guys, sorry, was on vacation. 🌴

@JamesHenry
The new version of the repro.js doesn't work too:

node repro.js
internal/modules/cjs/loader.js:969
  throw err;
  ^

Error: Cannot find module 'tmp'
Require stack:
- /Users/matthiaskurte/Desktop/test/repro.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:966:15)
    at Function.Module._load (internal/modules/cjs/loader.js:842:27)
    at Module.require (internal/modules/cjs/loader.js:1026:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (/Users/matthiaskurte/Desktop/test/repro.js:1:21)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/Users/matthiaskurte/Desktop/test/repro.js' ]

@JoshuaKGoldberg:

I guess you should call the converter with --defaults to avoid prompts. I'm pretty sure not all defaults had the option N

@JoshuaKGoldberg Ah sorry must have been because I copied the snippet from slack!

Josh, are you on a mac?

@mkurte ah ok, sorry I guess you will need to install the tmp dependency first then! Please could you try that and rerun?

Windows here.

Ah ok damn, so it's not a simple as being reproducible by OS (that was purely a guess anyway based on the commits you highlighted above)

FWIW, I also wasn't able to reproduce this (on Windows).
But did you run a npm install before running the schematic?
If you're using 3rd party plugins (e.g. codelyzer or rxjs-tslint), these must be installed.

Perhaps this is useful if the error message is different based on OS or node versions.

There was a critical error when trying to inspect your tslint.json: 

Failed to load C:\Users\tdeschryver\dev\throw-aways\my-app\tslint.json: Could not find custom rule directory: node_modules/codelyzer

@JamesHenry Output is now this (I forgot the quotes around tmp. 🙄 )

{
  convertFileComments: [Getter],
  convertTSLintConfig: [Getter],
  createESLintConfiguration: [Getter],
  findOriginalConfigurations: [Getter],
  findReportedConfiguration: [Getter],
  formatOutput: [Getter],
  joinConfigConversionResults: [Getter],
  ResultStatus: [Getter]
}

@mkurte Ok thanks then there is sadly no minimal repro for this...

I'm really not sure what to suggest at this point - I don't think there is anything more that can be done. @JoshuaKGoldberg this ended up impacting you the most because of the revert you did on the tslint-to-eslint-config side.

How would you like to move forward? Chalk this up to a transient/user env fluke and restore the change that was reverted?

Hmm, I'm not convinced this won't come back again if I revert the revert. The original issue (typescript-eslint/tslint-to-eslint-config#1150) is that tslint-to-eslint-config tries using cat on Windows. I can switch that to using type on Windows.

@JamesHenry I think we can close this issue out now. v2.10.1 of tslint-to-eslint-config works better on Windows 👍

This is still throwing error on my Windows 11 terminal having git bash installed with all latest versions
image

This is still throwing error on my Windows 11 terminal having git bash installed with all latest versions image

This is because u don't have a TS Lint in your app. So u don't need to migrate from TS Lint. Install ES Lint directly.

Else you can also install the TS Lint again and migrate - This a dumb way to proceed any way.