Adding additional properties to JSX components during typescript compilationIs there a way to include additional properties

Currently, I am employing JSX syntax in my TypeScript node.js project (without relying on React).

Within my tsconfig, I have specified my custom jsxFactory

{
  "compilerOptions": {
    ...
    "jsxFactory": "myJSXFactory",
    ...
  }
}
<MyBanner backgroundColor="red" />

Everything is functioning smoothly and the above JSX gets transformed to this post TSC compilation

myJSXFactory(MyBanner, {
  backgroundColor: 'red' 
});

At this juncture, I desire to append additional props to All JSX components at compile time, as shown below:

myJSXFactory(MyBanner, {
  backgroundColor: 'red',
  theme: myTheme // a variable accessible in the parent context
});

I delved into the Compiler API for guidance, but it's somewhat vague on how to edit the source during JSX transformation.

Answer №1

Solving the puzzle on my own...

import * as ts from "typescript";

type TYPESCRIPT = typeof ts;
function nodeVisitor(ts: TYPESCRIPT, ctx: ts.TransformationContext, sf: ts.SourceFile) {
  const visitor: ts.Visitor = (node) => {
      if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
          // Creating a theme attribute
          const themeAttr = ts.createPropertyAssignment(
              'theme',
              ts.createIdentifier('request.query.theme')
          )

          // Creating JSX Attribute __my_extra_prop={{ theme: request.query.theme }}
          const sourceJsxAttr = ts.createJsxAttribute(
              ts.createIdentifier('__my_extra_prop'),
              ts.createJsxExpression(undefined, ts.createObjectLiteral([themeAttr]))
          )
          const jsxAttributes = ts.createJsxAttributes([...node.attributes.properties, sourceJsxAttr])
          const clonedNode = ts.getMutableClone(node)
          clonedNode.attributes = jsxAttributes
          return clonedNode
      }
      return ts.visitEachChild(node, visitor, ctx)
  }
  return visitor
}

function transform(ts: TYPESCRIPT): ts.TransformerFactory<ts.SourceFile> {
  return (ctx) => (sf) => ts.visitNode(sf, nodeVisitor(ts, ctx, sf))
}

// JSX expression
const source = "<MyBanner backgroundColor='red' />";

const result = ts.transpileModule(source,
  {
    compilerOptions: {
      module: ts.ModuleKind.CommonJS,
      jsxFactory: "myJSXFactory",
      jsx: ts.JsxEmit.React
    },
    transformers: {
      before: [transform(ts)]
    }
  }
);

console.log(result.outputText);

https://repl.it>@ABMAdnan/TS-JSX-extra-props#index.ts

myJSXFactory(MyBanner, { backgroundColor: 'red', __my_extra_prop: { theme: request.query.theme } });

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

Can someone explain to me how this ternary operator works?

Can anyone demonstrate how to convert this function into a traditional if-else statement? export const orderArr = (arr: any[], key: string) => arr.sort((a, b) => ((a[key] > b[key]) ? 1 : (a[key] === b[key]) ? ((a[key] > b[key]) ? 1 : -1) : -1)) ...

How to retrieve the parent activated route in Angular 2

