This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch fix/antd-direct-imports in repository https://gitbox.apache.org/repos/asf/superset.git
commit 8371a5060ba3e77c5e63543bafbef9a705b35d89 Author: Evan Rusackas <[email protected]> AuthorDate: Fri Oct 2 15:16:28 2026 -0700 fix(frontend): finish migrating off direct antd imports, enforce it in custom rules Six files still imported from 'antd' or 'antd/<path>' directly instead of the @superset-ui/core/components wrappers CLAUDE.md mandates: - MenuItemWithTruncation.tsx and DrillBySubmenu.tsx/ChangeDatasourceModal only needed the MenuItemProps/InputRef *types*, both of which the Menu/Input wrappers already re-export (or are derivable via ComponentProps<typeof Menu.Item>), so no antd import was needed at all. - components/Tag/index.tsx needed TagProps/CheckableTagProps, which the Tag wrapper didn't re-export yet; added that export to packages/superset-ui-core/src/components/Tag (antd/es/tag has both), matching the pattern Input/Menu already follow. Deliberately left alone: theme/utils/antdTokenNames.ts (introspects antd's own design-token set at runtime; there's no wrapper for that) and theme/tests/ThemeController.test.ts (asserts against antd's real algorithm constants to verify the theme integration itself) -- both inside src/theme/, the same area already exempted from the sibling no-literal-colors custom rule for the same reason. Added checkNoDirectAntdImports to scripts/check-custom-rules.js so this doesn't quietly regress again, with the same two exemptions (theme/utils, plus the superset-ui-core/superset-core wrapper packages themselves, which are the one place importing antd directly is the point). Co-Authored-By: Evan Rusackas <[email protected]> Co-Authored-By: Claude Sonnet 5 <[email protected]> --- .../superset-ui-core/src/components/Tag/index.tsx | 1 + superset-frontend/scripts/check-custom-rules.js | 40 ++++++++++++++++++++++ .../components/Chart/DrillBy/DrillBySubmenu.tsx | 2 +- .../components/Chart/MenuItemWithTruncation.tsx | 5 ++- .../Datasource/ChangeDatasourceModal/index.tsx | 2 +- superset-frontend/src/components/Tag/index.tsx | 8 +++-- 6 files changed, 50 insertions(+), 8 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/components/Tag/index.tsx b/superset-frontend/packages/superset-ui-core/src/components/Tag/index.tsx index f9b7884464a..7275d529104 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/Tag/index.tsx +++ b/superset-frontend/packages/superset-ui-core/src/components/Tag/index.tsx @@ -17,3 +17,4 @@ * under the License. */ export { Tag } from 'antd'; +export type { TagProps, CheckableTagProps } from 'antd/es/tag'; diff --git a/superset-frontend/scripts/check-custom-rules.js b/superset-frontend/scripts/check-custom-rules.js index 11697b0515f..de049f4e5a6 100755 --- a/superset-frontend/scripts/check-custom-rules.js +++ b/superset-frontend/scripts/check-custom-rules.js @@ -174,6 +174,44 @@ function checkNoFaIcons(ast, filepath) { }); } +/** + * App code (and plugins) must go through the @superset-ui/core/components + * wrappers rather than importing from antd directly, so theming and behavior + * stay centralized in one place. The wrapper packages themselves + * (superset-ui-core, superset-core) are the legitimate exception -- they're + * what the wrappers are built from -- and `theme/utils` files that introspect + * antd's own design tokens are exempted the same way checkNoLiteralColors + * already exempts that directory (there is no wrapper for "list antd's own + * token names"). + */ +const ANTD_DIRECT_IMPORT_EXEMPT = [ + /\/theme\/utils\//, + /packages\/superset-ui-core\//, + /packages\/superset-core\//, +]; + +function checkNoDirectAntdImports(ast, filepath) { + if (ANTD_DIRECT_IMPORT_EXEMPT.some(pattern => pattern.test(filepath))) { + return; + } + + traverse(ast, { + ImportDeclaration(path) { + const source = path.node.source.value; + if (source === 'antd' || source.startsWith('antd/')) { + if (hasEslintDisable(path, 'no-restricted-imports')) return; + + // eslint-disable-next-line no-console + console.error( + `${RED}✖${RESET} ${filepath}: Direct import from "${source}". ` + + `Use the @superset-ui/core/components wrapper instead.`, + ); + errorCount += 1; + } + }, + }); +} + /** * Check for improper i18n template usage */ @@ -625,6 +663,7 @@ function processFile(filepath) { // Run all checks checkNoLiteralColors(ast, filepath); checkNoFaIcons(ast, filepath); + checkNoDirectAntdImports(ast, filepath); checkI18nTemplates(ast, filepath); checkEagerTranslationsInConfig(ast, filepath); checkUntranslatedStrings(ast, filepath); @@ -787,6 +826,7 @@ if (__filename === process.argv[1]) { export default { checkNoLiteralColors, checkNoFaIcons, + checkNoDirectAntdImports, checkI18nTemplates, checkUntranslatedStrings, checkTypeScriptOnlySource, diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx index 3e8a5fea83a..5d0fedf5ce9 100644 --- a/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx +++ b/superset-frontend/src/components/Chart/DrillBy/DrillBySubmenu.tsx @@ -42,11 +42,11 @@ import { Loading, Popover, Icons, + type InputRef, } from '@superset-ui/core/components'; import { Radio } from '@superset-ui/core/components/Radio'; import { debounce } from 'lodash-es'; import { List, type RowComponentProps } from 'react-window'; -import { InputRef } from 'antd'; import { MenuItemTooltip } from '../DisabledMenuItemTooltip'; import { VirtualizedMenuItem } from '../MenuItemWithTruncation'; import { Dataset } from '../types'; diff --git a/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx b/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx index 89cdec80c32..d2c67c2d4e5 100644 --- a/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx +++ b/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx @@ -17,17 +17,16 @@ * under the License. */ -import { ReactNode, CSSProperties, useCallback } from 'react'; +import { ComponentProps, ReactNode, CSSProperties, useCallback } from 'react'; import { truncationCSS, useCSSTextTruncation } from '@superset-ui/core'; import { css, useTheme } from '@apache-superset/core/theme'; import { Menu, type ItemType } from '@superset-ui/core/components/Menu'; import { Flex, Tooltip } from '@superset-ui/core/components'; -import { MenuItemProps } from 'antd'; export type MenuItemWithTruncationProps = { tooltipText: ReactNode; children: ReactNode; - onClick?: MenuItemProps['onClick']; + onClick?: ComponentProps<typeof Menu.Item>['onClick']; style?: CSSProperties; menuKey?: string; }; diff --git a/superset-frontend/src/components/Datasource/ChangeDatasourceModal/index.tsx b/superset-frontend/src/components/Datasource/ChangeDatasourceModal/index.tsx index 093fe8c25f7..9697b8a6fec 100644 --- a/superset-frontend/src/components/Datasource/ChangeDatasourceModal/index.tsx +++ b/superset-frontend/src/components/Datasource/ChangeDatasourceModal/index.tsx @@ -37,6 +37,7 @@ import { Loading, Modal, TableView, + type InputRef, } from '@superset-ui/core/components'; import { ServerPagination, @@ -51,7 +52,6 @@ import { SORT_BY as DATASET_SORT_BY, } from 'src/features/datasets/constants'; import withToasts from 'src/components/MessageToasts/withToasts'; -import { InputRef } from 'antd'; import type { Datasource, ChangeDatasourceModalProps } from '../types'; import { datasetLabelLower } from 'src/features/semanticLayers/label'; diff --git a/superset-frontend/src/components/Tag/index.tsx b/superset-frontend/src/components/Tag/index.tsx index 0605977bc08..6229632f27c 100644 --- a/superset-frontend/src/components/Tag/index.tsx +++ b/superset-frontend/src/components/Tag/index.tsx @@ -20,10 +20,12 @@ import { styled } from '@apache-superset/core/theme'; import { Link } from 'react-router-dom'; import type { TagType } from 'src/types/TagType'; -import { Tag as AntdTag } from '@superset-ui/core/components/Tag'; +import { + Tag as AntdTag, + type TagProps, + type CheckableTagProps, +} from '@superset-ui/core/components/Tag'; import { Tooltip } from '@superset-ui/core/components/Tooltip'; -import type { TagProps } from 'antd/es'; -import type { CheckableTagProps } from 'antd/es/tag'; import { useMemo } from 'react'; const StyledTag = styled(AntdTag)`
