Skip to content

Commit

Permalink
feat: more work on the configs
Browse files Browse the repository at this point in the history
Signed-off-by: prisis <[email protected]>
  • Loading branch information
prisis committed Jan 6, 2025
1 parent 7518502 commit 3a9b982
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 78 deletions.
12 changes: 11 additions & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"eslint-plugin-eslint-comments",
"eslint-plugin-html",
"eslint-plugin-i",
"@tanstack/eslint-plugin-router",
"eslint-plugin-jsonc",
"eslint-markdown",
"eslint-plugin-no-secrets",
Expand All @@ -47,6 +48,7 @@
"eslint-plugin-tailwindcss",
"eslint-plugin-testing-library",
"eslint-plugin-tsdoc",
"eslint-plugin-no-for-of-array",
"eslint-plugin-you-dont-need-lodash-underscore"
],
"homepage": "https://anolilab.com/nodejs/packages/eslint-config",
Expand Down Expand Up @@ -146,11 +148,13 @@
"eslint-plugin-jsdoc": "^50.6.1",
"eslint-plugin-jsonc": "^2.18.2",
"eslint-plugin-n": "^17.15.1",
"eslint-plugin-no-for-of-array": "^0.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-no-secrets": "^2.1.1",
"eslint-plugin-no-unsanitized": "^4.1.2",
"eslint-plugin-perfectionist": "^4.6.0",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-react-refresh": "^0.4.16",
"eslint-plugin-regexp": "^2.7.0",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-simple-import-sort": "^12.1.1",
Expand All @@ -173,6 +177,8 @@
"@eslint-react/eslint-plugin": "^1.23.1",
"@eslint/config-inspector": "^0.7.0",
"@stylistic/eslint-plugin-migrate": "^2.12.1",
"@tanstack/eslint-plugin-query": "^5.0.0",
"@tanstack/eslint-plugin-router": "^1.92.7",
"@testing-library/dom": "^10.4.0",
"@total-typescript/ts-reset": "^0.6.1",
"@types/confusing-browser-globals": "^1.0.3",
Expand Down Expand Up @@ -210,7 +216,8 @@
"peerDependencies": {
"@babel/core": "^7.22.20",
"@eslint-react/eslint-plugin": "^1.22.1",
"@tanstack/eslint-plugin-query": "^4.34.1 || ^5.0.0",
"@tanstack/eslint-plugin-query": "^5.0.0",
"@tanstack/eslint-plugin-router": "^1.92.7",
"@unocss/eslint-plugin": "^0.65.3",
"astro-eslint-parser": "^1.1.0",
"eslint": "^9.10.0",
Expand All @@ -232,6 +239,9 @@
"@tanstack/eslint-plugin-query": {
"optional": true
},
"@tanstack/eslint-plugin-router": {
"optional": true
},
"@unocss/eslint-plugin": {
"optional": true
},
Expand Down
6 changes: 6 additions & 0 deletions packages/eslint-config/scripts/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ import perfectionist from "../src/config/plugins/perfectionist";
import playwright from "../src/config/plugins/playwright";
import promise from "../src/config/plugins/promise";
import react from "../src/config/plugins/react";
import reactRefresh from "../src/config/plugins/react-refresh";
import regexp from "../src/config/plugins/regexp";
import simpleImportSort from "../src/config/plugins/simple-import-sort";
import sonarjs from "../src/config/plugins/sonarjs";
import storybook from "../src/config/plugins/storybook";
import stylistic from "../src/config/plugins/stylistic";
import tailwindcss from "../src/config/plugins/tailwindcss";
import tanstackQuery from "../src/config/plugins/tanstack-query";
import tanstackRouter from "../src/config/plugins/tanstack-router";
import testingLibrary from "../src/config/plugins/testing-library";
import toml from "../src/config/plugins/toml";
import tsdoc from "../src/config/plugins/tsdoc";
Expand Down Expand Up @@ -74,6 +76,7 @@ const configs = await combine(
storybook({}),
tailwindcss({}),
tanstackQuery({}),
tanstackRouter({}),
tsdoc({}),
validateJsxNesting({}),
variables({}),
Expand Down Expand Up @@ -101,6 +104,9 @@ const configs = await combine(
react({
packageJson: fakePackageJson,
}),
reactRefresh({
hasVite: false,
}),
node({
packageJson: fakePackageJson,
}),
Expand Down
12 changes: 12 additions & 0 deletions packages/eslint-config/src/config/plugins/react-refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { TypedFlatConfigItem } from "../../types";
import interopDefault from "../../utils/interop-default";

const reactRefresh = async ({ hasVite }: { hasVite: boolean }): Promise<TypedFlatConfigItem[]> => {
const reactRefreshPlugin = await interopDefault(import("eslint-plugin-react-refresh"));

return [
hasVite ? reactRefreshPlugin.configs.vite : reactRefreshPlugin.configs.recommended,
];
};

export default reactRefresh;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default createConfig<OptionsFiles & OptionsOverrides>("all", async (confi
return [{
files,
plugins: {
"tanstack-query": pluginTanstackQuery,
"@tanstack/query": pluginTanstackQuery,
},
rules: {
...pluginTanstackQuery.configs["recommended"].rules,
Expand Down
20 changes: 20 additions & 0 deletions packages/eslint-config/src/config/plugins/tanstack-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { OptionsFiles, OptionsOverrides } from "../../types";
import { createConfig } from "../../utils/create-config";
import interopDefault from "../../utils/interop-default";

export default createConfig<OptionsFiles & OptionsOverrides>("all", async (config, oFiles) => {
const { files = oFiles, overrides } = config;

const pluginTanstackRouter = await interopDefault(import("@tanstack/eslint-plugin-router"));

return [{
files,
plugins: {
"@tanstack/router": pluginTanstackRouter,
},
rules: {
...pluginTanstackRouter.configs["recommended"].rules,
...overrides,
},
}];
});
102 changes: 59 additions & 43 deletions packages/eslint-config/src/config/plugins/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import { styleRules } from "../style";
import { variablesRules } from "../variables";

export default createConfig<
OptionsComponentExtensions & OptionsFiles & OptionsHasPrettier & OptionsOverrides & OptionsStylistic & OptionsTypeScriptParserOptions & OptionsTypeScriptWithTypes
OptionsComponentExtensions &
OptionsFiles &
OptionsHasPrettier &
OptionsOverrides &
OptionsStylistic &
OptionsTypeScriptParserOptions &
OptionsTypeScriptWithTypes
>("ts", async (config, oFiles) => {
const {
componentExts: componentExtensions = [],
Expand All @@ -29,15 +35,16 @@ export default createConfig<
} = config;

// eslint-disable-next-line compat/compat
const [pluginTs, parserTs, tseslint] = await Promise.all([
const [pluginTs, parserTs, tseslint, noForOfArrayPlugin] = await Promise.all([
interopDefault(import("@typescript-eslint/eslint-plugin")),
interopDefault(import("@typescript-eslint/parser")),
interopDefault(import("typescript-eslint")),
interopDefault(import("eslint-plugin-no-for-of-array")),
] as const);

const filesTypeAware = config.filesTypeAware ?? getFilesGlobs("ts");
const ignoresTypeAware = config.ignoresTypeAware ?? [`**/*.md/**`, ...getFilesGlobs("astro")];
const tsconfigPath = config?.tsconfigPath ? config.tsconfigPath : undefined;
const tsconfigPath = config?.tsconfigPath ?? undefined;
const isTypeAware = tsconfigPath !== undefined;

const makeParser = (typeAware: boolean, pFiles: string[], ignores?: string[]): TypedFlatConfigItem => {
Expand Down Expand Up @@ -71,6 +78,7 @@ export default createConfig<
name: "anolilab/typescript/setup",
plugins: {
"@typescript-eslint": pluginTs,
"no-for-of-array": noForOfArrayPlugin,
},
},
// assign type-aware parser for type-aware files and type-unaware parser for the rest
Expand All @@ -79,47 +87,55 @@ export default createConfig<
];

if (isTypeAware) {
rules.push(...(tseslint.configs.strictTypeCheckedOnly as TypedFlatConfigItem[]));

rules.push({
files: [...filesTypeAware, ...componentExtensions.map(extension => `**/*.${extension}`)],
name: "anolilab/typescript/rules-type-aware",
rules: {
// Disallow type assertions that do not change the type of expression.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
"@typescript-eslint/no-unnecessary-type-assertion": "error",

// Disallow calling a function with a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-argument.md
"@typescript-eslint/no-unsafe-argument": "error",

// Disallow assigning a value with type any to variables and properties.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md
"@typescript-eslint/no-unsafe-assignment": "error",

// Disallow calling a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-call.md
"@typescript-eslint/no-unsafe-call": "error",

// Disallow member access on a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md
"@typescript-eslint/no-unsafe-member-access": "error",

// Disallow returning a value with type any from a function.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-return.md
"@typescript-eslint/no-unsafe-return": "error",

// Enforce using the nullish coalescing operator instead of logical chaining.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md
"@typescript-eslint/prefer-nullish-coalescing": "error",

// Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/prefer-optional-chain.md
"@typescript-eslint/prefer-optional-chain": "error",

...overridesTypeAware,
rules.push(
...(tseslint.configs.strictTypeCheckedOnly as TypedFlatConfigItem[]),
{
files: [...filesTypeAware, ...componentExtensions.map(extension => `**/*.${extension}`)],
name: "anolilab/typescript/rules-type-aware",
rules: {
// Disallow type assertions that do not change the type of expression.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md
"@typescript-eslint/no-unnecessary-type-assertion": "error",

// Disallow calling a function with a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-argument.md
"@typescript-eslint/no-unsafe-argument": "error",

// Disallow assigning a value with type any to variables and properties.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-assignment.md
"@typescript-eslint/no-unsafe-assignment": "error",

// Disallow calling a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-call.md
"@typescript-eslint/no-unsafe-call": "error",

// Disallow member access on a value with type any.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md
"@typescript-eslint/no-unsafe-member-access": "error",

// Disallow returning a value with type any from a function.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/no-unsafe-return.md
"@typescript-eslint/no-unsafe-return": "error",

// Enforce using the nullish coalescing operator instead of logical chaining.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md
"@typescript-eslint/prefer-nullish-coalescing": "error",

// Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects.
// https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin/docs/rules/prefer-optional-chain.md
"@typescript-eslint/prefer-optional-chain": "error",

...overridesTypeAware,
},
},
{
files: getFilesGlobs("all"),
name: "anolilab/typescript/no-for-of-array/rules",
rules: {
"no-for-of-array/no-for-of-array": "error",
},
},
});
);
}

if (stylistic) {
Expand Down
8 changes: 8 additions & 0 deletions packages/eslint-config/src/config/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ export const styleRules: Partial<Linter.RulesRecord> = {
message: "Use `.key` instead of `.keyCode`",
selector: "MemberExpression > .property[type=Identifier][name=keyCode]",
},
{
message: "Do not use full-width tilde. Use wave dash instead.",
selector: ":matches(Literal[value=/~/],TemplateElement[value.raw=/~/])",
},
{
message: "Use `.toString()` instead of template literal if you want to convert a value to string.",
selector: "TemplateLiteral[quasis.0.value.raw=\"\"][quasis.1.tail=true][quasis.1.value.raw=\"\"]",
},
],

// disallow space between function identifier and application
Expand Down
40 changes: 32 additions & 8 deletions packages/eslint-config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ import perfectionist from "./config/plugins/perfectionist";
import playwright from "./config/plugins/playwright";
import promise from "./config/plugins/promise";
import react from "./config/plugins/react";
import reactRefresh from "./config/plugins/react-refresh";
import regexp from "./config/plugins/regexp";
import simpleImportSort from "./config/plugins/simple-import-sort";
import sonarjs from "./config/plugins/sonarjs";
import storybook from "./config/plugins/storybook";
import stylistic from "./config/plugins/stylistic";
import tailwindcss from "./config/plugins/tailwindcss";
import tanstackQuery from "./config/plugins/tanstack-query";
import tanstackRouter from "./config/plugins/tanstack-router";
import testingLibrary from "./config/plugins/testing-library";
import toml from "./config/plugins/toml";
import tsdoc from "./config/plugins/tsdoc";
Expand Down Expand Up @@ -443,7 +445,8 @@ export const createConfig = async (
regexp: enableRegexp = true,
storybook: enableStorybook = hasPackageJsonAnyDependency(packageJson, ["storybook", "eslint-plugin-storybook"]),
tailwindcss: enableTailwindCss = false,
tanstack: enableTanstack = false,
tanstackQuery: enableTanstackQuery = hasPackageJsonAnyDependency(packageJson, ["@tanstack/react-query"]),
tanstackRouter: enableTanstackRouter = hasPackageJsonAnyDependency(packageJson, ["@tanstack/react-router"]),
testingLibrary: enableTestingLibrary = hasPackageJsonAnyDependency(packageJson, ["@testing-library/dom", "@testing-library/react"]),
tsdoc: enableTsdoc = false,
typescript: enableTypeScript = hasPackageJsonAnyDependency(packageJson, ["typescript"]),
Expand All @@ -464,10 +467,14 @@ export const createConfig = async (
packages.push("@unocss/eslint-plugin");
}

if (enableTanstack) {
if (enableTanstackQuery) {
packages.push("@tanstack/eslint-plugin-query");
}

if (enableTanstackRouter) {
packages.push("@tanstack/eslint-plugin-router");
}

if (enableTailwindCss) {
packages.push("eslint-plugin-tailwindcss");
}
Expand Down Expand Up @@ -618,9 +625,6 @@ export const createConfig = async (
overrides: getOverrides(options, "antfu"),
packageJson,
}),
compat({
files: getFiles(options, "compat"),
}),
noSecrets({
overrides: getOverrides(options, "noSecrets"),
}),
Expand All @@ -642,6 +646,14 @@ export const createConfig = async (
}),
);

if (packageJson["browserlist"] !== undefined) {
configs.push(
compat({
files: getFiles(options, "compat"),
}),
);
}

if (enableUnicorn) {
configs.push(
unicorn({
Expand Down Expand Up @@ -761,11 +773,20 @@ export const createConfig = async (
);
}

if (enableTanstack) {
if (enableTanstackQuery) {
configs.push(
tanstackQuery({
files: getFiles(options, "tanstack"),
overrides: getOverrides(options, "tanstack"),
files: getFiles(options, "tanstackQuery"),
overrides: getOverrides(options, "tanstackQuery"),
}),
);
}

if (enableTanstackRouter) {
configs.push(
tanstackRouter({
files: getFiles(options, "tanstackRouter"),
overrides: getOverrides(options, "tanstackRouter"),
}),
);
}
Expand Down Expand Up @@ -807,6 +828,9 @@ export const createConfig = async (
packageJson,
tsconfigPath,
}),
reactRefresh({
hasVite: hasPackageJsonAnyDependency(packageJson, ["vite"]),
}),
);
}

Expand Down
Loading

0 comments on commit 3a9b982

Please sign in to comment.