diff --git a/packages/angular/src/lib/create-angular-project.ts b/packages/angular/src/lib/create-angular-project.ts index ad1c450..e700184 100644 --- a/packages/angular/src/lib/create-angular-project.ts +++ b/packages/angular/src/lib/create-angular-project.ts @@ -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) { + return createProject(new NgTreeFileSystem(tree), options); } diff --git a/packages/angular/src/lib/metadata/get-metadata.ts b/packages/angular/src/lib/metadata/get-metadata.ts new file mode 100644 index 0000000..35e34dc --- /dev/null +++ b/packages/angular/src/lib/metadata/get-metadata.ts @@ -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); +} diff --git a/packages/angular/src/lib/module/add-bootstrap-to-ng-module.ts b/packages/angular/src/lib/module/add-bootstrap-to-ng-module.ts index 2f89b20..1dcc1db 100644 --- a/packages/angular/src/lib/module/add-bootstrap-to-ng-module.ts +++ b/packages/angular/src/lib/module/add-bootstrap-to-ng-module.ts @@ -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( @@ -11,4 +13,6 @@ export function addBootstrapToNgModule( unique, forceToArray: true, }); + + getDecorators(classDeclaration, { name: 'NgModule' }); } diff --git a/packages/core/src/lib/accessors/add-accessors.spec.ts b/packages/core/src/lib/accessors/add-accessors.spec.ts index 7035698..6039e06 100644 --- a/packages/core/src/lib/accessors/add-accessors.spec.ts +++ b/packages/core/src/lib/accessors/add-accessors.spec.ts @@ -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', diff --git a/packages/core/src/lib/accessors/add-accessors.ts b/packages/core/src/lib/accessors/add-accessors.ts index 17ebb70..8963f74 100644 --- a/packages/core/src/lib/accessors/add-accessors.ts +++ b/packages/core/src/lib/accessors/add-accessors.ts @@ -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, accessors: | Array | GetAccessorDeclarationStructure diff --git a/packages/core/src/lib/accessors/get-accessors.ts b/packages/core/src/lib/accessors/get-accessors.ts index d685ed9..4671736 100644 --- a/packages/core/src/lib/accessors/get-accessors.ts +++ b/packages/core/src/lib/accessors/get-accessors.ts @@ -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, ): AccessorDeclaration[] { @@ -17,3 +19,37 @@ export function getAccessors( .flat() .filter((accessor) => matchQuery(accessor.getStructure(), query)); } + +export function getObjectAccessors( + objects: ObjectLiteralExpression | ObjectLiteralExpression[], + query?: Query, +): 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, + query?: Query, +): AccessorDeclaration[] { + return coerceArray(classes) + .map((klass) => { + if (Node.isClassDeclaration(klass)) { + return getClassAccessors(klass, query); + } + + return getObjectAccessors(klass, query); + }) + .filter(Boolean) + .flat(); +} diff --git a/packages/core/src/lib/arrays/push-to-array.ts b/packages/core/src/lib/arrays/push-to-array.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/core/src/lib/decorators/add-decorators.spec.ts b/packages/core/src/lib/decorators/add-decorators.spec.ts index d7d53f9..530f2fb 100644 --- a/packages/core/src/lib/decorators/add-decorators.spec.ts +++ b/packages/core/src/lib/decorators/add-decorators.spec.ts @@ -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'; @@ -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' }); diff --git a/packages/core/src/lib/decorators/get-decorators.ts b/packages/core/src/lib/decorators/get-decorators.ts index e2a150a..156ed57 100644 --- a/packages/core/src/lib/decorators/get-decorators.ts +++ b/packages/core/src/lib/decorators/get-decorators.ts @@ -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'; @@ -23,7 +23,7 @@ export const getAllDecorators = getDeclarationGetter((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); diff --git a/packages/core/src/lib/imports/get-import-refs.spec.ts b/packages/core/src/lib/imports/get-import-refs.spec.ts new file mode 100644 index 0000000..5e7be3f --- /dev/null +++ b/packages/core/src/lib/imports/get-import-refs.spec.ts @@ -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); + }); +}); diff --git a/packages/core/src/lib/imports/get-import-refs.ts b/packages/core/src/lib/imports/get-import-refs.ts new file mode 100644 index 0000000..b9eab06 --- /dev/null +++ b/packages/core/src/lib/imports/get-import-refs.ts @@ -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; + }); +} diff --git a/packages/core/src/lib/imports/get-named-imports.spec.ts b/packages/core/src/lib/imports/get-named-imports.spec.ts new file mode 100644 index 0000000..213829b --- /dev/null +++ b/packages/core/src/lib/imports/get-named-imports.spec.ts @@ -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); + }); +}); diff --git a/packages/core/src/lib/imports/get-named-imports.ts b/packages/core/src/lib/imports/get-named-imports.ts new file mode 100644 index 0000000..84c5ab0 --- /dev/null +++ b/packages/core/src/lib/imports/get-named-imports.ts @@ -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>, +): ImportSpecifier[] { + return coerceArray(imports) + .flatMap((imp) => imp.getNamedImports()) + .filter((named) => matchQuery(named.getStructure(), query)); +} diff --git a/packages/core/src/lib/methods/add-methods.ts b/packages/core/src/lib/methods/add-methods.ts index 2a92438..526a38e 100644 --- a/packages/core/src/lib/methods/add-methods.ts +++ b/packages/core/src/lib/methods/add-methods.ts @@ -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, methods: | Array> | OptionalKind, diff --git a/packages/core/src/lib/methods/get-methods.ts b/packages/core/src/lib/methods/get-methods.ts index be90eef..6372277 100644 --- a/packages/core/src/lib/methods/get-methods.ts +++ b/packages/core/src/lib/methods/get-methods.ts @@ -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, ): MethodDeclaration[] { @@ -12,3 +18,35 @@ export function getMethods( .flat() .filter((method) => !method.isOverload() && matchQuery(method.getStructure(), query)); } + +export function getObjectMethods( + objects: ObjectLiteralExpression | ObjectLiteralExpression[], + query?: Query, +): 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, + query?: Query, +): MethodDeclaration[] { + return coerceArray(classes) + .map((klass) => { + if (Node.isClassDeclaration(klass)) { + return getClassMethods(klass, query); + } + + return getObjectMethods(klass, query); + }) + .flat(); +} diff --git a/packages/core/src/lib/project/project.spec.ts b/packages/core/src/lib/project/project.spec.ts new file mode 100644 index 0000000..96ec47c --- /dev/null +++ b/packages/core/src/lib/project/project.spec.ts @@ -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'); + }); +}); diff --git a/packages/core/src/lib/project/project.ts b/packages/core/src/lib/project/project.ts index 168ff89..7ccbbd2 100644 --- a/packages/core/src/lib/project/project.ts +++ b/packages/core/src/lib/project/project.ts @@ -1,5 +1,7 @@ import { FileSystemHost, Project, ProjectOptions } from 'ts-morph'; +import { Pattern } from '../utils'; + let prevProject: Project | null = null; function setActiveProject(project: Project | null): Project | null { @@ -23,7 +25,7 @@ export function resetActiveProject(): Project | null { } export function createProject( - fileSystem: FileSystemHost, + fileSystem?: FileSystemHost, options?: Omit, ): Project { const project = new Project({ fileSystem, ...options }); @@ -40,3 +42,7 @@ export async function saveActiveProjectAsync(): Promise { export function saveProject(): void { getActiveProject().saveSync(); } + +export function addSourceFiles(paths: Pattern): void { + getActiveProject().addSourceFilesAtPaths(paths); +} diff --git a/packages/core/src/lib/properties/add-properties.spec.ts b/packages/core/src/lib/properties/add-class-properties.spec.ts similarity index 79% rename from packages/core/src/lib/properties/add-properties.spec.ts rename to packages/core/src/lib/properties/add-class-properties.spec.ts index 443b3a5..8e79a35 100644 --- a/packages/core/src/lib/properties/add-properties.spec.ts +++ b/packages/core/src/lib/properties/add-class-properties.spec.ts @@ -3,9 +3,9 @@ import { readFileSync } from '../fs/file-system'; import { resetActiveProject, saveProject } from '../project'; import { createSourceFile } from '../source-file'; import { createTestingProject } from '../testing'; -import { addProperties } from './add-properties'; +import { addClassProperties } from './add-class-properties'; -describe('addProperties', () => { +describe('addClassProperties', () => { beforeEach(() => { createTestingProject(); @@ -19,7 +19,7 @@ class B {} }); it('should add properties', () => { - addProperties(getClasses('some/path/file.ts', { name: 'B' }), { + addClassProperties(getClasses('some/path/file.ts', { name: 'B' }), { name: 'test', initializer: '3', }); diff --git a/packages/core/src/lib/properties/add-properties.ts b/packages/core/src/lib/properties/add-class-properties.ts similarity index 92% rename from packages/core/src/lib/properties/add-properties.ts rename to packages/core/src/lib/properties/add-class-properties.ts index 93c9b31..10d51ca 100644 --- a/packages/core/src/lib/properties/add-properties.ts +++ b/packages/core/src/lib/properties/add-class-properties.ts @@ -2,7 +2,7 @@ import type { ClassDeclaration, OptionalKind, PropertyDeclarationStructure } fro import { coerceArray } from '../utils'; -export function addProperties( +export function addClassProperties( classes: ClassDeclaration | ClassDeclaration[], properties: | Array> diff --git a/packages/core/src/lib/properties/add-object-property.spec.ts b/packages/core/src/lib/properties/add-object-property.spec.ts new file mode 100644 index 0000000..3ba485f --- /dev/null +++ b/packages/core/src/lib/properties/add-object-property.spec.ts @@ -0,0 +1,47 @@ +import { ObjectLiteralExpression } from 'ts-morph'; + +import { readFileSync } from '../fs/file-system'; +import { resetActiveProject, saveProject } from '../project'; +import { createSourceFile } from '../source-file'; +import { createTestingProject } from '../testing'; +import { getVariables } from '../variables'; +import { addObjectProperty } from './add-object-property'; + +describe('addObjectProperties', () => { + beforeEach(() => { + createTestingProject(); + + createSourceFile( + 'some/path/file.ts', + ` +const a = {}; +`, + ); + }); + + it('should add properties', () => { + addObjectProperty( + getVariables('some/path/file.ts') + .at(0) + ?.getDeclarations() + .at(0) + ?.getInitializer() as ObjectLiteralExpression, + { + name: 'test', + initializer: '3', + }, + ); + + saveProject(); + + expect(readFileSync('some/path/file.ts')).toBe(` +const a = { + test: 3 +}; +`); + }); + + afterEach(() => { + resetActiveProject(); + }); +}); diff --git a/packages/core/src/lib/properties/add-object-property.ts b/packages/core/src/lib/properties/add-object-property.ts new file mode 100644 index 0000000..3c5b4a0 --- /dev/null +++ b/packages/core/src/lib/properties/add-object-property.ts @@ -0,0 +1,24 @@ +import { + ObjectLiteralExpression, + OptionalKind, + PropertyAssignmentStructure, + StructureKind, +} from 'ts-morph'; + +import { coerceArray } from '../utils'; + +export function addObjectProperty( + obj: ObjectLiteralExpression | ObjectLiteralExpression[], + properties: + | Array> + | OptionalKind, +): void { + coerceArray(obj).forEach((object) => { + object.addProperties( + coerceArray(properties).map((property) => ({ + ...property, + kind: StructureKind.PropertyAssignment, + })), + ); + }); +} diff --git a/packages/core/src/lib/properties/edit-properties.spec.ts b/packages/core/src/lib/properties/edit-class-properties.spec.ts similarity index 69% rename from packages/core/src/lib/properties/edit-properties.spec.ts rename to packages/core/src/lib/properties/edit-class-properties.spec.ts index c7f41f8..53f8667 100644 --- a/packages/core/src/lib/properties/edit-properties.spec.ts +++ b/packages/core/src/lib/properties/edit-class-properties.spec.ts @@ -3,10 +3,10 @@ import { readFileSync } from '../fs/file-system'; import { resetActiveProject, saveProject } from '../project'; import { createSourceFile } from '../source-file'; import { createTestingProject } from '../testing'; -import { editProperties } from './edit-properties'; -import { getProperties } from './get-properties'; +import { editClassProperties } from './edit-class-properties'; +import { getClassProperties } from './get-class-properties'; -describe('editProperties', () => { +describe('editClassProperties', () => { beforeEach(() => { createTestingProject(); @@ -21,9 +21,9 @@ class A { }); it('should edit properties', () => { - const declarations = getProperties(getClasses('some/path/file.ts')); + const declarations = getClassProperties(getClasses('some/path/file.ts')); - editProperties(declarations, () => ({ + editClassProperties(declarations, () => ({ name: 'b', initializer: "'s'", })); diff --git a/packages/core/src/lib/properties/edit-properties.ts b/packages/core/src/lib/properties/edit-class-properties.ts similarity index 77% rename from packages/core/src/lib/properties/edit-properties.ts rename to packages/core/src/lib/properties/edit-class-properties.ts index f102cb3..900616d 100644 --- a/packages/core/src/lib/properties/edit-properties.ts +++ b/packages/core/src/lib/properties/edit-class-properties.ts @@ -2,7 +2,7 @@ import type { PropertyDeclaration, PropertyDeclarationStructure } from 'ts-morph import { getDeclarationEditor } from '../utils'; -export const editProperties = getDeclarationEditor< +export const editClassProperties = getDeclarationEditor< PropertyDeclaration, PropertyDeclarationStructure >(); diff --git a/packages/core/src/lib/properties/edit-object-properties.spec.ts b/packages/core/src/lib/properties/edit-object-properties.spec.ts new file mode 100644 index 0000000..850d434 --- /dev/null +++ b/packages/core/src/lib/properties/edit-object-properties.spec.ts @@ -0,0 +1,53 @@ +import { ObjectLiteralExpression } from 'ts-morph'; + +import { readFileSync } from '../fs/file-system'; +import { resetActiveProject, saveProject } from '../project'; +import { createSourceFile } from '../source-file'; +import { createTestingProject } from '../testing'; +import { getVariables } from '../variables'; +import { editObjectProperties } from './edit-object-properties'; +import { getObjectProperties } from './get-object-properties'; + +describe('editObjectProperties', () => { + beforeEach(() => { + createTestingProject(); + + createSourceFile( + 'some/path/file.ts', + ` +const a = { + b: 0; +} +`, + ); + }); + + it('should edit properties', () => { + const declarations = getObjectProperties( + getVariables('some/path/file.ts') + .at(0) + ?.getDeclarations() + .at(0) + ?.getInitializer() as ObjectLiteralExpression, + ); + + editObjectProperties(declarations, ({ initializer }) => { + console.log(initializer); + return { + initializer: "'s'", + }; + }); + + saveProject(); + + expect(readFileSync('some/path/file.ts')).toBe(` +const a = { + b: 's'; +} +`); + }); + + afterEach(() => { + resetActiveProject(); + }); +}); diff --git a/packages/core/src/lib/properties/edit-object-properties.ts b/packages/core/src/lib/properties/edit-object-properties.ts new file mode 100644 index 0000000..7c4b535 --- /dev/null +++ b/packages/core/src/lib/properties/edit-object-properties.ts @@ -0,0 +1,8 @@ +import { PropertyAssignment, PropertyAssignmentStructure } from 'ts-morph'; + +import { getDeclarationEditor } from '../utils'; + +export const editObjectProperties = getDeclarationEditor< + PropertyAssignment, + PropertyAssignmentStructure +>(); diff --git a/packages/core/src/lib/properties/get-properties.spec.ts b/packages/core/src/lib/properties/get-class-properties.spec.ts similarity index 74% rename from packages/core/src/lib/properties/get-properties.spec.ts rename to packages/core/src/lib/properties/get-class-properties.spec.ts index 560c6f3..b079423 100644 --- a/packages/core/src/lib/properties/get-properties.spec.ts +++ b/packages/core/src/lib/properties/get-class-properties.spec.ts @@ -2,7 +2,7 @@ import { getClasses } from '../classes'; import { resetActiveProject } from '../project'; import { createSourceFile } from '../source-file'; import { createTestingProject } from '../testing'; -import { getProperties } from './get-properties'; +import { getClassProperties } from './get-class-properties'; describe('getProperties', () => { beforeEach(() => { @@ -30,19 +30,19 @@ class B { }); it('should find two properties', () => { - const declarations = getProperties(getClasses('some/path/**.ts')); + const declarations = getClassProperties(getClasses('some/path/**.ts')); expect(declarations.length).toBe(3); }); it('should find one property', () => { - const declarations = getProperties(getClasses('some/path/file.ts')); + const declarations = getClassProperties(getClasses('some/path/file.ts')); expect(declarations.length).toBe(1); }); it('should find one property by name pattern **', () => { - const declarations = getProperties(getClasses('some/path/**.ts'), { + const declarations = getClassProperties(getClasses('some/path/**.ts'), { name: 'd', isStatic: true, }); @@ -51,7 +51,7 @@ class B { }); it('should find one property by blob pattern **/*', () => { - const declarations = getProperties(getClasses('**/*.ts'), { + const declarations = getClassProperties(getClasses('**/*.ts'), { name: 'hello*', isStatic: true, }); diff --git a/packages/core/src/lib/properties/get-properties.ts b/packages/core/src/lib/properties/get-class-properties.ts similarity index 87% rename from packages/core/src/lib/properties/get-properties.ts rename to packages/core/src/lib/properties/get-class-properties.ts index 6b9aba6..cbb4f2b 100644 --- a/packages/core/src/lib/properties/get-properties.ts +++ b/packages/core/src/lib/properties/get-class-properties.ts @@ -3,7 +3,7 @@ import { ClassDeclaration, PropertyDeclaration, PropertyDeclarationStructure } f import type { Query } from '../utils'; import { coerceArray, matchQuery } from '../utils'; -export function getProperties( +export function getClassProperties( classes: T | T[], query?: Query, ): Array { diff --git a/packages/core/src/lib/properties/get-object-properties.spec.ts b/packages/core/src/lib/properties/get-object-properties.spec.ts new file mode 100644 index 0000000..1b1f356 --- /dev/null +++ b/packages/core/src/lib/properties/get-object-properties.spec.ts @@ -0,0 +1,61 @@ +import { ObjectLiteralExpression } from 'ts-morph'; + +import { resetActiveProject } from '../project'; +import { createSourceFile } from '../source-file'; +import { createTestingProject } from '../testing'; +import { getVariables } from '../variables'; +import { getObjectProperties } from './get-object-properties'; + +describe('getObjectProperties', () => { + beforeEach(() => { + createTestingProject(); + + createSourceFile( + 'some/path/file.ts', + ` +const a = { + b: 1; +} +`, + ); + + createSourceFile( + 'some/path/one-more-file.ts', + ` +const b = { + d: 's'; + + hello2: 'hello'; +} +`, + ); + }); + + it('should find two properties', () => { + const declarations = getObjectProperties( + getVariables('some/path/**.ts') + ?.map((v) => v.getDeclarations()) + .flat() + .map((d) => d.getInitializer()) + .flat() as ObjectLiteralExpression[], + ); + + expect(declarations.length).toBe(3); + }); + + it('should find one property', () => { + const declarations = getObjectProperties( + getVariables('some/path/file.ts') + .at(0) + ?.getDeclarations() + .at(0) + ?.getInitializer() as ObjectLiteralExpression, + ); + + expect(declarations.length).toBe(1); + }); + + afterEach(() => { + resetActiveProject(); + }); +}); diff --git a/packages/core/src/lib/properties/get-object-properties.ts b/packages/core/src/lib/properties/get-object-properties.ts new file mode 100644 index 0000000..b061a47 --- /dev/null +++ b/packages/core/src/lib/properties/get-object-properties.ts @@ -0,0 +1,20 @@ +import { + Node, + ObjectLiteralExpression, + PropertyAssignment, + PropertyAssignmentStructure, +} from 'ts-morph'; + +import type { Query } from '../utils'; +import { coerceArray, matchQuery } from '../utils'; + +export function getObjectProperties( + objs: T | T[], + query?: Query, +): Array { + return coerceArray(objs) + .map((obj) => obj.getProperties()) + .flat() + .filter((property): property is PropertyAssignment => Node.isPropertyAssignment(property)) + .filter((method) => matchQuery(method.getStructure(), query)); +} diff --git a/packages/core/src/lib/properties/index.ts b/packages/core/src/lib/properties/index.ts index c4268c6..39a92a9 100644 --- a/packages/core/src/lib/properties/index.ts +++ b/packages/core/src/lib/properties/index.ts @@ -1,4 +1,8 @@ -export * from './add-properties'; -export * from './edit-properties'; -export * from './get-properties'; -export * from './remove-properties'; +export * from './add-class-properties'; +export * from './add-object-property'; +export * from './edit-class-properties'; +export * from './edit-object-properties'; +export * from './get-class-properties'; +export * from './get-object-properties'; +export * from './remove-class-properties'; +export * from './remove-object-properties'; diff --git a/packages/core/src/lib/properties/remove-properties.spec.ts b/packages/core/src/lib/properties/remove-class-properties.spec.ts similarity index 73% rename from packages/core/src/lib/properties/remove-properties.spec.ts rename to packages/core/src/lib/properties/remove-class-properties.spec.ts index 53e7ec8..8149aae 100644 --- a/packages/core/src/lib/properties/remove-properties.spec.ts +++ b/packages/core/src/lib/properties/remove-class-properties.spec.ts @@ -3,8 +3,8 @@ import { readFileSync } from '../fs/file-system'; import { resetActiveProject, saveProject } from '../project'; import { createSourceFile } from '../source-file'; import { createTestingProject } from '../testing'; -import { getProperties } from './get-properties'; -import { removeProperties } from './remove-properties'; +import { getClassProperties } from './get-class-properties'; +import { removeClassProperties } from './remove-class-properties'; describe('removeMethods', () => { beforeEach(() => { @@ -25,11 +25,11 @@ class A { }); it('should remove methods', () => { - const declarations = getProperties(getClasses('some/path/file.ts', { name: 'B' }), { + const declarations = getClassProperties(getClasses('some/path/file.ts', { name: 'B' }), { name: 'test', }); - removeProperties(declarations); + removeClassProperties(declarations); saveProject(); diff --git a/packages/core/src/lib/properties/remove-properties.ts b/packages/core/src/lib/properties/remove-class-properties.ts similarity index 55% rename from packages/core/src/lib/properties/remove-properties.ts rename to packages/core/src/lib/properties/remove-class-properties.ts index f1cd5ca..47d3ecc 100644 --- a/packages/core/src/lib/properties/remove-properties.ts +++ b/packages/core/src/lib/properties/remove-class-properties.ts @@ -2,4 +2,4 @@ import type { PropertyDeclaration } from 'ts-morph'; import { getDeclarationRemover } from '../utils'; -export const removeProperties = getDeclarationRemover(); +export const removeClassProperties = getDeclarationRemover(); diff --git a/packages/core/src/lib/properties/remove-object-properties.spec.ts b/packages/core/src/lib/properties/remove-object-properties.spec.ts new file mode 100644 index 0000000..20be4f5 --- /dev/null +++ b/packages/core/src/lib/properties/remove-object-properties.spec.ts @@ -0,0 +1,47 @@ +import { ObjectLiteralExpression } from 'ts-morph'; + +import { readFileSync } from '../fs/file-system'; +import { resetActiveProject, saveProject } from '../project'; +import { createSourceFile } from '../source-file'; +import { createTestingProject } from '../testing'; +import { getVariables } from '../variables'; +import { getObjectProperties } from './get-object-properties'; +import { removeObjectProperties } from './remove-object-properties'; + +describe('removeObjectMethods', () => { + beforeEach(() => { + createTestingProject(); + + createSourceFile( + 'some/path/file.ts', + ` +const b = { + test: 'test' +} +`, + ); + }); + + it('should remove methods', () => { + const declarations = getObjectProperties( + getVariables('some/path/file.ts') + .at(0) + ?.getDeclarations() + .at(0) + ?.getInitializer() as ObjectLiteralExpression, + ); + + removeObjectProperties(declarations); + + saveProject(); + + expect(readFileSync('some/path/file.ts')).toBe(` +const b = { +} +`); + }); + + afterEach(() => { + resetActiveProject(); + }); +}); diff --git a/packages/core/src/lib/properties/remove-object-properties.ts b/packages/core/src/lib/properties/remove-object-properties.ts new file mode 100644 index 0000000..6a812ed --- /dev/null +++ b/packages/core/src/lib/properties/remove-object-properties.ts @@ -0,0 +1,5 @@ +import type { PropertyAssignment } from 'ts-morph'; + +import { getDeclarationRemover } from '../utils'; + +export const removeObjectProperties = getDeclarationRemover(); diff --git a/packages/core/src/lib/source-file/get-source-files.ts b/packages/core/src/lib/source-file/get-source-files.ts index 14d0091..071bc34 100644 --- a/packages/core/src/lib/source-file/get-source-files.ts +++ b/packages/core/src/lib/source-file/get-source-files.ts @@ -1,8 +1,9 @@ import type { SourceFile } from 'ts-morph'; import { getActiveProject } from '../project'; +import { Pattern } from '../utils'; -export function getSourceFiles(pattern?: string[] | string): SourceFile[] { +export function getSourceFiles(pattern?: Pattern): SourceFile[] { return getActiveProject().getSourceFiles(pattern as string); } diff --git a/packages/core/src/lib/utils/types/pattern.ts b/packages/core/src/lib/utils/types/pattern.ts index 15faa12..a150485 100644 --- a/packages/core/src/lib/utils/types/pattern.ts +++ b/packages/core/src/lib/utils/types/pattern.ts @@ -1 +1 @@ -export type Pattern = string[] | string; +export type Pattern = readonly string[] | string;