cleanup old experiments and typescript-rtti, update readme
This commit is contained in:
parent
dea521029e
commit
cbb79d46fe
16 changed files with 68 additions and 518 deletions
|
@ -1,6 +1,3 @@
|
|||
import "reflect-metadata"
|
||||
import { reflect, type CallSite } from 'typescript-rtti'
|
||||
|
||||
/* A Dispatcher is a collection of operations that do run-time
|
||||
* dispatch on the types of their arguments. Thus, every individual
|
||||
* method is like a typed-function (from the library by that name),
|
||||
|
@ -51,15 +48,15 @@ export class Dispatcher {
|
|||
console.log('Pretending to install', name, signature, '=>', returns)
|
||||
|
||||
// @ts-ignore
|
||||
console.log(name, 'signature', reflect(signature))
|
||||
console.log(name, 'dependencies', reflect(dependencies))
|
||||
// console.log(name, 'signature', reflect(signature))
|
||||
// console.log(name, 'dependencies', reflect(dependencies))
|
||||
//TODO: implement me
|
||||
}
|
||||
installType(name: TypeName, typespec: TypeSpecification) {
|
||||
console.log('Pretending to install type', name, typespec)
|
||||
//TODO: implement me
|
||||
}
|
||||
constructor(collection: SpecificationsGroup) {
|
||||
constructor(collection: SpecificationsGroup) {
|
||||
for (const key in collection) {
|
||||
console.log('Working on', key)
|
||||
for (const identifier in collection[key]) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { typed } from '../generic/infer'
|
||||
|
||||
export const square = typed('__infer__', <T>(dep: {
|
||||
export const square = typed('square', '__infer__', <T>(dep: {
|
||||
multiply: (a: T, b: T) => T,
|
||||
unaryMinus: (x: T) => T, // just for the experiment
|
||||
}): (a: T) => T =>
|
|
@ -1,8 +0,0 @@
|
|||
import {infer} from '../generic/infer'
|
||||
|
||||
export const square = infer(<T>(dep: {
|
||||
multiply: (a: number, b: number) => number,
|
||||
unaryMinus: (x: number) => number, // just for the experiment
|
||||
}): (a: number) => number =>
|
||||
z => dep.multiply(z, z)
|
||||
)
|
|
@ -1,15 +0,0 @@
|
|||
import { infer } from '../generic/infer'
|
||||
import { Dependencies, Signature } from '../interfaces/type'
|
||||
|
||||
export type multiplyDep<T> = Dependencies<'multiply', T>
|
||||
|
||||
export const square1 =
|
||||
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
||||
z => dep.multiply(z, z)
|
||||
|
||||
export const square2 = infer(<T>(dep: {
|
||||
multiply: (a: number, b: number) => number,
|
||||
unaryMinus: (x: number) => number, // just for the experiment
|
||||
}): (a: number) => number =>
|
||||
z => dep.multiply(z, z)
|
||||
)
|
|
@ -1,16 +0,0 @@
|
|||
// @ts-check
|
||||
|
||||
/**
|
||||
* Function square
|
||||
*
|
||||
* Description of function square bla bla bla
|
||||
*
|
||||
* @param {{
|
||||
* multiply: (a: number, b: number) => number,
|
||||
* unaryMinus: (x: number) => number
|
||||
* }} dep
|
||||
* @return {(a: number) => number}
|
||||
*/
|
||||
export function square3 (dep) {
|
||||
return z => dep.multiply(z, z)
|
||||
}
|
|
@ -3,7 +3,12 @@ export function infer<T>(arg: T) : T {
|
|||
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')
|
||||
export function typed<T>(name: string, types: string, arg: T) : T {
|
||||
// TODO: implement typed-function for real
|
||||
if (types === '__infer__') {
|
||||
console.error('__infer__ should be replaced with runtime type information by the TypeScript plugin')
|
||||
}
|
||||
|
||||
console.log(`Creating typed-function "${name}" with types ${types}`)
|
||||
return arg
|
||||
}
|
||||
|
|
113
src/index.ts
113
src/index.ts
|
@ -1,99 +1,28 @@
|
|||
import 'reflect-metadata'
|
||||
import { Complex } from './Complex/type.js'
|
||||
import { absquare as absquare_complex } from './Complex/arithmetic.js'
|
||||
import { square } from './experiment/arithmeticInfer.js'
|
||||
|
||||
import {Dispatcher} from './core/Dispatcher.js'
|
||||
import * as specifications from './all.js'
|
||||
import {Complex} from './Complex/type.js'
|
||||
import {absquare as absquare_complex} from './Complex/arithmetic.js'
|
||||
|
||||
import { CallSite, ReflectedObjectRef, reflect } from 'typescript-rtti'
|
||||
import { square } from './generic/arithmetic.js'
|
||||
|
||||
// verify that typescript-rtti works (just as experiment)
|
||||
const add = (a: number, b: number): number => a + b
|
||||
console.log('reflect add')
|
||||
console.log('parameterNames', reflect(add).parameterNames)
|
||||
console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString()))
|
||||
console.log('returnType', reflect(add).returnType.toString())
|
||||
console.log()
|
||||
// output:
|
||||
// reflect function add
|
||||
// parameterNames [ 'a', 'b' ]
|
||||
// parameterTypes [ 'class Number', 'class Number' ]
|
||||
// returnType class Number
|
||||
|
||||
// try out a very simple case (just as experiment)
|
||||
function createSquare(deps: {
|
||||
multiply: (a: number, b: number) => number,
|
||||
subtract: (a: number, b: number) => number
|
||||
}) {
|
||||
return (a: number) => deps.multiply(a, a)
|
||||
}
|
||||
console.log('reflect createSquare')
|
||||
console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name))
|
||||
// console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter))
|
||||
// @ts-ignore
|
||||
console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m)
|
||||
console.log('parameterTypes[0].ref.m[0]',
|
||||
// @ts-ignore
|
||||
(reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n,
|
||||
// @ts-ignore
|
||||
(reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0]
|
||||
)
|
||||
console.log('parameterTypes[0].ref.m[0].t.m',
|
||||
// @ts-ignore
|
||||
(reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].t.m
|
||||
)
|
||||
// @ts-ignore
|
||||
// console.log('parameters[0]', reflect(createSquare).parameters[0])
|
||||
// FIXME: where to find the information of the types of the dependencies multiply and subtract?
|
||||
|
||||
// Test whether we loose the type information when casting to a generic interface
|
||||
// Conclusion: we keep the information, that is good.
|
||||
console.log()
|
||||
console.log('reflect createFunction')
|
||||
type MathjsDependencies = Record<string, Function>
|
||||
type MathjsCreateFunction = (deps: MathjsDependencies) => Function
|
||||
const createFunction: MathjsCreateFunction = createSquare as MathjsCreateFunction
|
||||
console.log('parameter names', reflect(createFunction).parameters.map(parameter => parameter.name))
|
||||
// @ts-ignore
|
||||
console.log('parameterTypes[0]', (reflect(createFunction).parameterTypes[0] as ReflectedObjectRef)._ref.m)
|
||||
|
||||
// TODO: more specific definition of Specifications
|
||||
type Specifications = Record<string, Record<string, unknown>>
|
||||
|
||||
console.log()
|
||||
console.log('CallSite')
|
||||
function reflectSpecifications<T>(specifications: Specifications, callSite? : CallSite) {
|
||||
console.log('specifications', reflect(callSite).parameters[0])
|
||||
// @ts-ignore
|
||||
console.log('specifications', reflect(callSite).parameters[0]._ref) // shows 'numbers', 'Complex, 'complex', 'generic'
|
||||
// @ts-ignore
|
||||
console.log('specifications', reflect(callSite).parameters[0]._ref.m
|
||||
.find(item => item.n === 'generic').t.m) // shows 'square', 'unequal'
|
||||
// @ts-ignore
|
||||
console.log('specifications', reflect(callSite).parameters[0]._ref.m
|
||||
.find(item => item.n === 'generic').t.m
|
||||
.find(item => item.n === 'square').t.p) // [ { n: 'dep', t: [Function: t], b: undefined, v: null } ]
|
||||
// @ts-ignore
|
||||
// FIXME: now, we should be able to get the signature of the multiply dependency of the function square, but how?
|
||||
}
|
||||
reflectSpecifications<Specifications>(specifications);
|
||||
|
||||
|
||||
// TODO: import all specifications (turned off for debugging purposes)
|
||||
// export default new Dispatcher(Specifications)
|
||||
|
||||
|
||||
const mockRealAdd = (a: number, b: number) => a+b
|
||||
const mockComplexAbsquare = (z: Complex<number>) => z.re*z.re + z.im*z.im
|
||||
const add = (a: number, b: number) => a + b
|
||||
const multiply = (a: number, b: number) => a * b
|
||||
const unaryMinus = (a: number) => -a
|
||||
const absquare = (z: Complex<number>) => z.re * z.re + z.im * z.im
|
||||
|
||||
const quatAbsquare = absquare_complex({
|
||||
add: mockRealAdd,
|
||||
absquare: mockComplexAbsquare
|
||||
add,
|
||||
absquare
|
||||
})
|
||||
|
||||
const myabs = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}})
|
||||
const typeTest: typeof myabs = 7 // check myabs is just a number
|
||||
const result = quatAbsquare({re: {re: 0, im: 1}, im: {re:2, im: 3}})
|
||||
const typeTest: typeof result = 7 // check myabs is just a number
|
||||
|
||||
console.log()
|
||||
console.log('Result is', myabs)
|
||||
console.log('Result is', result)
|
||||
|
||||
|
||||
const mySquare = square({
|
||||
multiply,
|
||||
unaryMinus
|
||||
})
|
||||
|
||||
console.log()
|
||||
console.log('mySquare(4)=', mySquare(4))
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import { readFileSync } from "fs";
|
||||
import * as ts from "typescript";
|
||||
import { inspect } from 'util'
|
||||
|
||||
export function infer(sourceFile: ts.SourceFile) {
|
||||
recurse(sourceFile);
|
||||
|
||||
function getType(kind: number) {
|
||||
switch(kind) {
|
||||
case ts.SyntaxKind.NumberKeyword: return 'number'
|
||||
case ts.SyntaxKind.StringKeyword: return 'string'
|
||||
case ts.SyntaxKind.BooleanKeyword: return 'boolean'
|
||||
default: return String(ts.SyntaxKind[kind]) // TODO: work out all types
|
||||
}
|
||||
}
|
||||
|
||||
function recurse(node: ts.Node) {
|
||||
if (node.kind === ts.SyntaxKind.Identifier) {
|
||||
console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind])
|
||||
}
|
||||
|
||||
// recognize a structure like:
|
||||
//
|
||||
// export const square = infer(<T>(dep: {
|
||||
// multiply: (a: number, b: number) => number
|
||||
// }): (a: number) => number =>
|
||||
// z => dep.multiply(z, z)
|
||||
// )
|
||||
if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') {
|
||||
// console.log('dep', getType(node['type'].kind), node)
|
||||
|
||||
node['type']?.members?.forEach(member => {
|
||||
console.log('member', {
|
||||
name: member.name.escapedText,
|
||||
parameters: member.type.parameters.map(parameter => {
|
||||
return parameter.name.escapedText + ': ' + getType(parameter.type.kind)
|
||||
}),
|
||||
returns: getType(member.type.type.kind)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// recognize a structure like:
|
||||
//
|
||||
// export const square =
|
||||
// <T>(dep: Dependencies<'multiply' | 'unaryMinus', T>): Signature<'square', T> =>
|
||||
// z => dep.multiply(z, z)
|
||||
if (node?.['name']?.kind === ts.SyntaxKind.Identifier && node?.['name']['escapedText'] === 'dep') {
|
||||
// TODO
|
||||
}
|
||||
|
||||
ts.forEachChild(node, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
const fileNames = process.argv.slice(2);
|
||||
console.log('infer files', fileNames)
|
||||
fileNames.forEach(fileName => {
|
||||
// Parse a file
|
||||
const sourceFile = ts.createSourceFile(
|
||||
fileName,
|
||||
readFileSync(fileName).toString(),
|
||||
ts.ScriptTarget.ES2022,
|
||||
/*setParentNodes */ true
|
||||
);
|
||||
|
||||
console.log('AST', fileName, inspect(sourceFile, { depth: null, colors: true }))
|
||||
|
||||
console.log(sourceFile.text)
|
||||
console.log()
|
||||
|
||||
infer(sourceFile);
|
||||
});
|
|
@ -1,74 +0,0 @@
|
|||
import * as ts from "typescript"
|
||||
|
||||
// based on: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker
|
||||
|
||||
infer2(process.argv.slice(2), {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
})
|
||||
|
||||
function infer2(
|
||||
fileNames: string[],
|
||||
options: ts.CompilerOptions
|
||||
): void {
|
||||
const program = ts.createProgram(fileNames, options)
|
||||
const typeChecker = program.getTypeChecker()
|
||||
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
if (!sourceFile.isDeclarationFile) {
|
||||
ts.forEachChild(sourceFile, visit)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
function visit(node: ts.Node) {
|
||||
// // Only consider exported nodes
|
||||
// if (!isNodeExported(node)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// console.log('Node', node.kind, node?.['name']?.escapedText)
|
||||
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
// This is a namespace, visit its children
|
||||
console.log('check')
|
||||
ts.forEachChild(node, visit);
|
||||
} else if (ts.isTypeAliasDeclaration(node)) {
|
||||
console.log('isTypeAliasDeclaration', node.name.escapedText)
|
||||
|
||||
let symbol = typeChecker.getSymbolAtLocation(node.name);
|
||||
if (symbol) {
|
||||
const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)
|
||||
const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call)
|
||||
|
||||
// checker.getResolvedSignature(symbol)
|
||||
console.log('symbol', symbol.getName(), symbolSignature)
|
||||
|
||||
// getTypeOfSymbolAtLocation
|
||||
// getResolvedSignature
|
||||
}
|
||||
} else if (ts.isCallExpression(node)) {
|
||||
console.log('isCallExpression', node.expression)
|
||||
} else if (ts.isFunctionDeclaration(node)) {
|
||||
console.log('isFunctionDeclaration', node.name.escapedText, { typeParameter0: node.typeParameters[0] })
|
||||
|
||||
if (node.name.escapedText === 'infer') {
|
||||
const param0 = node.typeParameters[0]
|
||||
if (ts.isPropertyDeclaration(param0)) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(param0)
|
||||
|
||||
// TODO: get resolving
|
||||
|
||||
// console.log('getResolvedSignature', typeChecker.getResolvedSignature(node) )
|
||||
|
||||
// const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)
|
||||
// const symbolSignature = typeChecker.getSignaturesOfType(symbolType, ts.SignatureKind.Call)
|
||||
// console.log('symbol', symbol.getName(), symbolSignature)
|
||||
|
||||
// console.log('getSignaturesOfType', typeChecker.getSignaturesOfType(param0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
import { readFileSync } from "fs";
|
||||
import * as ts from "typescript";
|
||||
import { inspect } from 'util'
|
||||
|
||||
export function infer3(sourceFile: ts.SourceFile) {
|
||||
recurse(sourceFile);
|
||||
|
||||
function getType(kind: number) {
|
||||
switch(kind) {
|
||||
case ts.SyntaxKind.NumberKeyword: return 'number'
|
||||
case ts.SyntaxKind.StringKeyword: return 'string'
|
||||
case ts.SyntaxKind.BooleanKeyword: return 'boolean'
|
||||
case ts.SyntaxKind.JSDoc: return 'jsdoc'
|
||||
default: return String(ts.SyntaxKind[kind]) // TODO: work out all types
|
||||
}
|
||||
}
|
||||
|
||||
function recurse(node: ts.Node) {
|
||||
if (node.kind === ts.SyntaxKind.Identifier) {
|
||||
console.log('Identifier', node['escapedText'], ts.SyntaxKind[node.kind])
|
||||
}
|
||||
|
||||
if (node['jsDoc']) {
|
||||
console.log('Found a JSDoc comment:')
|
||||
// console.log(inspect(node['jsDoc'], { depth: null, colors: true }))
|
||||
|
||||
const fullComment = sourceFile.text.slice(node.pos, node.end)
|
||||
console.log(fullComment)
|
||||
// TODO: next steps:
|
||||
// - either get the types from the TypeScript AST,
|
||||
// or extract them ourselves with regex or anything from the comment text
|
||||
// - After that, we have to transform the source file and insert the comments
|
||||
// as string or object that is runtime accessible in JavaScript
|
||||
}
|
||||
|
||||
ts.forEachChild(node, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
const fileNames = process.argv.slice(2);
|
||||
console.log('infer files', fileNames)
|
||||
fileNames.forEach(fileName => {
|
||||
// Parse a file
|
||||
const sourceFile = ts.createSourceFile(
|
||||
fileName,
|
||||
readFileSync(fileName).toString(),
|
||||
ts.ScriptTarget.ES2022,
|
||||
/*setParentNodes */ true
|
||||
);
|
||||
|
||||
console.log('FILE')
|
||||
console.log(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')
|
||||
infer3(sourceFile);
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
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);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import * as ts from 'typescript';
|
||||
import 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?
|
||||
|
@ -7,24 +7,23 @@ const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
|
|||
|
||||
return sourceFile => {
|
||||
// For the experiment we only want to influence a single file
|
||||
if (!sourceFile.fileName.endsWith('arithmeticInfer4.ts')) {
|
||||
if (!sourceFile.fileName.endsWith('arithmeticInfer.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)
|
||||
console.log('FOUND AN OCCURRENCE OF __infer__')
|
||||
|
||||
const parentNode = node.parent
|
||||
console.log('PARENT')
|
||||
console.log(parentNode)
|
||||
// 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 argNode = parentNode.arguments[2]
|
||||
const returnType = argNode.type.getText(sourceFile)
|
||||
console.log('RETURN TYPE')
|
||||
console.log(returnType)
|
||||
|
@ -48,11 +47,11 @@ const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
|
|||
return ts.factory.createStringLiteral(depsAndReturnType)
|
||||
}
|
||||
|
||||
return ts.visitEachChild(node, visitor, context);
|
||||
};
|
||||
return ts.visitEachChild(node, visitor, context)
|
||||
}
|
||||
|
||||
return ts.visitNode(sourceFile, visitor);
|
||||
};
|
||||
};
|
||||
return ts.visitNode(sourceFile, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
export default transformer;
|
||||
export default transformer
|
Loading…
Add table
Add a link
Reference in a new issue