chore(ui): soften antd import ban from error to warn

The antd ban shipped in the prior lint pass as an error under
no-restricted-imports, which blocks in-flight antd -> shadcn/ui refactors
whenever they move or touch a file that still imports antd. Split antd out
into a dedicated local/no-antd-import rule set to warn so new antd imports
stay visible without failing CI, while the tremor ban stays a hard error.
Pruned the now-stale antd suppressions, dropping no-restricted-imports back
to its tremor-only baseline (625 -> 180)

The new rule ships with RuleTester coverage over import, export-from,
dynamic import, require, and deep antd/* paths
This commit is contained in:
ryan-crabbe-berri 2026-07-22 20:02:27 -07:00
parent abf18f8760
commit 5c43cb9cb3
5 changed files with 178 additions and 1295 deletions

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ const eslintConfig = [
"local/no-large-inline-object-arg": "warn",
"local/no-long-condition-chain": "warn",
"local/no-complex-jsx-arrow": ["error", { maxStatements: 2 }],
"local/no-antd-import": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"no-console": ["warn", { allow: ["warn", "error"] }],
"@typescript-eslint/no-unused-vars": "off",
@ -55,11 +56,6 @@ const eslintConfig = [
message:
"@tremor/react is being phased out; build new UI with shadcn/ui primitives instead of adding tremor imports.",
},
{
group: ["antd", "antd/*"],
message:
"antd is being phased out; build new UI with shadcn/ui primitives instead of adding antd imports.",
},
],
},
],

View file

@ -2,6 +2,7 @@ import noLargeInlineObjectArg from "./no-large-inline-object-arg.mjs";
import noLongConditionChain from "./no-long-condition-chain.mjs";
import noComplexJsxArrow from "./no-complex-jsx-arrow.mjs";
import filenamePascalCase from "./filename-pascal-case.mjs";
import noAntdImport from "./no-antd-import.mjs";
const plugin = {
rules: {
@ -9,6 +10,7 @@ const plugin = {
"no-long-condition-chain": noLongConditionChain,
"no-complex-jsx-arrow": noComplexJsxArrow,
"filename-pascal-case": filenamePascalCase,
"no-antd-import": noAntdImport,
},
};

View file

@ -0,0 +1,34 @@
const isAntd = (source) => typeof source === "string" && /^antd(\/|$)/.test(source);
const rule = {
meta: {
type: "suggestion",
docs: {
description: "Discourage antd imports; antd is being phased out in favor of shadcn/ui primitives.",
},
schema: [],
messages: {
antdImport: "antd is being phased out; build new UI with shadcn/ui primitives instead of adding antd imports.",
},
},
create(context) {
const report = (node) => context.report({ node, messageId: "antdImport" });
const checkSource = (node) => {
if (node?.source && isAntd(node.source.value)) report(node.source);
};
return {
ImportDeclaration: checkSource,
ExportNamedDeclaration: checkSource,
ExportAllDeclaration: checkSource,
ImportExpression(node) {
if (node.source?.type === "Literal" && isAntd(node.source.value)) report(node.source);
},
"CallExpression[callee.name='require']"(node) {
const arg = node.arguments[0];
if (arg?.type === "Literal" && isAntd(arg.value)) report(arg);
},
};
},
};
export default rule;

View file

@ -0,0 +1,25 @@
import { RuleTester } from "eslint";
import rule from "../../scripts/eslint-rules/no-antd-import.mjs";
const ruleTester = new RuleTester({
languageOptions: { ecmaVersion: "latest", sourceType: "module" },
});
ruleTester.run("no-antd-import", rule as never, {
valid: [
"import { Button } from '@/components/ui/button';",
"import x from 'antdesign';",
"import x from 'not-antd';",
"const x = require('@/lib/foo');",
"export { Foo } from './Foo';",
],
invalid: [
{ code: "import { Button } from 'antd';", errors: [{ messageId: "antdImport" }] },
{ code: "import Button from 'antd/es/button';", errors: [{ messageId: "antdImport" }] },
{ code: "import 'antd/dist/reset.css';", errors: [{ messageId: "antdImport" }] },
{ code: "export { Button } from 'antd';", errors: [{ messageId: "antdImport" }] },
{ code: "export * from 'antd/es/button';", errors: [{ messageId: "antdImport" }] },
{ code: "const x = require('antd');", errors: [{ messageId: "antdImport" }] },
{ code: "const p = import('antd');", errors: [{ messageId: "antdImport" }] },
],
});