Skip to content

Commit

Permalink
feat: simplify ESM message imports
Browse files Browse the repository at this point in the history
  • Loading branch information
mshanemc committed Dec 8, 2023
1 parent 4249b80 commit e749190
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 45 deletions.
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 -->
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
111 changes: 111 additions & 0 deletions src/rules/esm-message-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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 { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';

export const esmMessageImport = ESLintUtils.RuleCreator.withoutDocs({
meta: {
docs: {
description:
'Looks for the verbose `Messages.importMessagesDirectory(dirname(fileURLToPath(import.meta.url)))` to offer a simpler alternative',
recommended: 'error',
},
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.getSourceCode().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 {
if (
node.specifiers.length === 1 &&
node.specifiers[0].type === AST_NODE_TYPES.ImportSpecifier &&
node.specifiers[0].local.type === AST_NODE_TYPES.Identifier &&
((node.source.value === 'node:url' &&
node.specifiers[0].local.name === 'fileURLToPath' &&
Array.from(
context
.getSourceCode()
.getText()
.matchAll(/fileURLToPath/g)
).length === 1) ||
(node.source.value === 'node:path' &&
node.specifiers[0].local.name === 'dirname' &&
Array.from(
context
.getSourceCode()
.getText()
.matchAll(/dirname/g)
).length === 1)) &&
// we've already removed the old way of doing it
context.getSourceCode().getText().includes('Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)')
) {
return context.report({
node,
messageId: 'unnecessaryImport',
fix: (fixer) => fixer.remove(node),
});
}
},
// now we're going to clean up unused imports if they're left over
// ImportDeclaration(node): void {
// // verify it extends SfCommand
// if (node.source.value === '@salesforce/command') {
// context.report({
// node,
// messageId: 'import',
// fix: (fixer) =>
// fixer.replaceText(node, "import {Flags, SfCommand} from '@salesforce/sf-plugins-core';"),
// });
// }
// },(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.getSourceCode().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)'),
// });
// }
// },
};
},
});
87 changes: 87 additions & 0 deletions test/rules/esm-message-imports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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
*/
import { ESLintUtils } from '@typescript-eslint/utils';
import { esmMessageImport } from '../../src/rules/esm-message-import';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
});

ruleTester.run('esmMessageImport', esmMessageImport, {
valid: [
{
name: 'new loader method',
code: 'Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)',
},
{
name: 'old loader method but not ESM',
code: 'Messages.importMessagesDirectory(__dirname)',
},
{
name: 'leave the unrelated import alone',
code: `
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
`,
},
{
name: 'imports used by other code',
code: `
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)
const foo = dirname('foo/bar')
const foo = fileURLToPath('file:///foo/bar')
`,
},
],
invalid: [
{
name: 'new loader with extra imports updates the import',
errors: [
{
messageId: 'changeImport',
},
],
code: `
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
Messages.importMessagesDirectory(dirname(fileURLToPath(import.meta.url)))
`,
// other code (ex: prettier) can handle the extra whitespaces
output: `
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)
`,
},
{
name: 'new loader with extra imports updates the import',
errors: [
{
messageId: 'unnecessaryImport',
},
{
messageId: 'unnecessaryImport',
},
],
code: `
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)
`,
// other code (ex: prettier) can handle the extra whitespaces
output: `
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url)
`,
},
],
});

0 comments on commit e749190

Please sign in to comment.