UNPKG

chrome-devtools-frontend

Version:
168 lines (151 loc) 5.51 kB
// Copyright 2024 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. import fs from 'node:fs'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; import {createRule} from './utils/ruleCreator.ts'; // @ts-expect-error const filename = fileURLToPath(import.meta.url); const FILE = 'front_end/ui/visual_logging/KnownContextValues.ts'; const FRONT_END_PARENT_FOLDER = path.join(filename, '..', '..', '..', '..'); const ABSOLUTE_FILE_PATH = path.join(FRONT_END_PARENT_FOLDER, FILE); const LICENSE_HEADER = `// Copyright 2024 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. `; let formattedValues = new Set(); const statsCache = { mtimeMs: Infinity, size: Infinity, }; function writeToFile() { const finalContents = LICENSE_HEADER + 'export const knownContextValues = new Set([\n' + [...formattedValues].sort().join('\n') + '\n]);\n'; fs.writeFileSync(ABSOLUTE_FILE_PATH, finalContents, 'utf-8'); const stats = fs.statSync(ABSOLUTE_FILE_PATH); statsCache.mtimeMs = stats.mtimeMs; statsCache.size = stats.size; } function updateLocalCacheIfNeeded() { const stats = fs.statSync(ABSOLUTE_FILE_PATH); let needsUpdate = false; if (stats.mtimeMs !== statsCache.mtimeMs) { statsCache.mtimeMs = stats.mtimeMs; needsUpdate = true; } if (stats.size !== statsCache.size) { statsCache.size = stats.size; needsUpdate = true; } if (needsUpdate) { formattedValues = new Set( fs.readFileSync(ABSOLUTE_FILE_PATH, 'utf-8').split('\n').filter(l => l.startsWith(' \'')), ); } } export default createRule({ name: 'jslog-context-list', meta: { type: 'problem', docs: { description: 'Puts jslog context values into KnownContextValues.ts file', category: 'Possible Errors', }, messages: { unknownJslogContextValue: 'Found jslog context value \'{{ value }}\' that is not listed in ' + FILE, }, fixable: 'code', schema: [], // no options }, defaultOptions: [], create: function(context) { let valuesAdded = false; const checkValue = (value, node) => { if (typeof value !== 'string') { return; } if (!value.length) { return; } const formattedValue = ' ' + JSON.stringify(value).replaceAll('"', '\'') + ','; if (formattedValues.has(formattedValue)) { return; } formattedValues.add(formattedValue); valuesAdded = true; if (process.env.ESLINT_FAIL_ON_UNKNOWN_JSLOG_CONTEXT_VALUE) { context.report({ node, messageId: 'unknownJslogContextValue', data: {value}, }); } }; const checkPropertyValue = (propertyName, node) => { for (const property of node?.properties || []) { if (property.key?.name === propertyName || property.key?.value === propertyName) { checkValue(property.value?.value, node); } } }; updateLocalCacheIfNeeded(); return { CallExpression(node) { const firstArg = node.arguments[0]; if (!firstArg) { return; } if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'VisualLogging') { if (firstArg.type === 'Literal') { checkValue(firstArg.value, node); } } else if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') { const propertyName = node.callee.property.name; if (propertyName === 'registerActionExtension') { checkPropertyValue('actionId', firstArg); } else if (propertyName === 'registerViewExtension') { checkPropertyValue('id', firstArg); } else if (propertyName === 'registerSettingExtension') { checkPropertyValue('settingName', firstArg); } else if (propertyName === 'createSetting') { if (firstArg.type === 'Literal') { checkValue(firstArg.value, node); } } } }, ObjectExpression(node) { checkPropertyValue('jslogContext', node); }, VariableDeclarator(node) { if (node.id.type === 'Identifier' && node.id.name === 'generatedProperties' && node.init?.type === 'ArrayExpression') { for (const element of node.init.elements) { checkPropertyValue('name', element); } } if (node.id.type === 'Identifier' && node.id.name === 'generatedAliasesFor' && node.init?.type === 'NewExpression') { const firstArg = node.init?.arguments?.[0]; const elements = firstArg.type === 'ArrayExpression' ? firstArg.elements : []; for (const outerElement of elements) { const innerElements = outerElement?.type === 'ArrayExpression' ? outerElement.elements : []; for (const innerElement of innerElements) { if (innerElement && 'value' in innerElement) { checkValue(innerElement.value, innerElement); } } } } }, 'Program:exit'() { if (process.env.ESLINT_FAIL_ON_UNKNOWN_JSLOG_CONTEXT_VALUE || !valuesAdded) { return; } writeToFile(); }, }; }, });