Skip to content

Instantly share code, notes, and snippets.

@cevek
Last active December 15, 2020 05:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cevek/b3abc1de42303eab69d9bab0220a75c0 to your computer and use it in GitHub Desktop.
Save cevek/b3abc1de42303eab69d9bab0220a75c0 to your computer and use it in GitHub Desktop.
Typescript pre transformer
"use strict";
function Bar(props) { return null; }
React.createElement(Bar, { sdf: true, id: 'hello' });
React.createElement("div", { id: 'hi' }, "abc");
React.createElement("div", null, "abc");
function Bar(props: {id: string}){return null}
<Bar.hello sdf={true}></Bar>;
<div.hi>abc</div>;
<div>abc</div> ;
import * as ts from 'typescript';
const createProgram: any = ts.createProgram;
const options = {
jsx: ts.JsxEmit.React,
strict: true,
};
const host = ts.createCompilerHost(options);
const originalGetSource = host.getSourceFile;
host.getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget, ...args: any[]) => {
if (fileName.match(/(\.d\.ts|\.js)$/)) {
return originalGetSource(fileName, languageVersion, ...args);
}
const sourceText = ts.sys.readFile(fileName)!;
patchJsxClosingElementToAvoidDifferentTagNamesError();
const source = ts.createSourceFile(fileName, sourceText, languageVersion);
const { transformed } = (ts as any).transformNodes(
undefined,
host,
options,
[source],
[transformer],
/*allowDtsFiles*/ false
);
const changedSource = transformed[0];
// if (!changed.ambientModuleNames) {
// changed.ambientModuleNames = [];
// }
changedSource.flags = 0;
return changedSource;
};
const myprogram = ts.createProgram({
rootNames: ['./test.tsx'],
host,
options,
});
myprogram.emit();
function error(m: any) {
return m.messageText;
}
console.log({
global: myprogram.getGlobalDiagnostics().map(error),
options: myprogram.getOptionsDiagnostics().map(error),
syntactic: myprogram.getSyntacticDiagnostics().map(error),
semantic: myprogram.getSemanticDiagnostics().map(error),
});
function transformer(ctx: ts.TransformationContext) {
return function transform(source: ts.SourceFile) {
function visitor(node: ts.Node): ts.Node {
if (
ts.isJsxOpeningElement(node) &&
ts.isPropertyAccessExpression(node.tagName) &&
ts.isIdentifier(node.tagName.expression)
) {
const attrs = node.attributes;
const idProp: ts.JsxAttributeLike = updatePos(
ts.createJsxAttribute(
updatePos(ts.createIdentifier('id')),
updatePos(ts.createLiteral(node.tagName.name.text))
)
);
const newProps = ts.createNodeArray([...attrs.properties, idProp]);
const newAttrs = ts.updateJsxAttributes(attrs, newProps);
const nnode = ts.updateJsxOpeningElement(node, node.tagName.expression, node.typeArguments, newAttrs);
nnode.flags = 0;
return nnode;
}
if (
ts.isJsxClosingElement(node) &&
ts.isPropertyAccessExpression(node.tagName) &&
ts.isIdentifier(node.tagName.expression)
) {
const nnode = ts.updateJsxClosingElement(node, node.tagName.expression);
nnode.flags = 0;
return nnode;
}
return ts.visitEachChild(node, visitor, ctx);
}
return ts.visitNode(source, visitor);
};
function updatePos<T extends ts.Node>(node: T) {
node.pos = 0;
node.end = 1;
node.flags = 0;
return node;
}
}
function patchJsxClosingElementToAvoidDifferentTagNamesError() {
const NodeObj = ts.createJsxClosingElement('ab' as any).constructor;
Object.defineProperty(NodeObj.prototype, 'closingElement', {
get() {
return this._closingElement;
},
set(node: ts.JsxClosingElement) {
debugger;
if (
ts.isPropertyAccessExpression(this.openingElement.tagName) &&
ts.isIdentifier(this.openingElement.tagName.expression)
) {
node = ts.updateJsxClosingElement(node, this.openingElement.tagName);
node.flags = 0;
}
this._closingElement = node;
},
configurable: true,
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment