mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 04:09:28 +00:00
Fix localized tool markdownDescriptions (#277589)
* Fix localized tool markdownDescriptions And add a lint rule * Just keep this the same * Fixes
This commit is contained in:
128
.eslint-plugin-local/code-no-localized-model-description.ts
Normal file
128
.eslint-plugin-local/code-no-localized-model-description.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user