feat: 完善注释处理

This commit is contained in:
Pan Qiancheng 2026-01-06 17:49:52 +08:00
parent a7a5623cde
commit 7613df14e6
2 changed files with 217 additions and 24 deletions

View File

@ -557,17 +557,95 @@ function performCustomChecks(pwd, program, typeChecker, customConfig) {
};
const preprocessIgnoreComments = (sourceFile) => {
const fullText = sourceFile.getFullText();
// 收集文件中所有的注释及其位置
const allComments = [];
const scanComments = (pos, end) => {
const leading = ts.getLeadingCommentRanges(fullText, pos);
const trailing = ts.getTrailingCommentRanges(fullText, end);
if (leading) {
leading.forEach(comment => {
const text = fullText.substring(comment.pos, comment.end);
allComments.push({
pos: comment.pos,
end: comment.end,
text,
isIgnore: isIgnoreComment(text)
});
});
}
if (trailing) {
trailing.forEach(comment => {
const text = fullText.substring(comment.pos, comment.end);
allComments.push({
pos: comment.pos,
end: comment.end,
text,
isIgnore: isIgnoreComment(text)
});
});
}
};
// 扫描整个文件的注释
const scanNode = (node) => {
scanComments(node.getFullStart(), node.getEnd());
ts.forEachChild(node, scanNode);
};
scanNode(sourceFile);
// 检查节点是否在忽略注释的影响范围内
const isNodeCoveredByIgnoreComment = (node) => {
const nodeStart = node.getStart(sourceFile);
const nodeEnd = node.getEnd();
for (const comment of allComments) {
if (!comment.isIgnore)
continue;
// 情况1注释在节点之前前导注释
// 允许注释和节点之间有少量空白最多50个字符
if (comment.end <= nodeStart && nodeStart - comment.end <= 50) {
return true;
}
// 情况2注释在节点之后尾随注释
// 允许节点和注释之间有少量空白最多50个字符
if (comment.pos >= nodeEnd && comment.pos - nodeEnd <= 50) {
return true;
}
// 情况3注释在节点内部JSX 表达式中的注释)
if (comment.pos >= nodeStart && comment.end <= nodeEnd) {
return true;
}
}
return false;
};
const visit = (node) => {
const nodeFullStart = node.getFullStart();
const leadingComments = ts.getLeadingCommentRanges(fullText, nodeFullStart);
if (leadingComments) {
for (const comment of leadingComments) {
const commentText = fullText.substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
// 标记当前节点及其子节点
markNodeAndChildren(node);
return; // 不需要继续遍历子节点
}
// 检查当前节点是否被忽略注释覆盖
if (isNodeCoveredByIgnoreComment(node)) {
markNodeAndChildren(node);
return;
}
// 特殊处理JSX 表达式
if (ts.isJsxExpression(node)) {
// 检查 JSX 表达式内部是否有忽略注释
if (node.expression && isNodeCoveredByIgnoreComment(node.expression)) {
markNodeAndChildren(node.expression);
}
}
// 特殊处理:条件表达式(三元运算符)
if (ts.isConditionalExpression(node)) {
// 检查 whenTrue 和 whenFalse 分支
if (isNodeCoveredByIgnoreComment(node.whenTrue)) {
markNodeAndChildren(node.whenTrue);
}
if (isNodeCoveredByIgnoreComment(node.whenFalse)) {
markNodeAndChildren(node.whenFalse);
}
}
// 特殊处理:二元表达式
if (ts.isBinaryExpression(node)) {
// 检查左右操作数
if (isNodeCoveredByIgnoreComment(node.left)) {
markNodeAndChildren(node.left);
}
if (isNodeCoveredByIgnoreComment(node.right)) {
markNodeAndChildren(node.right);
}
}
ts.forEachChild(node, visit);
@ -584,6 +662,16 @@ function performCustomChecks(pwd, program, typeChecker, customConfig) {
if (ignoreCommentNodes.has(node)) {
return true;
}
// 如果文件开头有忽略注释,整个文件都忽略
const fileStartComments = ts.getLeadingCommentRanges(sourceFile.getFullText(), 0);
if (fileStartComments) {
for (const comment of fileStartComments) {
const commentText = sourceFile.getFullText().substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
return true;
}
}
}
// 向上查找父节点最多3层
let current = node.parent;
let depth = 0;
@ -2286,7 +2374,7 @@ function performCustomChecks(pwd, program, typeChecker, customConfig) {
}
else {
// 普通格式的 key但本地没有 i18n 文件
addDiagnostic(callNode, `i18n key "${key}" 无法检查,因为未找到本地化文件: ${localePath}`, 9202);
addDiagnostic(callNode, `i18n key "${key}" 无法检查,因为找不到locales文件: ${localePath}`, 9202);
}
}
else if (ts.isTemplateExpression(firstArg) ||

View File

@ -727,18 +727,108 @@ export function performCustomChecks(
const preprocessIgnoreComments = (sourceFile: ts.SourceFile): void => {
const fullText = sourceFile.getFullText();
const visit = (node: ts.Node): void => {
const nodeFullStart = node.getFullStart();
const leadingComments = ts.getLeadingCommentRanges(fullText, nodeFullStart);
// 收集文件中所有的注释及其位置
const allComments: Array<{ pos: number; end: number; text: string; isIgnore: boolean }> = [];
if (leadingComments) {
for (const comment of leadingComments) {
const commentText = fullText.substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
// 标记当前节点及其子节点
markNodeAndChildren(node);
return; // 不需要继续遍历子节点
}
const scanComments = (pos: number, end: number) => {
const leading = ts.getLeadingCommentRanges(fullText, pos);
const trailing = ts.getTrailingCommentRanges(fullText, end);
if (leading) {
leading.forEach(comment => {
const text = fullText.substring(comment.pos, comment.end);
allComments.push({
pos: comment.pos,
end: comment.end,
text,
isIgnore: isIgnoreComment(text)
});
});
}
if (trailing) {
trailing.forEach(comment => {
const text = fullText.substring(comment.pos, comment.end);
allComments.push({
pos: comment.pos,
end: comment.end,
text,
isIgnore: isIgnoreComment(text)
});
});
}
};
// 扫描整个文件的注释
const scanNode = (node: ts.Node): void => {
scanComments(node.getFullStart(), node.getEnd());
ts.forEachChild(node, scanNode);
};
scanNode(sourceFile);
// 检查节点是否在忽略注释的影响范围内
const isNodeCoveredByIgnoreComment = (node: ts.Node): boolean => {
const nodeStart = node.getStart(sourceFile);
const nodeEnd = node.getEnd();
for (const comment of allComments) {
if (!comment.isIgnore) continue;
// 情况1注释在节点之前前导注释
// 允许注释和节点之间有少量空白最多50个字符
if (comment.end <= nodeStart && nodeStart - comment.end <= 50) {
return true;
}
// 情况2注释在节点之后尾随注释
// 允许节点和注释之间有少量空白最多50个字符
if (comment.pos >= nodeEnd && comment.pos - nodeEnd <= 50) {
return true;
}
// 情况3注释在节点内部JSX 表达式中的注释)
if (comment.pos >= nodeStart && comment.end <= nodeEnd) {
return true;
}
}
return false;
};
const visit = (node: ts.Node): void => {
// 检查当前节点是否被忽略注释覆盖
if (isNodeCoveredByIgnoreComment(node)) {
markNodeAndChildren(node);
return;
}
// 特殊处理JSX 表达式
if (ts.isJsxExpression(node)) {
// 检查 JSX 表达式内部是否有忽略注释
if (node.expression && isNodeCoveredByIgnoreComment(node.expression)) {
markNodeAndChildren(node.expression);
}
}
// 特殊处理:条件表达式(三元运算符)
if (ts.isConditionalExpression(node)) {
// 检查 whenTrue 和 whenFalse 分支
if (isNodeCoveredByIgnoreComment(node.whenTrue)) {
markNodeAndChildren(node.whenTrue);
}
if (isNodeCoveredByIgnoreComment(node.whenFalse)) {
markNodeAndChildren(node.whenFalse);
}
}
// 特殊处理:二元表达式
if (ts.isBinaryExpression(node)) {
// 检查左右操作数
if (isNodeCoveredByIgnoreComment(node.left)) {
markNodeAndChildren(node.left);
}
if (isNodeCoveredByIgnoreComment(node.right)) {
markNodeAndChildren(node.right);
}
}
@ -760,6 +850,21 @@ export function performCustomChecks(
return true;
}
// 如果文件开头有忽略注释,整个文件都忽略
const fileStartComments = ts.getLeadingCommentRanges(
sourceFile.getFullText(),
0
);
if (fileStartComments) {
for (const comment of fileStartComments) {
const commentText = sourceFile.getFullText().substring(comment.pos, comment.end);
if (isIgnoreComment(commentText)) {
return true;
}
}
}
// 向上查找父节点最多3层
let current: ts.Node | undefined = node.parent;
let depth = 0;
@ -2839,7 +2944,7 @@ export function performCustomChecks(
// 普通格式的 key但本地没有 i18n 文件
addDiagnostic(
callNode,
`i18n key "${key}" 无法检查,因为未找到本地化文件: ${localePath}`,
`i18n key "${key}" 无法检查,因为找不到locales文件: ${localePath}`,
9202
);
}
@ -3368,4 +3473,4 @@ export const build = (pwd: string, args: any[]) => {
options.project = configPath;
compile(pwd, options);
}
}