From 5ae9cafa6105a377123c8774380a98f0507ece78 Mon Sep 17 00:00:00 2001 From: mararok <5163714+Mararok@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:06:49 +0200 Subject: [PATCH] feat(feature): enchant feature meta --- package.json | 3 +- .../Feature/FeatureApplicationDiscoverer.ts | 54 +++++ src/Util/Feature/FeatureDiscovererHelper.ts | 99 +++++++++ src/Util/Feature/FeatureDomainDiscoverer.ts | 65 ++++++ .../FeatureInfrastructureDiscoverer.ts | 17 ++ src/Util/Feature/FeatureModuleDiscoverer.ts | 193 +++-------------- src/Util/Feature/FeatureModuleMeta.ts | 106 --------- src/Util/Feature/Meta/CommonFeatureMeta.ts | 69 ++++++ .../Feature/Meta/FeatureApplicationMeta.ts | 182 ++++++++++++++++ src/Util/Feature/Meta/FeatureDomainMeta.ts | 204 ++++++++++++++++++ .../Feature/Meta/FeatureInfrastructureMeta.ts | 24 +++ src/Util/Feature/Meta/FeatureMeta.ts | 70 ++++++ src/Util/Feature/Meta/index.ts | 5 + .../Create/BookCreateCommandHandler.ts | 5 +- .../Book => Application/Book/Dto}/BookDto.ts | 0 .../Book/Event/BookCreatedEventHandler.ts | 10 - .../Query/GetById/BookGetByIdQueryHandler.ts | 2 +- .../test-lib/src/Book/Domain/Book/Book.ts | 2 +- .../test-lib/src/Book/Domain/Book/BookCopy.ts | 2 +- .../src/Book/Domain/Book/BookCreatedEvent.ts | 3 - .../Shared/{ => ValueObject}/BookCopyId.ts | 0 .../Book/Shared/{ => ValueObject}/BookId.ts | 0 .../src/Book/Domain/Book/Shared/index.ts | 4 +- .../ValueObject/FeatureSharedValueObject.ts | 0 .../test-lib/src/Book/Domain/Shared/index.ts | 0 .../Feature/FeatureModuleDiscoverer.test.ts | 12 +- .../FeatureModuleDiscoverer.test.ts.snap | 146 +++++++------ tsconfig.build.json | 1 + tsconfig.json | 19 +- yarn.lock | 8 + 30 files changed, 941 insertions(+), 364 deletions(-) create mode 100644 src/Util/Feature/FeatureApplicationDiscoverer.ts create mode 100644 src/Util/Feature/FeatureDiscovererHelper.ts create mode 100644 src/Util/Feature/FeatureDomainDiscoverer.ts create mode 100644 src/Util/Feature/FeatureInfrastructureDiscoverer.ts delete mode 100644 src/Util/Feature/FeatureModuleMeta.ts create mode 100644 src/Util/Feature/Meta/CommonFeatureMeta.ts create mode 100644 src/Util/Feature/Meta/FeatureApplicationMeta.ts create mode 100644 src/Util/Feature/Meta/FeatureDomainMeta.ts create mode 100644 src/Util/Feature/Meta/FeatureInfrastructureMeta.ts create mode 100644 src/Util/Feature/Meta/FeatureMeta.ts create mode 100644 src/Util/Feature/Meta/index.ts rename test/helper/libs/test-lib/src/Book/{Domain/Book => Application/Book/Dto}/BookDto.ts (100%) delete mode 100644 test/helper/libs/test-lib/src/Book/Application/Book/Event/BookCreatedEventHandler.ts delete mode 100644 test/helper/libs/test-lib/src/Book/Domain/Book/BookCreatedEvent.ts rename test/helper/libs/test-lib/src/Book/Domain/Book/Shared/{ => ValueObject}/BookCopyId.ts (100%) rename test/helper/libs/test-lib/src/Book/Domain/Book/Shared/{ => ValueObject}/BookId.ts (100%) create mode 100644 test/helper/libs/test-lib/src/Book/Domain/Shared/ValueObject/FeatureSharedValueObject.ts create mode 100644 test/helper/libs/test-lib/src/Book/Domain/Shared/index.ts diff --git a/package.json b/package.json index ab75701..c7cd0e3 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "jest": "node --disable-warning=ExperimentalWarning --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.config.ts --runInBand", "test": "yarn run jest --runInBand", "test:cc": "yarn run jest --clearCache", - "test:compiler": "yarn run build yarn && run test:cc && yarn run test", + "test:compiler": "yarn run build && yarn run test:cc && yarn run test", "test:unit": "yarn run jest --runInBand --group=unit", "test:watch": "yarn run jest --runInBand --watchAll", "test:cov": "yarn run jest --coverage", @@ -114,6 +114,7 @@ "@nestjs/swagger": "^7.1.8", "@nestjs/testing": "^10.3.9", "@types/jest": "29.0.*", + "@types/js-yaml": "^4.0.9", "@types/node": "^20.14.2", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", diff --git a/src/Util/Feature/FeatureApplicationDiscoverer.ts b/src/Util/Feature/FeatureApplicationDiscoverer.ts new file mode 100644 index 0000000..0096c5f --- /dev/null +++ b/src/Util/Feature/FeatureApplicationDiscoverer.ts @@ -0,0 +1,54 @@ +import { type PathsOutput } from "fdir"; + +import path from "node:path"; +import { FeatureDiscovererHelper } from "./FeatureDiscovererHelper"; +import { FeatureApplicationCommandMeta, FeatureApplicationMeta, FeatureApplicationQueryMeta, FeatureApplicationServiceMeta, FeatureDtoMeta, HObjectType } from "./Meta"; + +export class FeatureApplicationDiscoverer { + + public async discover(featureDir: string): Promise { + const contextDirs: PathsOutput = await FeatureDiscovererHelper.crawlDirOnly(path.join(featureDir, "Application")); + + const commands: FeatureApplicationCommandMeta[] = []; + const queries: FeatureApplicationQueryMeta[] = []; + const dtos: FeatureDtoMeta[] = []; + const services: FeatureApplicationServiceMeta[] = []; + + for (const contextDir of contextDirs) { + const contextName = path.basename(contextDir); + commands.push(...(await this.discoverCommands(contextName, contextDir, featureDir))); + queries.push(...(await this.discoverQueries(contextName, contextDir, featureDir))); + services.push(...(await this.discoverServices(contextName, contextDir, featureDir))); + dtos.push(...(await this.discoverDtos(contextName, contextDir, featureDir))); + } + + return new FeatureApplicationMeta(commands, queries, dtos, services); + } + + private async discoverCommands(context: string, contextDir: string, featureDir: string): Promise { + return FeatureDiscovererHelper.discoverInDirOnlyDirs( + contextDir + 'Command', + featureDir, + (name) => new FeatureApplicationCommandMeta(name, context)); + } + + private async discoverQueries(context: string, contextDir: string, featureDir: string): Promise { + return FeatureDiscovererHelper.discoverInDirOnlyDirs( + contextDir + 'Query', + featureDir, + (name) => new FeatureApplicationQueryMeta(name, context)); + } + + private async discoverDtos(context: string, contextDir: string, featureDir: string): Promise { + return FeatureDiscovererHelper.discoverInDir(contextDir + 'Dto', featureDir, + (name, pathInFeature) => new FeatureDtoMeta(name, context, pathInFeature) + ); + } + + private async discoverServices(context: string, contextDir: string, featureDir: string): Promise { + return FeatureDiscovererHelper.discoverInDir(contextDir + 'Service', featureDir, + async (name, pathInFeature, filePath) => + new FeatureApplicationServiceMeta(name, context, pathInFeature, await FeatureDiscovererHelper.isUsing(filePath, "@Injectable(")) + ); + } +} \ No newline at end of file diff --git a/src/Util/Feature/FeatureDiscovererHelper.ts b/src/Util/Feature/FeatureDiscovererHelper.ts new file mode 100644 index 0000000..7efbf55 --- /dev/null +++ b/src/Util/Feature/FeatureDiscovererHelper.ts @@ -0,0 +1,99 @@ +import { fdir, type PathsOutput } from "fdir"; +import { readFile } from 'node:fs/promises'; +import { pathExists } from 'fs-extra'; +import path from "node:path"; + +export class FeatureDiscovererHelper { + + public static createDirClawler(): fdir { + return (new fdir()).withPathSeparator("/"); + } + + public static async crawlDirOnly(dir: string): Promise { + const crawler = this.createDirClawler() + .onlyDirs() + .withFullPaths() + .withMaxDepth(1); + + const dirs = await crawler.crawl(dir).withPromise(); + dirs.shift(); + return dirs; + } + + public static async isUsing(filePath: string, classOrInterfaceName: string): Promise { + return (await readFile(filePath)).indexOf(classOrInterfaceName, 0, 'utf8') !== -1; + } + + public static async discoverInDirOnlyDirs( + dir: string, + featureDir: string, + mapper: (name: string, pathInFeature: string, filePath: string) => T | Promise, + filter?: (filePath: string) => boolean | Promise + ): Promise { + if (!(await pathExists(dir))) { + return []; + } + + const dirIt = (new fdir()).withPathSeparator("/").onlyDirs().withMaxDepth(1).withFullPaths(); + dirIt.filter((path) => !path.endsWith("index.ts")); + const filePaths = await dirIt.crawl(dir).withPromise(); + filePaths.shift(); + const metas: T[] = []; + for (const p of filePaths) { + if (filter) { + let result = filter(p); + result = result instanceof Promise ? await result : result; + if (!result) { + continue; + } + } + + const name = path.basename(p); + const pathInFeature = this.relativePathInFeature(p, featureDir,true); + const mapped = mapper(name, pathInFeature, p); + + metas.push(mapped instanceof Promise ? await mapped : mapped); + } + + return metas; + } + + + public static async discoverInDir( + dir: string, + featureDir: string, + mapper: (name: string, pathInFeature: string, filePath: string) => T | Promise, + filter?: (filePath: string) => boolean | Promise + ): Promise { + if (!(await pathExists(dir))) { + return []; + } + + const dirIt = (new fdir()).withPathSeparator("/").withMaxDepth(0).withFullPaths(); + dirIt.filter((path) => !path.endsWith("index.ts")); + const filePaths = await dirIt.crawl(dir).withPromise(); + const metas: T[] = []; + for (const p of filePaths) { + if (filter) { + let result = filter(p); + result = result instanceof Promise ? await result : result; + if (!result) { + continue; + } + } + + const name = path.basename(p, '.ts'); + const pathInFeature = this.relativePathInFeature(p, featureDir, false); + const mapped = mapper(name, pathInFeature, p); + + metas.push(mapped instanceof Promise ? await mapped : mapped); + } + + return metas; + } + + public static relativePathInFeature(path: string, featureDir: string, pathIsDir: boolean): string { + return path.substring(featureDir.length + 1, path.length - (pathIsDir ? 1 : 3)); + } + +} \ No newline at end of file diff --git a/src/Util/Feature/FeatureDomainDiscoverer.ts b/src/Util/Feature/FeatureDomainDiscoverer.ts new file mode 100644 index 0000000..6fe1539 --- /dev/null +++ b/src/Util/Feature/FeatureDomainDiscoverer.ts @@ -0,0 +1,65 @@ +import { pathExists } from 'fs-extra'; +import { type PathsOutput } from "fdir"; +import path from "node:path"; +import { FeatureAggregateRootMeta, FeatureDomainMeta, FeatureEntityMeta, FeatureValueObjectMeta } from "./Meta"; +import { FeatureDiscovererHelper } from "./FeatureDiscovererHelper"; + +export class FeatureDomainDiscoverer { + + public async discover(featureDir: string): Promise { + return new FeatureDomainMeta( + await this.discoverAggregateRoots(featureDir), + await this.discoverValueObjects('', path.join(featureDir, 'Domain'), featureDir), + ); + } + + private async discoverAggregateRoots(featureDir: string): Promise { + const dirs: PathsOutput = await FeatureDiscovererHelper.crawlDirOnly(path.join(featureDir, "Domain")); + + const roots: FeatureAggregateRootMeta[] = []; + for (const rootDir of dirs) { + const name = path.basename(rootDir, '.ts'); + // skip not aggregates + if (name === 'Service' || name === 'Shared' || name === 'ValueObject' || name === 'Event') { + continue; + } + + const entities = await this.discoverAggregateRootEntities(featureDir, rootDir, name); + const valueObjects = await this.discoverValueObjects(name, rootDir, featureDir); + roots.push(new FeatureAggregateRootMeta(name, entities, valueObjects)); + } + + return roots; + } + + private async discoverAggregateRootEntities(featureDir: string, rootDir: string, aggregateRootName: string): Promise { + return FeatureDiscovererHelper.discoverInDir(rootDir, featureDir, + (name) => new FeatureEntityMeta(name, aggregateRootName), + (filePath) => filePath.endsWith('.ts') && FeatureDiscovererHelper.isUsing(filePath, "AbstractEntity") + ); + } + + private async discoverValueObjects(context: string, contextDir: string, featureDir: string): Promise { + const sharedValueObjectDir = path.join(contextDir, 'Shared/ValueObject'); + let sharedValueObjects: FeatureValueObjectMeta[] | null = null; + if (await pathExists(sharedValueObjectDir)) { + sharedValueObjects = await FeatureDiscovererHelper.discoverInDir(sharedValueObjectDir, featureDir, + (name) => new FeatureValueObjectMeta(name, context, true) + ); + } + + const valueObjectDir = path.join(contextDir, 'ValueObject'); + let valueObjects: FeatureValueObjectMeta[] | null = null; + if (await pathExists(valueObjectDir)) { + valueObjects = await FeatureDiscovererHelper.discoverInDir(valueObjectDir, featureDir, + (name) => new FeatureValueObjectMeta(name, '', false), + ); + } + + if (sharedValueObjects) { + return valueObjects ? [...sharedValueObjects, ...valueObjects] : sharedValueObjects; + } + + return valueObjects ?? []; + } +} \ No newline at end of file diff --git a/src/Util/Feature/FeatureInfrastructureDiscoverer.ts b/src/Util/Feature/FeatureInfrastructureDiscoverer.ts new file mode 100644 index 0000000..f8d1047 --- /dev/null +++ b/src/Util/Feature/FeatureInfrastructureDiscoverer.ts @@ -0,0 +1,17 @@ +import { FeatureInfrastructureMeta, type FeatureClassMeta } from "./Meta"; + +export class FeatureInfrastructureDiscoverer { + + public async discover(featureDir: string, featureName: string): Promise { + return new FeatureInfrastructureMeta( + this.discoverInfrastructureModule(featureName) + ); + + } + + private discoverInfrastructureModule(featureName: string): FeatureClassMeta { + const className = `${featureName}InfraModule`; + const pathInFeature = `Infrastructure/${className}`; + return { name: className, path: pathInFeature }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/FeatureModuleDiscoverer.ts b/src/Util/Feature/FeatureModuleDiscoverer.ts index 4ef7daf..81e1a6e 100644 --- a/src/Util/Feature/FeatureModuleDiscoverer.ts +++ b/src/Util/Feature/FeatureModuleDiscoverer.ts @@ -1,17 +1,27 @@ -import { fdir, type PathsOutput } from "fdir"; -import { pathExists } from 'fs-extra'; -import { readFile } from 'node:fs/promises'; +import { type PathsOutput } from "fdir"; import path from "node:path"; -import { PerformanceHelper } from "../Performance/PerformanceHelper"; -import { FeatureModuleMeta, type ClassMeta, type FeatureAggregateRootMeta, type FeatureApplicationMessageMeta, type FeatureApplicationMeta, type FeatureApplicationServiceMeta, type FeatureDomainMeta, type FeatureEntityMeta, type FeatureInfrastructureMeta } from "./FeatureModuleMeta"; import { FsHelper } from "../Filesystem/FsHelper"; +import { PerformanceHelper } from "../Performance/PerformanceHelper"; +import { FeatureApplicationDiscoverer } from "./FeatureApplicationDiscoverer"; +import { FeatureDiscovererHelper } from "./FeatureDiscovererHelper"; +import { FeatureDomainDiscoverer } from "./FeatureDomainDiscoverer"; +import { FeatureInfrastructureDiscoverer } from "./FeatureInfrastructureDiscoverer"; +import { FeatureMeta } from "./Meta/FeatureMeta"; export const CORE_FEATURE_NAME = "Core"; export class FeatureModuleDiscoverer { + private applicationDiscoverer: FeatureApplicationDiscoverer; + private domainDiscoverer: FeatureDomainDiscoverer; + private infrastructureDiscoverer: FeatureInfrastructureDiscoverer; + public constructor(private sourceRoot: string) { this.sourceRoot = FsHelper.normalizePathSep(this.sourceRoot); + + this.applicationDiscoverer = new FeatureApplicationDiscoverer(); + this.domainDiscoverer = new FeatureDomainDiscoverer(); + this.infrastructureDiscoverer = new FeatureInfrastructureDiscoverer(); } public static extractFeatureNameFromPath(sourceRoot: string, sourcePath: string): string | null { @@ -28,9 +38,9 @@ export class FeatureModuleDiscoverer { return featureName === CORE_FEATURE_NAME ? null : featureName; } - public async discoverAll(): Promise> { - const featureDirs: PathsOutput = await FeatureModuleDiscoverer.crawlDirOnly(this.sourceRoot); - const features = new Map(); + public async discoverAll(): Promise> { + const featureDirs: PathsOutput = await FeatureDiscovererHelper.crawlDirOnly(this.sourceRoot); + const features = new Map(); for (const featureDir of featureDirs) { const featureName = path.basename(featureDir); if (featureName === CORE_FEATURE_NAME) { @@ -44,169 +54,16 @@ export class FeatureModuleDiscoverer { return features; } - private async discover(featureName: string, featureDir: string): Promise { + private async discover(featureName: string, featureDir: string): Promise { featureDir = featureDir.endsWith("/") ? featureDir.substring(0, featureDir.length - 1) : featureDir; return PerformanceHelper.measureFunction("hc.core.feature_module_discovery" + featureName, async () => { - return FeatureModuleMeta.create({ - name: featureName, - application: await this.discoverFeatureApplication(featureDir), - domain: await this.discoverFeatureDomain(featureDir), - infrastructure: await this.discoverFeatureInfrastructure(featureName) - }); + return new FeatureMeta( + featureName, + await this.applicationDiscoverer.discover(featureDir), + await this.domainDiscoverer.discover(featureDir), + await this.infrastructureDiscoverer.discover(featureDir, featureName) + ); }); } - - private async discoverFeatureApplication(featureDir: string): Promise { - const contextDirs: PathsOutput = await FeatureModuleDiscoverer.crawlDirOnly(path.join(featureDir, "Application")); - - const commands: FeatureApplicationMessageMeta[] = []; - const queries: FeatureApplicationMessageMeta[] = []; - const events: FeatureApplicationMessageMeta[] = []; - const services: FeatureApplicationServiceMeta[] = []; - - for (const contextDir of contextDirs) { - const contextName = path.basename(contextDir); - commands.push(...(await this.discoverMessageHandlers(contextName, contextDir, featureDir, "Command"))); - queries.push(...(await this.discoverMessageHandlers(contextName, contextDir, featureDir, "Query"))); - events.push(...(await this.discoverMessageHandlers(contextName, contextDir, featureDir, "Event"))); - services.push(...(await this.discoverServices(contextName, contextDir, featureDir))); - } - - return { - commands, - queries, - events, - services, - }; - } - - private async discoverFeatureDomain(featureDir: string): Promise { - return { - aggregateRoots: await this.discoverAggregateRoots(featureDir), - }; - } - - private async discoverAggregateRoots(featureDir: string): Promise { - const dirs: PathsOutput = await FeatureModuleDiscoverer.crawlDirOnly(path.join(featureDir, "Domain")); - - const roots: FeatureAggregateRootMeta[] = []; - for (const rootDir of dirs) { - const name = path.basename(rootDir); - // Domain/Service is dir with Domain Services working on multiple aggregate roots - if (name === "Service") { - continue; - } - - roots.push({ - name, - repositoryName: name + "Repository", - infraRepositoryName: "Infra" + name + "Repository", - entities: await this.discoverAggregateRootEntities(rootDir) - }); - } - - return roots; - } - - private async discoverAggregateRootEntities(rootDir: string): Promise { - const filePaths: PathsOutput = await FeatureModuleDiscoverer.createDirClawler() - .withFullPaths() - .withMaxDepth(0) - .crawl(path.join(rootDir)).withPromise(); - - const entities: FeatureEntityMeta[] = []; - for (const filePath of filePaths) { - const isEntity = (await readFile(filePath)).indexOf("AbstractEntity", 0, 'utf8') !== -1; - if (!isEntity) { - continue; - } - - const name = path.basename(filePath, ".ts"); - entities.push({ - name, - repositoryName: name + "Repository", - infraRepositoryName: "Infra" + name + "Repository", - }); - } - - return entities; - } - - private async discoverMessageHandlers(context: string, contextDir: string, featureDir: string, messageKind: 'Command' | 'Query' | 'Event'): Promise { - const dir = contextDir + `${messageKind}`; - if (!(await pathExists(dir))) { - return []; - } - - const dirIt = (new fdir()).withPathSeparator("/").onlyDirs().withMaxDepth(1).withFullPaths(); - const messagePaths = await dirIt.crawl(dir).withPromise(); - messagePaths.shift(); - const messages: FeatureApplicationMessageMeta[] = []; - for (const messagePath of messagePaths) { - const name = path.basename(messagePath); - messages.push({ - context: context, - name: path.basename(messagePath), - className: `${context}${name}${messageKind}`, - handlerClassName: `${context}${name}${messageKind}Handler`, - path: messagePath.substring(featureDir.length + 1, messagePath.length - 1), - - }); - } - - return messages; - } - - private async discoverServices(context: string, contextDir: string, featureDir: string): Promise { - const dir = contextDir + `Service`; - if (!(await pathExists(dir))) { - return []; - } - - const dirIt = (new fdir()).withPathSeparator("/").withFullPaths(); - dirIt.filter((path) => !path.endsWith("index.ts")); - - const filePaths = await dirIt.crawl(dir).withPromise(); - const classes: FeatureApplicationServiceMeta[] = []; - for (const filePath of filePaths) { - const isInjectable = (await readFile(filePath)).indexOf("@Injectable(", 0, 'utf8') !== -1; - classes.push({ - className: path.basename(filePath, ".ts"), - context: context, - isInjectable, - path: filePath.substring(featureDir.length + 1, filePath.length - 3), - }); - } - - return classes; - } - - private async discoverFeatureInfrastructure(featureName: string): Promise { - return { - module: this.discoverInfrastructureModule(featureName), - }; - } - - private discoverInfrastructureModule(featureName: string): ClassMeta { - const className = `${featureName}InfraModule`; - const filePath = `Infrastructure/${className}`; - return { className, path: filePath }; - } - - private static createDirClawler(): fdir { - return (new fdir()).withPathSeparator("/"); - } - - private static async crawlDirOnly(dir: string): Promise { - const crawler = this.createDirClawler() - .onlyDirs() - .withFullPaths() - .withMaxDepth(1); - - const dirs = await crawler.crawl(dir).withPromise(); - dirs.shift(); - return dirs; - } - } \ No newline at end of file diff --git a/src/Util/Feature/FeatureModuleMeta.ts b/src/Util/Feature/FeatureModuleMeta.ts deleted file mode 100644 index a5ec15d..0000000 --- a/src/Util/Feature/FeatureModuleMeta.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { hash } from "node:crypto"; - -export interface ClassMeta { - className: string; - path: string; -} - -export interface FeatureApplicationServiceMeta { - className: string; - /** - * Name of context where service belongs. - */ - context: string; - /** - * Indicate is marked with `@Injectable` - */ - isInjectable: boolean; - - /** - * Relative to feature root. - */ - path: string; -} - -export interface FeatureApplicationMessageMeta { - context: string; - className: string, - handlerClassName: string, - name: string; - path: string; -} - -export interface FeatureApplicationMeta { - commands: FeatureApplicationMessageMeta[]; - queries: FeatureApplicationMessageMeta[]; - events: FeatureApplicationMessageMeta[]; - services: FeatureApplicationServiceMeta[]; -} - -export interface FeatureAggregateRootMeta { - name: string; - repositoryName: string; - infraRepositoryName: string; - entities: FeatureEntityMeta[]; -} - -export interface FeatureEntityMeta { - name: string; - repositoryName: string; - infraRepositoryName: string; -} - -export interface FeatureDomainMeta { - aggregateRoots: FeatureAggregateRootMeta[]; -} - -export interface FeatureInfrastructureMeta { - module: ClassMeta; -} - -export type FeatureModuleMap = Map; - -export class FeatureModuleMeta { - public readonly name!: string; - public readonly cacheKey!: string; - - public readonly application!: FeatureApplicationMeta; - public readonly domain!: FeatureDomainMeta; - public readonly infrastructure!: FeatureInfrastructureMeta; - - private constructor(props: Omit & { cacheKey?: string; }) { - Object.assign(this, props); - if (!this.cacheKey) { - this.cacheKey = FeatureModuleMeta.calcCacheKey(this); - } - } - - public static create(props: Omit & { cacheKey?: string; }): FeatureModuleMeta { - return new this(props); - } - - private static calcCacheKey(meta: Omit): string { - let data = ""; - for (const m of meta.application.commands) { - data += m.context + m.name; - } - - for (const m of meta.application.queries) { - data += m.context + m.name; - } - - for (const m of meta.application.events) { - data += m.context + m.name; - } - - for (const m of meta.application.services) { - data += m.path; - } - - for (const r of meta.domain.aggregateRoots) { - data += r.name; - } - - return hash("sha1", data, "hex"); - } -} \ No newline at end of file diff --git a/src/Util/Feature/Meta/CommonFeatureMeta.ts b/src/Util/Feature/Meta/CommonFeatureMeta.ts new file mode 100644 index 0000000..64cc028 --- /dev/null +++ b/src/Util/Feature/Meta/CommonFeatureMeta.ts @@ -0,0 +1,69 @@ +import type { JsonSerialize } from "@hexancore/common"; + +export interface FeatureClassMeta { + name: string; + + /** Relative to feature root without extension */ + path: string; + + /** Relative to feature root */ + filePath: string; +} + +export enum HObjectType { + Command = 'Command', + Query = 'Query', + Event = 'Event', + Dto = 'Dto', + ValueObject = 'ValueObject', + Entity = 'Entity', + AggregateRoot = 'AggregateRoot' +} + +export interface FeatureHObjectMeta extends FeatureClassMeta, JsonSerialize { + context: string; + className: string; + get hashData(): string; + get objectType(): HObjectType; +} + +export type FeatureHObjectMap = Map; + +export class FeatureDtoMeta implements FeatureHObjectMeta { + public constructor( + public name: string, + public context: string, + public path: string, + ) { + + } + + public static parse(plain: unknown): FeatureDtoMeta { + const data = plain as Record; + return new this(data.name, data.context, data.path); + } + + public get className(): string { + return this.name; + } + + public get filePath(): string { + return this.path + '.ts'; + } + + public get objectType(): HObjectType { + return HObjectType.Dto; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + context: this.context, + path: this.path, + }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/Meta/FeatureApplicationMeta.ts b/src/Util/Feature/Meta/FeatureApplicationMeta.ts new file mode 100644 index 0000000..91fc6e5 --- /dev/null +++ b/src/Util/Feature/Meta/FeatureApplicationMeta.ts @@ -0,0 +1,182 @@ +import { type JsonSerialize, LogicError } from "@hexancore/common"; +import { FeatureClassMeta, FeatureDtoMeta, FeatureHObjectMap, FeatureHObjectMeta, HObjectType } from "./CommonFeatureMeta"; + +export interface FeatureApplicationMessageMeta extends FeatureHObjectMeta { + handlerClass: string; +} +export class FeatureApplicationCommandMeta implements FeatureApplicationMessageMeta { + public readonly className: string; + public readonly handlerClass: string; + public readonly path: string; + + public constructor( + public readonly name: string, + public readonly context: string, + ) { + this.className = `${this.context}${this.name}Command`; + this.handlerClass = `${this.context}${this.name}CommandHandler`; + this.path = `Application/${this.context}/Command/${this.name}`; + } + + public static parse(plain: unknown): FeatureApplicationCommandMeta { + const data = plain as Record; + + return new this(data.name, data.context); + } + + public get filePath(): string { + return this.path + '/' + this.className + '.ts'; + } + + public get objectType(): HObjectType.Command { + return HObjectType.Command; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + context: this.context, + }; + } +} + +export class FeatureApplicationQueryMeta implements FeatureApplicationMessageMeta { + public readonly className: string; + public readonly handlerClass: string; + public readonly path: string; + + public constructor( + public readonly name: string, + public readonly context: string, + ) { + this.className = `${this.context}${this.name}Query`; + this.handlerClass = `${this.context}${this.name}QueryHandler`; + this.path = `Application/${this.context}/Query/${this.name}`; + } + + public static parse(plain: unknown): FeatureApplicationQueryMeta { + const data = plain as Record; + return new this(data.name, data.context); + } + + public get filePath(): string { + return this.path + '/' + this.className + '.ts'; + } + + public get objectType(): HObjectType.Query { + return HObjectType.Query; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + context: this.context, + }; + } +} + +export class FeatureApplicationServiceMeta implements FeatureClassMeta { + public constructor( + public name: string, + public context: string, + public path: string, + + public isInjectable: boolean, + ) { + } + + public get filePath(): string { + return this.path + '.ts'; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + context: this.context, + path: this.path, + isInjectable: this.isInjectable + }; + } +} + +export class FeatureApplicationMeta implements JsonSerialize { + + public constructor( + public commands: FeatureApplicationCommandMeta[], + public queries: FeatureApplicationQueryMeta[], + public dtos: FeatureDtoMeta[], + public services: FeatureApplicationServiceMeta[], + ) { + + } + + public static parse(plain: unknown): FeatureApplicationMeta { + if (typeof plain !== 'object' && plain !== null) { + throw new LogicError('Wrong plain in FeatureApplicationMeta.parse()'); + } + + const data = plain as Record; + + const commands = data.commands.map(i => FeatureApplicationCommandMeta.parse(i)); + const queries = data.queries.map(i => FeatureApplicationQueryMeta.parse(i)); + const dtos = data.dtos.map(FeatureDtoMeta.parse); + const services = data.services.map(i => new FeatureApplicationServiceMeta(i.className, i.context, i.path, i.isInjectable)); + return new this(commands, queries, dtos, services); + } + + public collectHObjects(map: FeatureHObjectMap): void { + for (const i of this.commands) { + map.set(i.filePath, i); + } + + for (const i of this.queries) { + map.set(i.filePath, i); + } + + for (const i of this.dtos) { + map.set(i.filePath, i); + } + } + + public get hashData(): string { + let data = ""; + for (const i of this.commands) { + data += i.hashData; + } + + for (const i of this.queries) { + data += i.hashData; + } + + for (const i of this.dtos) { + data += i.hashData; + } + + for (const i of this.services) { + data += i.hashData; + } + + return data; + } + + public toJSON(): any { + return { + commands: this.commands.map(v => v.toJSON()), + queries: this.queries.map(v => v.toJSON()), + dtos: this.dtos.map(v => v.toJSON()), + services: this.services.map(v => v.toJSON()), + }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/Meta/FeatureDomainMeta.ts b/src/Util/Feature/Meta/FeatureDomainMeta.ts new file mode 100644 index 0000000..061a2e5 --- /dev/null +++ b/src/Util/Feature/Meta/FeatureDomainMeta.ts @@ -0,0 +1,204 @@ +import { LogicError } from "@hexancore/common"; +import { FeatureHObjectMeta, HObjectType, type FeatureHObjectMap } from "./CommonFeatureMeta"; + +export class FeatureAggregateRootMeta implements FeatureHObjectMeta { + public repositoryName: string; + public infraRepositoryName: string; + + public constructor( + public name: string, + public entities: FeatureEntityMeta[], + public valueObjects: FeatureValueObjectMeta[] + ) { + this.repositoryName = name + "Repository"; + this.infraRepositoryName = "Infra" + name + "Repository"; + } + + public static parse(plain: unknown): FeatureAggregateRootMeta { + const data = plain as Record; + + const entities = data.entities.map(FeatureEntityMeta.parse); + const valueObjects = data.valueObjects.map(FeatureValueObjectMeta.parse); + return new this(data.name, entities, valueObjects); + } + + public get context(): string { + return this.name; + } + + public get path(): string { + return `Domain/${this.name}/${this.name}`; + } + + public get filePath(): string { + return this.path + '.ts'; + } + + public get className(): string { + return this.name; + } + + public get objectType(): HObjectType { + return HObjectType.AggregateRoot; + } + + public get hashData(): string { + return this.path + this.entities.map(i => i.hashData) + this.valueObjects.map(i => i.hashData); + } + + public toJSON(): any { + return { + name: this.name, + entities: this.entities.map(v => v.toJSON()), + valueObjects: this.valueObjects.map(v => v.toJSON()), + }; + } +} + +export class FeatureEntityMeta implements FeatureHObjectMeta { + public repositoryName: string; + public infraRepositoryName: string; + + public constructor( + public name: string, + public aggregateRootName: string, + ) { + this.repositoryName = name + "Repository"; + this.infraRepositoryName = "Infra" + name + "Repository"; + } + + public static parse(plain: unknown): FeatureEntityMeta { + const data = plain as Record; + return new this(data.name, data.aggregateRootName); + } + + public get path(): string { + return `Domain/${this.aggregateRootName}/${this.name}`; + } + + public get filePath(): string { + return this.path + '.ts'; + } + + public get className(): string { + return this.name; + } + + public get context(): string { + return this.aggregateRootName; + } + + public get objectType(): HObjectType { + return HObjectType.Entity; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + aggregateRootName: this.aggregateRootName + }; + } +} + +export class FeatureValueObjectMeta implements FeatureHObjectMeta { + public constructor( + public name: string, + public context: string, + + public shared: boolean, + ) { } + + public static parse(plain: unknown): FeatureValueObjectMeta { + const data = plain as Record; + return new this(data.name, data.context, data.shared); + } + + public get path(): string { + if (this.context === '') { + return `Domain/Shared/ValueObject/${this.name}`; + } + + return `Domain/${this.context}/${this.shared ? 'Shared/ValueObject/' : 'ValueObject/'}${this.name}`; + } + + public get filePath(): string { + return this.path + '.ts'; + } + + public get className(): string { + return this.name; + } + + public get objectType(): HObjectType.ValueObject { + return HObjectType.ValueObject; + } + + public get hashData(): string { + return this.path; + } + + public toJSON(): any { + return { + name: this.name, + context: this.context, + shared: this.shared + }; + } +} + +export class FeatureDomainMeta { + public constructor( + public aggregateRoots: FeatureAggregateRootMeta[], + public valueObjects: FeatureValueObjectMeta[], + ) { } + + public static parse(plain: unknown): FeatureDomainMeta { + if (typeof plain !== 'object' && plain !== null) { + throw new LogicError('Wrong plain in FeatureDomainMeta.parse()'); + } + + const data = plain as Record; + const aggregateRoots = data.messages.map(FeatureAggregateRootMeta.parse); + const valueObjects = data.valueObjects.map(FeatureValueObjectMeta.parse); + return new this(aggregateRoots, valueObjects); + } + + public collectHObjects(map: FeatureHObjectMap): void { + for (const ar of this.aggregateRoots) { + for (const vo of ar.valueObjects) { + const key = vo.path + '.ts'; + map.set(key, vo); + } + } + + for (const vo of this.valueObjects) { + const key = vo.path + '.ts'; + map.set(vo.path, vo); + + } + } + + public get hashData(): string { + let data = ""; + for (const i of this.aggregateRoots) { + data += i.hashData; + } + + for (const i of this.valueObjects) { + data += i.hashData; + } + + return data; + } + + public toJSON(): any { + return { + aggregateRoots: this.aggregateRoots.map(v => v.toJSON()), + valueObjects: this.valueObjects.map(v => v.toJSON()), + }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/Meta/FeatureInfrastructureMeta.ts b/src/Util/Feature/Meta/FeatureInfrastructureMeta.ts new file mode 100644 index 0000000..b476c75 --- /dev/null +++ b/src/Util/Feature/Meta/FeatureInfrastructureMeta.ts @@ -0,0 +1,24 @@ +import { type JsonSerialize, LogicError } from "@hexancore/common"; +import type { FeatureClassMeta } from "./CommonFeatureMeta"; + +export class FeatureInfrastructureMeta implements JsonSerialize { + public constructor( + public module: FeatureClassMeta, + ) { + } + + public static parse(plain: unknown): FeatureInfrastructureMeta { + if (typeof plain !== 'object' && plain !== null) { + throw new LogicError('Wrong plain in FeatureInfrastructureMeta.parse()'); + } + + const data = plain as Record; + return new this(data.module as FeatureClassMeta); + } + + public toJSON(): any { + return { + module: this.module, + }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/Meta/FeatureMeta.ts b/src/Util/Feature/Meta/FeatureMeta.ts new file mode 100644 index 0000000..298b346 --- /dev/null +++ b/src/Util/Feature/Meta/FeatureMeta.ts @@ -0,0 +1,70 @@ +import { type JsonSerialize, LogicError } from '@hexancore/common'; +import { hash } from "node:crypto"; +import type { FeatureHObjectMap, FeatureHObjectMeta } from './CommonFeatureMeta'; +import { FeatureApplicationMeta } from './FeatureApplicationMeta'; +import { FeatureDomainMeta } from './FeatureDomainMeta'; +import { FeatureInfrastructureMeta } from './FeatureInfrastructureMeta'; + +export type FeatureMap = Map; + +export class FeatureMeta implements JsonSerialize { + private _hObjectMap!: Map; + public cacheKey!: string; + + public constructor( + public name: string, + public application: FeatureApplicationMeta, + public domain: FeatureDomainMeta, + public infrastructure: FeatureInfrastructureMeta, + cacheKey?: string + ) { + if (!cacheKey) { + this.cacheKey = this.calcCacheKey(); + } + } + + public static parse(plain: unknown): FeatureMeta { + if (typeof plain !== 'object' && plain !== null) { + throw new LogicError('Wrong plain in FeatureMeta.parse()'); + } + + const data = plain as Record; + const application = FeatureApplicationMeta.parse(data.application); + const domain = FeatureDomainMeta.parse(data.application); + const infrastructure = FeatureInfrastructureMeta.parse(data.application); + + return new this(data.name, application, domain, infrastructure, data.cacheKey); + } + + private calcCacheKey(): string { + const data = this.application.hashData + this.domain.hashData; + return hash("sha1", data, "hex"); + } + + public get hObjectMap(): Map { + if (!this._hObjectMap) { + this._hObjectMap = this.createHObjectMap(); + } + + return this._hObjectMap + } + + private createHObjectMap(): FeatureHObjectMap { + const map = new Map(); + this.application.collectHObjects(map); + this.domain.collectHObjects(map); + + return map; + } + + public toJSON(): any { + return { + name: this.name, + cacheKey: this.cacheKey, + + application: this.application.toJSON(), + domain: this.domain.toJSON(), + infrastructure: this.infrastructure.toJSON(), + }; + } +} \ No newline at end of file diff --git a/src/Util/Feature/Meta/index.ts b/src/Util/Feature/Meta/index.ts new file mode 100644 index 0000000..9bb6595 --- /dev/null +++ b/src/Util/Feature/Meta/index.ts @@ -0,0 +1,5 @@ +export * from "./CommonFeatureMeta"; +export * from "./FeatureApplicationMeta"; +export * from "./FeatureDomainMeta"; +export * from "./FeatureInfrastructureMeta"; +export * from "./FeatureMeta"; \ No newline at end of file diff --git a/test/helper/libs/test-lib/src/Book/Application/Book/Command/Create/BookCreateCommandHandler.ts b/test/helper/libs/test-lib/src/Book/Application/Book/Command/Create/BookCreateCommandHandler.ts index 44e9d12..a36882d 100644 --- a/test/helper/libs/test-lib/src/Book/Application/Book/Command/Create/BookCreateCommandHandler.ts +++ b/test/helper/libs/test-lib/src/Book/Application/Book/Command/Create/BookCreateCommandHandler.ts @@ -1,13 +1,14 @@ import { CommandHandler, type ICommandHandler } from "@nestjs/cqrs"; import { BookCreateCommand } from "./BookCreateCommand"; import { OKAP, type ARP } from "@hexancore/common"; -import { BookDto } from "@test/libs/test-lib/src/Book/Domain/Book/BookDto"; +import { BookDto } from "../../Dto/BookDto"; @CommandHandler(BookCreateCommand) export class BookCreateCommandHandler implements ICommandHandler { public execute(command: BookCreateCommand): ARP { return OKAP(BookDto.cs({ - title: command.title + title: command.title, + })); } } \ No newline at end of file diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/BookDto.ts b/test/helper/libs/test-lib/src/Book/Application/Book/Dto/BookDto.ts similarity index 100% rename from test/helper/libs/test-lib/src/Book/Domain/Book/BookDto.ts rename to test/helper/libs/test-lib/src/Book/Application/Book/Dto/BookDto.ts diff --git a/test/helper/libs/test-lib/src/Book/Application/Book/Event/BookCreatedEventHandler.ts b/test/helper/libs/test-lib/src/Book/Application/Book/Event/BookCreatedEventHandler.ts deleted file mode 100644 index ea47955..0000000 --- a/test/helper/libs/test-lib/src/Book/Application/Book/Event/BookCreatedEventHandler.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { OKAP, type ARP } from "@hexancore/common"; -import { EventsHandler, type IEventHandler } from "@nestjs/cqrs"; -import { BookCreatedEvent } from "@test/libs/test-lib/src/Book/Domain/Book/BookCreatedEvent"; - -@EventsHandler(BookCreatedEvent) -export class BookCreatedEventHandler implements IEventHandler { - public handle(event: BookCreatedEvent): ARP { - return OKAP(true); - } -} \ No newline at end of file diff --git a/test/helper/libs/test-lib/src/Book/Application/Book/Query/GetById/BookGetByIdQueryHandler.ts b/test/helper/libs/test-lib/src/Book/Application/Book/Query/GetById/BookGetByIdQueryHandler.ts index 0e27189..63d4e48 100644 --- a/test/helper/libs/test-lib/src/Book/Application/Book/Query/GetById/BookGetByIdQueryHandler.ts +++ b/test/helper/libs/test-lib/src/Book/Application/Book/Query/GetById/BookGetByIdQueryHandler.ts @@ -1,7 +1,7 @@ import { OKAP, type ARP } from "@hexancore/common"; import { CommandHandler, type IQueryHandler } from "@nestjs/cqrs"; -import { BookDto } from "@test/libs/test-lib/src/Book/Domain/Book/BookDto"; import { BookGetByIdQuery } from "./BookGetByIdQuery"; +import { BookDto } from "../../Dto/BookDto"; @CommandHandler(BookGetByIdQuery) export class BookGetByIdQueryHandler implements IQueryHandler { diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/Book.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/Book.ts index f5e755a..84b0bd6 100644 --- a/test/helper/libs/test-lib/src/Book/Domain/Book/Book.ts +++ b/test/helper/libs/test-lib/src/Book/Domain/Book/Book.ts @@ -1,5 +1,5 @@ import { AggregateRoot, AbstractAggregateRoot, EntityCollection, type IEntityCollection } from '@'; -import type { BookId } from './Shared/BookId'; +import type { BookId } from './Shared/ValueObject/BookId'; import { BookCopy } from './BookCopy'; @AggregateRoot() diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/BookCopy.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/BookCopy.ts index b80f63e..a6388b2 100644 --- a/test/helper/libs/test-lib/src/Book/Domain/Book/BookCopy.ts +++ b/test/helper/libs/test-lib/src/Book/Domain/Book/BookCopy.ts @@ -1,6 +1,6 @@ import { AbstractEntity, Entity } from '@'; import type { Book } from './Book'; -import type { BookCopyId } from './Shared/BookCopyId'; +import type { BookCopyId } from './Shared/ValueObject/BookCopyId'; @Entity() export class BookCopy extends AbstractEntity { diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/BookCreatedEvent.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/BookCreatedEvent.ts deleted file mode 100644 index 3882647..0000000 --- a/test/helper/libs/test-lib/src/Book/Domain/Book/BookCreatedEvent.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class BookCreatedEvent { - public constructor(public readonly title: string) {} -} diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/BookCopyId.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/ValueObject/BookCopyId.ts similarity index 100% rename from test/helper/libs/test-lib/src/Book/Domain/Book/Shared/BookCopyId.ts rename to test/helper/libs/test-lib/src/Book/Domain/Book/Shared/ValueObject/BookCopyId.ts diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/BookId.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/ValueObject/BookId.ts similarity index 100% rename from test/helper/libs/test-lib/src/Book/Domain/Book/Shared/BookId.ts rename to test/helper/libs/test-lib/src/Book/Domain/Book/Shared/ValueObject/BookId.ts diff --git a/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/index.ts b/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/index.ts index 16e6b34..056d99f 100644 --- a/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/index.ts +++ b/test/helper/libs/test-lib/src/Book/Domain/Book/Shared/index.ts @@ -1,2 +1,2 @@ -export * from "./BookId"; -export * from "./BookCopyId"; \ No newline at end of file +export * from "./ValueObject/BookId"; +export * from "./ValueObject/BookCopyId"; \ No newline at end of file diff --git a/test/helper/libs/test-lib/src/Book/Domain/Shared/ValueObject/FeatureSharedValueObject.ts b/test/helper/libs/test-lib/src/Book/Domain/Shared/ValueObject/FeatureSharedValueObject.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/helper/libs/test-lib/src/Book/Domain/Shared/index.ts b/test/helper/libs/test-lib/src/Book/Domain/Shared/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/Util/Feature/FeatureModuleDiscoverer.test.ts b/test/unit/Util/Feature/FeatureModuleDiscoverer.test.ts index ca8ba90..c7c69fc 100644 --- a/test/unit/Util/Feature/FeatureModuleDiscoverer.test.ts +++ b/test/unit/Util/Feature/FeatureModuleDiscoverer.test.ts @@ -4,13 +4,23 @@ import { FeatureModuleDiscoverer } from "@/Util/Feature/FeatureModuleDiscoverer"; import { LIBS_DIRNAME } from "@test/libs"; +import * as yaml from "js-yaml"; describe(FeatureModuleDiscoverer.constructor.name, () => { + test('discoverAll', async () => { const discoverer = new FeatureModuleDiscoverer(LIBS_DIRNAME + "/test-lib/src"); const current = await discoverer.discoverAll(); - expect(current).toMatchSnapshot(); + expect(yaml.dump(Array.from(current.entries()).map(v => v[1].toJSON()))).toMatchSnapshot(); + }); + + test('hObjectMap', async () => { + const discoverer = new FeatureModuleDiscoverer(LIBS_DIRNAME + "/test-lib/src"); + + const current = await discoverer.discoverAll(); + + expect(yaml.dump(Array.from(current.entries()).map(v => Object.fromEntries(v[1].hObjectMap.entries())))).toMatchSnapshot(); }); }); diff --git a/test/unit/Util/Feature/__snapshots__/FeatureModuleDiscoverer.test.ts.snap b/test/unit/Util/Feature/__snapshots__/FeatureModuleDiscoverer.test.ts.snap index 4255485..f0559ce 100644 --- a/test/unit/Util/Feature/__snapshots__/FeatureModuleDiscoverer.test.ts.snap +++ b/test/unit/Util/Feature/__snapshots__/FeatureModuleDiscoverer.test.ts.snap @@ -1,67 +1,87 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Function discoverAll 1`] = ` -Map { - "Book" => FeatureModuleMeta { - "application": { - "commands": [ - { - "className": "BookCreateCommand", - "context": "Book", - "handlerClassName": "BookCreateCommandHandler", - "name": "Create", - "path": "Application/Book/Command/Create", - }, - ], - "events": [], - "queries": [ - { - "className": "BookGetByIdQuery", - "context": "Book", - "handlerClassName": "BookGetByIdQueryHandler", - "name": "GetById", - "path": "Application/Book/Query/GetById", - }, - ], - "services": [ - { - "className": "BookInfraPort", - "context": "Book", - "isInjectable": false, - "path": "Application/Book/Service/BookInfraPort", - }, - { - "className": "BookService", - "context": "Book", - "isInjectable": true, - "path": "Application/Book/Service/BookService", - }, - ], - }, - "cacheKey": "1261b3da77abfb8027ef4327b11cd95dc26847c0", - "domain": { - "aggregateRoots": [ - { - "entities": [ - { - "infraRepositoryName": "InfraBookCopyRepository", - "name": "BookCopy", - "repositoryName": "BookCopyRepository", - }, - ], - "infraRepositoryName": "InfraBookRepository", - "name": "Book", - "repositoryName": "BookRepository", - }, - ], - }, - "infrastructure": { - "module": { - "className": "BookInfraModule", - "path": "Infrastructure/BookInfraModule", - }, - }, - "name": "Book", - }, -} +"- name: Book + cacheKey: 76938fdd0fe1c572144becfed1ef7427b3f52342 + application: + commands: + - name: Create + context: Book + queries: + - name: GetById + context: Book + dtos: + - name: BookDto + context: Book + path: Application/Book/Dto/BookDto + - name: TestTransformDto + context: Book + path: Application/Book/Dto/TestTransformDto + services: + - name: BookInfraPort + context: Book + path: Application/Book/Service/BookInfraPort + isInjectable: false + - name: BookService + context: Book + path: Application/Book/Service/BookService + isInjectable: true + domain: + aggregateRoots: + - name: Book + entities: + - name: BookCopy + aggregateRootName: Book + valueObjects: + - name: BookCopyId + context: Book + shared: true + - name: BookId + context: Book + shared: true + valueObjects: + - name: FeatureSharedValueObject + context: '' + shared: true + infrastructure: + module: + name: BookInfraModule + path: Infrastructure/BookInfraModule +" +`; + +exports[`Function hObjectMap 1`] = ` +"- Application/Book/Command/Create/BookCreateCommand.ts: + name: Create + context: Book + className: BookCreateCommand + handlerClass: BookCreateCommandHandler + path: Application/Book/Command/Create + Application/Book/Query/GetById/BookGetByIdQuery.ts: + name: GetById + context: Book + className: BookGetByIdQuery + handlerClass: BookGetByIdQueryHandler + path: Application/Book/Query/GetById + Application/Book/Dto/BookDto.ts: + name: BookDto + context: Book + path: Application/Book/Dto/BookDto + Application/Book/Dto/TestTransformDto.ts: + name: TestTransformDto + context: Book + path: Application/Book/Dto/TestTransformDto + Domain/Book/Shared/ValueObject/BookCopyId.ts: + name: BookCopyId + context: Book + shared: true + Domain/Book/Shared/ValueObject/BookId.ts: + name: BookId + context: Book + shared: true + Domain/Shared/ValueObject/FeatureSharedValueObject: + name: FeatureSharedValueObject + context: '' + shared: true +" `; diff --git a/tsconfig.build.json b/tsconfig.build.json index 32f2f4e..3a8f6bd 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "declaration": true, "declarationMap": false, "sourceMap": false, }, diff --git a/tsconfig.json b/tsconfig.json index 0a4109d..c64d270 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "noImplicitThis": false, "noImplicitAny": false, "importHelpers": true, - "declaration": true, + "declaration": false, "sourceMap": true, "removeComments": false, "emitDecoratorMetadata": true, @@ -21,14 +21,22 @@ "forceConsistentCasingInFileNames": true, "useDefineForClassFields": false, "allowJs": true, - "types": ["jest", "reflect-metadata", "node"], + "types": [ + "jest", + "reflect-metadata", + "node" + ], "incremental": false, "resolveJsonModule": true, "outDir": "./lib", "baseUrl": "./", "paths": { - "@": ["src"], - "@/*": ["src/*"], + "@": [ + "src" + ], + "@/*": [ + "src/*" + ], "@test/*": [ "test/helper/*" ], @@ -42,7 +50,8 @@ ], "include": [ "src/**/*.ts", - "test/helper/*" + "test/helper/*", + "test/**/*.ts" ], "typedocOptions": { "mode": "file", diff --git a/yarn.lock b/yarn.lock index d678f26..2ac4630 100644 --- a/yarn.lock +++ b/yarn.lock @@ -710,6 +710,7 @@ __metadata: "@nestjs/swagger": "npm:^7.1.8" "@nestjs/testing": "npm:^10.3.9" "@types/jest": "npm:29.0.*" + "@types/js-yaml": "npm:^4.0.9" "@types/node": "npm:^20.14.2" "@typescript-eslint/eslint-plugin": "npm:^7.13.1" "@typescript-eslint/parser": "npm:^7.13.1" @@ -1612,6 +1613,13 @@ __metadata: languageName: node linkType: hard +"@types/js-yaml@npm:^4.0.9": + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: 10c0/24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 + languageName: node + linkType: hard + "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15"