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

Receiving the error "Potential null object. TS2531" while working with a form input field

I am currently working on developing a straightforward form to collect email and password details from new users signing up on Firebase. I am utilizing React with Typescript, and encountering an error labeled "Object is possibly 'null'. TS2531" s ...

Tips for triggering an event from a function instead of the window

Everything is functioning as expected, with the event listener successfully capturing the custom event when it is dispatched from the window and listened for as loading, all seems to be working well. const MyLib = mylib(); function mylib() { const re ...

Can anyone provide a solution for fixing TypeScript/React error code TS7053?

I encountered an error message with code TS7053 which states: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; isLandlocked: boolean; }'. No index signa ...

The MaxDuration feature for a 5-minute time limit is malfunctioning on the Serverless Pro Plan, resulting in a 504 ERROR on

I am currently using Next.js@latest with App Directory My application is hosted on Vercel I'm experiencing a 504 error from Vercel and I'm on the pro plan. My serverless functions are set to run for up to 5 minutes, but usually, they only take ...

The function `find()` will not provide any data, it will only output `undefined`

I am trying to extract the `s_id` field from this JSON data: { "StatusCode": 0, "StatusMessage": "OK", "StatusDescription": [ { "s_id": "11E8C70C8A5D78888E6EFA163EBBBC1D", "s_serial": " ...

The attribute 'xxx' is not found within the 'Readonly<{}>' type

I am currently in the process of writing tests for a React Typescript component. App.tsx: import * as React from 'react'; import { Button } from 'react-bootstrap'; interface Igift { id: number; } interface IAppState { gifts: Igi ...

Creating a custom event handler for form input changes using React hooks

A unique React hook was created specifically for managing form elements. This hook provides access to the current state of form fields and a factory for generating change handlers. While it works seamlessly with text inputs, there is a need to modify the c ...

React App Creation: Issue with ESLint configuration in TypeScript environment

I recently built a React app with the CRA (typescript template), but I noticed that TypeScript isn't adhering to the rules specified in the ESLint configuration. This is puzzling because I have consistently used this configuration in all my React proj ...

Pause and anticipate the subscription within the corresponding function

Is there a way to make an If-Else branch wait for all REST calls to finish, even if the Else side has no REST calls? Let's take a look at this scenario: createNewList(oldList: any[]) { const newList = []; oldList.forEach(element => { if (eleme ...

React input delay handling during onChange event

Upon closer inspection, I've come across an issue that I had not previously noticed. I am unsure if there is a bug within my code or if the onChange function always behaves in this manner, but I am experiencing input delay and am uncertain on how to r ...

Utilizing a segment of one interface within another interface is the most effective method

In my current project using nextjs and typescript, I have defined two interfaces as shown below: export interface IAccordion { accordionItems: { id: string | number; title: string | React.ReactElement; content: string | React. ...

An unexpected error occurs when attempting to invoke the arrow function of a child class within an abstract parent class in Typescript

Here is a snippet of code that I'm working on. In my child class, I need to use an arrow function called hello(). When I try calling the.greeting() in the parent class constructor, I encounter an error: index.ts:29 Uncaught TypeError: this.hello is ...

Guide on building an npm package that seamlessly allows for installation both locally and globally (-g) using webpack and typescript

As I work on developing an npm package with options for both local and global (-g) installations, I find myself puzzled by the distinctions between the src and lib directories and the purpose of the bin directory. In my previous projects, I typically util ...

Ensuring that a service is completely initialized before Angular injects it into the system

When Angular starts, my service fetches documents and stores them in a Map<string, Document>. I use the HttpClient to retrieve these documents. Is there a way to postpone the creation of the service until all the documents have been fetched? In ot ...

Creating a progressive prototype chain in TypeScript: A step-by-step guide

With JavaScript, it is possible to create a "derived class" whose "base class" is dynamic using code like the following: function NewBaseClass(sF) { function DynamicBaseClass(iF) { this.instanceField = iF; } // EDIT: oops, this is not really static i ...

Extra assistance might be required to manage the output from these loaders

I'm in the process of developing a State Management Library for ReactJs. However, when I integrate it into my React project (built with create-react-app), an error is thrown: Failed to compile. path/to/agile/dist/runtime.js 116:104 Module parse faile ...

Need help resolving the issue of "Type Property '' does not exist on type 'IntrinsicAttributes & Function'? Let's find a solution together

Looking at my parent component below: import FilterComponent from 'filter/filterComponent' const handleReload = useCallback( (event: MouseEvent) => { event.preventDefault(); // implement logic here }, [Reload, Fetch ...

TypeError: Unable to find TextEncoder in mongoose and jest when using TypeScript

Currently, I am working on a project using Node 14 along with Express v4.16.3 and Typescript (v4.7.4). Recently, I added Mongoose (v6.5.2) to the project, and while the logic code seems fine, most of the tests executed by Jest (v26.4.2) are failing with th ...

tips for closing mat select when clicked outside

When a user clicks on the cell, it should display the value. If the user clicks outside the cell, the input field will close and show the field value. I am facing an issue on how to implement this with mat select and mat date picker. Any suggestions? Than ...

Issue regarding locating Material-UI components while resolving TypeScript modules

I am encountering an issue with my Visual Studio 2019 (v16.3.2) React TypeScript project that involves Material-UI components. The TypeScript compiler is unable to resolve any of the @material-ui imports, resulting in errors like the one below which preven ...