I have a TypeScript code generation scenario, where I construct an AST and then print it and save to a file. By default, the printed string literals are wrapped in double quotes, I would like an option to have single quotes. As mentioned here, I should be able to walk the tree and replace the string literals, I am unsure how, though.
export const quotemarkTransformer = <T extends ts.Node>(context: ts.TransformationContext) => (rootNode: T) => {
function visit(node: ts.Node): ts.Node {
node = ts.visitEachChild(node, visit, context);
if (node.kind === ts.SyntaxKind.StringLiteral) {
const stringLiteral = node as ts.StringLiteral;
// change raw node text?
return ts.createLiteral(stringLiteral.text);
}
return node;
}
return ts.visitNode(rootNode, visit);
}
Creating string literals with TS factory functions ts.createLiteral(stringLiteral.text) will always use double quotes. Any way to access and change emitted text directly?
There is an internal property you can set on the StringLiteral to do this:
if (node.kind === ts.SyntaxKind.StringLiteral)
(node as any).singleQuote = true;
See here and here.
It's very important to note that this would be depending on a property that's not present in the public API so it might stop working one day. If you're uncomfortable doing this then follow the instructions below.
Given the emitted text:
Here's some code that shows an example:
// setup
const emittedFilePath = "/file.js";
const emittedText = `'this'; 'is a'; "test";`;
const emittedSourceFile = ts.createSourceFile(
emittedFilePath,
emittedText,
ts.ScriptTarget.Latest,
false);
// replace all ' with "
console.log(replaceQuoteChars(emittedSourceFile, `'`, `"`));
// main code...
type QuoteChar = "'" | "\"";
function replaceQuoteChars<OldChar extends QuoteChar>(
sourceFile: ts.SourceFile,
oldChar: OldChar,
newChar: Exclude<QuoteChar, OldChar>
) {
return getNewText(
getQuoteCharPositions(emittedSourceFile, oldChar)
);
function getNewText(quoteCharPositions: number[]) {
const fileText = sourceFile.getFullText();
let result = "";
let lastPos = 0;
for (const pos of quoteCharPositions) {
result += fileText.substring(lastPos, pos) + newChar;
lastPos = pos + 1;
}
result += fileText.substring(lastPos);
return result;
}
}
function getQuoteCharPositions(
sourceFile: ts.SourceFile,
searchingChar: QuoteChar
) {
const sourceFileText = sourceFile.getFullText();
const result: number[] = [];
visitNode(sourceFile);
return result;
function visitNode(node: ts.Node) {
if (ts.isStringLiteral(node))
handleStringLiteral(node);
else
ts.forEachChild(node, visitNode);
}
function handleStringLiteral(node: ts.StringLiteral) {
const start = node.getStart(sourceFile);
const quoteChar = sourceFileText[start];
if (quoteChar === searchingChar) {
result.push(start);
result.push(node.end - 1);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With