Skip to content

Commit

Permalink
Merge pull request #358 from salesforcecli/sm/simplify-message-imports
Browse files Browse the repository at this point in the history
Sm/simplify-message-imports
  • Loading branch information
mdonnalley authored Dec 14, 2023
2 parents cdd3cd2 + 4ac2e33 commit 9a4f426
Show file tree
Hide file tree
Showing 98 changed files with 1,700 additions and 1,437 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/externalLint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
command:
required: false
type: string
default: yarn eslint "src/**/*.ts"
default: yarn eslint "src/**/*.ts" --fix
description: 'command to execute (ex: yarn lint)'
preBuildCommands:
required: false
Expand All @@ -39,6 +39,9 @@ jobs:
with:
node-version: ${{ inputs.nodeVersion }}
- run: git clone ${{ inputs.externalProjectGitUrl}} $(pwd)
- run: npm install -g yarn-deduplicate
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
- run: npx yarn-deduplicate
- uses: salesforcecli/github-workflows/.github/actions/yarnInstallWithRetries@main
- name: swap this dependency for the version on this branch
run: |
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ jobs:
- https://github.com/salesforcecli/plugin-sobject
- https://github.com/salesforcecli/plugin-templates
- https://github.com/salesforcecli/plugin-user

command:
- yarn eslint "src/**/*.ts" --fix
- yarn eslint "src/**/*.ts"
with:
packageName: 'eslint-plugin-sf-plugin'
externalProjectGitUrl: ${{ matrix.externalProjectGitUrl }}
preBuildCommands: yarn remove @types/jest
command: ${{ matrix.command }}
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ yarn build
yarn add --dev file:/absolute/path/to/eslint-plugin-sf-plugin
```

If you're using @salesforce/dev-scripts, add the plugin in your `.sfdevrc.json` file to keep dev-scripts from trying to enforce version minimums

```json
{
"devDepOverrides": ["eslint-plugin-sf-plugin"]
}
```

To get changes in your IDE, restart the eslint server.

include in the plugin's `eslint.rc`
Expand Down
88 changes: 45 additions & 43 deletions README.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions docs/rules/esm-message-import.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Looks for the verbose `Messages.importMessagesDirectory(dirname(fileURLToPath(import.meta.url)))` to offer a simpler alternative (`sf-plugin/esm-message-import`)

💼 This rule is enabled in the following configs: 📚 `library`, ✈️ `migration`, ✅ `recommended`.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->
2 changes: 1 addition & 1 deletion docs/rules/no-execcmd-double-quotes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Do not use double quotes in NUT examples. They will not work on windows (`sf-plugin/no-execcmd-double-quotes`)

💼 This rule is enabled in the following configs: 📚 `library`, ✈️ `migration`, ✅ `recommended`.
🚫 This rule is _disabled_ in the following configs: 📚 `library`, ✈️ `migration`, ✅ `recommended`.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

Expand Down
4 changes: 3 additions & 1 deletion docs/rules/no-hardcoded-messages-flags.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Use loaded messages and separate files for messages (`sf-plugin/no-hardcoded-messages-flags`)
# Use loaded messages and separate files for messages. Follow the message naming guidelines (`sf-plugin/no-hardcoded-messages-flags`)

⚠️ This rule _warns_ in the following configs: ✈️ `migration`, ✅ `recommended`.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->
25 changes: 13 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-sf-plugin",
"version": "1.16.15",
"version": "1.16.16-qa.0",
"engines": {
"node": ">=16.0.0"
},
Expand All @@ -14,35 +14,36 @@
],
"repository": "salesforcecli/eslint-plugin-sf-plugin",
"dependencies": {
"@salesforce/core": "^5.3.20",
"@typescript-eslint/utils": "^5.59.11"
"@salesforce/core": "^6.4.0",
"@typescript-eslint/utils": "^6.13.2"
},
"devDependencies": {
"@salesforce/prettier-config": "^0.0.3",
"@types/eslint": "^8.44.8",
"@types/estree": "^1.0.5",
"@types/jest": "^28.1.7",
"@types/node": "^18.19.3",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.62.0",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.4",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@typescript-eslint/rule-tester": "^6.13.2",
"eslint": "^8.55.0",
"eslint-config-prettier": "^8.10.0",
"eslint-config-salesforce": "^2.0.2",
"eslint-config-salesforce-license": "^0.2.0",
"eslint-config-salesforce-typescript": "^1.1.3",
"eslint-doc-generator": "^1.5.2",
"eslint-doc-generator": "^1.6.1",
"eslint-plugin-eslint-plugin": "^5.1.1",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsdoc": "^46.9.0",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^7",
"jest": "^28.1.3",
"jest": "^29.7.0",
"lint-staged": "^13.3.0",
"prettier": "^2.8.8",
"pretty-quick": "^3.1.3",
"ts-jest": "^28.0.8",
"typescript": "^4.9.5"
"ts-jest": "^29.1.1",
"typescript": "^5.3.3"
},
"scripts": {
"build": "tsc -p .",
Expand Down Expand Up @@ -70,4 +71,4 @@
"path": "cz-conventional-changelog"
}
}
}
}
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ import { noHyphenAliases } from './rules/no-hyphens-aliases';
import { noClassesInCommandReturnType } from './rules/no-classes-in-command-return-type';
import { noExecCmdDoubleQuotes } from './rules/no-execCmd-double-quotes';
import { noMessagesLoad } from './rules/no-messages-load';
import { esmMessageImport } from './rules/esm-message-import';

const library = {
plugins: ['sf-plugin'],
rules: {
'sf-plugin/esm-message-import': 'error',
'sf-plugin/no-missing-messages': 'error',
'sf-plugin/no-messages-load': 'error',
'sf-plugin/no-execcmd-double-quotes': 'off',
Expand Down Expand Up @@ -110,6 +112,7 @@ export = {
},
},
rules: {
'esm-message-import': esmMessageImport,
'no-h-short-char': dashH,
'no-duplicate-short-characters': noDuplicateShortCharacters,
'run-matches-class-type': runMatchesClassType,
Expand Down
6 changes: 3 additions & 3 deletions src/rules/command-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
* 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
*/
import { ESLintUtils } from '@typescript-eslint/utils';
import { extendsSfCommand, getClassPropertyIdentifierName, isInCommandDirectory } from '../shared/commands';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';

const createRule = ESLintUtils.RuleCreator((name) => `https://example.com/rule/${name}`);
const createRule = RuleCreator((name) => `https://example.com/rule/${name}`);

