Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tsdx lint command #99

Merged
merged 34 commits into from Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a53365a
feat(lint): Add tsdx lint command
sam-kvale-sap May 11, 2019
c2d5ff8
feat(lint): process.exit(1) when errorCount > 0
sam-kvale-sap May 11, 2019
b34a8f4
style(lint): Fix lint errors that failed the CI build
sam-kvale-sap May 11, 2019
641f5fb
feat(lint): Play nicely with pretty-quick husky hook
sam-kvale-sap May 11, 2019
87d26f6
feat(lint): Install eslint-prettier dependencies, remove tslint devDeps
sam-kvale-sap May 11, 2019
2009b77
feat(lint): Add jsx linting support for React by default
sam-kvale-sap May 11, 2019
ec9ee11
fix(lint): Fix local ignore-pattern
sam-kvale-sap May 11, 2019
dcf82df
feat(lint): Use @typescript-eslint/no-unused-vars instead of typescri…
sam-kvale-sap May 13, 2019
9467d40
Merge branch 'master' into feat/linting-command
swyxio May 17, 2019
5f82e13
Update lockfile
jaredpalmer May 30, 2019
6be8bc4
Merge branch 'master' into pr/skvale/99
jaredpalmer May 30, 2019
17a1f6c
Merge branch 'master' into pr/skvale/99
jaredpalmer May 30, 2019
97e97c2
Add pre-commit lint hook
jaredpalmer May 30, 2019
6fafc73
Specify no-unused-locals in tsconfig
jaredpalmer May 30, 2019
0794e13
feat(lint): Extend react-app from eslint-config-react-app
sam-kvale-sap May 30, 2019
eae9bac
test(lint): Tests no longer failed from spacing violations
sam-kvale-sap May 30, 2019
63cb7eb
feat(lint): Could just extend react-app instead of overriding anything
sam-kvale-sap May 30, 2019
42fb986
feat(lint): Use prettier eslint for tsdx as an opt-in
sam-kvale-sap May 30, 2019
4332881
feat(lint): Optionally use prettier with --prettier flag
sam-kvale-sap May 30, 2019
12f2e99
feat(lint): Specify how to customize the lint rules in the README
sam-kvale-sap May 30, 2019
92adf03
feat(lint): Use prettier by default again
sam-kvale-sap May 31, 2019
eb2fe5b
Merge branch 'master' into feat/linting-command
sam-kvale-sap Jun 22, 2019
3f57f61
feat(lint): Use eslint@6
sam-kvale-sap Jun 22, 2019
4197482
chore(lockfile): Commit updated lockfile
sam-kvale-sap Jun 22, 2019
257a196
feat(lint): Update to @typescript-eslint/{parser,eslint-plugin} v1.11.0
sam-kvale-sap Jun 24, 2019
83c753d
chore(deps): Bump eslint and @typescript-eslint
sam-kvale-sap Jul 12, 2019
1604e99
Merge branch 'master' into feat/linting-command
sam-kvale-sap Jul 12, 2019
a51795a
Merge branch 'master' into feat/linting-command
sam-kvale-sap Jul 27, 2019
9d595a5
feat(lint): Bump deps and simplify createEslintConfig
sam-kvale-sap Aug 10, 2019
215cd93
feat(lint): Simplify written eslintrc
sam-kvale-sap Aug 10, 2019
530b8b5
chore(docs): Remove wrongfully merged README addition
sam-kvale-sap Aug 10, 2019
fe37a21
chore(yarn.lock): Fix merge issue with the babel/core version
sam-kvale-sap Aug 10, 2019
65ea74f
feat(lint): Bump eslint-config-react-app dep
sam-kvale-sap Aug 13, 2019
00584a1
feat(lint): Last small updates
sam-kvale-sap Aug 14, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
33 changes: 33 additions & 0 deletions README.md
Expand Up @@ -12,15 +12,22 @@ 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)
- [`invariant`](#invariant)
- [`warning`](#warning)
- [Using lodash](#using-lodash)
- [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 @@ -71,6 +78,10 @@ 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.

## 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 @@ -316,6 +327,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

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 test
$ tsdx lint src test --fix
$ tsdx lint src test --ignore-pattern test/foobar.ts
$ tsdx lint src test --write-file
```

## Author

- [Jared Palmer](https://twitter.com/jaredpalmer)
Expand Down
19 changes: 13 additions & 6 deletions package.json
Expand Up @@ -22,6 +22,8 @@
},
"scripts": {
"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 @@ -31,6 +33,8 @@
"dependencies": {
"@babel/core": "^7.2.2",
"@jaredpalmer/rollup-plugin-preserve-shebang": "^0.1.7",
"@typescript-eslint/eslint-plugin": "^1.8.0",
"@typescript-eslint/parser": "^1.8.0",
"ansi-escapes": "^3.1.0",
"asyncro": "^3.0.0",
"babel-plugin-annotate-pure-calls": "^0.4.0",
Expand All @@ -40,6 +44,10 @@
"chalk": "^2.4.2",
"cross-env": "5.0.5",
"enquirer": "^2.3.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.13.0",
"execa": "^1.0.0",
"fs-extra": "^7.0.1",
"jest": "^24.7.1",
Expand Down Expand Up @@ -67,27 +75,26 @@
"devDependencies": {
"@types/ansi-escapes": "^4.0.0",
"@types/camelcase": "^5.2.0",
"@types/eslint": "^4.16.6",
"@types/execa": "^0.9.0",
"@types/fs-extra": "^5.0.5",
"@types/mkdirp": "^0.5.2",
"@types/ms": "^0.7.30",
"@types/node": "^11.13.8",
"@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.1.0",
"prettier": "^1.17.0",
"pretty-quick": "^1.10.0",
"ps-tree": "^1.2.0",
"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"
"react": "^16.8.6",
"shelljs": "^0.8.3"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
"pre-commit": "pretty-quick --staged --pattern '!test/tests/lint/**'"
}
},
"prettier": {
Expand Down
74 changes: 74 additions & 0 deletions src/createEslintConfig.ts
@@ -0,0 +1,74 @@
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: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
'plugin:react/recommended',
jaredpalmer marked this conversation as resolved.
Show resolved Hide resolved
],
root: true,
env: {
node: true,
es6: true,
jest: true,
browser: true,
},
parserOptions: {
ecmaVersion: 2017,
ecmaFeatures: {
impliedStrict: true,
jsx: true,
experimentalObjectRestSpread: true,
},
skvale marked this conversation as resolved.
Show resolved Hide resolved
sourceType: 'module',
},
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
'no-console': 'off',
'no-empty': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
},
settings: {
react: {
version: 'detect',
},
},
};

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/createRollupConfig.ts
@@ -1,3 +1,4 @@
/* eslint @typescript-eslint/camelcase: 0 */
import { DEFAULT_EXTENSIONS } from '@babel/core';
import { safeVariableName, safePackageName, external } from './utils';
import { paths } from './constants';
Expand Down
56 changes: 47 additions & 9 deletions src/index.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env node
/* eslint @typescript-eslint/no-var-requires: 0 */

import sade from 'sade';
import glob from 'tiny-glob/sync';
Expand All @@ -8,6 +9,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 @@ -17,13 +19,8 @@ import { paths } from './constants';
import * as Messages from './messages';
import { createRollupConfig } from './createRollupConfig';
import { createJestConfig } from './createJestConfig';
import {
safeVariableName,
resolveApp,
safePackageName,
clearConsole,
} from './utils';
import * as Output from './output';
import { createEslintConfig } from './createEslintConfig';
import { resolveApp, safePackageName, clearConsole } from './utils';
import { concatAllArray } from 'jpjs';
import getInstallCmd from './getInstallCmd';
import getInstallArgs from './getInstallArgs';
Expand All @@ -42,6 +39,7 @@ let appPackageJson: {
name: string;
source?: string;
jest?: any;
eslint?: any;
};
try {
appPackageJson = fs.readJSONSync(resolveApp('package.json'));
Expand Down Expand Up @@ -370,7 +368,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 @@ -394,8 +392,48 @@ prog
})
);

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

prog
.command('lint')
.describe('Run eslint')
.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({
extensions: ['.ts', '.tsx'],
fix: opts.fix,
ignorePattern: opts['ignore-pattern'],
baseConfig: {
...createEslintConfig({
rootDir: paths.appRoot,
writeFile: opts['write-file'],
}),
...appPackageJson.eslint,
},
});
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);
4 changes: 4 additions & 0 deletions test/tests/lint/file-with-lint-errors.ts
@@ -0,0 +1,4 @@
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';
7 changes: 7 additions & 0 deletions test/tests/lint/react-file-with-lint-errors.tsx
@@ -0,0 +1,7 @@
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>;
};
34 changes: 34 additions & 0 deletions test/tests/lint/tsdx-lint.test.js
@@ -0,0 +1,34 @@
/**
* @jest-environment node
*/
'use strict';

const shell = require('shelljs');

shell.config.silent = true;

describe('tsdx lint', () => {
it('should fail to lint a ts file with errors', () => {
const testFile = 'test/tests/lint/file-with-lint-errors.ts';
const output = shell.exec(`node dist/index.js lint ${testFile}`);
expect(output.code).toBe(1);
});

it('should succeed linting a ts file without errors', () => {
const testFile = 'test/tests/lint/file-without-lint-error.ts';
const output = shell.exec(`node dist/index.js lint ${testFile}`);
expect(output.code).toBe(0);
});

it('should fail to lint a tsx file with errors', () => {
const testFile = 'test/tests/lint/react-file-with-lint-errors.tsx';
const output = shell.exec(`node dist/index.js lint ${testFile}`);
expect(output.code).toBe(1);
});

it('should succeed linting a tsx file without errors', () => {
const testFile = 'test/tests/lint/react-file-without-lint-error.tsx';
const output = shell.exec(`node dist/index.js lint ${testFile}`);
expect(output.code).toBe(0);
});
});
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -3,6 +3,7 @@
"exclude": ["src/template/**/*"],
"compilerOptions": {
"allowJs": true,
"jsx": "react",
"importHelpers": true,
"esModuleInterop": true,
"outDir": "dist",
Expand Down