Skip to content

Commit

Permalink
TS improvements (#551)
Browse files Browse the repository at this point in the history
  • Loading branch information
calebeby authored Aug 22, 2023
1 parent d49bc51 commit f11e794
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 270 deletions.
22 changes: 22 additions & 0 deletions .changeset/funny-guests-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@cloudfour/eslint-plugin': major
---

Improve linting of `type` modifiers on imports.

**Reconfigured Rules (`recommended` Config)**

- [`@cloudfour/import/no-duplicates`](https://github.com/import-js/eslint-plugin-import/blob/v2.28.0/docs/rules/no-duplicates.md)
```diff
- "error"
+ [
+ "error",
+ {
+ "prefer-inline": true
+ }
+ ]
```

**Newly Enabled Rules (`recommended` Config » TS Overrides)**

- [`@cloudfour/typescript-eslint/no-import-type-side-effects`](https://typescript-eslint.io/rules/no-import-type-side-effects)
5 changes: 5 additions & 0 deletions .changeset/thick-trains-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cloudfour/eslint-plugin': minor
---

Re-export `disable-type-checked` config from `@typescript-eslint/eslint-plugin`
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ with `cloudfour/`, because the plugins are exposed through this "super-plugin".
}
```

In addition to the `recommended` configuration, `@cloudfour/eslint-plugin` also re-exports typescript-eslint's [`disable-type-checked` configuration](https://typescript-eslint.io/linting/configs/#disable-type-checked). This configuration disables any rules that depend on typescript-eslint's typescript integration.

## Installation

```sh
Expand Down
119 changes: 76 additions & 43 deletions generate-changeset.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const writeChangeset = _writeChangeset.default;
* @param {string} command
* @param {readonly string[]} args
* @param {import("child_process").SpawnOptionsWithoutStdio} [opts]
* @returns {Promise<void>}
*/
const runCommand = (command, args, opts) =>
new Promise((resolve, reject) => {
Expand All @@ -32,20 +33,21 @@ const runCommand = (command, args, opts) =>
});

/** @param {string} path */
const stat = (path) => fs.stat(path).catch(() => /** @type {null} */ (null));
const stat = (path) => fs.stat(path).catch(() => null);

/** @param {string} message */
const log = (message) =>
console.log(kleur.bold(kleur.green(`\n🌀 ${message}`)));

/** @param {string} dir */
/**
* @param {string} dir
* @returns {Promise<import('eslint').ESLint.Plugin>}
*/
const loadConfig = async (dir) => {
const configModule = await import(join(dir, 'index.js')).then(
(m) => m.default,
);
const allRules = configModule.rules;
const config = configModule.configs.recommended.rules;
return [allRules, config];
return configModule;
};

/**
Expand Down Expand Up @@ -95,18 +97,21 @@ log('Building on this branch');
await runCommand('npm', ['run', 'build']);
log('Parsing out differences');

const [mainRules, mainConfig] = await loadConfig(dir);
const [branchRules, branchConfig] = await loadConfig(process.cwd());
const mainConfig = await loadConfig(dir);
const branchConfig = await loadConfig(process.cwd());

const mainRules = mainConfig.rules || {};
const branchRules = mainConfig.rules || {};

/** @param {string} _ruleName */
const printRuleLink = (_ruleName) => {
const isBuiltIn = !_ruleName.includes('/');
const ruleName = removePrefix(_ruleName);
const fullName = isBuiltIn ? ruleName : prefix + ruleName;
const rule = branchRules[ruleName] || mainRules[ruleName];
const rule = branchRules?.[ruleName] || mainRules?.[ruleName];
const url = isBuiltIn
? `https://eslint.org/docs/rules/${fullName}`
: rule?.meta?.docs?.url;
: typeof rule === 'object' && rule?.meta?.docs?.url;
return url ? `[\`${fullName}\`](${url})` : `\`${fullName}\``;
};

Expand Down Expand Up @@ -146,45 +151,73 @@ const isEnabled = (rule) =>
? isEnabled(rule[0])
: rule === 'error' || rule === 2 || rule === 'warn' || rule === 1);

const newlyEnabledRules = Object.entries(branchConfig)
.filter(
([ruleName, value]) => isEnabled(value) && !isEnabled(mainConfig[ruleName]),
)
.map(([ruleName]) => ruleName);
printRuleList(newlyEnabledRules, 'Newly Enabled Rules');

const newlyDisabledRules = Object.entries(mainConfig)
.filter(
([ruleName, value]) =>
isEnabled(value) && !isEnabled(branchConfig[ruleName]),
)
.map(([ruleName]) => ruleName);
printRuleList(newlyDisabledRules, 'Newly Disabled Rules');

let hasOutputReconfiguredRules = false;
for (const ruleName of Object.keys(branchConfig)) {
const branchConfigPrinted = printRuleConfig(branchConfig[ruleName]);
const mainConfigPrinted = printRuleConfig(mainConfig[ruleName]);
if (
branchConfigPrinted !== mainConfigPrinted &&
// Make sure that the enabled status did not change
isEnabled(branchConfig[ruleName]) === isEnabled(mainConfig[ruleName])
) {
if (!hasOutputReconfiguredRules) {
output += '\n**Reconfigured Rules**\n';
console.log(`${kleur.blue(kleur.bold('Reconfigured Rules'))}`);
hasOutputReconfiguredRules = true;
}
console.log(printRuleForCLI(ruleName));
console.log(kleur.red(indent(mainConfigPrinted, '- ')));
console.log(kleur.green(indent(branchConfigPrinted, '+ ')));
/**
* @type {{
* name: string,
* get: (config: import('eslint').ESLint.Plugin) => Partial<import('eslint').Linter.RulesRecord> | undefined}[]
} */
const scopes = [
{
name: '`recommended` Config',
get: (config) => config.configs?.recommended?.rules,
},
{
name: '`recommended` Config » TS Overrides',
get: (config) => config.configs?.recommended?.overrides?.[0].rules,
},
{
name: '`disable-type-checked` Config',
get: (config) => config.configs?.['disable-type-checked']?.rules,
},
];

output += `
for (const scope of scopes) {
const branchScopeRules = scope.get(branchConfig) || {};
const mainScopeRules = scope.get(mainConfig) || {};
const newlyEnabledRules = Object.entries(branchScopeRules)
.filter(
([ruleName, value]) =>
isEnabled(value) && !isEnabled(mainScopeRules[ruleName]),
)
.map(([ruleName]) => ruleName);
printRuleList(newlyEnabledRules, `Newly Enabled Rules (${scope.name})`);

const newlyDisabledRules = Object.entries(mainScopeRules)
.filter(
([ruleName, value]) =>
isEnabled(value) && !isEnabled(branchScopeRules[ruleName]),
)
.map(([ruleName]) => ruleName);
printRuleList(newlyDisabledRules, `Newly Disabled Rules (${scope.name})`);

let hasOutputReconfiguredRules = false;
for (const ruleName of Object.keys(branchScopeRules)) {
const branchConfigPrinted = printRuleConfig(branchScopeRules[ruleName]);
const mainConfigPrinted = printRuleConfig(mainScopeRules[ruleName]);
if (
branchConfigPrinted !== mainConfigPrinted &&
// Make sure that it is enabled on both branches
isEnabled(branchScopeRules[ruleName]) &&
isEnabled(mainScopeRules[ruleName])
) {
if (!hasOutputReconfiguredRules) {
output += `\n**Reconfigured Rules (${scope.name})**\n`;
console.log(
`${kleur.blue(kleur.bold(`Reconfigured Rules (${scope.name})`))}`,
);
hasOutputReconfiguredRules = true;
}
console.log(printRuleForCLI(ruleName));
console.log(kleur.red(indent(mainConfigPrinted, '- ')));
console.log(kleur.green(indent(branchConfigPrinted, '+ ')));

output += `
- ${printRuleLink(ruleName)}
\`\`\`diff
${indent(mainConfigPrinted, ' - ')}
${indent(branchConfigPrinted, ' + ')}
\`\`\``;
}
}
}

Expand Down Expand Up @@ -215,7 +248,7 @@ const { versionBump, summary } = await prompts(
output = `${summary}\n${output}`;

const changeset = {
summary: prettier.format(output, { parser: 'markdown' }),
summary: await prettier.format(output, { parser: 'markdown' }),
releases: [{ name: pkgName, type: versionBump }],
};

Expand Down
Loading

0 comments on commit f11e794

Please sign in to comment.