chrome-devtools-frontend
Version:
Chrome DevTools UI
168 lines (151 loc) • 5.51 kB
text/typescript
// 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();
},
};
},
});