UNPKG

rawsql-ts

Version:

High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.

231 lines 8.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AstCommentAttachmentExtractor = void 0; exports.extractAstCommentAttachments = extractAstCommentAttachments; const SelectQuery_1 = require("../models/SelectQuery"); const Clause_1 = require("../models/Clause"); const SqlComponent_1 = require("../models/SqlComponent"); /** * Extracts comment attachment facts already represented in the SQL AST. * * @remarks This API does not infer product-specific meanings or rewrite * comments. It returns a deterministic flat list without mutating the input AST. */ class AstCommentAttachmentExtractor { constructor() { this.attachments = []; this.visited = new Set(); this.suppressedDetachedCommentArrays = new WeakSet(); } /** * Extract comment attachment facts from a SQL AST component. */ static extract(input) { if (!input) { return []; } const extractor = new AstCommentAttachmentExtractor(); extractor.visit(input); return extractor.attachments; } visit(value) { if (!value || typeof value !== "object") { return; } if (this.visited.has(value)) { return; } this.visited.add(value); if (Array.isArray(value)) { for (const item of value) { this.visit(item); } return; } if (value instanceof SqlComponent_1.SqlComponent) { this.visitSqlComponent(value); return; } } visitSqlComponent(component) { this.emitHeaderComments(component); this.emitPositionedComments(component, "before"); if (!(component instanceof SelectQuery_1.SimpleSelectQuery)) { this.emitLegacyComments(component); } if (component instanceof Clause_1.SelectItem) { this.emitPositionedComments(component, "after"); this.emitSelectItemKeywordComments(component); this.visit(component.value); this.visit(component.identifier); return; } if (component instanceof Clause_1.CommonTable) { this.visit(component.aliasExpression); this.visit(component.query); this.emitPositionedComments(component, "after"); return; } if (component instanceof Clause_1.WithClause) { this.visit(component.tables); this.emitDetachedComments(component.trailingComments); this.emitDetachedComments(component.globalComments); this.emitPositionedComments(component, "after"); return; } if (component instanceof SelectQuery_1.SimpleSelectQuery) { this.suppressWithClauseTrailingCommentsAlreadyCopiedToQuery(component); this.visit(component.withClause); this.emitLegacyComments(component); this.visit(component.selectClause); this.visit(component.fromClause); this.visit(component.whereClause); this.visit(component.groupByClause); this.visit(component.havingClause); this.visit(component.windowClause); this.visit(component.orderByClause); this.visit(component.limitClause); this.visit(component.offsetClause); this.visit(component.fetchClause); this.visit(component.forClause); this.emitPositionedComments(component, "after"); return; } if (component instanceof SelectQuery_1.BinarySelectQuery) { this.visit(component.left); this.visit(component.right); this.emitPositionedComments(component, "after"); return; } if (component instanceof SelectQuery_1.ValuesQuery) { this.visit(component.withClause); this.visit(component.tuples); this.emitPositionedComments(component, "after"); return; } if (component instanceof Clause_1.SelectClause) { this.visit(component.distinct); this.visit(component.hints); this.visit(component.items); this.emitPositionedComments(component, "after"); return; } if (component instanceof Clause_1.SourceAliasExpression) { this.visit(component.table); this.visit(component.columns); this.emitPositionedComments(component, "after"); return; } this.visitFallbackProperties(component); this.emitPositionedComments(component, "after"); } visitFallbackProperties(component) { for (const key of Object.keys(component)) { if (this.shouldSkipProperty(key)) { continue; } this.visit(component[key]); } } shouldSkipProperty(key) { return key === "comments" || key === "positionedComments" || key === "headerComments" || key === "trailingComments" || key === "globalComments" || key === "asKeywordPositionedComments" || key === "asKeywordComments" || key === "aliasPositionedComments" || key === "aliasComments" || key === "cteNameCache"; } emitHeaderComments(component) { if (!this.isSelectQuery(component)) { return; } this.emitComments(component.headerComments, "leading", component); } emitLegacyComments(component) { this.emitComments(component.comments, "detached"); } emitPositionedComments(component, position) { if (!component.positionedComments) { return; } for (const positionedComment of component.positionedComments) { if (positionedComment.position !== position) { continue; } this.emitComments(positionedComment.comments, position === "before" ? "leading" : "trailing", component); } } emitSelectItemKeywordComments(selectItem) { const itemWithCommentFields = selectItem; this.emitKeywordPositionedComments(itemWithCommentFields.asKeywordPositionedComments, selectItem, "inner"); this.emitComments(itemWithCommentFields.asKeywordComments, "inner", selectItem); this.emitKeywordPositionedComments(itemWithCommentFields.aliasPositionedComments, selectItem); this.emitComments(itemWithCommentFields.aliasComments, "trailing", selectItem); } emitKeywordPositionedComments(positionedComments, targetNode, placementOverride) { if (!positionedComments) { return; } for (const positionedComment of positionedComments) { const placement = placementOverride !== null && placementOverride !== void 0 ? placementOverride : (positionedComment.position === "before" ? "leading" : "trailing"); this.emitComments(positionedComment.comments, placement, targetNode); } } emitDetachedComments(comments) { if (comments && this.suppressedDetachedCommentArrays.has(comments)) { return; } this.emitComments(comments, "detached"); } emitComments(comments, placement, targetNode) { if (!comments) { return; } for (const text of comments) { this.attachments.push({ text, sourceOrder: this.attachments.length, placement, ...(targetNode ? { targetNode } : {}) }); } } isSelectQuery(component) { return "__selectQueryType" in component && component.__selectQueryType === "SelectQuery"; } suppressWithClauseTrailingCommentsAlreadyCopiedToQuery(query) { var _a; const trailingComments = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.trailingComments; if (!trailingComments || !query.comments) { return; } if (this.containsCommentSequence(query.comments, trailingComments)) { this.suppressedDetachedCommentArrays.add(trailingComments); } } containsCommentSequence(source, candidate) { if (candidate.length === 0) { return true; } for (let start = 0; start <= source.length - candidate.length; start++) { const matches = candidate.every((comment, index) => source[start + index] === comment); if (matches) { return true; } } return false; } } exports.AstCommentAttachmentExtractor = AstCommentAttachmentExtractor; /** * Extract comment attachment facts from a SQL AST component. */ function extractAstCommentAttachments(input) { return AstCommentAttachmentExtractor.extract(input); } //# sourceMappingURL=AstCommentAttachmentExtractor.js.map