UNPKG

chrome-devtools-frontend

Version:
154 lines (150 loc) • 6.81 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 A library to identify and templatize manually constructed DataGrid. */ import type {TSESTree} from '@typescript-eslint/utils'; import {isIdentifier, isIdentifierChain, isMemberExpression} from './ast.ts'; import {DomFragment} from './dom-fragment.ts'; type Identifier = TSESTree.Identifier; type Node = TSESTree.Node; type CallExpression = TSESTree.CallExpression; export const dataGrid = { create(context) { const sourceCode = context.getSourceCode(); return { methodCall( property: Identifier, firstArg: Node, _secondArg: Node|undefined, domFragment: DomFragment, _call: CallExpression) { if (domFragment.tagName !== 'devtools-data-grid') { return false; } if (isIdentifier(property, 'setRowContextMenuCallback')) { domFragment.eventListeners.push({ key: 'contextmenu', value: firstArg, }); return true; } if (isIdentifier(property, 'setStriped')) { domFragment.booleanAttributes.push({ key: 'striped', value: firstArg, }); return true; } if (isIdentifier(property, 'renderInline')) { domFragment.booleanAttributes.push({ key: 'inline', value: 'true', }); return true; } return false; }, getEvent(event: Node): string | null { switch (sourceCode.getText(event)) { case 'DataGrid.DataGrid.Events.SELECTED_NODE': case 'DataGrid.DataGrid.Events.DESELECTED_NODE': return 'select'; case 'DataGrid.DataGrid.Events.SORTING_CHANGED': return 'sort'; default: return null; } }, NewExpression(node) { if (isIdentifierChain(node.callee, ['DataGrid', 'DataGrid', 'DataGridImpl']) || isIdentifierChain(node.callee, ['DataGrid', 'ViewportDataGrid', 'ViewportDataGrid']) || isIdentifierChain(node.callee, ['DataGrid', 'SortableDataGrid', 'SortableDataGrid'])) { const domFragment = DomFragment.getOrCreate(node, sourceCode); domFragment.tagName = 'devtools-data-grid'; if (!node.arguments.length) { return; } const tableFragment = domFragment.appendChild(node.arguments[0], sourceCode); tableFragment.tagName = 'table'; const params = tableFragment.initializer ?? node.arguments[0]; if (params.type !== 'ObjectExpression') { return; } for (const property of params.properties) { if (property.type !== 'Property') { continue; } if (isIdentifier(property.key, 'displayName')) { domFragment.attributes.push({key: 'name', value: property.value}); } else if (isIdentifier(property.key, 'columns')) { const columnsFragment = tableFragment.appendChild(property.value, sourceCode); columnsFragment.tagName = 'tr'; const columns = columnsFragment.initializer ?? property.value; if (columns?.type !== 'ArrayExpression') { continue; } for (const column of columns.elements) { if (column?.type !== 'ObjectExpression') { continue; } const columnFragment = columnsFragment.appendChild(column, sourceCode); columnFragment.tagName = 'th'; for (const property of column.properties) { if (property.type !== 'Property' || isIdentifier(property.value, 'undefined')) { continue; } if (isIdentifier(property.key, ['id', 'weight', 'width'])) { columnFragment.attributes.push({key: property.key.name, value: property.value}); } else if (isIdentifier(property.key, 'align')) { const value: Node|string|null = isMemberExpression( property.value, n => isIdentifierChain(n, ['DataGrid', 'DataGrid', 'Align']), n => n.type === 'Identifier'); columnFragment.attributes.push({ key: property.key.name, value: value ? (value as Identifier).name.toLowerCase() : property.value }); } else if (isIdentifier(property.key, ['sortable', 'editable'])) { columnFragment.booleanAttributes.push({key: property.key.name, value: property.value}); } else if (isIdentifier(property.key, 'fixedWidth')) { columnFragment.booleanAttributes.push({key: 'fixed', value: property.value}); } else if (isIdentifier(property.key, 'dataType')) { const value: Node|string|null = isMemberExpression( property.value, n => isIdentifierChain(n, ['DataGrid', 'DataGrid', 'DataType']), n => n.type === 'Identifier'); columnFragment.attributes.push({ key: property.key.name, value: value ? (value as Identifier).name.toLowerCase() : property.value }); } else if (isIdentifier(property.key, ['title', 'titleDOMFragment'])) { columnFragment.textContent = property.value; } } } } else if (isIdentifier(property.key, 'refreshCallback') && !isIdentifier(property.value, 'undefined')) { domFragment.eventListeners.push({ key: 'refresh', value: property.value, }); } else if (isIdentifier(property.key, 'deleteCallback') && !isIdentifier(property.value, 'undefined')) { domFragment.eventListeners.push({ key: 'delete', value: property.value, }); } } } }, CallExpression(node: CallExpression) { if (node.callee.type !== 'MemberExpression' || !isIdentifier(node.callee.property, ['createTD', 'createTDWithClass', 'createCell'])) { return; } const domFragment = DomFragment.getOrCreate(node, sourceCode); domFragment.tagName = 'td'; if (isIdentifier(node.callee.property, 'createTDWithClass')) { domFragment.classList.push(node.arguments[0]); } }, }; } };