Skip to content

Commit

Permalink
Add tsdx lint command (#99)
Browse files Browse the repository at this point in the history
* feat(lint): Add tsdx lint command

Use it to lint itself

* feat(lint): process.exit(1) when errorCount > 0

* style(lint): Fix lint errors that failed the CI build

* feat(lint): Play nicely with pretty-quick husky hook

* feat(lint): Install eslint-prettier dependencies, remove tslint devDeps

* feat(lint): Add jsx linting support for React by default

* fix(lint): Fix local ignore-pattern

* feat(lint): Use @typescript-eslint/no-unused-vars instead of typescript/no-unused-vars

* Update lockfile

* Merge branch 'master' into pr/skvale/99

* Add pre-commit lint hook

* Specify no-unused-locals in tsconfig

* feat(lint): Extend react-app from eslint-config-react-app

* test(lint): Tests no longer failed from spacing violations

* feat(lint): Could just extend react-app instead of overriding anything

* feat(lint): Use prettier eslint for tsdx as an opt-in

* feat(lint): Optionally use prettier with --prettier flag

* feat(lint): Specify how to customize the lint rules in the README

* feat(lint): Use prettier by default again

* feat(lint): Use eslint@6

* chore(lockfile): Commit updated lockfile

* feat(lint): Update to @typescript-eslint/{parser,eslint-plugin} v1.11.0

* chore(deps): Bump eslint and @typescript-eslint

* feat(lint): Bump deps and simplify createEslintConfig

* feat(lint): Simplify written eslintrc

* chore(docs): Remove wrongfully merged README addition

* chore(yarn.lock): Fix merge issue with the babel/core version

* feat(lint): Bump eslint-config-react-app dep

* feat(lint): Last small updates
  • Loading branch information
sam-kvale-sap authored and jaredpalmer committed Aug 14, 2019
1 parent 1a2e50c commit e2f2983
Show file tree
Hide file tree
Showing 18 changed files with 1,882 additions and 549 deletions.
4 changes: 4 additions & 0 deletions .circleci/config.yml
Expand Up @@ -24,6 +24,10 @@ jobs:
name: Build
command: yarn build

- run:
name: Lint
command: yarn lint:post-build

- run:
name: Run tests
command: yarn test --runInBand --no-cache --coverage
Expand Down
7 changes: 7 additions & 0 deletions .eslintrc.js
@@ -0,0 +1,7 @@
module.exports = {
extends: [
'react-app',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
};
33 changes: 31 additions & 2 deletions README.md
Expand Up @@ -12,21 +12,23 @@ Despite all the recent hype, setting up a new TypeScript (x React) library can b
- [`npm start` or `yarn start`](#npm-start-or-yarn-start)
- [`npm run build` or `yarn build`](#npm-run-build-or-yarn-build)
- [`npm test` or `yarn test`](#npm-test-or-yarn-test)
- [`npm run lint` or `yarn lint`](#npm-run-lint-or-yarn-lint)
- [Optimizations](#optimizations)
- [Development-only Expressions + Treeshaking](#development-only-expressions--treeshaking)
- [Rollup Treeshaking](#rollup-treeshaking)
- [Advanced `babel-plugin-dev-expressions`](#advanced-babel-plugin-dev-expressions)
- [`__DEV__`](#__dev__)
- [`__DEV__`](#dev)
- [`invariant`](#invariant)
- [`warning`](#warning)
- [Using lodash](#using-lodash)
- [Hosting extracted errors](#hosting-extracted-errors)
- [Error extraction](#error-extraction)
- [Inspiration](#inspiration)
- [Comparison to Microbundle](#comparison-to-microbundle)
- [API Reference](#api-reference)
- [`tsdx watch`](#tsdx-watch)
- [`tsdx build`](#tsdx-build)
- [`tsdx test`](#tsdx-test)
- [`tsdx lint`](#tsdx-lint)
- [Author](#author)
- [License](#license)

Expand Down Expand Up @@ -77,6 +79,11 @@ The package is optimized and bundled with Rollup into multiple formats (CommonJS
Runs the test watcher (Jest) in an interactive mode.
By default, runs tests related to files changed since the last commit.

### `npm run lint` or `yarn lint`

Runs Eslint with Prettier on .ts and .tsx files.
If you want to customize eslint you can add an `eslint` block to your package.json, or you can run `yarn lint --write-file` and edit the generated `.eslintrc.js` file.

## Optimizations

Aside from just bundling your module into different formats, TSDX comes with some optimizations for your convenience. They yield objectively better code and smaller bundle sizes.
Expand Down Expand Up @@ -343,6 +350,28 @@ Examples

This runs Jest v24.x in watch mode. See [https://jestjs.io](https://jestjs.io) for options. If you are using the React template, jest uses the flag `--env=jsdom` by default.

### `tsdx lint`

```shell
Description
Run eslint with Prettier

Usage
$ tsdx lint [options]

Options
--fix Fixes fixable errors and warnings
--ignore-pattern Ignore a pattern
--write-file Write the config file locally
-h, --help Displays this message

Examples
$ tsdx lint src
$ tsdx lint src --fix
$ tsdx lint src test --ignore-pattern test/foo.ts
$ tsdx lint src --write-file
```

## Author

- [Jared Palmer](https://twitter.com/jaredpalmer)
Expand Down
24 changes: 18 additions & 6 deletions package.json
Expand Up @@ -23,6 +23,8 @@
"scripts": {
"prepare": "tsc -p tsconfig.json",
"build": "tsc -p tsconfig.json",
"lint": "yarn build && yarn lint:post-build",
"lint:post-build": "node dist/index.js lint src test --ignore-pattern 'test/tests/lint'",
"test": "jest --config ./test/jest.config.json"
},
"files": [
Expand All @@ -35,6 +37,8 @@
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^1.13.0",
"ansi-escapes": "^3.2.0",
"asyncro": "^3.0.0",
"babel-plugin-annotate-pure-calls": "^0.4.0",
Expand All @@ -47,6 +51,15 @@
"chalk": "^2.4.2",
"cross-env": "5.2.0",
"enquirer": "^2.3.0",
"eslint": "^6.1.0",
"eslint-config-prettier": "^6.0.0",
"eslint-config-react-app": "^5.0.1",
"eslint-plugin-flowtype": "^4.2.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.14.3",
"eslint-plugin-react-hooks": "^1.7.0",
"execa": "^1.0.0",
"fs-extra": "^8.0.1",
"jest": "^24.8.0",
Expand All @@ -55,6 +68,7 @@
"mkdirp": "^0.5.1",
"ora": "^3.4.0",
"pascal-case": "^2.0.1",
"prettier": "^1.18.2",
"progress-estimator": "^0.2.2",
"rollup": "^1.12.0",
"rollup-plugin-babel": "^4.3.2",
Expand All @@ -74,32 +88,30 @@
"devDependencies": {
"@types/ansi-escapes": "^4.0.0",
"@types/camelcase": "^5.2.0",
"@types/eslint": "^4.16.8",
"@types/execa": "^0.9.0",
"@types/fs-extra": "^8.0.0",
"@types/jest": "^24.0.15",
"@types/mkdirp": "^0.5.2",
"@types/ms": "^0.7.30",
"@types/node": "^12.0.2",
"@types/ora": "^3.2.0",
"@types/react": "^16.8.17",
"@types/rollup-plugin-json": "^3.0.2",
"@types/rollup-plugin-sourcemaps": "^0.4.2",
"husky": "^2.3.0",
"prettier": "^1.17.1",
"pretty-quick": "^1.10.0",
"ps-tree": "^1.2.0",
"react": "^16.8.6",
"shelljs": "^0.8.3",
"tslint": "^5.16.0",
"tslint-config-palmerhq": "^1.0.2",
"tslint-config-prettier": "^1.18.0",
"tslint-react": "^4.0.0",
"typescript": "^3.4.5"
},
"peerDependencies": {
"typescript": "3"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
"pre-commit": "pretty-quick --staged --pattern '!test/tests/lint/**' && yarn lint"
}
},
"prettier": {
Expand Down
41 changes: 41 additions & 0 deletions src/createEslintConfig.ts
@@ -0,0 +1,41 @@
import fs from 'fs';
import path from 'path';
import { CLIEngine } from 'eslint';

interface CreateEslintConfigArgs {
rootDir: string;
writeFile: boolean;
}
export function createEslintConfig({
rootDir,
writeFile,
}: CreateEslintConfigArgs): CLIEngine.Options['baseConfig'] {
const config = {
extends: [
'react-app',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
};

if (writeFile) {
const file = path.join(rootDir, '.eslintrc.js');
if (fs.existsSync(file)) {
console.error(
'Error trying to save the Eslint configuration file:',
`${file} already exists.`
);
} else {
try {
fs.writeFileSync(
file,
`module.exports = ${JSON.stringify(config, null, 2)}`
);
} catch (e) {
console.error(e);
}
}
}

return config;
}
3 changes: 0 additions & 3 deletions src/createJestConfig.ts
@@ -1,6 +1,3 @@
import fs from 'fs';
import path from 'path';

export function createJestConfig(
_: (relativePath: string) => void,
rootDir: string
Expand Down
1 change: 1 addition & 0 deletions src/env.d.ts
Expand Up @@ -5,6 +5,7 @@ declare module 'ora';
declare module 'sade';
declare module 'tiny-glob/sync';
declare module 'ansi-escapes';
declare module 'eslint-config-react-app';

// Patch Babel
// @see line 226 of https://unpkg.com/@babel/core@7.4.4/lib/index.js
Expand Down
6 changes: 2 additions & 4 deletions src/errors/extractErrors.ts
Expand Up @@ -4,8 +4,6 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';

import fs from 'fs-extra';
import * as babylon from 'babylon';
import traverse from 'babel-traverse';
Expand Down Expand Up @@ -137,8 +135,8 @@ function ErrorProd(code) {
url += '&args[]=' + encodeURIComponent(arguments[i]);
}
return new Error(
\`Minified ${prettyName} error #\$\{code\}; visit \$\{url\} for the full message or \` +
'use the non-minified dev environment for full errors and additional \' +
\`Minified ${prettyName} error #$\{code}; visit $\{url} for the full message or \` +
'use the non-minified dev environment for full errors and additional ' +
'helpful warnings. '
);
}
Expand Down
1 change: 0 additions & 1 deletion src/errors/invertObject.ts
Expand Up @@ -13,7 +13,6 @@ export function invertObject(
const result: Dict = {};
const mapKeys = Object.keys(targetObj);

// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const originalKey of mapKeys) {
const originalVal = targetObj[originalKey];

Expand Down
48 changes: 45 additions & 3 deletions src/index.ts
Expand Up @@ -15,6 +15,7 @@ import chalk from 'chalk';
import util from 'util';
import * as fs from 'fs-extra';
import jest from 'jest';
import { CLIEngine } from 'eslint';
import logError from './logError';
import path from 'path';
import mkdirp from 'mkdirp';
Expand All @@ -24,8 +25,8 @@ import { paths } from './constants';
import * as Messages from './messages';
import { createRollupConfig } from './createRollupConfig';
import { createJestConfig } from './createJestConfig';
import { createEslintConfig } from './createEslintConfig';
import { resolveApp, safePackageName, clearConsole } from './utils';
import * as Output from './output';
import { concatAllArray } from 'jpjs';
import getInstallCmd from './getInstallCmd';
import getInstallArgs from './getInstallArgs';
Expand All @@ -44,6 +45,7 @@ let appPackageJson: {
name: string;
source?: string;
jest?: any;
eslint?: any;
};
try {
appPackageJson = fs.readJSONSync(resolveApp('package.json'));
Expand Down Expand Up @@ -417,7 +419,7 @@ prog
.describe(
'Run jest test runner in watch mode. Passes through all flags directly to Jest'
)
.action(async (opts: any) => {
.action(async () => {
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
Expand All @@ -441,8 +443,48 @@ prog
})
);

const [_skipTheWordTest, ...argsToPassToJestCli] = argv;
const [, ...argsToPassToJestCli] = argv;
jest.run(argsToPassToJestCli);
});

prog
.command('lint')
.describe('Run eslint with Prettier')
.example('lint src test')
.option('--fix', 'Fixes fixable errors and warnings')
.example('lint src test --fix')
.option('--ignore-pattern', 'Ignore a pattern')
.example('lint src test --ignore-pattern test/foobar.ts')
.option('--write-file', 'Write the config file locally')
.example('lint src test --write-file')
.action(
async (opts: {
fix: boolean;
'ignore-pattern': string;
'write-file': boolean;
_: string[];
}) => {
const cli = new CLIEngine({
baseConfig: {
...createEslintConfig({
rootDir: paths.appRoot,
writeFile: opts['write-file'],
}),
...appPackageJson.eslint,
},
extensions: ['.ts', '.tsx'],
fix: opts.fix,
ignorePattern: opts['ignore-pattern'],
});
const report = cli.executeOnFiles(opts['_']);
if (opts.fix) {
CLIEngine.outputFixes(report);
}
console.log(cli.getFormatter()(report.results));
if (report.errorCount) {
process.exit(1);
}
}
);

prog.parse(process.argv);
1 change: 1 addition & 0 deletions test/tests/lint/file-with-lint-errors.ts
@@ -0,0 +1 @@
export const foo () => !!'bar';
5 changes: 5 additions & 0 deletions test/tests/lint/file-with-prettier-lint-errors.ts
@@ -0,0 +1,5 @@
export const foo = ( ) =>
!! ('bar')
;


1 change: 1 addition & 0 deletions test/tests/lint/file-without-lint-error.ts
@@ -0,0 +1 @@
export const foo = () => !!'bar';
5 changes: 5 additions & 0 deletions test/tests/lint/react-file-with-lint-errors.tsx
@@ -0,0 +1,5 @@
import React from 'react';

export const Foobar = (props: any) => {
return <<div {...props}>foobar</div>;
};
5 changes: 5 additions & 0 deletions test/tests/lint/react-file-without-lint-error.tsx
@@ -0,0 +1,5 @@
import React from 'react';

export const Foobar = (props: any) => {
return <div {...props}>foobar </div>;
};

0 comments on commit e2f2983

Please sign in to comment.