Skip to content

Commit

Permalink
feat(project): add support for new import syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
va-stefanek committed Jul 1, 2021
1 parent 751bc33 commit f660af9
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 57 deletions.
6 changes: 3 additions & 3 deletions src/collection/crud/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import * as ts from 'typescript';
import { findNode, findNodes } from '../../utils/ast.utils';
import { findDeclarationFileByName } from '../../utils/import.utils';
import { Names, names } from '../../utils/name.utils';
import { createActionAliasName } from '../../utils/naming.utils';
import { parseStateDir, StateFilePaths } from '../../utils/options-parsing.utils';
import { formatFiles } from '../../utils/rules/format-files';
import {
findClassNameInFile,
findDeclarationNodeByPartialName,
findNamespaceName,
readIntoSourceFile
} from '../../utils/ts.utils';
import { DataServiceBackend } from '../data-service/data-service-schema';
Expand All @@ -29,7 +29,7 @@ import { crudReducer } from './rules/crud-reducer.rule';

export interface CrudOptions {
actionPrefix: string;
actionsNamespace: string;
actionsAliasName: string;
dataService: {
names: Names;
path: string;
Expand Down Expand Up @@ -137,7 +137,7 @@ export function parseOptions(host: Tree, options: CrudSchema): CrudOptions {
const deleteResponseType = responseTypeParts.find(rtp => rtp.startsWith('d:'));

return {
actionsNamespace: findNamespaceName(host, parsedStateDir.actions),
actionsAliasName: createActionAliasName(parsedStateDir.actions),
effects: {
name: findClassNameInFile(host, parsedStateDir.effects)
},
Expand Down
42 changes: 21 additions & 21 deletions src/collection/crud/rules/crud-effects.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ function getEffectSpecTemplate(
actionName: string,
actionPayload = true
): string {
const { actionsNamespace, dataService } = options;
const { actionsAliasName, dataService } = options;
const actionNames = names(actionName);
const payload = actionPayload ? '{} as any' : '';

return `\n\ndescribe('${actionNames.propertyName}$', () => {
test('returns ${actionNames.className}Success action on success', () => {
const payload = {} as any;
const action = new ${actionsNamespace}.${actionNames.className}(${payload});
const completion = new ${actionsNamespace}.${actionNames.className}Success(payload);
const action = new ${actionsAliasName}.${actionNames.className}(${payload});
const completion = new ${actionsAliasName}.${actionNames.className}Success(payload);
actions = hot('-a', {a: action});
const response = cold('--b|', {b: payload});
Expand All @@ -38,8 +38,8 @@ function getEffectSpecTemplate(
test('returns ${actionNames.className}Fail action on fail', () => {
const payload = {} as any;
const action = new ${actionsNamespace}.${actionNames.className}(${payload});
const completion = new ${actionsNamespace}.${actionNames.className}Fail(payload);
const action = new ${actionsAliasName}.${actionNames.className}(${payload});
const completion = new ${actionsAliasName}.${actionNames.className}Fail(payload);
actions = hot('-a', { a: action });
const response = cold('-#', {}, payload);
Expand All @@ -59,20 +59,20 @@ function getEffectFetchTemplate(
actionName: string,
actionPayload = true
): string {
const { actionsNamespace } = options;
const { actionsAliasName } = options;
const actionNames = names(actionName);
const payload = actionPayload ? 'action.payload' : '';

return `@Effect()
${actionNames.propertyName}$ = this.dp.fetch(${actionsNamespace}.${config.action.typesEnumName}.${actionNames.className}, {
${actionNames.propertyName}$ = this.dp.fetch(${actionsAliasName}.${config.action.typesEnumName}.${actionNames.className}, {
id: () => {},
run: (action: ${actionsNamespace}.${actionNames.className}) => {
run: (action: ${actionsAliasName}.${actionNames.className}) => {
return this.${options.dataService.names.propertyName}
.${actionNames.propertyName}(${payload})
.pipe(map(data => new ${actionsNamespace}.${actionNames.className}Success(data)));
.pipe(map(data => new ${actionsAliasName}.${actionNames.className}Success(data)));
},
onError: (action: ${actionsNamespace}.${actionNames.className}, error: HttpErrorResponse) => {
return new ${actionsNamespace}.${actionNames.className}Fail(error);
onError: (action: ${actionsAliasName}.${actionNames.className}, error: HttpErrorResponse) => {
return new ${actionsAliasName}.${actionNames.className}Fail(error);
}
});\n\n`;
}
Expand All @@ -83,22 +83,22 @@ function getEffectUpdateTemplate(
update: 'pessimistic' | 'optimistic',
successPayload?: string
): string {
const { actionsNamespace } = options;
const { actionsAliasName } = options;
const actionNames = names(actionName);

return `@Effect()
${actionNames.propertyName}$ = this.dp.${update}Update(${actionsNamespace}.${
${actionNames.propertyName}$ = this.dp.${update}Update(${actionsAliasName}.${
config.action.typesEnumName
}.${actionNames.className}, {
run: (action: ${actionsNamespace}.${actionNames.className}) => {
run: (action: ${actionsAliasName}.${actionNames.className}) => {
return this.${options.dataService.names.propertyName}
.${actionNames.propertyName}(action.payload)
.pipe(map(data => new ${actionsNamespace}.${
actionNames.className
}Success(${successPayload || 'data'})));
.pipe(map(data => new ${actionsAliasName}.${actionNames.className}Success(${
successPayload || 'data'
})));
},
onError: (action: ${actionsNamespace}.${actionNames.className}, error: HttpErrorResponse) => {
return new ${actionsNamespace}.${actionNames.className}Fail(error);
onError: (action: ${actionsAliasName}.${actionNames.className}, error: HttpErrorResponse) => {
return new ${actionsAliasName}.${actionNames.className}Fail(error);
}
});\n\n`;
}
Expand Down Expand Up @@ -267,7 +267,7 @@ export function crudEffects(options: CrudOptions): Rule {
return (host: Tree, context: SchematicContext) => {
context.logger.info(`Generating effects.`);

const { actionsNamespace, dataService, stateDir, statePartialName } = options;
const { actionsAliasName, dataService, stateDir, statePartialName } = options;

insertConstructorArguments(host, options.stateDir.effects, [
{
Expand All @@ -288,7 +288,7 @@ export function crudEffects(options: CrudOptions): Rule {
insertCustomImport(host, stateDir.effects, 'HttpErrorResponse', '@angular/common/http');
insertCustomImport(host, stateDir.effects, 'map', 'rxjs/operators');

insertTypeImport(host, stateDir.effectsSpec, actionsNamespace);
insertTypeImport(host, stateDir.effectsSpec, actionsAliasName);
insertTypeImport(host, stateDir.effectsSpec, dataService.names.className);
insertTypeImport(host, stateDir.effectsSpec, `DataPersistence`);
insertCustomImport(host, stateDir.effectsSpec, 'hot', 'jest-marbles');
Expand Down
14 changes: 7 additions & 7 deletions src/collection/crud/rules/crud-facade-spec.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devk
import { Change, InsertChange } from '@schematics/angular/utility/change';
import { findDescribeBlockNode, insert } from '../../../utils/ast.utils';
import { insertTypeImport } from '../../../utils/import.utils';
import { names, toPropertyName } from '../../../utils/name.utils';
import { names } from '../../../utils/name.utils';
import { readIntoSourceFile } from '../../../utils/ts.utils';
import { CrudOptions } from '../index';

Expand All @@ -12,16 +12,16 @@ function getMethodTestTemplate(
method: string,
methodPayload = true
): string {
const { actionsNamespace } = options;
const { actionsAliasName } = options;
const actionNames = names(actionName);

return `\n\ndescribe('#${method}', () => {
test('should dispatch ${actionsNamespace}.${actionNames.className} action', () => {
test('should dispatch ${actionsAliasName}.${actionNames.className} action', () => {
${methodPayload ? 'const payload = {} as any;' : ''}
const action = new ${actionsNamespace}.${actionNames.className}(${
const action = new ${actionsAliasName}.${actionNames.className}(${
methodPayload ? 'payload' : ''
});
facade.${method}(${methodPayload ? 'payload' : ''});
expect(store.dispatch).toHaveBeenCalledWith(action);
});
Expand Down Expand Up @@ -101,11 +101,11 @@ function createCrudFacadeSpec(host: Tree, options: CrudOptions): Change[] {

export function crudFacadeSpec(options: CrudOptions): Rule {
return (host: Tree, context: SchematicContext) => {
const { actionsNamespace, stateDir } = options;
const { actionsAliasName, stateDir } = options;

insert(host, stateDir.facadeSpec, createCrudFacadeSpec(host, options));

insertTypeImport(host, stateDir.facadeSpec, actionsNamespace);
insertTypeImport(host, stateDir.facadeSpec, actionsAliasName);

return host;
};
Expand Down
14 changes: 7 additions & 7 deletions src/collection/crud/rules/crud-facade.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function getMethodTemplate(
}

function createFacade(host: Tree, options: CrudOptions): Change[] {
const { toGenerate, entity, stateDir, facade, actionsNamespace } = options;
const { toGenerate, entity, stateDir, facade, actionsAliasName } = options;
const entityPropertyName = toPropertyName(entity.name);
const selectorChanges: Change[] = [];
const methodsChanges: Change[] = [];
Expand Down Expand Up @@ -54,7 +54,7 @@ function createFacade(host: Tree, options: CrudOptions): Change[] {
stateDir.facade,
facadeClassBody.getEnd(),
getMethodTemplate(
actionsNamespace,
actionsAliasName,
`Get${entity.name}`,
getRequestPayloadClass(`Get${entity.name}`)
)
Expand Down Expand Up @@ -86,7 +86,7 @@ function createFacade(host: Tree, options: CrudOptions): Change[] {
stateDir.facade,
facadeClassBody.getEnd(),
getMethodTemplate(
actionsNamespace,
actionsAliasName,
`Get${entity.name}Collection`,
getRequestPayloadClass(`Get${entity.name}Collection`)
)
Expand All @@ -113,7 +113,7 @@ function createFacade(host: Tree, options: CrudOptions): Change[] {
stateDir.facade,
facadeClassBody.getEnd(),
getMethodTemplate(
actionsNamespace,
actionsAliasName,
`Create${entity.name}`,
getRequestPayloadClass(`Create${entity.name}`)
)
Expand All @@ -140,7 +140,7 @@ function createFacade(host: Tree, options: CrudOptions): Change[] {
stateDir.facade,
facadeClassBody.getEnd(),
getMethodTemplate(
actionsNamespace,
actionsAliasName,
`Update${entity.name}`,
getRequestPayloadClass(`Update${entity.name}`)
)
Expand All @@ -167,7 +167,7 @@ function createFacade(host: Tree, options: CrudOptions): Change[] {
stateDir.facade,
facadeClassBody.getEnd(),
getMethodTemplate(
actionsNamespace,
actionsAliasName,
`Remove${entity.name}`,
getRequestPayloadClass(`Remove${entity.name}`)
)
Expand All @@ -185,7 +185,7 @@ export function crudFacade(options: CrudOptions): Rule {
insert(host, options.stateDir.facade, createFacade(host, options));

insertTypeImport(host, options.stateDir.facade, options.facade.queryName);
insertTypeImport(host, options.stateDir.facade, options.actionsNamespace);
insertTypeImport(host, options.stateDir.facade, options.actionsAliasName);
insertTypeImport(host, options.stateDir.facade, options.entity.name);

if (options.toGenerate.read) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Action } from '@ngrx/store';

export namespace from<%= className %>Actions {
export enum Types {}
export enum Types {}

export type CollectiveType = Action;
}
export type CollectiveType = Action;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Effect } from '@ngrx/effects';
import { DataPersistence } from '@nrwl/angular';
import { from<%= className %>Actions } from './<%= fileName %>.actions';
import * as from<%= className %>Actions from './<%= fileName %>.actions';
import { <%= className %>PartialState } from './<%= fileName %>.reducer';

@Injectable()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { from<%= className %>Actions } from './<%= fileName %>.actions';
import * as from<%= className %>Actions from './<%= fileName %>.actions';
import { <%= className %>State, initialState, <%= propertyName %>Reducer } from './<%= fileName %>.reducer';

describe('<%= className %> Reducer', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { from<%= className %>Actions } from './<%= fileName %>.actions';
import * as from<%= className %>Actions from './<%= fileName %>.actions';

export const <%= className.toUpperCase() %>_FEATURE_KEY = '<%= propertyName %>';

Expand Down
12 changes: 8 additions & 4 deletions src/collection/reducer/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { parseReducerFile } from '../../utils/file-parsing.utils';
import { findActionsNamespace } from '../../utils/naming.utils';
import { createActionAliasName, createActionImportAlias } from '../../utils/naming.utils';
import { parseStateDir } from '../../utils/options-parsing.utils';
import { formatFiles } from '../../utils/rules/format-files';
import { readIntoSourceFile } from '../../utils/ts.utils';
Expand All @@ -25,16 +25,20 @@ export function reducer(options: ReducerSchema): Rule {
}

const stateDir = parseStateDir(options.stateDir, host);
const actionsSourceFile = readIntoSourceFile(host, stateDir.actions);
const actionsNamespace = findActionsNamespace(actionsSourceFile);
const actionsNamespace = createActionAliasName(stateDir.actions);
const reducerSourceFile = readIntoSourceFile(host, stateDir.reducer);
const reducerSpecSourceFile = readIntoSourceFile(host, stateDir.reducerSpec);
const selectorsSourceFile = readIntoSourceFile(host, stateDir.selectors);
const selectorsSpecSourceFile = readIntoSourceFile(host, stateDir.selectorsSpec);
const parsedReducerFile = parseReducerFile(reducerSourceFile);

const rules: Rule[] = [
insertNamespaceImport(reducerSourceFile, stateDir, actionsNamespace),
insertNamespaceImport(
reducerSourceFile,
stateDir,
createActionImportAlias(stateDir.actions),
true
),
updateReducer(reducerSourceFile, parsedReducerFile, actionsNamespace, stateDir, options),
updateReducerSpec(
reducerSpecSourceFile,
Expand Down
5 changes: 3 additions & 2 deletions src/collection/reducer/rules/insert-namespace-import.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { StateFilePaths } from '../../../utils/options-parsing.utils';
export function insertNamespaceImport(
reducerSourceFile: ts.SourceFile,
stateDir: StateFilePaths,
actionsNamespace: string
actionsNamespace: string,
isDefault = false
): Rule {
return (host: Tree) => {
const path = buildRelativePath(stateDir.reducer, stateDir.actions).slice(0, -3);

if (!isImported(reducerSourceFile, actionsNamespace, path)) {
insert(host, stateDir.reducer, [
insertImport(reducerSourceFile, stateDir.reducer, actionsNamespace, path)
insertImport(reducerSourceFile, stateDir.reducer, actionsNamespace, path, isDefault)
]);
}

Expand Down
9 changes: 3 additions & 6 deletions src/collection/reducer/rules/update-facade.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ import { Change, InsertChange } from '@schematics/angular/utility/change';
import * as ts from 'typescript';
import { getSourceNodes, insert } from '../../../utils/ast.utils';
import { names, toPropertyName } from '../../../utils/name.utils';
import { createActionAliasName } from '../../../utils/naming.utils';
import {
parsePropsToUpdate,
parseStateDir,
StateProperty
} from '../../../utils/options-parsing.utils';
import { classify } from '../../../utils/string.utils';
import {
findClassBodyInFile,
findNamespaceName,
readIntoSourceFile
} from '../../../utils/ts.utils';
import { findClassBodyInFile, readIntoSourceFile } from '../../../utils/ts.utils';
import { ReducerSchema } from '../reducer-schema.interface';

function getSelectorTemplate(prop: StateProperty, queryName: string): string {
Expand All @@ -36,7 +33,7 @@ function getMethodTemplate(
export function updateFacade(options: ReducerSchema): Rule {
return (host: Tree, context: SchematicContext) => {
const stateDir = parseStateDir(options.stateDir, host);
const namespace = findNamespaceName(host, stateDir.actions);
const namespace = createActionAliasName(stateDir.actions);
const stateProperties = parsePropsToUpdate(options.propsToUpdate);
const facadeClass = findClassBodyInFile(host, stateDir.facade);
const queryDeclarations = getSourceNodes(readIntoSourceFile(host, stateDir.selectors))
Expand Down
14 changes: 14 additions & 0 deletions src/utils/naming.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createActionAliasName, createActionImportAlias } from './naming.utils';

describe('naming utils', () => {
describe('createActionImportAlias', () => {
const actionPath = '/libs/test/lib/+state/auth.actions.ts';
it('create proper import alias', () => {
expect(createActionImportAlias(actionPath)).toBe('* as fromAuthActions');
});

it('create proper action alias name', () => {
expect(createActionAliasName(actionPath)).toBe('fromAuthActions');
});
});
});
20 changes: 20 additions & 0 deletions src/utils/naming.utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ts from 'typescript';
import { getSourceNodes } from './ast.utils';
import { capitalize } from './string.utils';

export function findActionsNamespace(actionsSourceFile: ts.SourceFile): string {
const nodes = getSourceNodes(actionsSourceFile);
Expand All @@ -14,3 +15,22 @@ export function findActionsNamespace(actionsSourceFile: ts.SourceFile): string {

return actionsNamespace;
}

export function createActionImportAlias(actionPath: string): string {
// hardcoded prefix of actions import e.g import * as fromAuthActions
const importPrefix = '* as ';

return importPrefix + createActionAliasName(actionPath);
}

export function createActionAliasName(actionPath: string): string {
const aliasNamePrefix = 'from';
// extract file name rom the path e.g /libs/test/lib/+state/auth.actions.ts into auth.actions.ts
const actionFileName = actionPath.replace(/^.*[\\\/]/, '');
// remove extension from the file name and transform it into array e.g from auth.actions.ts into [auth, actions]
const actionFileNameChunksArray = actionFileName.split('.').slice(0, -1);
// capitalize name chunks and join them with prefix to create action file name alias eg. fromAuthActions
return (
aliasNamePrefix + actionFileNameChunksArray.map(nameChunk => capitalize(nameChunk)).join('')
);
}

0 comments on commit f660af9

Please sign in to comment.