Skip to content

Commit

Permalink
refactor(compiler): FeatureSourcePath + small TS helpers (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mararok authored Sep 24, 2024
1 parent 04e3e67 commit 3be09f9
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 105 deletions.
24 changes: 12 additions & 12 deletions src/Compiler/Jest/HcJestTransformer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FeatureModuleDiscoverer } from '../../Util/Feature/FeatureModuleDiscoverer';
import { FeatureModuleDiscoverer, type FeatureSourcePath } from '../../Util/Feature/FeatureModuleDiscoverer';
import type { AsyncTransformer, TransformedSource } from '@jest/transform';
import { hash } from 'node:crypto';
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
Expand Down Expand Up @@ -72,12 +72,12 @@ export class HcJestTransformer implements AsyncTransformer<HcJestTransformerOpti

public process(sourceText: string, sourcePath: string, options: TsJestTransformOptions): TransformedSource {
sourcePath = FsHelper.normalizePathSep(sourcePath);
const featureName = this.extractFeatureNameFromPath(sourcePath);
if (!featureName || !this.featureTsTransformer.supports(sourcePath, featureName)) {
const featureSourcePath = this.extractFeatureNameFromPath(sourcePath);
if (!featureSourcePath || !this.featureTsTransformer.supports(featureSourcePath)) {
return this.tsJestTransformer.process(sourceText, sourcePath, options);
}

return this.processFeatureSourceFile(featureName, sourceText, sourcePath, options);
return this.processFeatureSourceFile(featureSourcePath, sourceText, options);
}

public async processAsync(
Expand All @@ -86,19 +86,19 @@ export class HcJestTransformer implements AsyncTransformer<HcJestTransformerOpti
options: TsJestTransformOptions,
): Promise<TransformedSource> {
sourcePath = FsHelper.normalizePathSep(sourcePath);
const featureName = this.extractFeatureNameFromPath(sourcePath);
if (!featureName || !this.featureTsTransformer.supports(sourcePath, featureName)) {
const featureSourcePath = this.extractFeatureNameFromPath(sourcePath);
if (!featureSourcePath || !this.featureTsTransformer.supports(featureSourcePath)) {
return this.tsJestTransformer.processAsync(sourceText, sourcePath, options);
}

return this.processFeatureSourceFile(featureName, sourceText, sourcePath, options);
return this.processFeatureSourceFile(featureSourcePath, sourceText, options);
}

private processFeatureSourceFile(featureName: string, sourceText: string, sourcePath: string, options: TsJestTransformOptions): TransformedSource {
private processFeatureSourceFile(featureSourcePath: FeatureSourcePath, sourceText: string, options: TsJestTransformOptions): TransformedSource {
this.setupTransformedTmpDir(options);

const inSourceFile = ts.createSourceFile(
sourcePath,
featureSourcePath.sourcePath,
sourceText,
this.compilerOptions.target ?? ts.ScriptTarget.Latest
);
Expand All @@ -107,7 +107,7 @@ export class HcJestTransformer implements AsyncTransformer<HcJestTransformerOpti
const outSourceFile = transformed.transformed[0];

const printed = TsTransfromerHelper.printFile(outSourceFile);
const tmpPath = this.tmpDir + '/' + featureName + '-' + hash('md5', sourcePath, 'hex').substring(0, 8) + '-' + path.basename(sourcePath);
const tmpPath = this.tmpDir + '/' + featureSourcePath.featureName+ '-' + hash('md5', featureSourcePath.sourcePath, 'hex').substring(0, 8) + '-' + path.basename(featureSourcePath.sourcePath);
writeFileSync(tmpPath, printed);

const outTranspile = ts.transpileModule(printed, {
Expand Down Expand Up @@ -144,13 +144,13 @@ export class HcJestTransformer implements AsyncTransformer<HcJestTransformerOpti
sourcePath = FsHelper.normalizePathSep(sourcePath);
const extracted = this.extractFeatureNameFromPath(sourcePath);
if (extracted) {
return this.featuresHashMap.get(extracted)!;
return this.featuresHashMap.get(extracted.featureName)!;
}

return null;
}

private extractFeatureNameFromPath(sourcePath: string): string | null {
private extractFeatureNameFromPath(sourcePath: string): FeatureSourcePath | null {
return FeatureModuleDiscoverer.extractFeatureNameFromPath(this.sourceRoot, sourcePath);
}
}
14 changes: 10 additions & 4 deletions src/Compiler/Test/TsTransformerTestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,19 @@ export class TsTransformerTestHelper {
return ts.createSourceFile(
filePath,
code,
this.compilerOptions.target ?? ts.ScriptTarget.Latest
this.compilerOptions.target ?? ts.ScriptTarget.Latest,
true,
);
}

public createTransformerFactory(transformer: (sourceFile: ts.SourceFile) => ts.SourceFile): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => transformer;
public createTransformerFactory(transformer: (sourceFile: ts.SourceFile, context: ts.TransformationContext,) => ts.SourceFile): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => transformer(sourceFile, context);
}


public transformExistingAndReturnAsString(sourceFilePath: string, transformers: ts.TransformerFactory<ts.SourceFile>[]): string {
const sourceFile = this.createSourceFileFromExisting(sourceFilePath);
return this.transformAndReturnAsString(sourceFile, transformers);
}

public transformAndReturnAsString(sourceFile: ts.SourceFile, transformers: ts.TransformerFactory<ts.SourceFile>[]): string {
Expand All @@ -63,7 +70,6 @@ export class TsTransformerTestHelper {

public transpileModule(transformer: ContextAwareCustomTransformer, sourceFilePath: string, sourceText?: string): ts.TranspileOutput {
const sourceFile = sourceText ? this.createSourceFile(sourceFilePath, sourceText) : this.createSourceFileFromExisting(sourceFilePath);

return ts.transpileModule(sourceFile.text, {
compilerOptions: this.compilerOptions,
fileName: sourceFilePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { FeatureMap, FeatureMeta } from "../../../Util/Feature/Meta/Feature
import { ModuleClassTsTransformer } from '../ModuleClassTsTransformer';
import type { TsImportHelper } from '../TsImportHelper';
import type { FeatureTransformContext } from './FeatureTransformContext';
import type { FeatureSourcePath } from "../../../Util/Feature/FeatureModuleDiscoverer";

export interface FeatureTsTransformerHelpers {
importHelper: TsImportHelper;
Expand All @@ -23,6 +24,6 @@ export abstract class AbstractFeatureTsTransformer {

public abstract transform(source: ts.SourceFile, context: FeatureTransformContext): ts.SourceFile;

public abstract supports(sourceFilePath: string, feature: FeatureMeta): boolean;
public abstract supports(featureSourcePath: FeatureSourcePath, feature: FeatureMeta): boolean;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AbstractFeatureTsTransformer } from './AbstractFeatureTsTransformer';
import { TsTransfromerHelper } from '../TsTransformerHelper';
import type { AddImportTransformDef } from '../ModuleClassTsTransformer';
import type { FeatureTransformContext } from './FeatureTransformContext';
import type { FeatureSourcePath } from "../../../Util/Feature/FeatureModuleDiscoverer";

export class FeatureInfraDomainModuleTsTransformer extends AbstractFeatureTsTransformer {

Expand Down Expand Up @@ -43,14 +44,14 @@ export class FeatureInfraDomainModuleTsTransformer extends AbstractFeatureTsTran
ts.factory.createPropertyAssignment("domainErrors", ts.factory.createIdentifier(domainErrorsClassName)),
]);

const createMeta = TsTransfromerHelper.createConstStatement("HcDomainInfraModuleMetaExtra", ts.factory.createCallExpression(
const createMeta = TsTransfromerHelper.createConstStatement("HcDomainInfraModuleMetaExtra", undefined, ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(classIdentifier, methodIdentifier),
undefined,
[optionsObject]
));

return [
TsTransfromerHelper.createConstStatement("HcDomainInfraAggrgateRootRepositories",
TsTransfromerHelper.createConstStatement("HcDomainInfraAggrgateRootRepositories", undefined,
ts.factory.createArrayLiteralExpression(repos.map((r) => ts.factory.createIdentifier(r)))
),
createMeta
Expand All @@ -65,7 +66,7 @@ export class FeatureInfraDomainModuleTsTransformer extends AbstractFeatureTsTran
});
}

public supports(sourcefilePath: string, feature: FeatureMeta): boolean {
return sourcefilePath.endsWith("DomainInfraModule.ts");
public supports(featureSourcePath: FeatureSourcePath, feature: FeatureMeta): boolean {
return featureSourcePath.localSourcePath.endsWith("DomainInfraModule.ts");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import { AbstractFeatureTsTransformer } from "./AbstractFeatureTsTransformer";
import type { ProviderModuleMetaTransformDef } from '../ModuleClassTsTransformer';
import type { FeatureTransformContext } from './FeatureTransformContext';
import type { FeatureApplicationMessageMeta } from '../../../Util/Feature/Meta';
import type { FeatureSourcePath } from "../../../Util/Feature/FeatureModuleDiscoverer";

/**
* Adding automatic injection of message handlers, services, infra module to `[Feature]Module` source.
* Less write, more fun !
*/
export class FeatureModuleTsTransformer extends AbstractFeatureTsTransformer {

public supports(sourcefilePath: string, feature: FeatureMeta): boolean {
return sourcefilePath.endsWith(feature.name + "Module.ts");
public supports(featureSourcePath: FeatureSourcePath, feature: FeatureMeta): boolean {
return featureSourcePath.localSourcePath.endsWith(feature.name + "Module.ts");
}

public transform(source: ts.SourceFile, context: FeatureTransformContext): ts.SourceFile {
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Transformer/Feature/FeatureTransformContext.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type ts from "typescript";
import type { FeatureMeta } from "../../../Util/Feature/Meta/FeatureMeta";
import type { FeatureSourcePath } from "../../../Util/Feature/FeatureModuleDiscoverer";

export interface FeatureTransformContext {
featureSourcePath: FeatureSourcePath;
feature: FeatureMeta;
sourceFilePathWithoutRoot: string;
tsContext: ts.TransformationContext;
diagnostics: ts.Diagnostic[];
}
42 changes: 22 additions & 20 deletions src/Compiler/Transformer/Feature/FeatureTsTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import type { AbstractFeatureTsTransformer } from "./AbstractFeatureTsTransforme
import { FeatureInfraDomainModuleTsTransformer } from "./FeatureInfraDomainTsTransformer";
import { FeatureModuleTsTransformer } from "./FeatureModuleTsTransformer";
import path from "node:path";
import { FeatureModuleDiscoverer } from "../../../Util/Feature/FeatureModuleDiscoverer";
import { FeatureModuleDiscoverer, type FeatureSourcePath } from "../../../Util/Feature/FeatureModuleDiscoverer";
import { TsImportHelper } from "../TsImportHelper";
import { ModuleClassTsTransformer } from "../ModuleClassTsTransformer";
import type { FeatureTransformContext } from "./FeatureTransformContext";
//import { HObjectTsTransformer } from "./HObject/HObjectTsTransformer";


/**
Expand All @@ -35,6 +36,7 @@ export class FeatureTsTransformer {
this.transformers = [
new FeatureInfraDomainModuleTsTransformer(helpers),
new FeatureModuleTsTransformer(helpers),
//new HObjectTsTransformer(helpers),
];
}

Expand All @@ -61,42 +63,42 @@ export class FeatureTsTransformer {
}

public transform(source: ts.SourceFile, context: ts.TransformationContext): ts.SourceFile {
const sourceFilePath = FsHelper.normalizePathSep(source.fileName);
const feature = this.getFeatureOfPath(sourceFilePath);
if (!feature) {
const featureSourceTransformContext = this.createFeatureSourceTransformContext(source, context);
if (!featureSourceTransformContext) {
return source;
}

const sourceFilePathWithoutRoot = sourceFilePath.substring(this.sourceRoot.length + 1);
const transformContext: FeatureTransformContext = {
feature,
sourceFilePathWithoutRoot,
tsContext: context,
diagnostics: [],
};

for (const t of this.transformers) {
if (t.supports(sourceFilePathWithoutRoot, feature)) {
const transformed = t.transform(source, transformContext);
if (t.supports(featureSourceTransformContext.featureSourcePath, featureSourceTransformContext.feature)) {
const transformed = t.transform(source, featureSourceTransformContext);
return transformed;
}
}

return source;
}

private getFeatureOfPath(sourceFilePath: string): FeatureMeta | null {
const featureName = FeatureModuleDiscoverer.extractFeatureNameFromPath(this.sourceRoot, sourceFilePath);
if (!featureName) {
private createFeatureSourceTransformContext(source: ts.SourceFile, context: ts.TransformationContext): FeatureTransformContext | null {
const sourceFilePath = FsHelper.normalizePathSep(source.fileName);
const featureSourcePath = FeatureModuleDiscoverer.extractFeatureNameFromPath(this.sourceRoot, sourceFilePath);
if (!featureSourcePath) {
return null;
}

return this.features.get(featureName) as FeatureMeta;
const feature = this.features.get(featureSourcePath.featureName)!;
return {
feature,
featureSourcePath,
tsContext: context,
diagnostics: [],
};
}

public supports(sourceFilePath: string, featureName: string): boolean {
const feature = this.features.get(featureName)!;
public supports(sourcePath: FeatureSourcePath): boolean {
const feature = this.features.get(sourcePath.featureName)!;
for (const t of this.transformers) {
if (t.supports(sourceFilePath, feature)) {
if (t.supports(sourcePath, feature)) {
return true;
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/Compiler/Transformer/Helper/ImportDeclarationWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import ts from "typescript";
import { TsTransfromerHelper } from "../TsTransformerHelper";

export class ImportDeclarationWrapper {
public constructor(
public declaration: ts.ImportDeclaration,
private context: ts.TransformationContext
) {

}

public static create(importName: string | string[], importPath: string, context: ts.TransformationContext): ImportDeclarationWrapper {
return ImportDeclarationWrapper.from(TsTransfromerHelper.createImportDeclaration(importName, importPath), context);
}

public static from(importDecl: ts.ImportDeclaration, context: ts.TransformationContext): ImportDeclarationWrapper {
return new this(importDecl, context);
}

public get(name: string): ts.PropertyAccessChain | ts.Identifier {
return TsTransfromerHelper.createNamedImportAccess(this.declaration, name, this.context);
}

public hasNamedAccess(name: string): boolean {
if (!this.declaration.importClause?.namedBindings) {
return false;
}

return (this.declaration.importClause.namedBindings as ts.NamedImports).elements.filter(i => i.name.text === name).length > 0;
}

public getEntityName(name: string): ts.EntityName {
const current = this.get(name);
if (ts.isIdentifier(current)) {
return current;
}

return ts.factory.createQualifiedName(current.expression as ts.Identifier, current.name as ts.Identifier);
}

public addNamedImports(elements: (ts.ImportSpecifier | string)[]): ts.ImportDeclaration {
const newNamedImports = ts.factory.createNamedImports([
...(this.declaration.importClause?.namedBindings as ts.NamedImports).elements.map(e => ts.factory.createImportSpecifier(e.isTypeOnly, e.propertyName, ts.factory.createIdentifier(e.name.text))),
...elements.map(e => typeof e === 'string' ? ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(e)) : e),
]);

this.declaration = ts.factory.updateImportDeclaration(
this.declaration,
undefined,
ts.factory.updateImportClause(this.declaration.importClause!,
this.declaration.importClause!.isTypeOnly, undefined, newNamedImports),
this.declaration.moduleSpecifier, this.declaration.attributes
);

return this.declaration;
}

public get importNames(): string[] {
return (this.declaration.importClause?.namedBindings as ts.NamedImports).elements.map(e => e.name.text);
}
}
2 changes: 1 addition & 1 deletion src/Compiler/Transformer/ModuleClassTsTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export class ModuleClassTsTransformer {

private createProviderDeclarationStatement(def: ProviderModuleMetaTransformDef): ts.VariableStatement {
const varName = def.name + "Provider";
return TsTransfromerHelper.createConstStatement(varName, def.expression!);
return TsTransfromerHelper.createConstStatement(varName, undefined, def.expression!);
}

private getArrayPropertyAsSpread(identifier: ts.Identifier, property: string) {
Expand Down
Loading

0 comments on commit 3be09f9

Please sign in to comment.