Skip to content

Commit

Permalink
feat: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
msp5382 committed Dec 30, 2023
1 parent 6580cdd commit 753877e
Show file tree
Hide file tree
Showing 22 changed files with 366 additions and 61 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ jobs:

- name: Run build
run: bun run build

- name: Run tests
run: bun test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,4 @@ dist
# Finder (MacOS) folder config
.DS_Store

tests/**/**/test
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"url": "https://softnetics.tech"
},
"scripts": {
"build": "tsup"
"build": "tsup",
"test": "bun test"
},
"devDependencies": {
"@vercel/ncc": "^0.38.1",
Expand Down
6 changes: 4 additions & 2 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { BunPlugin, OnLoadResult, OnResolveResult, Transpiler } from "bun";
import { transformer } from "./transformer";

// no option for now
type WhatTheDepOptions = {};
export type WhatTheDepOptions = {
debug?: boolean;
};

function plugin(opts?: WhatTheDepOptions): BunPlugin {
return {
name: "what-the-dep",
setup(build) {
build.onLoad({ filter: /\.ts$/ }, async (args): Promise<OnLoadResult> => {
const transpiler = new Transpiler({ loader: "ts" });
const newSource = await transformer(args.path);
const newSource = await transformer(args.path, opts ?? {});
return {
contents: transpiler.transformSync(newSource),
};
Expand Down
8 changes: 6 additions & 2 deletions src/transformer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ts from "typescript";
import { transformerFactory } from "./walk";
import { WhatTheDepOptions } from ".";

const tsConfigPath = ts.findConfigFile(
"./",
Expand All @@ -19,14 +20,17 @@ if (!parsedConfig) {
const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
const printer = ts.createPrinter();

export const transformer = async (filePath: string): Promise<string> => {
export const transformer = async (
filePath: string,
config: WhatTheDepOptions
): Promise<string> => {
const sourceFile = program.getSourceFile(filePath);
if (!sourceFile)
return await require("fs").promises.readFile(filePath, "utf8");

const transformedSourceFile = ts.transform(
sourceFile,
[transformerFactory(program)],
[transformerFactory(program, config)],
parsedConfig.options
).transformed[0] as ts.SourceFile;

Expand Down
90 changes: 52 additions & 38 deletions src/walk/1-create-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,38 +36,51 @@ export const handleContainer = (
) {
// dig into expression to find the identifier
let expressionChildren = node.expression.getChildren();

while (expressionChildren[0] && !(ts.isNewExpression(expressionChildren[0]) || ts.isIdentifier(expressionChildren[0]))) {

while (
expressionChildren[0] &&
!(
ts.isNewExpression(expressionChildren[0]) ||
ts.isIdentifier(expressionChildren[0])
)
) {
expressionChildren = expressionChildren[0].getChildren();
}

if(expressionChildren[0] && ts.isNewExpression(expressionChildren[0])) {
const maybeWhatTheDepContainer = expressionChildren[0].getChildren()[1]
if(!maybeWhatTheDepContainer) {
return ts.visitEachChild(node, visitor, globalContext);
}

if(
!(ts.isIdentifier(maybeWhatTheDepContainer) &&
globalTypeChecker.getTypeAtLocation(maybeWhatTheDepContainer).getSymbol() === container)
) {
return ts.visitEachChild(node, visitor, globalContext);
}
if (
expressionChildren[0] &&
ts.isNewExpression(expressionChildren[0])
) {
const maybeWhatTheDepContainer =
expressionChildren[0].getChildren()[1];
if (!maybeWhatTheDepContainer) {
return ts.visitEachChild(node, visitor, globalContext);
}

expressionChildren[0] = maybeWhatTheDepContainer;
if (
!(
ts.isIdentifier(maybeWhatTheDepContainer) &&
globalTypeChecker
.getTypeAtLocation(maybeWhatTheDepContainer)
.getSymbol() === container
)
) {
return ts.visitEachChild(node, visitor, globalContext);
}


expressionChildren[0] = maybeWhatTheDepContainer;
}

if (
!isWhatTheDepMethod(expressionChildren[2]) ||
!expressionChildren[0]
) {
return node;
return ts.visitEachChild(node, visitor, globalContext);
}
if (expressionChildren[2].getText() === "get") {
const identifierSymbol = globalTypeChecker.getTypeAtLocation(
expressionChildren[0]
).getSymbol();
const identifierSymbol = globalTypeChecker
.getTypeAtLocation(expressionChildren[0])
.getSymbol();
if (identifierSymbol) {
if (ts.isVariableDeclaration(identifierSymbol.valueDeclaration!)) {
// case 1 of get
Expand All @@ -89,11 +102,11 @@ export const handleContainer = (
}
}
}
const symbol = globalTypeChecker.getTypeAtLocation(
expressionChildren[0]
).getSymbol();
const symbol = globalTypeChecker
.getTypeAtLocation(expressionChildren[0])
.getSymbol();
// make sure this is a method calling of the container
if (symbol === container) {
if (symbol === container) {
// this iterate through all register and registerSingleton
if (ts.isCallExpression(node)) {
// method calling expression structure is
Expand All @@ -119,13 +132,14 @@ export const handleContainer = (

const InterfaceOrClass = node.typeArguments![0];
const Class = node.typeArguments?.[1];
if (ts.isTypeReferenceNode(InterfaceOrClass)) {
// this type reference should have only one child
if (ts.isTypeReferenceNode(InterfaceOrClass)) {
// this type reference should have only one child
const identifier =
Class?.getChildren()[0] ?? InterfaceOrClass.getChildren()[0];

const OriginalClassSymbol =
globalTypeChecker.getTypeAtLocation(identifier).getSymbol() ;

const OriginalClassSymbol = globalTypeChecker
.getTypeAtLocation(identifier)
.getSymbol();

let dependencies: string[] = [];

Expand All @@ -145,25 +159,25 @@ export const handleContainer = (
if (Class) {
// declare class using interface hash
const classSymbol = globalTypeChecker
.getTypeAtLocation(InterfaceOrClass)
.getSymbol()
.getTypeAtLocation(InterfaceOrClass)
.getSymbol();
graph.register(
graphRegisterMethod,
classSymbol ? hashSymbol(
classSymbol
) : hashNode(InterfaceOrClass),
classSymbol
? hashSymbol(classSymbol)
: hashNode(InterfaceOrClass),
factoryNode,
Class.getText(),
dependencies
);
return node;
return ts.visitEachChild(node, visitor, globalContext);
}



graph.register(
graphRegisterMethod,
OriginalClassSymbol ? hashSymbol(OriginalClassSymbol) : hashNode(identifier),
OriginalClassSymbol
? hashSymbol(OriginalClassSymbol)
: hashNode(identifier),
factoryNode,
InterfaceOrClass.getText(),
dependencies
Expand Down
9 changes: 6 additions & 3 deletions src/walk/2-list-dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export const listDependenciesOfClass = (
.getSymbol()?.valueDeclaration;

if (!classDeclaration) {
if (!classSymbol?.name) {
throw new Error(`There are only type provided, please provide a factory`);
}
throw new Error(`Could not find class declaration of ${classSymbol.name}`);
}

Expand Down Expand Up @@ -41,10 +44,10 @@ export const listDependenciesOfClass = (

const type = globalTypeChecker.getTypeAtLocation(parameter.type);
const symbol = type.symbol ?? type.getSymbol();
let moduleHash: string
if( symbol ) {
let moduleHash: string;
if (symbol) {
moduleHash = hashSymbol(symbol);
}else{
} else {
moduleHash = hashNode(parameter.type);
}
if (symbol && moduleHash) {
Expand Down
4 changes: 2 additions & 2 deletions src/walk/4-create-context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ts from "typescript";
import { DependencyGraph } from "./graph";
import { globalTypeChecker } from ".";
import { globalConfig, globalTypeChecker } from ".";
import { hashSymbol } from "./utils";
import {
defaultFactoryTemplate,
Expand Down Expand Up @@ -39,7 +39,7 @@ export const createContextFromGraph = (graph: DependencyGraph) => {
});

properties.push(initSingletonsTemplate(graph.getSingletons()));

if (globalConfig.debug) graph.print();
const objectLiteral = ts.factory.createObjectLiteralExpression(
properties,
true
Expand Down
1 change: 1 addition & 0 deletions src/walk/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class DependencyGraph {
className: string,
dependencies: string[]
): void {
console.log("register", interfaceHash, className);
this.dependencies.set(interfaceHash, {
type,
dependencies,
Expand Down
64 changes: 51 additions & 13 deletions src/walk/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import * as ts from "typescript";
import { __WTD_MODULE__ } from "./constants";
import { handleContainer } from "./1-create-container";
import { WhatTheDepOptions } from "..";
import { handleGet } from "./3-modify-get";

let whatTheDepModule: ts.Symbol | undefined;

export let globalContext: ts.TransformationContext;
export let globalTypeChecker: ts.TypeChecker;
export let globalConfig: WhatTheDepOptions;

export const transformerFactory = (
program: ts.Program
program: ts.Program,
config: WhatTheDepOptions
): ts.TransformerFactory<ts.Node> => {
const transformList = new Map<ts.Node, ts.Node>();
globalConfig = config;
globalTypeChecker = program.getTypeChecker();
return (context) => (rootNode) => {
globalContext = context;
Expand Down Expand Up @@ -39,7 +44,9 @@ export const transformerFactory = (
handleContainer(
rootNode,
declaration.initializer.expression,
globalTypeChecker.getSymbolAtLocation(declaration.name)!,
globalTypeChecker
.getTypeAtLocation(declaration.name)
.getSymbol()!,
program,
transformList
);
Expand All @@ -53,7 +60,9 @@ export const transformerFactory = (
handleContainer(
rootNode,
declaration.initializer.expression,
globalTypeChecker.getSymbolAtLocation(declaration.name)!,
globalTypeChecker
.getTypeAtLocation(declaration.name)
.getSymbol()!,
program,
transformList
);
Expand All @@ -76,10 +85,14 @@ export const transformerFactory = (
if (ts.isNewExpression(expressionChildren[0].expression)) {
const containerNode =
expressionChildren[0].expression.getChildren()[1]!;
const containerSymbol = globalTypeChecker.getTypeAtLocation(
containerNode
).getSymbol();
if(containerSymbol?.valueDeclaration?.getText().includes(__WTD_MODULE__)) {
const containerSymbol = globalTypeChecker
.getTypeAtLocation(containerNode)
.getSymbol();
if (
containerSymbol?.valueDeclaration
?.getText()
.includes(__WTD_MODULE__)
) {
handleContainer(
rootNode,
containerNode,
Expand All @@ -92,12 +105,15 @@ export const transformerFactory = (
}
if (ts.isNewExpression(expressionChildren[0])) {
const containerNode = expressionChildren[0].getChildren()[1]!;
const containerSymbol = globalTypeChecker.getTypeAtLocation(
containerNode
).getSymbol();
const containerSymbol = globalTypeChecker
.getTypeAtLocation(containerNode)
.getSymbol();


if(containerSymbol?.valueDeclaration?.getText().includes(__WTD_MODULE__)) {
if (
containerSymbol?.valueDeclaration
?.getText()
.includes(__WTD_MODULE__)
) {
handleContainer(
rootNode,
containerNode,
Expand All @@ -113,6 +129,24 @@ export const transformerFactory = (
}
}
}

// Find a property access expression .get
if (ts.isPropertyAccessExpression(node)) {
// is .get
if (node.name.getText() === "get") {
const symbol = globalTypeChecker
.getTypeAtLocation(node.expression)
.getSymbol();
if (symbol) {
if (symbol?.valueDeclaration?.getText().includes(__WTD_MODULE__)) {
if (!ts.isCallExpression(node.parent)) {
return ts.visitEachChild(node, visitor, context);
}
handleGet(node.parent, transformList);
}
}
}
}
return ts.visitEachChild(node, visitor, context);
};

Expand All @@ -121,7 +155,11 @@ export const transformerFactory = (
if (transformList.size > 0) {
const transform = (node: ts.Node): ts.Node => {
if (transformList.has(node)) {
return transformList.get(node)!;
return ts.visitEachChild(
transformList.get(node)!,
transform,
context
);
}
return ts.visitEachChild(node, transform, context);
};
Expand Down
1 change: 1 addition & 0 deletions src/walk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const hashSymbol = (symbol: ts.Symbol) => {
.update(symbolName)
.digest("hex")
.substring(0, 8);

return hash;
};

Expand Down
Loading

0 comments on commit 753877e

Please sign in to comment.