-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
963 additions
and
364 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { type PathsOutput } from "fdir"; | ||
import path from "node:path"; | ||
import { FeatureDiscovererHelper } from "./FeatureDiscovererHelper"; | ||
import { FeatureApplicationCommandMeta, FeatureApplicationMeta, FeatureApplicationQueryMeta, FeatureApplicationServiceMeta, FeatureDtoMeta } from "./Meta"; | ||
|
||
export class FeatureApplicationDiscoverer { | ||
|
||
public async discover(featureDir: string): Promise<FeatureApplicationMeta> { | ||
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<FeatureApplicationCommandMeta[]> { | ||
return FeatureDiscovererHelper.discoverInDirOnlyDirs( | ||
contextDir + 'Command', | ||
featureDir, | ||
(name) => new FeatureApplicationCommandMeta(name, context)); | ||
} | ||
|
||
private async discoverQueries(context: string, contextDir: string, featureDir: string): Promise<FeatureApplicationQueryMeta[]> { | ||
return FeatureDiscovererHelper.discoverInDirOnlyDirs( | ||
contextDir + 'Query', | ||
featureDir, | ||
(name) => new FeatureApplicationQueryMeta(name, context)); | ||
} | ||
|
||
private async discoverDtos(context: string, contextDir: string, featureDir: string): Promise<FeatureDtoMeta[]> { | ||
return FeatureDiscovererHelper.discoverInDir(contextDir + 'Dto', featureDir, | ||
(name, pathInFeature) => new FeatureDtoMeta(name, context, pathInFeature) | ||
); | ||
} | ||
|
||
private async discoverServices(context: string, contextDir: string, featureDir: string): Promise<FeatureApplicationServiceMeta[]> { | ||
return FeatureDiscovererHelper.discoverInDir(contextDir + 'Service', featureDir, | ||
async (name, pathInFeature, filePath) => | ||
new FeatureApplicationServiceMeta(name, context, pathInFeature, await FeatureDiscovererHelper.isUsing(filePath, "@Injectable(")) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<PathsOutput> { | ||
return (new fdir()).withPathSeparator("/"); | ||
} | ||
|
||
public static async crawlDirOnly(dir: string): Promise<PathsOutput> { | ||
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<boolean> { | ||
return (await readFile(filePath)).indexOf(classOrInterfaceName, 0, 'utf8') !== -1; | ||
} | ||
|
||
public static async discoverInDirOnlyDirs<T>( | ||
dir: string, | ||
featureDir: string, | ||
mapper: (name: string, pathInFeature: string, filePath: string) => T | Promise<T>, | ||
filter?: (filePath: string) => boolean | Promise<boolean> | ||
): Promise<T[]> { | ||
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<T>( | ||
dir: string, | ||
featureDir: string, | ||
mapper: (name: string, pathInFeature: string, filePath: string) => T | Promise<T>, | ||
filter?: (filePath: string) => boolean | Promise<boolean> | ||
): Promise<T[]> { | ||
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)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<FeatureDomainMeta> { | ||
return new FeatureDomainMeta( | ||
await this.discoverAggregateRoots(featureDir), | ||
await this.discoverValueObjects('', path.join(featureDir, 'Domain'), featureDir), | ||
); | ||
} | ||
|
||
private async discoverAggregateRoots(featureDir: string): Promise<FeatureAggregateRootMeta[]> { | ||
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<FeatureEntityMeta[]> { | ||
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<FeatureValueObjectMeta[]> { | ||
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 ?? []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { FeatureInfrastructureMeta, type FeatureClassMeta } from "./Meta"; | ||
|
||
export class FeatureInfrastructureDiscoverer { | ||
|
||
public async discover(featureDir: string, featureName: string): Promise<FeatureInfrastructureMeta> { | ||
return new FeatureInfrastructureMeta( | ||
this.discoverInfrastructureModule(featureName) | ||
); | ||
|
||
} | ||
|
||
private discoverInfrastructureModule(featureName: string): FeatureClassMeta { | ||
const className = `${featureName}InfraModule`; | ||
const pathInFeature = `Infrastructure/${className}`; | ||
return { name: className, path: pathInFeature }; | ||
} | ||
} |
Oops, something went wrong.