UNPKG

@ui5/linter

Version:

A static code analysis tool for UI5

109 lines (107 loc) 5.01 kB
import ts from "typescript"; export function findDirectives(sourceFile, metadata) { metadata.directives ??= new Set(); const possibleDirectives = collectPossibleDirectives(sourceFile); if (possibleDirectives.size === 0) { return; } const sourceText = sourceFile.getFullText(); traverseAndFindDirectives(sourceFile, sourceText, possibleDirectives, metadata.directives); } function traverseAndFindDirectives(node, sourceText, possibleDirectives, confirmedDirectives) { findDirectivesAroundNode(node, sourceText, possibleDirectives, confirmedDirectives); node.getChildren().forEach((child) => { traverseAndFindDirectives(child, sourceText, possibleDirectives, confirmedDirectives); }); } function findDirectivesAroundNode(node, sourceText, possibleDirectives, confirmedDirectives) { /* // This is a comment // ui5lint-disable myCallExpression() // ui5lint-enable // This is a comment */ for (const directive of possibleDirectives) { if (directive.pos >= node.getFullStart() && directive.pos + directive.length <= node.getStart()) { const leadingComments = ts.getLeadingCommentRanges(sourceText, node.getFullStart()); if (leadingComments?.length) { leadingComments.some((comment) => { if (comment.pos === directive.pos) { possibleDirectives.delete(directive); confirmedDirectives.add(directive); return true; } return false; }); break; } } else if (directive.pos > node.getEnd()) { const trailingComments = ts.getTrailingCommentRanges(sourceText, node.getEnd()); if (trailingComments?.length) { trailingComments.some((comment) => { if (comment.pos === directive.pos && comment.end === directive.pos + directive.length) { possibleDirectives.delete(directive); confirmedDirectives.add(directive); return true; } return false; }); break; } } } } /* Match things like: // ui5lint-disable-next-line no-deprecated-api, no-global // ui5lint-enable-next-line // ui5lint-enable-line // ui5lint-enable-line -- my description /* ui5lint-enable-line -- my description *\/ /* ui5lint-disable no-deprecated-api, no-global *\/ Must not match things like: ```` // ui5lint-disable-next-line -- my description expression(); ```` The above is a single line comment with a description followed by some code in the next line. The regex below is designed to match single- and multi-line comments, however it splits both cases into basically two regex combined with an OR operator. If we would try to match the above with a single regex instead (matching /* and // simultaneously), it would be impossible to know whether the code in the second line is part of the directive's description or not. */ /* eslint-disable max-len */ const DIRECTIVE_REGEX = /* | ----------------------------------------------- Multi-line comments ----------------------------------------------- | ------------------------------------------ Single-line comments ----------------------------------------------------------------- | */ /\/\*\s*ui5lint-(enable|disable)(?:-((?:next-)?line))?(\s+(?:[\w-]+\s*,\s*)*(?:\s*[\w-]+))?\s*,?\s*(?:--[\s\S]*?)?\*\/|\/\/\s*ui5lint-(enable|disable)(?:-((?:next-)?line))?([ \t]+(?:[\w-]+[ \t]*,[ \t]*)*(?:[ \t]*[\w-]+))?[ \t]*,?[ \t]*(?:--.*)?$/mg; /* |CG #1: action | | CG #2: scope | CG #3: rules |Dangling,| Description | |CG #4: action | | CG #5: scope | CG #6: rules |Dangling,| Description | */ /* eslint-enable max-len */ export function collectPossibleDirectives(sourceFile) { const text = sourceFile.getFullText(); const comments = new Set(); const matches = text.matchAll(DIRECTIVE_REGEX); for (const match of matches) { const action = (match[1] ?? match[4]); const scope = (match[2] ?? match[5]); const rules = match[3] ?? match[6]; const pos = match.index; const length = match[0].length; let ruleNames = rules?.split(",") ?? []; ruleNames = ruleNames.map((rule) => rule.trim()); const { line, character: column } = sourceFile.getLineAndCharacterOfPosition(pos + length); comments.add({ action, scope, ruleNames, pos, length, // Typescript positions are all zero-based line: line + 1, column: column + 1, }); } return comments; } //# sourceMappingURL=directives.js.map