Skip to content

Commit

Permalink
feat(mutates): refactoring, part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
IKatsuba committed Jun 11, 2024
1 parent f1e054e commit 72f9a3c
Show file tree
Hide file tree
Showing 36 changed files with 567 additions and 48 deletions.
9 changes: 3 additions & 6 deletions packages/angular/src/lib/create-angular-project.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Tree } from '@angular-devkit/schematics';
import { ProjectOptions } from 'ts-morph';

import { createProject } from '@mutates/core';

import { NgTreeFileSystem } from './ng-tree-file-system';

export function createAngularProject(tree: Tree) {
const project = createProject(new NgTreeFileSystem(tree));

project.addSourceFilesAtPaths('**/*.ts');

return project;
export function createAngularProject(tree: Tree, options?: Omit<ProjectOptions, 'fileSystem'>) {
return createProject(new NgTreeFileSystem(tree), options);
}
56 changes: 56 additions & 0 deletions packages/angular/src/lib/metadata/get-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ClassDeclaration, Node, ObjectLiteralExpression } from 'ts-morph';

import { getDecorators } from '@mutates/core';

export enum MetadataType {
NgModule = 'NgModule',
Component = 'Component',
Directive = 'Directive',
Pipe = 'Pipe',
Injectable = 'Injectable',
}

export function getMetadata(
klass: ClassDeclaration | ClassDeclaration[],
metadataType: MetadataType,
): ObjectLiteralExpression[] {
const decorators = getDecorators(klass, {
name: metadataType,
});

const metadatas = decorators.map((decorator) => decorator.getArguments()).flat();

return metadatas.filter((metadata): metadata is ObjectLiteralExpression =>
Node.isObjectLiteralExpression(metadata),
);
}

export function getNgModuleMetadata(
klass: ClassDeclaration | ClassDeclaration[],
): ObjectLiteralExpression[] {
return getMetadata(klass, MetadataType.NgModule);
}

export function getComponentMetadata(
klass: ClassDeclaration | ClassDeclaration[],
): ObjectLiteralExpression[] {
return getMetadata(klass, MetadataType.Component);
}

export function getDirectiveMetadata(
klass: ClassDeclaration | ClassDeclaration[],
): ObjectLiteralExpression[] {
return getMetadata(klass, MetadataType.Directive);
}

export function getPipeMetadata(
klass: ClassDeclaration | ClassDeclaration[],
): ObjectLiteralExpression[] {
return getMetadata(klass, MetadataType.Pipe);
}

export function getInjectableMetadata(
klass: ClassDeclaration | ClassDeclaration[],
): ObjectLiteralExpression[] {
return getMetadata(klass, MetadataType.Injectable);
}
4 changes: 4 additions & 0 deletions packages/angular/src/lib/module/add-bootstrap-to-ng-module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { ClassDeclaration } from 'ts-morph';

import { getDecorators } from '@mutates/core';

import { pushToDecoratorArrayProperty } from '../helpers/push-to-decorator-array-property';

export function addBootstrapToNgModule(
Expand All @@ -11,4 +13,6 @@ export function addBootstrapToNgModule(
unique,
forceToArray: true,
});

getDecorators(classDeclaration, { name: 'NgModule' });
}
2 changes: 1 addition & 1 deletion packages/core/src/lib/accessors/add-accessors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class A {}
);
});

