Get a real TypeScript plugin working
This commit is contained in:
parent
2cb8bc0099
commit
dea521029e
@ -50,6 +50,7 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful
|
|||||||
pnpm experiment:infer1-direct
|
pnpm experiment:infer1-direct
|
||||||
pnpm experiment:infer2
|
pnpm experiment:infer2
|
||||||
pnpm experiment:infer3
|
pnpm experiment:infer3
|
||||||
|
pnpm experiment:infer4
|
||||||
|
|
||||||
### Read more
|
### Read more
|
||||||
|
|
||||||
@ -61,3 +62,8 @@ None of the experiments (`infer1` and `infer2`) are outputting something useful
|
|||||||
- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function
|
- https://stackoverflow.com/questions/48886508/typechecker-api-how-do-i-find-inferred-type-arguments-to-a-function
|
||||||
- https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/
|
- https://blog.logrocket.com/using-typescript-transforms-to-enrich-runtime-code-3fd2863221ed/
|
||||||
- https://github.com/itsdouges/typescript-transformer-handbook#transforms
|
- https://github.com/itsdouges/typescript-transformer-handbook#transforms
|
||||||
|
|
||||||
|
### Interesting libraries
|
||||||
|
|
||||||
|
- https://github.com/GoogleFeud/ts-macros/
|
||||||
|
- https://ts-morph.com
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts',
|
'experiment:infer1-direct': 'ttsc -b && node build/plugins/infer1.js ./src/experiment/arithmeticInfer1.ts',
|
||||||
'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts',
|
'experiment:infer2': 'ttsc -b && node build/plugins/infer2.js ./src/experiment/arithmeticInfer2.ts',
|
||||||
'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js',
|
'experiment:infer3': 'ttsc -b && node build/plugins/infer3.js ./src/experiment/arithmeticInfer3.js',
|
||||||
|
'experiment:infer4': 'ttsc -b && node build/plugins/infer4.js ./src/experiment/arithmeticInfer4.ts',
|
||||||
|
'experiment:infer4:plugin': 'ttsc -b',
|
||||||
test: 'echo "Error: no test specified" && exit 1',
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
},
|
},
|
||||||
keywords: [
|
keywords: [
|
||||||
|
8
src/experiment/arithmeticInfer4.ts
Normal file
8
src/experiment/arithmeticInfer4.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { typed } from '../generic/infer'
|
||||||
|
|
||||||
|
export const square = typed('__infer__', <T>(dep: {
|
||||||
|
multiply: (a: T, b: T) => T,
|
||||||
|
unaryMinus: (x: T) => T, // just for the experiment
|
||||||
|
}): (a: T) => T =>
|
||||||
|
z => dep.multiply(z, z)
|
||||||
|
)
|
@ -2,3 +2,8 @@ export function infer<T>(arg: T) : T {
|
|||||||
console.error('infer should be replaced with runtime type information by a magic TypeScript plugin')
|
console.error('infer should be replaced with runtime type information by a magic TypeScript plugin')
|
||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function typed<T>(dep: string, arg: T) : T {
|
||||||
|
console.error('infer should be replaced with runtime type information by a magic TypeScript plugin')
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
75
src/plugins/infer4.ts
Normal file
75
src/plugins/infer4.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { readFileSync } from "fs";
|
||||||
|
import * as ts from "typescript";
|
||||||
|
import { inspect } from 'util'
|
||||||
|
|
||||||
|
export function infer(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) {
|
||||||
|
recurse(sourceFile);
|
||||||
|
|
||||||
|
function recurse(node: ts.Node) {
|
||||||
|
if (ts.isCallExpression(node)) {
|
||||||
|
if (ts.isIdentifier(node.expression) && node.expression.escapedText === 'infer') {
|
||||||
|
const argNode = node.arguments[0]
|
||||||
|
|
||||||
|
if (ts.isArrowFunction(argNode)) {
|
||||||
|
const text = sourceFile.text.slice(argNode.pos, argNode.end)
|
||||||
|
console.log('infer', text)
|
||||||
|
|
||||||
|
console.log('AST')
|
||||||
|
console.log(node)
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
const returnType = argNode.type.getText(sourceFile)
|
||||||
|
console.log('returnType', returnType)
|
||||||
|
// (a: number) => number
|
||||||
|
|
||||||
|
const paramNode = argNode.parameters[0]
|
||||||
|
|
||||||
|
const paramType = paramNode.type.getText(sourceFile)
|
||||||
|
console.log('paramType', paramType) // (a: number) => number
|
||||||
|
// {
|
||||||
|
// multiply: (a: number, b: number) => number,
|
||||||
|
// unaryMinus: (x: number) => number, // just for the experiment
|
||||||
|
// }
|
||||||
|
|
||||||
|
const type = typeChecker.getTypeAtLocation(paramNode)
|
||||||
|
const typeStr = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias)
|
||||||
|
console.log('paramTypeString', typeStr)
|
||||||
|
// { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.forEachChild(node, recurse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileNames = process.argv.slice(2);
|
||||||
|
console.log('infer files', fileNames)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
target: ts.ScriptTarget.ES2022,
|
||||||
|
module: ts.ModuleKind.ES2022
|
||||||
|
}
|
||||||
|
let program = ts.createProgram(fileNames, options);
|
||||||
|
const checker = program.getTypeChecker()
|
||||||
|
|
||||||
|
for (const sourceFile of program.getSourceFiles()) {
|
||||||
|
if (!sourceFile.isDeclarationFile) {
|
||||||
|
console.log('FILE')
|
||||||
|
console.log(sourceFile.fileName)
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
console.log('SOURCE')
|
||||||
|
console.log(sourceFile.text)
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
// console.log('AST')
|
||||||
|
// console.log(inspect(sourceFile, { depth: null, colors: true }))
|
||||||
|
// console.log()
|
||||||
|
|
||||||
|
console.log('INFER')
|
||||||
|
infer(sourceFile, checker);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
58
src/plugins/myFirstPlugin.ts
Normal file
58
src/plugins/myFirstPlugin.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
|
||||||
|
// TODO: get a reference to the program instance that the plugin is running in instead of creating a new program?
|
||||||
|
const program = ts.createProgram([], {})
|
||||||
|
const checker = program.getTypeChecker()
|
||||||
|
|
||||||
|
return sourceFile => {
|
||||||
|
// For the experiment we only want to influence a single file
|
||||||
|
if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) {
|
||||||
|
return sourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
const visitor = (node: ts.Node): ts.Node => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (ts.isStringLiteral(node) && node.text === '__infer__') {
|
||||||
|
console.log('STRING LITERAL', node.text)
|
||||||
|
console.log(node)
|
||||||
|
|
||||||
|
const parentNode = node.parent
|
||||||
|
console.log('PARENT')
|
||||||
|
console.log(parentNode)
|
||||||
|
|
||||||
|
// TODO: validate that the parent is indeed a mathjs typed function with deps
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const argNode = parentNode.arguments[1]
|
||||||
|
const returnType = argNode.type.getText(sourceFile)
|
||||||
|
console.log('RETURN TYPE')
|
||||||
|
console.log(returnType)
|
||||||
|
// (a: number) => number
|
||||||
|
|
||||||
|
const paramNode = argNode.parameters[0]
|
||||||
|
const paramTypeSrc = paramNode.type.getText(sourceFile)
|
||||||
|
console.log('PARAM TYPE SRC', paramTypeSrc)
|
||||||
|
// {
|
||||||
|
// multiply: (a: number, b: number) => number,
|
||||||
|
// unaryMinus: (x: number) => number, // just for the experiment
|
||||||
|
// }
|
||||||
|
|
||||||
|
const type = checker.getTypeAtLocation(paramNode)
|
||||||
|
const paramType = checker.typeToString(type, paramNode, ts.TypeFormatFlags.InTypeAlias)
|
||||||
|
console.log('PARAM TYPE STRING', paramType)
|
||||||
|
// { multiply: (a: number, b: number) => number; unaryMinus: (x: number) => number; }
|
||||||
|
|
||||||
|
const depsAndReturnType = `{ deps: ${paramType}; return: ${returnType} }`
|
||||||
|
|
||||||
|
return ts.factory.createStringLiteral(depsAndReturnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.visitEachChild(node, visitor, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
return ts.visitNode(sourceFile, visitor);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default transformer;
|
@ -8,10 +8,9 @@
|
|||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"plugins": [
|
"plugins": [{
|
||||||
{
|
"transform": "./src/plugins/myFirstPlugin.ts",
|
||||||
"transform": "typescript-rtti/dist/transformer"
|
"type": "raw"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user