UNPKG

chrome-devtools-frontend

Version:
147 lines (143 loc) 6.01 kB
// Copyright 2025 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @fileoverview Library to identify and templatize manually DOM API calls. */ import type {TSESTree} from '@typescript-eslint/utils'; import {isIdentifier, isLiteral} from './ast.ts'; import {DomFragment} from './dom-fragment.ts'; type CallExpression = TSESTree.CallExpression; type Identifier = TSESTree.Identifier; type Node = TSESTree.Node; export const domApi = { create: function(context) { const sourceCode = context.getSourceCode(); return { propertyAssignment(property: Identifier, propertyValue: Node, domFragment: DomFragment) { if (isIdentifier(property, 'className')) { domFragment.classList.push(propertyValue); return true; } if (isIdentifier(property, ['textContent', 'innerHTML', 'innerText'])) { domFragment.textContent = propertyValue; return true; } if (isIdentifier(property, [ 'alt', 'draggable', 'height', 'hidden', 'href', 'id', 'name', 'placeholder', 'rel', 'role', 'scope', 'slot', 'spellcheck', 'src', 'tabIndex', 'title', 'type', 'value', 'width' ])) { domFragment.attributes.push({key: property.name.toLowerCase(), value: propertyValue}); return true; } if (isIdentifier(property, ['checked', 'disabled'])) { domFragment.booleanAttributes.push({key: property.name.toLowerCase(), value: propertyValue}); return true; } return false; }, propertyMethodCall(property: Identifier, method: Node, firstArg: Node, domFragment: DomFragment): boolean { if (isIdentifier(property, 'classList') && isIdentifier(method, 'add')) { domFragment.classList.push(firstArg); return true; } return false; }, subpropertyAssignment(property: Identifier, subproperty: Node, subpropertyValue: Node, domFragment: DomFragment) { if (isIdentifier(property, 'style') && subproperty.type === 'Identifier') { const property = subproperty.name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); if (subpropertyValue.type !== 'SpreadElement') { domFragment.style.push({ key: property, value: subpropertyValue, }); return true; } } if (isIdentifier(property, 'dataset') && subproperty.type === 'Identifier') { const property = 'data-' + subproperty.name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); if (subpropertyValue.type !== 'SpreadElement') { domFragment.attributes.push({ key: property, value: subpropertyValue, }); return true; } } return false; }, methodCall( property: Identifier, firstArg: Node, secondArg: Node|undefined, domFragment: DomFragment, call: CallExpression) { if (isIdentifier(property, 'setAttribute')) { const attribute = firstArg; const value = secondArg; if (attribute.type === 'Literal' && value && value.type !== 'SpreadElement' && attribute.value) { domFragment.attributes.push({key: attribute.value.toString(), value}); return true; } } if (isIdentifier(property, 'appendChild')) { domFragment.appendChild(firstArg, sourceCode); return true; } if (domFragment.tagName === 'select' && isIdentifier(property, 'add')) { if (secondArg) { const index = domFragment.children.indexOf(DomFragment.getOrCreate(secondArg, sourceCode)); domFragment.insertChildAt(secondArg, index, sourceCode); } else { domFragment.appendChild(firstArg, sourceCode); } return true; } if (isIdentifier(property, 'append')) { for (const child of call.arguments) { domFragment.appendChild(child, sourceCode); } return true; } if (isIdentifier(property, 'prepend')) { for (const child of call.arguments) { domFragment.insertChildAt(child, 0, sourceCode); } return true; } if (isIdentifier(property, 'insertBefore') && secondArg) { const index = domFragment.children.indexOf(DomFragment.getOrCreate(secondArg, sourceCode)); if (index !== -1) { for (const reference of domFragment.children[index].references) { if (reference.node === secondArg) { reference.processed = true; } } domFragment.insertChildAt(firstArg, index, sourceCode); return true; } } if (isIdentifier(property, 'insertAdjacentElement') && secondArg) { if (domFragment.parent) { const index = domFragment.parent.children.indexOf(domFragment); if (isLiteral(firstArg, 'afterend')) { domFragment.parent.insertChildAt(secondArg, index + 1, sourceCode); return true; } if (isLiteral(firstArg, 'beforebegin')) { domFragment.parent.insertChildAt(secondArg, index, sourceCode); return true; } } } return false; }, MemberExpression(node) { if (isIdentifier(node.object, 'document') && isIdentifier(node.property, 'createElement') && node.parent.type === 'CallExpression' && node.parent.callee === node) { const domFragment = DomFragment.getOrCreate(node.parent, sourceCode); if (node.parent.arguments.length >= 1 && node.parent.arguments[0].type === 'Literal') { domFragment.tagName = node.parent.arguments[0].value; } } }, }; } };