My route structure includes parent and child routes as shown below: { path: 'dashboard', children: [{ path: '', canActivate: [CanActivateAuthGuard], component: DashboardComponent }, { path: & ...

Error: The jasmine framework is unable to locate the window object

Currently, I am testing a method that includes locking the orientation of the screen as one of its functionalities. However, when using Jasmine, I encountered an error at the following line: (<any>window).screen.orientation.lock('portrait&apos ...

Having difficulty implementing a versatile helper using Typescript in a React application

Setting up a generic for a Text Input helper has been quite challenging for me. I encountered an error when the Helper is used (specifically on the e passed to props.handleChange) <TextInput hiddenLabel={true} name={`${id}-number`} labelText=" ...

Enhance the functionality of 'takeWhile' by incorporating a limit parameter, similar to how 'take' operates

I am attempting to retrieve all pages until either there are no more pages or a certain limit (let's say 10 pages) is reached. If I follow this approach: obs.pipe(expand((page) => { return service.call(page).nextPage; }), take(10), takeWhil ...

Flow - secure actions to ensure type safety

Currently implementing flow and looking to enhance the type safety of my reducers. I stumbled upon a helpful comment proposing a solution that seems compatible with my existing codebase: https://github.com/reduxjs/redux/issues/992#issuecomment-191152574 I ...

What is the best way to preserve the state of a component in Angular?

I need to find a way to preserve the state of a data table within my Angular 7 & Typescript application. It's essentially like saving the state of a browser tab. When I navigate to another component and return to the one with the data table, how ...

In socket.io, the event occurs twice in a row

I am encountering an issue with my socket.io nodejs cluster setup in my project where the same event is triggered twice. Despite trying different versions of Socket.io, the problem persists and two instances of the same event are being emitted. The current ...

"Encountering an error stating 'SyntaxError: Unable to use import statement outside of a module' when attempting to import a module that itself imports another module for side

I'm currently working on writing Jest tests for my TypeScript code. To ensure compatibility with older browsers, I have included some polyfills and other necessary imports for side effects. Here is a snippet of my code (variables changed for anonymit ...

Anonymous function bundle where the imported namespace is undefined

Here is the code snippet I am working with: import * as Phaser from 'phaser'; new Phaser.Game({ width:300, height:300, scale: { mode: Phaser.Scale.FIT, }, type: Phaser.AUTO, scene: { create() {} }, }); Upon compi ...

The endpoint throws an error, yet the HTTP subscription fails to capture it

My code seems simple enough. let body = new FormData(); body.set("a", true); body.set("username", this.user); body.set("password", this.password); let headers = new HttpHeaders(); headers.set("Content-Type", 'application/x-www-form-urlencoded') ...

What is the mechanism behind Typescript interface scope? How can interfaces be defined globally in Typescript?

I'm diving into the world of Typescript and Deno, but I'm struggling to understand how interfaces scopes work. Here's the structure of my application: The first layer (App.ts) contains the core logic of my application. This layer can refer ...

What is the best way to set up Storybook with Vue Cli 3?

I'm facing difficulties installing Storybook in a Vue Cli 3 project. Every time I try to npm run storybook, I encounter this error: Cannot find module '@storybook/vue/dist/server/config/defaults/webpack.config.js' I suspect that this i ...

Angular developers may encounter a dependency conflict while attempting to install ngx-cookie

I'm currently facing an issue while attempting to add the ngx-cookie package for utilizing the CookieService in my application. Unfortunately, I am encountering some dependency conflicts that look like the following: $ npm install ngx-cookie --save np ...

Material UI Error TS1128: Expected declaration or statement for ButtonUnstyledProps

While working on my application that utilizes Material UI, I encountered an issue. I keep receiving a Typescript error and haven't been able to find a solution for it. TypeScript error in C:/.../node_modules/@mui/base/ButtonUnstyled/index.d.ts(3,1): D ...

What benefits do Definitely Typed TypeScript files offer for Knockout and jQuery?

I'm not sure if this is a silly question, but I was wondering if it's necessary to use definitely typed (.d.ts) versions of external libraries when working with Typescript. Currently, my code base uses jQuery and Knockout in the traditional manne ...

Exploring the power of Prosemirror with NextJS through Tiptap v2

Greetings everyone, I am a newcomer to Stack Overflow and I am reaching out for assistance regarding an issue that has arisen. The problem at hand pertains to the development of the Minimum Viable Product (MVP) for my startup which specializes in creating ...

How can I populate a mat-table in Angular 7 with data stored in an object?

At the moment, my code is set up to populate a table with data. In my component.ts file: import { HttpClient } from "@angular/common/http"; import { Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/fo ...

Angular 7's URL updates, but no other actions take place

I am new to posting here and feeling quite desperate. Currently, I am working with Angular 7 and facing an issue. The problem arises when I manually enter the desired URL, everything works perfectly. However, when I use RouterLink by clicking a button, the ...

Is there a way to specifically call the last promise in a loop?

I'm new to promises and could use some help. I have a situation where promises are resolving randomly, how can I ensure that the last promise in the loop is resolved after the full loop executes? For example, if this.selectedValues has 4 values, som ...