What techniques can be employed to dynamically modify Typescript's AST and run it while utilizing ts-node?

Below is my approach in executing a TypeScript file:

npx ts-node ./tinker.ts

In the file, I am reading and analyzing the Abstract Syntax Tree (AST) of another file named sample.ts, which contains the following line:

console.log(123)

The goal is to modify this code before executing it. For instance, I intend to replace 123 with 1337.

Consequently, when running npx ts-node ./tinker.ts, the output displayed on the terminal should show 1337 instead. Kindly refer to the draft provided below where I have added comments explaining the part that requires further clarification.

sample.ts

console.log(123);

tinker.ts

import * as fs from "fs";
const ts = require("typescript");
const path = "./sample.ts";
const code = fs.readFileSync(path, "utf-8"); // "console.log(123)"

const node = ts.createSourceFile("TEMP.ts", code, ts.ScriptTarget.Latest);

let logStatement = node.statements.at(0);
logStatement.expression.arguments.at(0).text = "1337";

// execute the modified code!
// anticipate seeing 1337 logged!

To summarize, upon running npx ts-node ./tinker.ts, the expected output is 1337 being displayed. What steps should I take to achieve this outcome?

Answer №1

Solution Approach

This solution involves the following steps:

  1. Creating a modified version of the input TypeScript module.
  2. Importing the modified TypeScript module dynamically to evaluate it.

Sample Program Example

package.json Configuration


{
  "name": "question-73432934",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "BSD",
  "dependencies": {
    "ts-morph": "15.1.0",
    "typescript": "4.7.4"
  },
  "devDependencies": {
    "ts-node": "10.9.1"
}
}

Update the content of the file and run the following command:

$ npm clean-install

tsconfig.json Configuration


