Solvedangular cli Limit lint to changed files

Bug Report or Feature Request (mark with an x)

- [ ] bug report -> please search issues before submitting
- [x] feature request

Desired functionality.

Currently, ng lint analyzes all files in the project. I work on a project that has ~260 .ts files in a single project (multiple modules), and to lint all of them takes a while and the output can be hard to digest if there are errors across many files. I think a feature flag (ng lint --staged or similar) to lint only the files that have changed would be a great addition. This is a pretty common feature (eg. lint-staged).

This would also help another specific use case: if a dev/team wants to run lint as a precommit hook, linting just the changed files would make a lot of sense without the burden of analyzing extra files.

Alternative

I could see an argument for not incorporating this into the CLI because it would either be dependent on a single version control system or have support all of them (maybe just git at first, others incrementally). A great alternative would be to expose a --files flag so that consumers can have control over which files are linted. This way would work easily with lint-staged. Below would be an example of how a sample config inside package.json might look (from lint-staged docs):

{
  "scripts": {
    "precommit": "lint-staged"
    ...
  },
  "lint-staged": {
    "src/**/*.ts": ["ng lint --fix", "git add"]
  }
}

The lint-staged docs seem to suggest that tslint already works but the way the CLI retrieves files doesn't seem to make this possible at the moment. Would love to be wrong! If I am wrong, perhaps it could be added to docs in some way showing how it could be done.

22 Answers

✔️Accepted Answer

Any updates ? This is really annoying for any real project, I first have to stash all my changes and mocks before pushing a single file.

Other Answers:

@johannesjo
You can use the --files flag multiple times, e.g.

ng lint --files=file1.ts --files=file2.ts

I made myself a little helper script to convert a lint-staged call to the given npm script with --files flags.

ng-lint-staged.js

const { exec } = require('child_process');

const cwd = process.cwd();

const script = process.argv[2];

const files = process.argv
    .slice(3)
    .map((f) => {
        // ng lint's --files argument only works with relative paths
        // strip cwd and leading path delimiter
        return `--files="${f.replace(cwd, '').slice(1)}"`;
    })
    .join(' ');

exec(
    `npm run ${script} -- --tsConfig=tsconfig.json ${files}`,
    (error, stdout) => {
        if (error) {
            console.log(stdout);
            process.exit(1);
        }
    }
);

And lint-staged calls the script in the following configurations

"src/**/*.ts": [
    "node ng-lint-staged.js lint:app",
    "git add"
],
"e2e/**/*.ts": [
    "node ng-lint-staged.js lint:e2e",
    "git add"
],

with the cli v6.0.0, we might be able to achieve the goal. The tslintBuilder supports tslint files input https://github.com/angular/devkit/blob/v6.0.0-rc.5/packages/angular_devkit/build_angular/src/tslint/index.ts#L172.

We have to add new params for lint command to pass the input to tslint.

Any workaround or advance on this front? It's really painful to wait for the whole project to be linted.

We also ran into this issue with @angular/cli 7.2+. The solution above did not work for us since we do not use lint-staged, so I implemented a small script that directly runs ng lint and finds out for itself the changed files. For that, I have installed the devDependency staged-git-files:
npm i -D staged-git-files.

Here's the script:

const { execSync } = require('child_process');
const stagedGitFiles = require('staged-git-files');

stagedGitFiles((error, stagedFiles) => {
    if (error) {
        console.error(error);
        process.exit(1);
    }
    if (stagedFiles && stagedFiles.length) {
        const stagedAngularTSFiles = stagedFiles.filter(
            (file) => file.status !== 'Deleted' && file.filename.startsWith('src') && file.filename.endsWith('.ts')
        );
        if (stagedAngularTSFiles.length) {
            let ngLint = 'ng lint angular-frontend --fix ';
            for (const { filename } of stagedAngularTSFiles) {
                ngLint = ngLint.concat(`--files ${filename} `);
            }
            console.log(`Linting ${stagedAngularTSFiles.length} staged Angular TS files...\n`);
            execSync(ngLint, { stdio: 'inherit' });
        } else {
            console.log('No Angular TS files staged...\n');
        }
    } else {
        console.log('No Angular TS files staged...\n');
    }
});

In package.json, I have added this to the husky pre-commit hook job list. Seems to work well so far and cut down our waiting time by a lot. Suggestions and feedback welcome :-)

More Issues: