Skip to content

Commit

Permalink
fix: handle non-literal tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
mshanemc committed Jan 26, 2023
1 parent d075e95 commit 0d3cdac
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 14 deletions.
24 changes: 20 additions & 4 deletions src/rules/no-missing-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
*/
/* eslint-disable complexity */

import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
import { ASTUtils, AST_NODE_TYPES, ESLintUtils, ParserServices, TSESTree } from '@typescript-eslint/utils';
import { Messages, SfError, StructuredMessage } from '@salesforce/core';
import * as ts from 'typescript';

const methods = ['createError', 'createWarning', 'createInfo', 'getMessage', 'getMessages'] as const;

Expand All @@ -33,6 +34,8 @@ export const noMissingMessages = ESLintUtils.RuleCreator.withoutDocs({

const loadedMessages = new Map<string, Messages<string>>();
const loadedMessageBundles = new Map<string, string>();
const parserServices = ESLintUtils.getParserServices(context);

return {
// load any messages, by const name, that are loaded in the file
VariableDeclarator(node): void {
Expand Down Expand Up @@ -73,8 +76,8 @@ export const noMissingMessages = ESLintUtils.RuleCreator.withoutDocs({
const bundleConstant = node.callee.object.name;
const messageKey = node.arguments[0].value;
const fileKey = loadedMessageBundles.get(bundleConstant);
const messageTokensCount = getTokensCount(node.arguments[1]);
const actionTokensCount = getTokensCount(node.arguments[2]);
const messageTokensCount = getTokensCount(parserServices, node.arguments[1]);
const actionTokensCount = getTokensCount(parserServices, node.arguments[2]);
let result: StructuredMessage | SfError | string | string[];
try {
// execute some method on Messages so we can inspect the result
Expand Down Expand Up @@ -134,13 +137,26 @@ const placeHolderersRegex = new RegExp(/(%s)|(%d)|(%i)|(%f)|(%j)|(%o)|(%O)|(%c)/
const isMessagesMethod = (method: string): method is (typeof methods)[number] =>
methods.includes(method as (typeof methods)[number]);

const getTokensCount = (node?: TSESTree.Node): number => {
const getTokensCount = (parserServices: ParserServices, node?: TSESTree.Node): number => {
if (!node) {
return 0;
}
if (ASTUtils.isNodeOfType(AST_NODE_TYPES.ArrayExpression)(node)) {
return node.elements.length ?? 0;
}
const realNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const checker = parserServices.program.getTypeChecker();

const underlyingNode = checker.getSymbolAtLocation(realNode).getDeclarations()[0];
// the literal value might not be an array, but it might a reference to an array
if (
underlyingNode &&
ts.isVariableDeclaration(underlyingNode) &&
ts.isArrayLiteralExpression(underlyingNode.initializer)
) {
return underlyingNode.initializer.elements.length;
}

return 0;
};

Expand Down
6 changes: 6 additions & 0 deletions test/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright (c) 2023, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
46 changes: 36 additions & 10 deletions test/rules/no-missing-messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { noMissingMessages } from '../../src/rules/no-missing-messages';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: path.join(path.join(__dirname, '..')),
},
});

const fileKey = 'test';
Expand All @@ -18,7 +22,6 @@ ruleTester.run('noMissingMessages', noMissingMessages, {
valid: [
{
name: 'correct examples with getMessage',
filename: path.normalize('src/commands/foo.ts'),
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
Expand All @@ -28,11 +31,20 @@ console.log(messages.getMessages('array'));
console.log(messages.getMessage('basic2Placeholders', ['foo', 'bar']));
console.log(messages.getMessages('arrayWith3Placeholders', ['a', 'b', 'c']));
console.log(messages.getMessages('nested.something'));
`,
},
{
name: 'placeholders can be a reference',
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('eslint-plugin-sf-plugin', 'test');
const tokens = ['foo', 'bar']
console.log(messages.getMessage('basic2Placeholders', tokens));
`,
},
{
name: 'creating an error with just a key',
filename: path.normalize('src/commands/foo.ts'),
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
Expand All @@ -43,7 +55,6 @@ console.log(messages.createError('eWith3Placeholders', ['a', 'b', 'c']));
},
{
name: 'error with Actions',
filename: path.normalize('src/commands/foo.ts'),
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
Expand All @@ -53,7 +64,6 @@ console.log(messages.createError('eWithActionsHaving4Placeholders', undefined, [
},
{
name: 'info with Actions',
filename: path.normalize('src/commands/foo.ts'),
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
Expand All @@ -66,7 +76,6 @@ console.log(messages.createInfo('iWithActions', undefined, ['a']));
invalid: [
{
name: 'message has placeholders but no tokens are passed',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'placeholders',
Expand All @@ -87,7 +96,6 @@ console.log(messages.getMessage('basic2Placeholders', []));
},
{
name: 'message has placeholders wrong number of tokens are passed',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'placeholders',
Expand All @@ -108,7 +116,6 @@ console.log(messages.getMessage('basic2Placeholders', ['a', 'b', 'c']));
},
{
name: 'messages with wrong number of placeholders',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'placeholders',
Expand All @@ -125,11 +132,32 @@ Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('eslint-plugin-sf-plugin', 'test');
console.log(messages.getMessages('arrayWith3Placeholders', ['a']));
console.log(messages.getMessages('arrayWith3Placeholders', ['a', 'b', ]));
`,
},
{
name: 'messages with wrong number of placeholders as variable',
errors: [
{
messageId: 'placeholders',
data: { placeholderCount: 3, argumentCount: 1, fileKey, messageKey: 'arrayWith3Placeholders' },
},
{
messageId: 'placeholders',
data: { placeholderCount: 3, argumentCount: 2, fileKey, messageKey: 'arrayWith3Placeholders' },
},
],
code: `
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('eslint-plugin-sf-plugin', 'test');
const oneToken = ['a'];
const twoTokens = ['a', 'b'];
console.log(messages.getMessages('arrayWith3Placeholders', oneToken));
console.log(messages.getMessages('arrayWith3Placeholders', twoTokens));
`,
},
{
name: 'error with actions with wrong number of placeholders',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'actionPlaceholders',
Expand All @@ -155,7 +183,6 @@ console.log(messages.createError('eWithActionsHaving4Placeholders'));
},
{
name: 'createInfo missing message',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'missing',
Expand All @@ -171,7 +198,6 @@ console.log(messages.createInfo('foo', undefined, ['a']));
},
{
name: 'createInfo wrong tokens',
filename: path.normalize('src/commands/foo.ts'),
errors: [
{
messageId: 'placeholders',
Expand Down

0 comments on commit 0d3cdac

Please sign in to comment.