mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-10 14:16:15 -06:00
* Fix localized tool markdownDescriptions And add a lint rule * Just keep this the same * Fixes
129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { TSESTree } from '@typescript-eslint/utils';
|
|
import * as eslint from 'eslint';
|
|
import * as visitorKeys from 'eslint-visitor-keys';
|
|
import type * as ESTree from 'estree';
|
|
|
|
const MESSAGE_ID = 'noLocalizedModelDescription';
|
|
type NodeWithChildren = TSESTree.Node & {
|
|
[key: string]: TSESTree.Node | TSESTree.Node[] | null | undefined;
|
|
};
|
|
type PropertyKeyNode = TSESTree.Property['key'] | TSESTree.MemberExpression['property'];
|
|
type AssignmentTarget = TSESTree.AssignmentExpression['left'];
|
|
|
|
export default new class NoLocalizedModelDescriptionRule implements eslint.Rule.RuleModule {
|
|
meta: eslint.Rule.RuleMetaData = {
|
|
messages: {
|
|
[MESSAGE_ID]: 'modelDescription values describe behavior to the language model and must not use localized strings.'
|
|
},
|
|
type: 'problem',
|
|
schema: false
|
|
};
|
|
|
|
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
|
const reportIfLocalized = (expression: TSESTree.Expression | null | undefined) => {
|
|
if (expression && containsLocalizedCall(expression)) {
|
|
context.report({ node: expression, messageId: MESSAGE_ID });
|
|
}
|
|
};
|
|
|
|
return {
|
|
Property: (node: ESTree.Property) => {
|
|
const propertyNode = node as TSESTree.Property;
|
|
if (!isModelDescriptionKey(propertyNode.key, propertyNode.computed)) {
|
|
return;
|
|
}
|
|
reportIfLocalized(propertyNode.value as TSESTree.Expression);
|
|
},
|
|
AssignmentExpression: (node: ESTree.AssignmentExpression) => {
|
|
const assignment = node as TSESTree.AssignmentExpression;
|
|
if (!isModelDescriptionAssignmentTarget(assignment.left)) {
|
|
return;
|
|
}
|
|
reportIfLocalized(assignment.right);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
function isModelDescriptionKey(key: PropertyKeyNode, computed: boolean | undefined): boolean {
|
|
if (!computed && key.type === 'Identifier') {
|
|
return key.name === 'modelDescription';
|
|
}
|
|
if (key.type === 'Literal' && key.value === 'modelDescription') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isModelDescriptionAssignmentTarget(target: AssignmentTarget): target is TSESTree.MemberExpression {
|
|
if (target.type === 'MemberExpression') {
|
|
return isModelDescriptionKey(target.property, target.computed);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function containsLocalizedCall(expression: TSESTree.Expression): boolean {
|
|
let found = false;
|
|
|
|
const visit = (node: TSESTree.Node) => {
|
|
if (found) {
|
|
return;
|
|
}
|
|
|
|
if (isLocalizeCall(node)) {
|
|
found = true;
|
|
return;
|
|
}
|
|
|
|
for (const key of visitorKeys.KEYS[node.type] ?? []) {
|
|
const value = (node as NodeWithChildren)[key];
|
|
if (Array.isArray(value)) {
|
|
for (const child of value) {
|
|
if (child) {
|
|
visit(child);
|
|
if (found) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else if (value) {
|
|
visit(value);
|
|
}
|
|
}
|
|
};
|
|
|
|
visit(expression);
|
|
return found;
|
|
}
|
|
|
|
function isLocalizeCall(node: TSESTree.Node): boolean {
|
|
if (node.type === 'CallExpression') {
|
|
return isLocalizeCallee(node.callee);
|
|
}
|
|
if (node.type === 'ChainExpression') {
|
|
return isLocalizeCall(node.expression);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function isLocalizeCallee(callee: TSESTree.CallExpression['callee']): boolean {
|
|
if (callee.type === 'Identifier') {
|
|
return callee.name === 'localize';
|
|
}
|
|
if (callee.type === 'MemberExpression') {
|
|
if (!callee.computed && callee.property.type === 'Identifier') {
|
|
return callee.property.name === 'localize';
|
|
}
|
|
if (callee.property.type === 'Literal' && callee.property.value === 'localize') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|