【问题标题】:ts.setSyntheticLeadingComments doesn't remove existing commentsts.setSyntheticLeadingComments 不会删除现有评论
【发布时间】:2019-03-15 21:12:07
【问题描述】:

说明

你好,

我正在尝试构建一些可以在函数之上添加 cmets 的东西。 不幸的是,ts.setSyntheticLeadingComments 似乎不允许我替换现有的 cmets。

我试过了:

  • ts.setSyntheticLeadingComments(node, [])
  • ts.setSyntheticLeadingComments(node, undefined)
  • node = ts.setSyntheticLeadingComments(node, [])

但这些都不起作用。最终,我的目标是能够用新的 cmets 替换现有的 cmets。

有什么想法吗?谢谢??????

复制

const transformFactory = (context: ts.TransformationContext) => (
  rootNode: ts.SourceFile
): ts.SourceFile => {
  const visit = (node: ts.Node) => {
    node = ts.visitEachChild(node, visit, context);

    ts.setSyntheticLeadingComments(node, []);

    return node;
  };

  return ts.visitNode(rootNode, visit);
};

const sourceFile = ts.createSourceFile(
  path,
  source,
  ts.ScriptTarget.ESNext,
  true,
  ts.ScriptKind.TS
);
const result = ts.transform(sourceFile, [transformFactory]);
const resultPrinter = ts.createPrinter({ removeComments: false });

console.log(resultPrinter.printFile(result.transformed[0]));

试试下面的转换器,看看如何完全没有移除 cmets

使用ts.createPrinter(..., { substituteNode(hint, node) { ... } }) 也无济于事

旁注

ts.getSyntheticLeadingComments() 似乎也没有按照我的预期工作。它总是返回undefined,这导致我使用以下工具虽然我不确定完全理解它的用途(借用https://github.com/angular/tsickle/blob/6f5835a644f3c628a61e3dcd558bb9c59c73dc2f/src/transformer_util.ts#L257-L266

/**
 * A replacement for ts.getLeadingCommentRanges that returns the union of synthetic and
 * non-synthetic comments on the given node, with their text included. The returned comments must
 * not be mutated, as their content might or might not be reflected back into the AST.
 */
export function getAllLeadingComments(node: ts.Node):
    ReadonlyArray<Readonly<ts.CommentRange&{text: string}>> {
  const allRanges: Array<Readonly<ts.CommentRange&{text: string}>> = [];
  const nodeText = node.getFullText();
  const cr = ts.getLeadingCommentRanges(nodeText, 0);
  if (cr) allRanges.push(...cr.map(c => ({...c, text: nodeText.substring(c.pos, c.end)})));
  const synthetic = ts.getSyntheticLeadingComments(node);
  if (synthetic) allRanges.push(...synthetic);
  return allRanges;
}

【问题讨论】:

标签: typescript typescript-compiler-api


【解决方案1】:

问题是您希望*SyntheticLeadingComments 函数会影响源 cmets。他们不会。它们只会影响之前合成的 cmets(即您在代码中添加的)。

实际的 cmets 不作为节点保存在 AST 中。您可以使用 getLeadingCommentRangesgetTrailingCommentRanges 获取实际的源 cmets。

节点具有不包含任何 cmets 的 startend 位置。节点还有一个 fullStart,它是包含任何前导 cmets 的位置。当一个节点被输出时,这就是 typescript 知道如何将 cmets 复制到输出的方式。

如果我们使用setTextRange 设置节点的范围以排除这些现有的 cmets,结果是我们有效地将它们从输出中删除,我们可以使用setSyntheticLeadingComments 添加新的 cmets:

import * as ts from 'typescript'

const transformFactory = (context: ts.TransformationContext) => (
    rootNode: ts.SourceFile
): ts.SourceFile => {
    const visit = (node: ts.Node) => {
        node = ts.visitEachChild(node, visit, context);
            if(ts.isFunctionDeclaration(node)) {
            let sourceFileText = node.getSourceFile().text;
            const existingComments = ts.getLeadingCommentRanges(sourceFileText, node.pos);
            if (existingComments) {
                // Log existing comments just for fun 
                for (const comment of existingComments) {
                    console.log(sourceFileText.substring(comment.pos, comment.end))
                }
                // Comment also attaches to the first child, we must remove it recursively.
                let removeComments =  (c: ts.Node) => {
                    if (c.getFullStart() === node.getFullStart()) {
                        ts.setTextRange(c, { pos: c.getStart(), end: c.getEnd() });
                    }
                    c = ts.visitEachChild(c, removeComments, context);
                    return c;
                }
                ts.visitEachChild(node, removeComments, context);
                ts.setTextRange(node, { pos: node.getStart(), end: node.getEnd() })
                ts.setSyntheticLeadingComments(node, [{
                    pos: -1,
                    end: -1,
                    hasTrailingNewLine: false,
                    text: "Improved comment",
                    kind: ts.SyntaxKind.SingleLineCommentTrivia
                }]);

            }
        }
        return node;
    };

    return ts.visitNode(rootNode, visit);
};

const sourceFile = ts.createSourceFile(
    "path.ts",
    `
// Original comment
function test () {

}
`,
    ts.ScriptTarget.ESNext,
    true,
    ts.ScriptKind.TS
);
const result = ts.transform(sourceFile, [transformFactory]);
const resultPrinter = ts.createPrinter({ removeComments: false });

console.log("!");
console.log(resultPrinter.printFile(result.transformed[0]));

【讨论】:

  • 感谢您的帮助!我有点不精确,这使您的代码不适用于我正在尝试做的事情。我不是要在FunctionDeclaration 的顶部添加cmets,而是在VariableStatement 的顶部添加。并且由于某些原因,ts.setTextRange 的效果似乎与FunctionDeclaration 的效果不同。它不会删除现有的评论,尽管node.startnode.end 不包含评论。抱歉,最初的主题是错误的,我想我会使用函数来使其更易于理解。你知道为什么吗?
  • @FlavianDesverne 它应该 .. 我现在无法检查,但我会稍后再回复你。您能否提供一些您想要更改的示例代码?文件中有什么预期的输出是什么?这会有所帮助
  • 这是我为以下代码得到的输出:``` import { objectType } from "nexus"; //改进的评论 // 原始评论 export const Toto = objectType({ name: "Query", definition(t) { t.string("hello"); } }); ```
  • 更新:代码块在 cmets 中不起作用。这是一个包含输入、预期输出、实际输出和我的实现的 pastebin(它只是用 ts.isVariableStatement 替换 ts.isFunctionDeclaration):pastebin.com/axhmk8eP。非常感谢您的帮助?
  • @FlavianDesverne 显然注释也附加到第一个孩子,我们需要递归删除它,我会尽快更新代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 2011-12-18
  • 2021-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多