diff options
Diffstat (limited to 'src/context.ts')
| -rw-r--r-- | src/context.ts | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..35fa1ae --- /dev/null +++ b/src/context.ts @@ -0,0 +1,80 @@ +import { Project, ScriptTarget, Type, Node, SyntaxKind } from 'ts-morph' +import path from 'path' +import { v4 as uuid } from 'uuid'; +import { Ctx } from './types'; + +const RESULT_TYPE_NAME = '__$result' + +export const createContext = (): Ctx => { + const project = new Project({ + compilerOptions: { + target: ScriptTarget.ES3, + }, + }) + + const typeChecker = project.getTypeChecker() + + const [filePath] = process.argv.slice(2) + + if (!filePath) { + throw new Error('Must specify ts file to run') + } + + const sourceFile = project.addSourceFileAtPath(path.resolve(filePath)) + + const entryPoint = sourceFile.getExportedDeclarations().get('main')?.[0] + + if (!entryPoint) { + throw new Error('No "main" entrypoint defined in source file') + } + + const typeToString = (ty: Type | undefined): string => + ty ? typeChecker.compilerObject.typeToString(ty.compilerType) : '' + + const [resultTypeNode] = sourceFile.addStatements(`type ${RESULT_TYPE_NAME} = {}`) + + const getResultExpr = (resultKey?: string) => `${RESULT_TYPE_NAME}[${JSON.stringify(resultKey)}` + + const addResult = (name: string, ty: string): Node | undefined => + resultTypeNode + ?.asKind(SyntaxKind.TypeAliasDeclaration) + ?.getChildAtIndexIfKind(3, SyntaxKind.TypeLiteral) + ?.addProperty({ + name: JSON.stringify(name), + type: `{ output: ${ty} }`, + }) + + const createResult = (ty: string): [string, Node | undefined] => { + const resultKey = uuid() + const node = addResult(resultKey, ty) + return [resultKey, node] + } + + const customEffects: Record<string, (...args: Type[]) => any> = {} + + return { + sourceFile, + typeChecker, + entryPoint, + typeToString, + + createResult, + getResultExpr, + printResultNode: () => console.log(resultTypeNode?.print()), + + addCustomEffect: (name, expr) => { + const func = eval(expr) + Object.assign(customEffects, { [name]: func }) + }, + runCustomEffect: async (name, args) => { + const output = await customEffects[name]?.(...args) + if (output) { + const [resultKey, _] = createResult(`${JSON.stringify(output)}`) + return [resultKey] + } + return [] + }, + hasCustomEffect: (name) => customEffects[name] !== undefined, + } +} + |
