| /** | 
|  * @typedef { { exceptions?: string[] } } CommentParserConfig | 
|  * @typedef { (comment: ParsedHTMLComment) => void } HTMLCommentVisitor | 
|  * @typedef { { includeDirectives?: boolean } } CommentVisitorOption | 
|  * | 
|  * @typedef { Token & { type: 'HTMLCommentOpen' } } HTMLCommentOpen | 
|  * @typedef { Token & { type: 'HTMLCommentOpenDecoration' } } HTMLCommentOpenDecoration | 
|  * @typedef { Token & { type: 'HTMLCommentValue' } } HTMLCommentValue | 
|  * @typedef { Token & { type: 'HTMLCommentClose' } } HTMLCommentClose | 
|  * @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration | 
|  * @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment | 
|  */ | 
| // ----------------------------------------------------------------------------- | 
| // Requirements | 
| // ----------------------------------------------------------------------------- | 
|   | 
| const utils = require('./') | 
|   | 
| // ------------------------------------------------------------------------------ | 
| // Helpers | 
| // ------------------------------------------------------------------------------ | 
|   | 
| const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/ | 
| const IE_CONDITIONAL_IF = /^\[if\s+/ | 
| const IE_CONDITIONAL_ENDIF = /\[endif\]$/ | 
|   | 
| /** @type { 'HTMLCommentOpen' } */ | 
| const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen' | 
| /** @type { 'HTMLCommentOpenDecoration' } */ | 
| const TYPE_HTML_COMMENT_OPEN_DECORATION = 'HTMLCommentOpenDecoration' | 
| /** @type { 'HTMLCommentValue' } */ | 
| const TYPE_HTML_COMMENT_VALUE = 'HTMLCommentValue' | 
| /** @type { 'HTMLCommentClose' } */ | 
| const TYPE_HTML_COMMENT_CLOSE = 'HTMLCommentClose' | 
| /** @type { 'HTMLCommentCloseDecoration' } */ | 
| const TYPE_HTML_COMMENT_CLOSE_DECORATION = 'HTMLCommentCloseDecoration' | 
|   | 
| /** | 
|  * @param {HTMLComment} comment | 
|  * @returns {boolean} | 
|  */ | 
| function isCommentDirective(comment) { | 
|   return COMMENT_DIRECTIVE.test(comment.value) | 
| } | 
|   | 
| /** | 
|  * @param {HTMLComment} comment | 
|  * @returns {boolean} | 
|  */ | 
| function isIEConditionalComment(comment) { | 
|   return ( | 
|     IE_CONDITIONAL_IF.test(comment.value) || | 
|     IE_CONDITIONAL_ENDIF.test(comment.value) | 
|   ) | 
| } | 
|   | 
| /** | 
|  * Define HTML comment parser | 
|  * | 
|  * @param {SourceCode} sourceCode The source code instance. | 
|  * @param {CommentParserConfig | null} config The config. | 
|  * @returns { (node: Token) => (ParsedHTMLComment | null) } HTML comment parser. | 
|  */ | 
| function defineParser(sourceCode, config) { | 
|   config = config || {} | 
|   | 
|   const exceptions = config.exceptions || [] | 
|   | 
|   /** | 
|    * Get a open decoration string from comment contents. | 
|    * @param {string} contents comment contents | 
|    * @returns {string} decoration string | 
|    */ | 
|   function getOpenDecoration(contents) { | 
|     let decoration = '' | 
|     for (const exception of exceptions) { | 
|       const length = exception.length | 
|       let index = 0 | 
|       while (contents.startsWith(exception, index)) { | 
|         index += length | 
|       } | 
|       const exceptionLength = index | 
|       if (decoration.length < exceptionLength) { | 
|         decoration = contents.slice(0, exceptionLength) | 
|       } | 
|     } | 
|     return decoration | 
|   } | 
|   | 
|   /** | 
|    * Get a close decoration string from comment contents. | 
|    * @param {string} contents comment contents | 
|    * @returns {string} decoration string | 
|    */ | 
|   function getCloseDecoration(contents) { | 
|     let decoration = '' | 
|     for (const exception of exceptions) { | 
|       const length = exception.length | 
|       let index = contents.length | 
|       while (contents.endsWith(exception, index)) { | 
|         index -= length | 
|       } | 
|       const exceptionLength = contents.length - index | 
|       if (decoration.length < exceptionLength) { | 
|         decoration = contents.slice(index) | 
|       } | 
|     } | 
|     return decoration | 
|   } | 
|   | 
|   /** | 
|    * Parse HTMLComment. | 
|    * @param {ASTToken} node a comment token | 
|    * @returns {HTMLComment | null} the result of HTMLComment tokens. | 
|    */ | 
|   return function parseHTMLComment(node) { | 
|     if (node.type !== 'HTMLComment') { | 
|       // Is not HTMLComment | 
|       return null | 
|     } | 
|   | 
|     const htmlCommentText = sourceCode.getText(node) | 
|   | 
|     if ( | 
|       !htmlCommentText.startsWith('<!--') || | 
|       !htmlCommentText.endsWith('-->') | 
|     ) { | 
|       // Is not normal HTML Comment | 
|       // e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment" | 
|       return null | 
|     } | 
|   | 
|     let valueText = htmlCommentText.slice(4, -3) | 
|     const openDecorationText = getOpenDecoration(valueText) | 
|     valueText = valueText.slice(openDecorationText.length) | 
|     const firstCharIndex = valueText.search(/\S/) | 
|     const beforeSpace = | 
|       firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText | 
|     valueText = valueText.slice(beforeSpace.length) | 
|   | 
|     const closeDecorationText = getCloseDecoration(valueText) | 
|     if (closeDecorationText) { | 
|       valueText = valueText.slice(0, -closeDecorationText.length) | 
|     } | 
|     const lastCharIndex = valueText.search(/\S\s*$/) | 
|     const afterSpace = | 
|       lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText | 
|     if (afterSpace) { | 
|       valueText = valueText.slice(0, -afterSpace.length) | 
|     } | 
|   | 
|     let tokenIndex = node.range[0] | 
|     /** | 
|      * @param {string} type | 
|      * @param {string} value | 
|      * @returns {any} | 
|      */ | 
|     const createToken = (type, value) => { | 
|       /** @type {Range} */ | 
|       const range = [tokenIndex, tokenIndex + value.length] | 
|       tokenIndex = range[1] | 
|       /** @type {SourceLocation} */ | 
|       let loc | 
|       return { | 
|         type, | 
|         value, | 
|         range, | 
|         get loc() { | 
|           if (loc) { | 
|             return loc | 
|           } | 
|           return (loc = { | 
|             start: sourceCode.getLocFromIndex(range[0]), | 
|             end: sourceCode.getLocFromIndex(range[1]) | 
|           }) | 
|         } | 
|       } | 
|     } | 
|   | 
|     /** @type {HTMLCommentOpen} */ | 
|     const open = createToken(TYPE_HTML_COMMENT_OPEN, '<!--') | 
|     /** @type {HTMLCommentOpenDecoration | null} */ | 
|     const openDecoration = openDecorationText | 
|       ? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText) | 
|       : null | 
|     tokenIndex += beforeSpace.length | 
|     /** @type {HTMLCommentValue | null} */ | 
|     const value = valueText | 
|       ? createToken(TYPE_HTML_COMMENT_VALUE, valueText) | 
|       : null | 
|     tokenIndex += afterSpace.length | 
|     /** @type {HTMLCommentCloseDecoration | null} */ | 
|     const closeDecoration = closeDecorationText | 
|       ? createToken(TYPE_HTML_COMMENT_CLOSE_DECORATION, closeDecorationText) | 
|       : null | 
|     /** @type {HTMLCommentClose} */ | 
|     const close = createToken(TYPE_HTML_COMMENT_CLOSE, '-->') | 
|   | 
|     return { | 
|       /** HTML comment open (`<!--`) */ | 
|       open, | 
|       /** decoration of the start of HTML comments. (`*****` when `<!--*****`) */ | 
|       openDecoration, | 
|       /** value of HTML comment. whitespaces and other tokens are not included. */ | 
|       value, | 
|       /** decoration of the end of HTML comments.  (`*****` when `*****-->`) */ | 
|       closeDecoration, | 
|       /** HTML comment close (`-->`) */ | 
|       close | 
|     } | 
|   } | 
| } | 
|   | 
| /** | 
|  * Define HTML comment visitor | 
|  * | 
|  * @param {RuleContext} context The rule context. | 
|  * @param {CommentParserConfig | null} config The config. | 
|  * @param {HTMLCommentVisitor} visitHTMLComment The HTML comment visitor. | 
|  * @param {CommentVisitorOption} [visitorOption] The option for visitor. | 
|  * @returns {RuleListener} HTML comment visitor. | 
|  */ | 
| function defineVisitor(context, config, visitHTMLComment, visitorOption) { | 
|   return { | 
|     Program(node) { | 
|       visitorOption = visitorOption || {} | 
|       if (utils.hasInvalidEOF(node)) { | 
|         return | 
|       } | 
|       if (!node.templateBody) { | 
|         return | 
|       } | 
|       const parse = defineParser(context.getSourceCode(), config) | 
|   | 
|       for (const comment of node.templateBody.comments) { | 
|         if (comment.type !== 'HTMLComment') { | 
|           continue | 
|         } | 
|         if (!visitorOption.includeDirectives && isCommentDirective(comment)) { | 
|           // ignore directives | 
|           continue | 
|         } | 
|         if (isIEConditionalComment(comment)) { | 
|           // ignore IE conditional | 
|           continue | 
|         } | 
|   | 
|         const tokens = parse(comment) | 
|         if (tokens) { | 
|           visitHTMLComment(tokens) | 
|         } | 
|       } | 
|     } | 
|   } | 
| } | 
|   | 
| module.exports = { | 
|   defineVisitor | 
| } |