it('should add accessors', async () => {
it('should add accessors to the class', async () => {
addAccessors(getClasses('some/path/file.ts'), [
{
name: 'setter',
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/lib/accessors/add-accessors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import type {
import {
ClassDeclaration,
GetAccessorDeclarationStructure,
ObjectLiteralExpression,
SetAccessorDeclarationStructure,
Structure,
} from 'ts-morph';
import { Structure } from 'ts-morph';

import { coerceArray } from '../utils';

export function addAccessors(
classes: ClassDeclaration | ClassDeclaration[],
classes:
| ClassDeclaration
| ObjectLiteralExpression
| Array<ClassDeclaration | ObjectLiteralExpression>,
accessors:
| Array<GetAccessorDeclarationStructure | SetAccessorDeclarationStructure>
| GetAccessorDeclarationStructure
Expand Down
40 changes: 38 additions & 2 deletions packages/core/src/lib/accessors/get-accessors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type {
import {
AccessorDeclaration,
ClassDeclaration,
GetAccessorDeclarationStructure,
Node,
ObjectLiteralExpression,
SetAccessorDeclarationStructure,
} from 'ts-morph';

import type { Query } from '../utils';
import { coerceArray, matchQuery } from '../utils';

export function getAccessors(
export function getClassAccessors(
classes: ClassDeclaration | ClassDeclaration[],
query?: Query<GetAccessorDeclarationStructure | SetAccessorDeclarationStructure>,
): AccessorDeclaration[] {
Expand All @@ -17,3 +19,37 @@ export function getAccessors(
.flat()
.filter((accessor) => matchQuery(accessor.getStructure(), query));
}

export function getObjectAccessors(
objects: ObjectLiteralExpression | ObjectLiteralExpression[],
query?: Query<GetAccessorDeclarationStructure | SetAccessorDeclarationStructure>,
): AccessorDeclaration[] {
const accessors = coerceArray(objects)
.map((object) => object.getProperties())
.flat()
.filter(
(accessor) =>
Node.isGetAccessorDeclaration(accessor) || Node.isSetAccessorDeclaration(accessor),
) as AccessorDeclaration[];

return accessors.filter((accessor) => matchQuery(accessor.getStructure(), query));
}

export function getAccessors(
classes:
| ClassDeclaration
| ObjectLiteralExpression
| Array<ClassDeclaration | ObjectLiteralExpression>,
query?: Query<GetAccessorDeclarationStructure | SetAccessorDeclarationStructure>,
): AccessorDeclaration[] {
return coerceArray(classes)
.map((klass) => {
if (Node.isClassDeclaration(klass)) {
return getClassAccessors(klass, query);
}

return getObjectAccessors(klass, query);
})
.filter(Boolean)
.flat();
}
Empty file.
4 changes: 2 additions & 2 deletions packages/core/src/lib/decorators/add-decorators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { readFileSync } from '../fs/file-system';
import { getMethods } from '../methods';
import { getParams } from '../params';
import { resetActiveProject, saveProject } from '../project';
import { getProperties } from '../properties';
import { getClassProperties } from '../properties';
import { createSourceFile } from '../source-file';
import { createTestingProject } from '../testing';
import { addDecorators } from './add-decorators';
Expand Down Expand Up @@ -40,7 +40,7 @@ class A {
name: 'param',
});
const methodParams = getParams(methods);
const properties = getProperties(classes, { name: 'property' });
const properties = getClassProperties(classes, { name: 'property' });
const getAccessorss = getAccessors(classes, { name: 'getAccessor' });
const setAccessors = getAccessors(classes, { name: 'setAccessor' });

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/lib/decorators/get-decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getClasses } from '../classes';
import { getConstructors } from '../constructors';
import { getMethods } from '../methods';
import { getParams } from '../params';
import { getProperties } from '../properties';
import { getClassProperties } from '../properties';
import type { Query } from '../utils';
import { coerceArray, getDeclarationGetter, matchQuery } from '../utils';

Expand All @@ -23,7 +23,7 @@ export const getAllDecorators = getDeclarationGetter<Decorator>((pattern) => {
const classes = getClasses(pattern);
const methods = getMethods(classes);
const constructors = getConstructors(classes);
const properties = getProperties(classes);
const properties = getClassProperties(classes);
const constructorParams = getParams(constructors);
const methodParams = getParams(methods);
const accessors = getAccessors(classes);
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/lib/imports/get-import-refs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createSourceFile } from '../source-file';
import { createTestingProject } from '../testing';
import { getImportRefs } from './get-import-refs';
import { getImports } from './get-imports';
import { getNamedImports } from './get-named-imports';

describe('getImportRefs', () => {
beforeEach(() => {
createTestingProject();
});

it('should find one named import', () => {
createSourceFile(
'some/path/one-more-file.ts',
`
import { a } from 'd';
a();
`,
);

const imports = getNamedImports(getImports('some/path/**.ts'), {
name: 'a',
});

const refs = getImportRefs(imports);
console.log(refs);
expect(refs.length).toBe(1);
});
});
19 changes: 19 additions & 0 deletions packages/core/src/lib/imports/get-import-refs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ImportSpecifier, Node } from 'ts-morph';

import { coerceArray } from '../utils';

export function getImportRefs(imports: ImportSpecifier | ImportSpecifier[]): Node[] {
const importNames = coerceArray(imports).map((imp) => imp.getName());

return coerceArray(imports)
.flatMap((imp) => imp.getNameNode().findReferencesAsNodes())
.filter((node) => {
const parent = node.getParent();

if (parent && Node.isImportSpecifier(parent)) {
return !importNames.includes(parent.getName());
}

return true;
});
}
25 changes: 25 additions & 0 deletions packages/core/src/lib/imports/get-named-imports.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createSourceFile } from '../source-file';
import { createTestingProject } from '../testing';
import { getImports } from './get-imports';
import { getNamedImports } from './get-named-imports';

describe('getNamedImports', () => {
beforeEach(() => {
createTestingProject();
});

it('should find one named import', () => {
createSourceFile(
'some/path/one-more-file.ts',
`
import { a } from 'd';
`,
);

const imports = getNamedImports(getImports('some/path/**.ts'), {
name: 'a',
});

expect(imports.length).toBe(1);
});
});
12 changes: 12 additions & 0 deletions packages/core/src/lib/imports/get-named-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ImportDeclaration, ImportSpecifier, ImportSpecifierStructure } from 'ts-morph';

import { coerceArray, matchQuery, type Query } from '../utils';

export function getNamedImports(
imports: ImportDeclaration | ImportDeclaration[],
query?: Query<Omit<ImportSpecifierStructure, 'kind'>>,
): ImportSpecifier[] {
return coerceArray(imports)
.flatMap((imp) => imp.getNamedImports())
.filter((named) => matchQuery(named.getStructure(), query));
}
12 changes: 10 additions & 2 deletions packages/core/src/lib/methods/add-methods.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import type { ClassDeclaration, MethodDeclarationStructure, OptionalKind } from 'ts-morph';
import type {
ClassDeclaration,
MethodDeclarationStructure,
ObjectLiteralExpression,
OptionalKind,
} from 'ts-morph';

import { coerceArray } from '../utils';

export function addMethods(
classes: ClassDeclaration | ClassDeclaration[],
classes:
| ClassDeclaration
| ObjectLiteralExpression
| Array<ClassDeclaration | ObjectLiteralExpression>,
methods:
| Array<OptionalKind<MethodDeclarationStructure>>
| OptionalKind<MethodDeclarationStructure>,
Expand Down
42 changes: 40 additions & 2 deletions packages/core/src/lib/methods/get-methods.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type { ClassDeclaration, MethodDeclaration, MethodDeclarationStructure } from 'ts-morph';
import {
ClassDeclaration,
MethodDeclaration,
MethodDeclarationStructure,
Node,
ObjectLiteralExpression,
} from 'ts-morph';

import type { Query } from '../utils';
import { coerceArray, matchQuery } from '../utils';

export function getMethods(
export function getClassMethods(
classes: ClassDeclaration | ClassDeclaration[],
query?: Query<MethodDeclarationStructure>,
): MethodDeclaration[] {
Expand All @@ -12,3 +18,35 @@ export function getMethods(
.flat()
.filter((method) => !method.isOverload() && matchQuery(method.getStructure(), query));
}

export function getObjectMethods(
objects: ObjectLiteralExpression | ObjectLiteralExpression[],
query?: Query<MethodDeclarationStructure>,
): MethodDeclaration[] {
const methods = coerceArray(objects)
.map((object) => object.getProperties())
.flat()
.filter((accessor) => Node.isMethodDeclaration(accessor)) as MethodDeclaration[];

return methods.filter(
(method) => !method.isOverload() && matchQuery(method.getStructure(), query),
);
}

export function getMethods(
classes:
| ClassDeclaration
| ObjectLiteralExpression
| Array<ClassDeclaration | ObjectLiteralExpression>,
query?: Query<MethodDeclarationStructure>,
): MethodDeclaration[] {
return coerceArray(classes)
.map((klass) => {
if (Node.isClassDeclaration(klass)) {
return getClassMethods(klass, query);
}

return getObjectMethods(klass, query);
})
.flat();
}
14 changes: 14 additions & 0 deletions packages/core/src/lib/project/project.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getSourceFiles } from '../source-file';
import { addSourceFiles, createProject } from './project';

describe('Project', () => {
beforeEach(() => {
createProject();

addSourceFiles('./package.json');
});

it('should not add files from node_modules', () => {
expect(getSourceFiles('./**/*.json').at(0)?.getFilePath()).contain('package.json');
});
});
Loading

0 comments on commit 72f9a3c

Please sign in to comment.