Skip to content

Instantly share code, notes, and snippets.

@sheepla
Last active July 12, 2024 12:37
Show Gist options
  • Save sheepla/b0a0bfc8e8d42bcd3d8e40efaeb3731d to your computer and use it in GitHub Desktop.
Save sheepla/b0a0bfc8e8d42bcd3d8e40efaeb3731d to your computer and use it in GitHub Desktop.
Parse TypeScript AST in Deno
import * as ts from "https://esm.sh/typescript";
interface BaseNode {
pos: number;
end: number;
kind: number;
id: number;
flags: number;
modifierFlagsCache: number;
transformFlags: number;
}
interface IdentifierNode extends BaseNode {
kind: 80;
escapedText: string;
}
interface ParameterNode extends BaseNode {
kind: 169;
name: IdentifierNode;
}
interface ArgumentNode extends BaseNode {
expression: ExpressionNode;
}
interface ExpressionNode extends BaseNode {
expression: IdentifierNode | BinaryExpressionNode;
arguments?: ArgumentNode[];
}
interface BinaryExpressionNode extends BaseNode {
left: StringLiteralNode;
operatorToken: TokenNode;
right: IdentifierNode;
}
interface StringLiteralNode extends BaseNode {
kind: 11;
text: string;
hasExtendedUnicodeEscape: boolean;
}
interface TokenNode extends BaseNode {
kind: number;
}
interface StatementNode extends BaseNode {
name: IdentifierNode;
parameters: ParameterNode[];
body: BlockNode;
}
interface BlockNode extends BaseNode {
statements: ExpressionStatementNode[];
multiLine: boolean;
}
interface ExpressionStatementNode extends BaseNode {
expression: ExpressionNode;
}
interface Ast extends BaseNode {
statements: StatementNode[];
endOfFileToken: TokenNode;
text: string;
fileName: string;
path: string;
resolvedPath: string;
originalFileName: string;
languageVersion: number;
languageVariant: number;
scriptKind: number;
isDeclarationFile: boolean;
hasNoDefaultLib: boolean;
nodeCount: number;
identifierCount: number;
symbolCount: number;
parseDiagnostics: any[];
bindDiagnostics: any[];
pragmas: any;
referencedFiles: any[];
typeReferenceDirectives: any[];
libReferenceDirectives: any[];
amdDependencies: any[];
identifiers: any;
jsDocParsingMode: number;
}
async function parseSourceCode(sourceCode: string): Promise<Ast> {
const srcFile = ts.createSourceFile(
"example.ts",
sourceCode,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS
);
const serializer = () => {
const seen = new WeakSet();
return (_: any, value: any) => {
if (typeof value === "object" && value !== null) {
const containsCicularRef = seen.has(value)
if (containsCicularRef) {
return;
}
seen.add(value);
}
return value;
};
};
return JSON.parse(JSON.stringify(srcFile, serializer()));
}
async function parseSourceFile(path: string): Promise<Ast> {
const text = await Deno.readTextFile(path);
return parseSourceCode(text);
}
const ast = await parseSourceFile(Deno.args[0])
console.log(ast);
@sheepla
Copy link
Author

sheepla commented Jul 12, 2024

Run

deno run --allow-env --allow-read parse_ts_ast.ts example.ts

Result:

{
  pos: 0,
  end: 81,
  kind: 307,
  id: 0,
  flags: 0,
  modifierFlagsCache: 0,
  transformFlags: 0,
  statements: [
    {
      pos: 0,
      end: 58,
      kind: 262,
      id: 0,
      flags: 0,
      modifierFlagsCache: 0,
      transformFlags: 4194304,
      name: {
        pos: 9,
        end: 15,
        kind: 80,
        id: 0,
        flags: 0,
        transformFlags: 0,
        escapedText: "greet"
      },
      parameters: [
        {
          pos: 16,
          end: 20,
          kind: 169,
          id: 0,
          flags: 0,
          modifierFlagsCache: 0,
          transformFlags: 0,
          name: [Object]
        }
      ],
      body: {
        pos: 21,
        end: 58,
        kind: 241,
        id: 0,
        flags: 0,
        modifierFlagsCache: 0,
        transformFlags: 0,
        statements: [ [Object] ],
        multiLine: true
      }
    },
    {
      pos: 58,
      end: 80,
      kind: 244,
      id: 0,
      flags: 0,
      modifierFlagsCache: 0,
      transformFlags: 0,
      expression: {
        pos: 58,
        end: 79,
        kind: 213,
        id: 0,
        flags: 0,
        modifierFlagsCache: 0,
        transformFlags: 0,
        expression: {
          pos: 58,
          end: 65,
          kind: 80,
          id: 0,
          flags: 0,
          transformFlags: 0,
          escapedText: "greet"
        },
        arguments: [ [Object] ]
      }
    }
  ],
  endOfFileToken: { pos: 80, end: 81, kind: 1, id: 0, flags: 0, transformFlags: 0 },
  text: "\n" +
    "function greet(name) {\n" +
    "  console.log('Hello, ' + name);\n" +
    "}\n" +
    "\n" +
    "greet('JavaScript');\n",
  fileName: "example.ts",
  path: "",
  resolvedPath: "",
  originalFileName: "",
  languageVersion: 99,
  languageVariant: 0,
  scriptKind: 3,
  isDeclarationFile: false,
  hasNoDefaultLib: false,
  nodeCount: 20,
  identifierCount: 6,
  symbolCount: 0,
  parseDiagnostics: [],
  bindDiagnostics: [],
  pragmas: {},
  referencedFiles: [],
  typeReferenceDirectives: [],
  libReferenceDirectives: [],
  amdDependencies: [],
  identifiers: {},
  jsDocParsingMode: 0
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment