itsdouges / typescript-transformer-handbook Goto Github PK
View Code? Open in Web Editor NEW๐ A comprehensive handbook on how to create transformers for TypeScript with code examples
๐ A comprehensive handbook on how to create transformers for TypeScript with code examples
How can I share a custom argument from a user's tsconfig.json
to my custom plugin?
To install a plugin we do:
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-my-plugin"
}
]
}
}
I would love to be able to write something akin to:
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-my-plugin",
"foo": "bar",
"options": ["verbose", "production"]
}
]
}
}
or even
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-my-plugin"
}
],
"foo": "bar",
"options": ["verbose", "production"]
}
}
I checked the Intro to the TSConfig Reference without finding what I was looking for.
I also posted this question on Stack Overflow: https://stackoverflow.com/questions/71255330/how-can-i-share-a-custom-argument-from-a-users-tsconfig-json-to-my-custom-plu
I'm frequently working with symbolic mathematical expressions. Sadly, JavaScript doesn't support operator overloading, so I have to write a lot of ugly code like add(u, mul(2, v))
. I would love to make a transformer that changes operators into function calls:
u + 2*v โ op_add( u, op_mul(2, v) )
This transformation alone is quite easy, I just have to find BinaryExpression(A, op, B)
and turn it into CallExpression(fn, [A, B])
. However I would love to use the type checking of the resulting function. In the handbook you say that:
โType checking should not happen after transforming. If it does it's more than likely a bug - file an issue!โ
Does this mean that my transformed code will still be type-checked as if it were a + b
, not op_add(a, b)
? Or is it just a misunderstanding on my side?
Thanks!
First of all; thanks for the handbook; I really appreciate it. Great work.
In the section Advanced - Following module imports
you write the following:
You can also traverse the imported node as well using ts.visitChild and the like.
I really would like to know how this could be achieved. I do not understand how I get the imported node and how I can traverse it then.
As a matter of fact I would like to achieve the following:
transform(PlaygroundComponent)
PlaygroundComponent
(maybe imported from another file and adjust it)I would appreciate any help. Thanks. :)
You are mentioning the following tipp:
Tip - Use ts-creator to quickly get factory functions for a piece of TypeScript source - instead of meticulously writing out an AST for a node you can write a code string and have it converted to AST for you.
Could you provide an example for e.g. inserting a node with ts-creator
.
Is there a way to determine if a class inherits (indirectly) from another class X. I understand that I can read the heritage clause or that I can ask the type checker for the base type. But this does not seem to work recursively. I am so far only able to extract the direct parent and it seems that I would have to walk the tree two times: first to build up my class inheritance tree and second to check if a node represents a class that extends (indirectly) my class X.
I need to transform ES6 syntax into ES5 but keep types. So I need TypeScript ES6 to TypeScript ES5 transformer (not JS).
ts.transpile()
strips all the types and essentially outputs JS (which is not what I need).
Can I use built-in typescript transformers to achieve this?
Hi again!
I am wondering if it is possible to adjust the prototype of a class declaration? E.g. add specific properties? If anyone has an idea I would really appreciate it.
Thanks in advance.
Greets, Lukas
General discussion about what more to add and todo items -
TODO items -
When running remove-node
transformer example, the generated file with transformed code does not match with the README.md documentation
ttsc
under example-transformers/remove-node
To use return undefined
instead of ts.createEmptyStatement();
ts.createImportClause(
is deprecated
ts.factory.createImportClause(
is current version
also for many other methods here
Hello,
I've been trying to implement the following imports
section this so I can get all interface types from all files.
The currently example seems to be broken since using it results the following line in returning undefined:
const importSymbol = typeChecker.getSymbolAtLocation(node.moduleSpecifier);
Now my question is I've got the following code:
interface TestComponent extends Component {
}
interface TestComponent3 extends Component {
}
interface TestComponent2 extends TestComponent3 {
}
interface TestEntity extends TestComponent, Entity, TestComponent2 {
}
const testEntity: TestEntity = {}
I want to inject all interface types from all files in the testEntity constant.
It's correctly working for the current file now but it does not seem to follow any imports.
I've wasted hours trying to get the imported sourceFile AST but I can't seem to get it to work.
Any advice?
Thanks in advance!
https://medium.com/@Quramy/manipulate-comments-with-typescript-api-73d5f1d43d7f
Just leaving this here as a reference for future work.
Hey!
Once again I would like first of all to say that I really appreciate this repository and the provided examples. E.g. the example regarding Adding new import declarations
.
Although I have a question regarding this topic. Let's assume I have the following class:
export class TestComponent {
public greet(): string {
return "Hello";
}
}
The builded file with my custom TypeScript Transformer in place looks like this:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TestComponent = /** @class */ (function () {
function TestComponent() {
}
TestComponent.prototype.greet = function () {
return jasmine.createSpy().and.callFake(function () {
if (!Ttransformer.isInTransformContext()) {
return "Hello";
}
});
};
return TestComponent;
}());
exports.TestComponent = TestComponent;
//# sourceMappingURL=test.component.js.map
I would like to add an import statement to provide the Ttransformer
class. Therefore I adjusted my transformer to do so. The output looks now like this:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// === THE FOLLOWING LINE WAS ADDED NOW ===
var ttransformer_1 = require("../ttransformer");
var TestComponent = /** @class */ (function () {
function TestComponent() {
}
TestComponent.prototype.greet = function () {
return jasmine.createSpy().and.callFake(function () {
if (!Ttransformer.isInTransformContext()) {
return "Hello";
}
});
};
return TestComponent;
}());
exports.TestComponent = TestComponent;
//# sourceMappingURL=test.component.js.map
I am really wondering if the resulting source code is correct. If I already import the class in the first place, like in this example...
import { Ttransformer } from "../ttransformer";
Ttransformer.isInTransformContext();
...the ouptut looks like this:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var ttransformer_1 = require("../ttransformer");
ttransformer_1.Ttransformer.isInTransformContext();
//# sourceMappingURL=source.dev.js.map
The actual difference is shown here:
Ttransformer.isInTransformContext()
ttransformer_1.Ttransformer.isInTransformContext();
Do I have to make sure in my custom transformer that ttransformer_1
get's added? I would have imagined that this happens automatically. As I said; I really like the provided example; but I am wondering how it would look like if you would try to use the actual import. Do you know what I mean?
I appreciate any thoughts, ideas and explanation. Thanks in advance.
Greets, Lukas
Hey!
I have a really general question which I need to ask providing an example.
Let's assume we add some kind of function with the typescript transformer. - function whatever() {}
And then I would like to use this function somewhere else, like whatever()
.
Should this work or would I have to consider something regarding bindings?
E.g. what's the point of adding import statements if the stuff imported can't be used. Or am I missing something here?
Thanks in advance.
Unfortunately, most examples in this guide (and most of my code too ๐ ) is broken in TypeScript 5.0 because the transformer
type now requires its return to be a SourceFile
, not just a Node
.
If you have ideas of how we can get this translated to as much languages as possible - please chime in!
Hello again!
Sorry for using this channel again asking a general usage question. But I really appreciated your help the last time.
How can I or how would you add a child node to a given node?
E.g. add static instances = [];
to class Test {}
.
I know that I can find the class declaration with ts.isClassDeclaration(node)
but how would you proceed then?
Do I really have to provide a new class declaration node (like in the code snippet) or is there a more "elegant" way?
if (ts.isClassDeclaration(node) {
// createInstancesProperty ==> custom fn to create "static instances = [];"
const extendedMembers = ts.createNodeArray([createInstancesProperty(symbol.name), ...node.members]);
return ts.createClassDeclaration(node.decorators, node.modifiers, node.name, node.typeParameters, node.heritageClauses, extendedMembers);
}
I am wondering what's the best approach if I would like to do something (on) ...
Add a static property to the class || e.g. static instances = [];
ts.isConstructorDeclaration
ClassName.instances.push(this)
ts.isMethodDeclaration
ts.isPropertyDeclaration
I really enjoy using the visitor pattern approach but I do not know how to use it in combination with my desired tasks 1) and 2).
Any help appreciated. Thanks in advance.
Hey, I want to replace a call expression ( someFn() ) with multiple nodes (a NodeArray full with statements), but I'm getting the error:
Error: Debug Failure. False expression: Too many nodes written to output.
How can I make it work? The call expression itself comes from an ExpressionStatement
. Also, I'd like to replace the call expression with statements, ex. (const a = 5).
Is there any way to ignore errors like that, I'm attempting to write a macro system in TS, but these errors are stopping me.
Hey!
Let's assume that I want to modify specific classes with my custom typescript transformer. The actual use case does not matter for this question but e.g. changing all method declarations to property declarations.
Therefore I collect all the targeted class declaration symbols before the actual transformation starts. I already achieved this. Therefore I do not want to get into detail here. Just assume that there is an array of symbols representing the transformer targets - class declaration symbols.
With this setup I can do something like the following in my visitor:
if (ts.isClassDeclaration(node) && isTransformerTarget(node)) {
if (ts.isMethodDeclaration(node)) {
// return new property declaration instead
}
}
Now asking the actual question: Does the symbol of the class declaration change after modify the corresponding class declaration?
I am asking because my setup gets a little bit more specific: I am composing multiple transformers like explained in the handbook. And actually I am doing a similar thing in the next transformer. The real use case does not matter again. But for the sake of the example let's assume modifying the constructor of the class declaration with the next transformer.
if (ts.isClassDeclaration(node) && isTransformerTarget(node)) {
if (ts.isConstructorDeclaration(node)) {
// add something to the constructor and return the updated node
}
}
Here the previously asked question gets visible for me. I am not able to enter the first if-statement anymore. Exploring my source code lead me to the assumption that the symbol must have changed after applying the first transformer. Therefore the function isTransformerTarget(node) would return false now. This is maybe responsible for not entering the block anymore. Do do know what I mean?
Therefore again summarizing the actual questions:
Hopefully my explanation and questions make any sense. I would appreciate any thoughts on this topic considering symbols.
Thanks in advance,
Lukas
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.