Skip to content

Commit

Permalink
Merge pull request #29 from crazyfactory/15-import-react-rule
Browse files Browse the repository at this point in the history
feat: import-react rule
  • Loading branch information
paibamboo authored Oct 15, 2019
2 parents b33dc24 + 9772644 commit d33e49b
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 9 deletions.
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,30 @@ Initially you should:

## Rules

- `create-async-actions`
### `create-async-actions`
- In [ts-react-boilerplate](https://github.com/crazyfactory/ts-react-boilerplate), we use `createAsyncActions` to
create Redux async actions. Four actions are created from calling it - `BASE`, `BASE_PENDING`, `BASE_FULFILLED`, and
`BASE_REJECTED` as an example when `createAsyncActions("BASE", "BASE_PENDING", "BASE_FULFILLED", "BASE_REJECTED"` is
called. Still, as you see, we need to provide string literal as arugments due to typescript limitation, if we provide
any string variable, the type will be deduced to just `string`. This rule enforces 2nd, 3rd, and 4th argument to be
the concatenation of the first argument string and `_PENDING`, `_FULFILLED`, and `_REJECTED` respectively.
- `hex-format`
### `import-react`
- Specify how you should import `react`. Either `import *` or `import React`.
- Rule options:
- `type: "default" | "star"`. Default is `star`
### `hex-format`
- Requires literal string in hex format to be uppercase/lowercase and/or of specific lengths.
- Rule options:
- `case: "uppercase" | "lowercase"`
- `allowedLengths: number[]`
- `interface-sort-keys`
- `case: "uppercase" | "lowercase"`. Default is `lowercase`
- `allowedLengths: number[]`. Default is `[4, 7]`
### `interface-sort-keys`
- Same as [object-literal-sort-keys](https://palantir.github.io/tslint/rules/object-literal-sort-keys/) but applied to
interface keys
- `jsx-space-before-trailing-slash`
### `jsx-space-before-trailing-slash`
- Requires or bans space before `/>` part of jsx.
- Rule options:
- `["always", "never"]`
- `language`
- `["always", "never"]`. Default is `always`.
### `language`
- Requires that string argument called by `Translator` object is in the `reference.json`
- Rule options:
- `path`: path to `reference.json`
Expand All @@ -62,6 +66,6 @@ Initially you should:
}
```
- `callerNames: string[]`: Name of translator object type, default is `["Translator"]`
- `no-dup-actions`
### `no-dup-actions`
- Requires that all actions created by [createAsyncActions](https://github.com/crazyfactory/ts-react-boilerplate/blob/master/src/app/redux/modules/baseModule.ts)
and [createAction](https://github.com/piotrwitek/typesafe-actions#createaction) have unique name.
43 changes: 43 additions & 0 deletions src/importReactRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as Lint from "tslint";
import * as ts from "typescript";

interface IOption {
type: "star" | "default";
}

class ImportReactRule extends Lint.AbstractWalker<IOption> {
public walk(sourceFile: ts.SourceFile): void {
if (sourceFile.isDeclarationFile) {
return;
}
const cb = (node: ts.Node): void => {
if (
ts.isImportDeclaration(node)
&& node.moduleSpecifier.getText(sourceFile) === `"react"`
&& node.importClause
) {
// import * as React has namedBindings, and import React has only name
if (this.options.type === "star" && !node.importClause.namedBindings) {
this.addFailureAtNode(node, "You must use `import * as React from 'react'`");
} else if (this.options.type === "default" && node.importClause.namedBindings) {
this.addFailureAtNode(node, "You must use `import React from 'react'`");
}
}
return ts.forEachChild(node, cb);
};
return ts.forEachChild(sourceFile, cb);
}
}

// tslint:disable-next-line:export-name max-classes-per-file
export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(
new ImportReactRule(
sourceFile,
"import-react",
{type: this.ruleArguments[0] && this.ruleArguments[0].type ? this.ruleArguments[0].type : "star"}
)
);
}
}
3 changes: 3 additions & 0 deletions test/rules/import-react/default/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as React from "react";
import React from "react";
~~~~~~~~~~~~~~~~~~~~~~~~~~ [You must use `import * as React from 'react'`]
8 changes: 8 additions & 0 deletions test/rules/import-react/default/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"rulesDirectory": [
"../../../../lib"
],
"rules": {
"import-react": true
}
}
3 changes: 3 additions & 0 deletions test/rules/import-react/import-default/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as React from "react";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [You must use `import React from 'react'`]
import React from "react";
13 changes: 13 additions & 0 deletions test/rules/import-react/import-default/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"rulesDirectory": [
"../../../../lib"
],
"rules": {
"import-react": [
true,
{
"type": "default"
}
]
}
}
3 changes: 3 additions & 0 deletions test/rules/import-react/import-star/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as React from "react";
import React from "react";
~~~~~~~~~~~~~~~~~~~~~~~~~~ [You must use `import * as React from 'react'`]
13 changes: 13 additions & 0 deletions test/rules/import-react/import-star/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"rulesDirectory": [
"../../../../lib"
],
"rules": {
"import-react": [
true,
{
"type": "star"
}
]
}
}

0 comments on commit d33e49b

Please sign in to comment.