Skip to content

Commit

Permalink
feat(tslint-rules): add rule for removing star imports in store
Browse files Browse the repository at this point in the history
  • Loading branch information
MKless authored and dhhyi committed Jun 15, 2020
1 parent a0d5071 commit 3c0b528
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { filter, tap } from 'rxjs/operators';

import * as actions from './<%= dasherize(name) %>.actions';
import { Load<%= classify(name) %>, <%= classify(name) %>ActionTypes } from './<%= dasherize(name) %>.actions';

@Injectable()
export class <%= classify(name) %>Effects {
constructor(private actions$: Actions) {}

@Effect()
load<%= classify(name) %>$ = this.actions$.pipe(
ofType<actions.Load<%= classify(name) %>>(actions.<%= classify(name) %>ActionTypes.Load<%= classify(name) %>),
ofType<Load<%= classify(name) %>>(<%= classify(name) %>ActionTypes.Load<%= classify(name) %>),
// tslint:disable-next-line:no-console
tap(() => console.log('got Load<%= classify(name) %> in <%= classify(name) %>Effects.load<%= classify(name) %>$')),
filter(() => false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { StoreWithSnapshots, provideStoreSnapshots } from 'ish-core/utils/dev/ng
<% if(entity) { %>import { HttpError } from 'ish-core/models/http-error/http-error.model';
import { <%= classify(entity) %> } from '<% if(!extension) { %>ish-core<% } else { %>../..<% } %>/models/<%= dasherize(entity) %>/<%= dasherize(entity) %>.model';<% } %>

import * as actions from './<%= dasherize(name) %>.actions';
import { Load<%= classify(name) %><% if (entity) { %>, Load<%= classify(name) %>Fail, Load<%= classify(name) %>Success<% } %> } from './<%= dasherize(name) %>.actions';
import { <% if (entity) { %>getNumberOf<%= classify(name) %>, get<%= classify(name) %>, get<%= classify(name) %>Entities, get<%= classify(name) %>Error, <% } %>get<%= classify(name) %>Loading } from './<%= dasherize(name) %>.selectors';

describe('<%= classify(name) %> Selectors', () => {
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('<%= classify(name) %> Selectors', () => {
<% } %> });

describe('Load<%= classify(name) %>', () =>{
const action = new actions.Load<%= classify(name) %>();
const action = new Load<%= classify(name) %>();

beforeEach(() => {
store$.dispatch(action);
Expand All @@ -53,7 +53,7 @@ describe('<%= classify(name) %> Selectors', () => {
<% if (entity) { %>
describe('Load<%= classify(name) %>Success', () => {
const <%= camelize(name) %> = [{ id: '1' }, { id: '2' }] as <%= classify(entity) %>[];
const successAction = new actions.Load<%= classify(name) %>Success({ <%= camelize(name) %> });
const successAction = new Load<%= classify(name) %>Success({ <%= camelize(name) %> });

beforeEach(() => {
store$.dispatch(successAction);
Expand All @@ -76,7 +76,7 @@ describe('<%= classify(name) %> Selectors', () => {

describe('Load<%= classify(name) %>Fail', () => {
const error = { error: 'ERROR' } as HttpError;
const failAction = new actions.Load<%= classify(name) %>Fail({ error });
const failAction = new Load<%= classify(name) %>Fail({ error });

beforeEach(() => {
store$.dispatch(failAction);
Expand Down
53 changes: 53 additions & 0 deletions tslint-rules/src/noStarImportsInStoreRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { tsquery } from '@phenomnomnominal/tsquery';
import * as Lint from 'tslint';
import * as ts from 'typescript';

export class Rule extends Lint.Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
if (/^.*\.(effects|reducer|actions|selectors)\.(ts|spec\.ts)$/.test(sourceFile.fileName)) {
return this.applyWithFunction(sourceFile, ctx => {
sourceFile.statements
.filter(ts.isImportDeclaration)
.filter(importStatement =>
/^\..*\.(actions|selectors)$/.test(importStatement.moduleSpecifier.getText().replace(/['"]/g, ''))
)
.map(importStatement => importStatement.importClause.namedBindings)
.filter(x => !!x)
.filter(ts.isNamespaceImport)
.forEach(node => {
this.visitNamespaceImportDeclaration(ctx, node);
});
});
}
return [];
}

private visitNamespaceImportDeclaration(ctx: Lint.WalkContext<void>, importStatement: ts.NamespaceImport) {
const importString = importStatement.name.text;

// get all Nodes that use the star import
const importNodes = tsquery(
ctx.sourceFile,
`PropertyAccessExpression[expression.name=${importString}],TypeReference > QualifiedName[left.text=${importString}]`
).sort((a, b) => b.getStart() - a.getStart());

// replace all star import references
importNodes.forEach(node => {
const fix = Lint.Replacement.deleteText(node.getStart(), importString.length + 1);
ctx.addFailureAtNode(node, 'star imports are banned', fix);
});

// replace import itself
const newImportStrings = importNodes
.map(node => node.getText().replace(`${importString}.`, '').split('.')[0])
.filter((node, index, array) => array.indexOf(node) === index)
.sort();

const importFix = new Lint.Replacement(
importStatement.getStart(),
importStatement.getWidth(),
`{\n ${newImportStrings.join(',\n ')},\n}`
);
ctx.addFailureAtNode(importStatement, `Star imports in ngrx store files are banned.`, importFix);
}
}
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@
}
}
},
"no-star-imports-in-store": { "severity": "warning" },
"initialize-observables-in-ngoninit": true,
"project-structure": {
"severity": "warning",
Expand Down

0 comments on commit 3c0b528

Please sign in to comment.