{
  "compilerOptions": {
    "target": "es2017",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "ts-node": {
    "esm": true
  }
}

sample.ts File Content

console.log(123);

tinker.ts Code


import * as fs from "fs";
import { Project, SyntaxKind, CallExpression } from "ts-morph";

async function createModifiedFile(inputFilePath: string, outputFilePath: string) {
    const code = await fs.promises.readFile(inputFilePath, "utf8");

    const project = new Project();
    const sourceFile = project.createSourceFile(
        outputFilePath,
        code,
        { overwrite: true }
    );

    const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
    const callExpression = callExpressions[0] as CallExpression;

    callExpression.removeArgument(0);
    callExpression.addArgument("1337");

    await sourceFile.save();
};

async function importModuleDynamically(filePath: string) {
    await import(filePath);
};

await createModifiedFile("sample.ts", "temp.ts");
await importModuleDynamically("./temp.ts");

Execution Command

$ npx ts-node tinker.ts

Program Output:

1337

The modified TypeScript module:

$ cat temp.ts 
console.log(1337);

For Further Reading

TypeScript Resource for Source Code Transformation

  • Documentation on ts-morph.
  • TypeScript AST Viewer.

Exploring Dynamic Module import in TypeScript

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error in TypeScript: Typography type does not accept 'string' type as valid

As I attempt to implement the Typography component from material-ui using TypeScript, I encounter a perplexing error message TypeScript is throwing an error: Type 'string' is not assignable to type 'ComponentClass<HTMLAttributes<HTMLE ...

Creating a canvas that adjusts proportionally to different screen sizes

Currently, I am developing a pong game using Angular for the frontend, and the game is displayed inside an HTML canvas. Check out the HTML code below: <div style="height: 70%; width: 70%;" align="center"> <canvas id=&q ...

Vuefire encountering an issue with Vue 3 and throwing a Vue.use error

After setting up a Vue app and importing Vue from the vue module, I encountered an issue: ERROR in src/main.ts:4:5 TS2339: Property 'use' does not exist on type 'typeof import("/data/data/com.termux/files/home/ishankbg.tech/node_modules/vue/ ...

Ensure that parameters are validated correctly in the Next.JS application router using the searchParams method

When building the page, I need to properly validate params in the Next.JS app router using searchParams. My goal is to show a main image (coverImage) for each photo on the /gallery page. When a photo is clicked, I want to display more photos of the same k ...

Retrieving the input[text] value in TypeScript before trimming any special characters

One of the tasks I am working on involves a form where users can input text that may contain special characters such as \n, \t, and so on. My objective is to replace these special characters and then update the value of the input field accordingl ...

Simulate a complete class with its constructor, instance methods, and static functions

I have been searching for a comprehensive example that demonstrates how to properly mock all three elements (ClassA constructor, ClassA.func1 instance function, and ClassA.func2 static function) in my TypeScript project. In the code under test, I need to v ...

The production build encountered an error after upgrading Angular due to a problem with document.documentElement.setAttribute not being recognized

Recently, I updated my application to Angular 16 and the upgrade was successful. The application is running smoothly without any issues. However, when I attempted to run the command for a production build, ng build --configuration=production I encountere ...

Disabling a Field in Angular While Preserving its Value

Hey there, late night folks! I have a quick question about Angular. I'm working with a form that includes a specific field where I set the value using patchValue, but I also need to disable this field. The issue is that after disabling it, the value i ...

Angular: Exploring the most effective strategies for optimizing code within the ".subscribe()" method

Imagine having a component structured like this The Initial Code: import { HttpClient } from '@angular/common/http'; import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<pre>{{ ...

Using Typescript to automatically infer strongly-typed recursive index types

Commencing with an Animal interface and a detailed map of animals residing on my farm: export interface Animal { species: string; edible: boolean; } export interface FarmMap{ [key: string]: Animal; } Everything seems to be running smoothly. Here ...

Having trouble accessing the theme in a styled component with @emotion/styled

https://i.stack.imgur.com/zHLON.png I've been using @emotion/react for theming and successfully injected the theme into it. I can access the theme using useTheme within components, but I'm facing some difficulties in accessing the theme within s ...

Don't forget to include the line 'import "reflect-metadata"' at the beginning of your entry point for Jest tests

As I work on developing an application using dependency injection with tsyringe, I came across a case where a service receives the repository as a dependency. Here is an example: import { injectable, inject } from 'tsyringe' import IAuthorsRepos ...

Setting up React Context API with TypeScript: A Step-by-Step Guide

I recently updated my app.js file to app.tsx for improved functionality. However, I encountered an error with the current value. app.tsx import React, { createContext, useState } from "react"; import SelectPage from "./pages/select/select& ...

Theme not being rendered properly following the generation of a dynamic component in Angular

I am currently working on an Angular 9 application and I have successfully implemented a print functionality by creating components dynamically. However, I have encountered an issue where the CSS properties defined in the print-report.component.scss file a ...

Create the HTTP POST request body using an object in readiness for submission

When sending the body of an http post request in Angular, I typically use the following approach: let requestBody: String = ""; //dataObject is the object containing form values to send for (let key in dataObject) { if (dataObject[key]) { ...

The function Sync in the cp method of fs.default is not a valid function

When attempting to install TurboRepo, I encountered an issue after selecting npm. >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? ./my-turborepo ...

Generating swagger documentation for TypeScript-based Express applications

I have successfully set up the swagger URL following a helpful guide on configuring Swagger using Express API with autogenerated OpenAPI documentation through Swagger. Currently, I am utilizing TypeScript which outputs .js files in the dist folder without ...

Experiencing difficulty creating query files for the apollo-graphql client

I'm currently attempting to learn from the Apollo GraphQL tutorial but I've hit a roadblock while trying to set up the Apollo Client. Upon executing npm run codegen, which resolves to apollo client:codegen --target typescript --watch, I encounter ...

When I attempt to conceal the filter within mat-table using *ngIf, I encounter an issue where I am unable to read the property 'value' due to it being

After creating a mat-table, I encountered an issue when trying to show and hide my input filter area using a button. If I use *ngIf="showInputFilter" to hide the filter area, I receive the error message Cannot read property 'value' of u ...

Definition files (.d.ts) for JavaScript modules

I'm currently working on creating Typescript typings for the link2aws package in order to incorporate it into my Angular project. Despite generating a .d.ts file, I am still encountering the following error message: TypeError: (new link2aws__WEBPACK_I ...