@ealmansi/jagger
Advanced tools
Comparing version 0.1.29 to 0.1.30
@@ -8,3 +8,4 @@ import ts from "typescript"; | ||
modules: ts.ClassDeclaration[]; | ||
moduleImports: Map<ts.ClassDeclaration, ts.ClassDeclaration[]>; | ||
moduleIncludedModules: Map<ts.ClassDeclaration, ts.ClassDeclaration[]>; | ||
moduleRequiredTypes: Map<ts.ClassDeclaration, Set<ts.Type>>; | ||
moduleProviders: Map<ts.ClassDeclaration, (ts.PropertyDeclaration | ts.MethodDeclaration)[]>; | ||
@@ -11,0 +12,0 @@ providerModule: Map<ts.PropertyDeclaration | ts.MethodDeclaration, ts.ClassDeclaration>; |
@@ -21,6 +21,8 @@ import assert from "node:assert/strict"; | ||
} | ||
const moduleImports = new Map(); | ||
const moduleIncludedModules = new Map(); | ||
const moduleRequiredTypes = new Map(); | ||
const moduleProviders = new Map(); | ||
for (const module of modules) { | ||
moduleImports.set(module, getModuleImports(program, module)); | ||
moduleIncludedModules.set(module, getModuleIncludedModules(program, module)); | ||
moduleRequiredTypes.set(module, getModuleRequiredTypes(program, module)); | ||
moduleProviders.set(module, getModuleProviders(program, module)); | ||
@@ -46,3 +48,4 @@ } | ||
modules, | ||
moduleImports, | ||
moduleIncludedModules, | ||
moduleRequiredTypes, | ||
moduleProviders, | ||
@@ -139,3 +142,3 @@ providerModule, | ||
} | ||
function getModuleImports(program, module) { | ||
function getModuleIncludedModules(program, module) { | ||
const typeChecker = program.getTypeChecker(); | ||
@@ -147,3 +150,3 @@ return module.members | ||
ts.isIdentifier(propertyDeclaration.name) && | ||
propertyDeclaration.name.text === "imports") | ||
propertyDeclaration.name.text === "includes") | ||
.map((propertyDeclaration) => propertyDeclaration.type) | ||
@@ -161,2 +164,19 @@ .filter(isNotUndefined) | ||
} | ||
function getModuleRequiredTypes(program, module) { | ||
const typeChecker = program.getTypeChecker(); | ||
const types = module.members | ||
.filter(ts.isPropertyDeclaration) | ||
.filter((propertyDeclaration) => propertyDeclaration.modifiers !== undefined && | ||
propertyDeclaration.modifiers.some((modifierLike) => modifierLike.kind === ts.SyntaxKind.StaticKeyword) && | ||
ts.isIdentifier(propertyDeclaration.name) && | ||
propertyDeclaration.name.text === "requires") | ||
.map((propertyDeclaration) => propertyDeclaration.type) | ||
.filter(isNotUndefined) | ||
.filter(ts.isTupleTypeNode) | ||
.flatMap((tupleTypeNode) => tupleTypeNode.elements) | ||
.filter(ts.isTypeReferenceNode) | ||
.map((typeReferenceNode) => typeReferenceNode.typeName) | ||
.map(typeChecker.getTypeAtLocation); | ||
return new Set(types); | ||
} | ||
function getModuleProviders(program, module) { | ||
@@ -163,0 +183,0 @@ const typeChecker = program.getTypeChecker(); |
@@ -0,1 +1,46 @@ | ||
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
if (value !== null && value !== void 0) { | ||
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); | ||
var dispose; | ||
if (async) { | ||
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); | ||
dispose = value[Symbol.asyncDispose]; | ||
} | ||
if (dispose === void 0) { | ||
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); | ||
dispose = value[Symbol.dispose]; | ||
} | ||
if (typeof dispose !== "function") throw new TypeError("Object not disposable."); | ||
env.stack.push({ value: value, dispose: dispose, async: async }); | ||
} | ||
else if (async) { | ||
env.stack.push({ async: true }); | ||
} | ||
return value; | ||
}; | ||
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { | ||
return function (env) { | ||
function fail(e) { | ||
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; | ||
env.hasError = true; | ||
} | ||
function next() { | ||
while (env.stack.length) { | ||
var rec = env.stack.pop(); | ||
try { | ||
var result = rec.dispose && rec.dispose.call(rec.value); | ||
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); | ||
} | ||
catch (e) { | ||
fail(e); | ||
} | ||
} | ||
if (env.hasError) throw env.error; | ||
} | ||
return next(); | ||
}; | ||
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { | ||
var e = new Error(message); | ||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; | ||
}); | ||
import ts from "typescript"; | ||
@@ -18,4 +63,4 @@ import assert from "node:assert/strict"; | ||
const moduleStack = [module]; | ||
const moduleTypeMap = new Map(); | ||
const returnTypeResolutions = Array.from(getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, returnType, 0)); | ||
const moduleUnresolvedTypes = new Map(); | ||
const returnTypeResolutions = Array.from(getTypeResolutions(typeChecker, graph, moduleStack, returnType, moduleUnresolvedTypes)); | ||
if (returnTypeResolutions.length === 0) { | ||
@@ -64,76 +109,143 @@ assert.fail([ | ||
} | ||
function* getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, type, level) { | ||
if (moduleStack.length === 0) { | ||
return; | ||
function* getTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes) { | ||
const env_1 = { stack: [], error: void 0, hasError: false }; | ||
try { | ||
if (moduleStack.length === 0) { | ||
return; | ||
} | ||
const module = orThrow(moduleStack.at(-1)); | ||
if (!moduleUnresolvedTypes.has(module)) { | ||
moduleUnresolvedTypes.set(module, new Set()); | ||
} | ||
const unresolvedTypes = orThrow(moduleUnresolvedTypes.get(module)); | ||
if (unresolvedTypes.has(type)) { | ||
return; | ||
} | ||
unresolvedTypes.add(type); | ||
const _ = __addDisposableResource(env_1, { | ||
[Symbol.dispose]: () => { | ||
unresolvedTypes.delete(type); | ||
if (unresolvedTypes.size === 0) { | ||
moduleUnresolvedTypes.delete(module); | ||
} | ||
}, | ||
}, false); | ||
yield* getProviderTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
yield* getSetTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
yield* getImportedTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
const requiredTypes = orThrow(graph.moduleRequiredTypes.get(module)); | ||
if (requiredTypes.has(type)) { | ||
yield* getParentTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
} | ||
} | ||
const module = orThrow(moduleStack.at(-1)); | ||
if (!moduleTypeMap.has(module)) { | ||
moduleTypeMap.set(module, new WeakSet()); | ||
catch (e_1) { | ||
env_1.error = e_1; | ||
env_1.hasError = true; | ||
} | ||
if (orThrow(moduleTypeMap.get(module)).has(type)) { | ||
return; | ||
finally { | ||
__disposeResources(env_1); | ||
} | ||
orThrow(moduleTypeMap.get(module)).add(type); | ||
} | ||
function* getProviderTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes) { | ||
const module = orThrow(moduleStack.at(-1)); | ||
const providers = orThrow(graph.moduleProviders.get(module)); | ||
const importedModules = orThrow(graph.moduleImports.get(module)); | ||
for (const provider of providers) { | ||
const returnType = orThrow(graph.providerReturnType.get(provider)); | ||
if (returnType === type) { | ||
const parameterTypes = orThrow(graph.providerParameterTypes.get(provider)); | ||
const parameterTypeResolutions = []; | ||
for (const parameterType of parameterTypes) { | ||
const parameterTypeResolution = Array.from(getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, parameterType, level + 1)).at(0); | ||
if (parameterTypeResolution === undefined) { | ||
console.log([ | ||
" ".repeat(level), | ||
"could not satisfy ", | ||
typeChecker.typeToString(parameterType), | ||
" for ", | ||
provider.name.getText(), | ||
" in ", | ||
orThrow(module.name).text, | ||
].join("")); | ||
break; | ||
} | ||
parameterTypeResolutions.push(parameterTypeResolution); | ||
} | ||
if (parameterTypes.length === parameterTypeResolutions.length) { | ||
yield { | ||
kind: "ProviderTypeResolution", | ||
type, | ||
module, | ||
provider, | ||
parameterTypeResolutions, | ||
}; | ||
} | ||
if (!typesAreConsideredEqual(typeChecker, returnType, type)) { | ||
continue; | ||
} | ||
} | ||
if (type.symbol.getName() === "Set") { | ||
if (type.getFlags() & ts.TypeFlags.Object) { | ||
const objectType = type; | ||
if (objectType.objectFlags & ts.ObjectFlags.Reference) { | ||
const typeReference = type; | ||
const typeArguments = typeChecker.getTypeArguments(typeReference); | ||
if (typeArguments.length === 1) { | ||
const typeArgument = orThrow(typeArguments.at(0)); | ||
yield { | ||
kind: "SetTypeResolution", | ||
type, | ||
module, | ||
elementTypeResolutions: Array.from(getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, typeArgument, level + 1)), | ||
}; | ||
} | ||
const parameterTypes = orThrow(graph.providerParameterTypes.get(provider)); | ||
const parameterTypeResolutions = []; | ||
for (const parameterType of parameterTypes) { | ||
const typeResolutions = Array.from(getTypeResolutions(typeChecker, graph, moduleStack, parameterType, moduleUnresolvedTypes)); | ||
if (typeResolutions.length === 0) { | ||
console.log([ | ||
"could not satisfy ", | ||
typeChecker.typeToString(parameterType), | ||
" for ", | ||
provider.name.getText(), | ||
" in ", | ||
orThrow(module.name).text, | ||
].join("")); | ||
break; | ||
} | ||
const typeResolution = orThrow(typeResolutions.at(0)); | ||
parameterTypeResolutions.push(typeResolution); | ||
} | ||
if (parameterTypes.length !== parameterTypeResolutions.length) { | ||
continue; | ||
} | ||
yield { | ||
kind: "ProviderTypeResolution", | ||
type, | ||
module, | ||
provider, | ||
parameterTypeResolutions, | ||
}; | ||
} | ||
for (const importedModule of importedModules) { | ||
moduleStack.push(importedModule); | ||
yield* getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, type, level + 1); | ||
} | ||
function* getSetTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes) { | ||
const module = orThrow(moduleStack.at(-1)); | ||
const typeSymbol = type.getSymbol(); | ||
if (typeSymbol === undefined) { | ||
return; | ||
} | ||
if (typeSymbol.getName() !== "Set") { | ||
return; | ||
} | ||
if ((type.getFlags() & ts.TypeFlags.Object) === 0) { | ||
return; | ||
} | ||
const objectType = type; | ||
if ((objectType.objectFlags & ts.ObjectFlags.Reference) === 0) { | ||
return; | ||
} | ||
const typeReference = type; | ||
const typeArguments = typeChecker.getTypeArguments(typeReference); | ||
if (typeArguments.length !== 1) { | ||
return; | ||
} | ||
const typeArgument = orThrow(typeArguments.at(0)); | ||
const elementTypeResolutions = Array.from(getTypeResolutions(typeChecker, graph, moduleStack, typeArgument, moduleUnresolvedTypes)); | ||
yield { | ||
kind: "SetTypeResolution", | ||
type, | ||
module, | ||
elementTypeResolutions, | ||
}; | ||
} | ||
function* getImportedTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes) { | ||
const module = orThrow(moduleStack.at(-1)); | ||
const incudedModules = orThrow(graph.moduleIncludedModules.get(module)); | ||
for (const includedModule of incudedModules) { | ||
moduleStack.push(includedModule); | ||
yield* getTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
moduleStack.pop(); | ||
} | ||
} | ||
function* getParentTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes) { | ||
const module = orThrow(moduleStack.at(-1)); | ||
moduleStack.pop(); | ||
yield* getTypeResolutions(typeChecker, graph, moduleStack, moduleTypeMap, type, level + 1); | ||
yield* getTypeResolutions(typeChecker, graph, moduleStack, type, moduleUnresolvedTypes); | ||
moduleStack.push(module); | ||
orThrow(moduleTypeMap.get(module)).delete(type); | ||
} | ||
function typesAreConsideredEqual(typeChecker, typeA, typeB) { | ||
if (typeA === typeB) { | ||
return true; | ||
} | ||
for (const type of [typeA, typeB]) { | ||
if ((type.getFlags() & ts.TypeFlags.Any) !== 0) { | ||
return false; | ||
} | ||
if ((type.getFlags() & ts.TypeFlags.EnumLike) !== 0) { | ||
return false; | ||
} | ||
if ((type.getFlags() & ts.TypeFlags.Object) !== 0 && | ||
(type.objectFlags & ts.ObjectFlags.Class) !== 0) { | ||
return false; | ||
} | ||
} | ||
return (typeChecker.isTypeAssignableTo(typeA, typeB) && | ||
typeChecker.isTypeAssignableTo(typeB, typeA)); | ||
} | ||
function getTypeResolutionModules(typeResolution) { | ||
@@ -140,0 +252,0 @@ const modules = new Set(); |
{ | ||
"name": "@ealmansi/jagger", | ||
"version": "0.1.29", | ||
"version": "0.1.30", | ||
"description": "WIP", | ||
@@ -5,0 +5,0 @@ "files": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
85295
47
953