Read a `cf_attribute`'s value by name from a tag node's direct `cf_attribute`/`cf_tag_attributes` children.
(tag: SyntaxNode, attrName: string)
| 430 | |
| 431 | /** Read a `cf_attribute`'s value by name from a tag node's direct `cf_attribute`/`cf_tag_attributes` children. */ |
| 432 | private tagAttr(tag: SyntaxNode, attrName: string): string | undefined { |
| 433 | const attrs: SyntaxNode[] = []; |
| 434 | for (let i = 0; i < tag.namedChildCount; i++) { |
| 435 | const child = tag.namedChild(i); |
| 436 | if (!child) continue; |
| 437 | if (child.type === 'cf_attribute') attrs.push(child); |
| 438 | else if (child.type === 'cf_tag_attributes') { |
| 439 | for (let j = 0; j < child.namedChildCount; j++) { |
| 440 | const inner = child.namedChild(j); |
| 441 | if (inner?.type === 'cf_attribute') attrs.push(inner); |
| 442 | } |
| 443 | } |
| 444 | } |
| 445 | for (const attr of attrs) { |
| 446 | const nameNode = attr.namedChildren.find((c: SyntaxNode) => c.type === 'cf_attribute_name'); |
| 447 | if (!nameNode) continue; |
| 448 | const text = this.source.substring(nameNode.startIndex, nameNode.endIndex); |
| 449 | if (text.toLowerCase() !== attrName.toLowerCase()) continue; |
| 450 | // Values come wrapped as `quoted_cf_attribute_value` (name="init") or bare |
| 451 | // `cf_attribute_value` (name=init — legal and common in older CFML). |
| 452 | const valueWrapper = attr.namedChildren.find( |
| 453 | (c: SyntaxNode) => c.type === 'quoted_cf_attribute_value' || c.type === 'cf_attribute_value' |
| 454 | ); |
| 455 | const valueNode = valueWrapper?.namedChildren.find((c: SyntaxNode) => c.type === 'attribute_value'); |
| 456 | if (!valueNode) return ''; |
| 457 | return this.source.substring(valueNode.startIndex, valueNode.endIndex); |
| 458 | } |
| 459 | return undefined; |
| 460 | } |
| 461 | |
| 462 | private componentNameFromPath(): string { |
| 463 | const fileName = this.filePath.split(/[/\\]/).pop() || this.filePath; |
no outgoing calls
no test coverage detected