export const commandExamples = createRule({
name: 'command-example',
meta: {
docs: {
description: 'Ensure commands have a summary, description, and examples',
recommended: 'error',
recommended: 'stylistic',
},
messages: {
example: 'Commands should have an examples property',
Expand Down
8 changes: 5 additions & 3 deletions src/rules/command-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
* 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
*/
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';

import { extendsSfCommand, getClassPropertyIdentifierName, isInCommandDirectory } from '../shared/commands';
export const commandSummary = ESLintUtils.RuleCreator.withoutDocs({
export const commandSummary = RuleCreator.withoutDocs({
meta: {
docs: {
description: 'Ensure commands have a summary',
recommended: 'error',
recommended: 'recommended',
},
messages: {
summary: 'Commands should have a summary property',
Expand Down
7 changes: 4 additions & 3 deletions src/rules/dash-h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
* 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
*/
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';
import { ancestorsContainsSfCommand, isInCommandDirectory } from '../shared/commands';
import { flagPropertyIsNamed, isFlag } from '../shared/flags';

export const dashH = ESLintUtils.RuleCreator.withoutDocs({
export const dashH = RuleCreator.withoutDocs({
meta: {
docs: {
description: 'Do not allow creation of a flag with short char -h',
recommended: 'error',
recommended: 'recommended',
},
messages: {
message: '-h is reserved for help. Choose a different short character',
Expand Down
7 changes: 4 additions & 3 deletions src/rules/dash-o.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
* 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
*/
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';
import { ancestorsContainsSfCommand, isInCommandDirectory } from '../shared/commands';
import { flagPropertyIsNamed, isFlag } from '../shared/flags';

export const dashO = ESLintUtils.RuleCreator.withoutDocs({
export const dashO = RuleCreator.withoutDocs({
meta: {
docs: {
description: 'Warn on a flag that uses -o',
recommended: 'warn',
recommended: 'strict',
},
messages: {
message:
Expand Down
101 changes: 101 additions & 0 deletions src/rules/esm-message-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2020, 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
*/
/* eslint-disable complexity */
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';
import { ASTUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';

export const esmMessageImport = RuleCreator.withoutDocs({
meta: {
docs: {
description:
'Looks for the verbose `Messages.importMessagesDirectory(dirname(fileURLToPath(import.meta.url)))` to offer a simpler alternative',
recommended: 'strict',
},
messages: {
changeImport: 'use Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) instead',
unnecessaryImport: 'the only code using this import was removed by this rule',
},
fixable: 'code',
type: 'problem',
schema: [],
},
defaultOptions: [],
create(context) {
return {
MemberExpression(node): void {
if (
node.object.type === AST_NODE_TYPES.Identifier &&
node.object.name === 'Messages' &&
node.property.type === AST_NODE_TYPES.Identifier &&
node.property.name === 'importMessagesDirectory' &&
node.parent?.parent &&
context.sourceCode.getText(node.parent.parent).includes('dirname(fileURLToPath(import.meta.url))')
) {
const toReplace = node.parent.parent;
// we never found the message at all, we can report and exit
return context.report({
node,
messageId: 'changeImport',
fix: (fixer) =>
fixer.replaceText(toReplace, 'Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)'),
});
}
},
ImportDeclaration(node): void {
const allCode = context.sourceCode.getText();
// true for all cases
if (
((node.source.value === 'node:url' &&
node.specifiers.some((s) => s.local.name === 'fileURLToPath') &&
// it's the only reference to fileURLToPath
Array.from(allCode.matchAll(/fileURLToPath/g)).length === 1) ||
(node.source.value === 'node:path' &&
node.specifiers.some((s) => s.local.name === 'dirname') &&
// it's the only reference to dirname
Array.from(allCode.matchAll(/dirname/g)).length === 1)) &&
// we've already removed the old way of doing it
allCode.includes('Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)')
) {
if (
// case 1: single specifier removes the entire import line
node.specifiers.length === 1 &&
node.specifiers[0].type === AST_NODE_TYPES.ImportSpecifier &&
node.specifiers[0].local.type === AST_NODE_TYPES.Identifier
) {
return context.report({
node,
messageId: 'unnecessaryImport',
fix: (fixer) => fixer.remove(node),
});
} else {
// case 2, just remove the 1 unused specifier
if (node.specifiers.length > 1) {
const replacementSpecifiers = node.specifiers.filter(
(s) => s.local.name !== 'dirname' && s.local.name !== 'fileURLToPath'
);

return context.report({
node,
messageId: 'unnecessaryImport',
fix: (fixer) =>
replacementSpecifiers.every(ASTUtils.isNodeOfType(AST_NODE_TYPES.ImportDefaultSpecifier))
? fixer.replaceText(
node,
`import ${replacementSpecifiers[0].local.name} from '${node.source.value}'`
)
: fixer.replaceTextRange(
[node.specifiers[0].range[0], node.specifiers[node.specifiers.length - 1].range[1]],
replacementSpecifiers.map((s) => context.sourceCode.getText(s)).join(', ')
),
});
}
}
}
},
};
},
});
7 changes: 4 additions & 3 deletions src/rules/extract-message-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
* 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
*/
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { extendsSfCommand, getClassPropertyIdentifierName, isInCommandDirectory } from '../shared/commands';

const propertiesYouShouldntHardCode = ['description', 'summary'];

export const extractMessageCommand = ESLintUtils.RuleCreator.withoutDocs({
export const extractMessageCommand = RuleCreator.withoutDocs({
meta: {
docs: {
description: 'Use loaded messages and separate files for messages',
recommended: 'warn',
recommended: 'stylistic',
},
messages: {
message:
Expand Down
10 changes: 5 additions & 5 deletions src/rules/extract-message-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
* 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
*/
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
import { isNodeOfType } from '@typescript-eslint/utils/dist/ast-utils';
import { RuleCreator } from '@typescript-eslint/utils/eslint-utils';
import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import { isFlag, resolveFlagName } from '../shared/flags';
import { ancestorsContainsSfCommand, isInCommandDirectory } from '../shared/commands';

export const extractMessageFlags = ESLintUtils.RuleCreator.withoutDocs({
export const extractMessageFlags = RuleCreator.withoutDocs({
meta: {
docs: {
description: 'Use loaded messages and separate files for messages. Follow the message naming guidelines',
recommended: 'warn',
recommended: 'stylistic',
},
fixable: 'code',
messages: {
Expand Down Expand Up @@ -45,7 +45,7 @@ export const extractMessageFlags = ESLintUtils.RuleCreator.withoutDocs({
messageId: 'message',
});
}
const flag = ancestors.filter(isNodeOfType(AST_NODE_TYPES.Property)).find((a) => isFlag(a));
const flag = ancestors.filter(ASTUtils.isNodeOfType(AST_NODE_TYPES.Property)).find((a) => isFlag(a));
const flagName = flag ? resolveFlagName(flag) : undefined;
if (
flagName &&
Expand Down
Loading

0 comments on commit 9a4f426

Please sign in to comment.