Skip to content

Modernize the frontend toolchain: Lingui 6, ESLint 9 and @dagrejs/dagre - #391

Merged
cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:feature/frontend-toolchain
Jun 12, 2026
Merged

Modernize the frontend toolchain: Lingui 6, ESLint 9 and @dagrejs/dagre#391
cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:feature/frontend-toolchain

Conversation

@blaipr

@blaipr blaipr commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Modernizes three pieces of the frontend toolchain — Lingui 5 → 6, ESLint 8 → 9 (flat config) and the unmaintained dagre replaced by @dagrejs/dagre. They ship as one change because each regenerates package-lock.json and their lockfile entries interleave; together the lockfile is regenerated once, cleanly.

Lingui 5 → 6

.linguirclingui.config.js (+@lingui/format-po); removed i18n.loadLocaleData() everywhere (API deleted in v6; plurals come from Intl.PluralRules) and the now-unused make-plural dependency; webpack rule relaxing fully-specified ESM resolution (v6 is ESM-only, React 17 has no exports map); catalogs re-extracted with translations preserved (es: 1,896 → 1,898 filled msgstr).

ESLint 8 → 9.39 flat config

eslint.config.mjs replaces .eslintrc.json/.eslintignore; dead eslint-config-airbnb/eslint-config-react-app (capped at eslint ^8) replaced by eslint-config-airbnb-extended; import rules → import-x; i18next plugin 6 (option schema translated, shallow-merge defaults spread in); react-hooks 7 (new React-Compiler rules off — ~105 sites predate them, candidate follow-up); rules newer than the airbnb@19 baseline off for parity; npm audit fix clears GHSA-w7jw-789q-3m8p. ESLint 10 remains blocked by plugin peer ranges (react/jsx-a11y/import-x/babel-parser all ^9) and its removal of core formatting rules — the @Stylistic remaps here clear that hurdle in advance.

dagre → @dagrejs/dagre 3

The maintained continuation; one import line in WorkflowUtils.js (sole consumer); layout test now asserts geometry without library internals; licenses/ui file renamed.

Known benign artifact: one unused-eslint-disable warning in StatusLabel.js — the directive is required under #380's eslint-8 baseline, so it stays to keep #380 conflict-free; remove after both merge.

Fully independent of every other open PR — verified by pairwise git merge-tree against all of them.

ISSUE TYPE

  • New or Enhanced Feature

COMPONENT NAME

  • UI

ASCENDER VERSION

awx: 25.4.1.dev5+gcda0899.d20260610

ADDITIONAL INFORMATION

npm --prefix awx/ui run lint     # 0 errors (1 documented warning)
npm --prefix awx/ui run test     # 544 suites passed (1 skipped), 2855 tests passed (19 skipped)
npm --prefix awx/ui run build    # succeeds
py.test awx/main/tests/functional/test_licenses.py   # 1 passed

Combines three toolchain upgrades whose lockfile changes are mutually
conflicting (npm inserts their entries at the same alphabetical
positions), so they ship as one change. Each part is independent in
content:

Lingui 5 -> 6:
- .linguirc replaced by lingui.config.js (v6 removed string-based format
  config; po format now comes from @lingui/format-po)
- i18n.loadLocaleData() calls removed (API removed in v6; plural rules
  come from Intl.PluralRules) from i18nLoader, enzymeHelpers and tests;
  the make-plural dependency is dropped with them
- webpack rule relaxing fully-specified ESM resolution for node_modules
  (@lingui v6 is ESM-only and imports react/jsx-runtime without an
  extension, which react 17 cannot satisfy under strict resolution)
- locale catalogs re-extracted and recompiled with the v6 toolchain;
  existing translations preserved (es: 1896 -> 1898 filled msgstr)

ESLint 8 -> 9.39 with flat config:
- eslint.config.mjs replaces .eslintrc.json/.eslintignore; the
  unmaintained eslint-config-airbnb and eslint-config-react-app (both
  capped at eslint ^8) are replaced by eslint-config-airbnb-extended;
  import rules move to import-x
- eslint-plugin-i18next 5 -> 6 (options translated to the new schema,
  built-in default excludes spread in explicitly - the plugin merges
  options shallowly), eslint-plugin-react-hooks 4 -> 7 (the new React
  Compiler rules are off: the codebase predates them, ~105 sites),
  eslint-webpack-plugin 6
- rules newer than the airbnb@19 baseline are off for parity;
  formatting customizations move to @Stylistic equivalents
- mechanical cleanup: import/* disable comments renamed to import-x/*,
  unused directives removed, three small code fixes (duplicate api
  import in contexts/Config.js, promise-executor return in
  setupTests.js, max-len directive rename in util/validators.js)
- npm audit fix bumps transitive shell-quote past GHSA-w7jw-789q-3m8p
- ESLint 10 is blocked: eslint-plugin-react, jsx-a11y, import-x and
  @babel/eslint-parser all cap at eslint ^9, and ESLint 10 removed the
  core formatting rules (the @Stylistic remaps here clear that hurdle
  in advance)

dagre -> @dagrejs/dagre 3:
- the maintained continuation, one import line in WorkflowUtils.js (the
  only consumer); the layout test now asserts geometry without library
  internals (the fork exposes rank/order metadata and picks sibling
  order within a rank differently); licenses/ui file renamed

Known benign artifact: one unused eslint-disable directive warning in
StatusLabel.js - the directive is required under the parallel
styled-components PR's eslint-8 baseline, so it stays to keep that PR
conflict-free; it can be removed after both merge.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes Ascender’s UI toolchain by upgrading Lingui to v6, migrating ESLint to v9 (flat config), and replacing the unmaintained dagre dependency with @dagrejs/dagre, with associated code/test and configuration updates.

Changes:

  • Upgrade Lingui configuration to v6 (switch .linguirclingui.config.js) and remove make-plural locale data loading across app/tests.
  • Migrate ESLint to v9 flat config (eslint.config.mjs) and update codebase eslint directives/rules to match the new plugin ecosystem (import-x, @stylistic, etc.).
  • Replace dagre with @dagrejs/dagre and update workflow layout tests to assert geometry without relying on library internals; adjust webpack to better handle ESM packages in node_modules.

Reviewed changes

Copilot reviewed 73 out of 84 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
licenses/ui/make-plural.txt Removes license for dropped make-plural dependency.
licenses/ui/dagrejs-dagre.txt Adds license for new @dagrejs/dagre dependency.
awx/ui/testUtils/enzymeHelpers.js Removes Lingui plural data loading in test helpers; adjusts eslint comment usage.
awx/ui/testUtils/apiReusable.js Updates eslint-disable rule name to import-x.
awx/ui/src/util/validators.test.js Removes deprecated Lingui locale data loading in tests.
awx/ui/src/util/validators.js Updates eslint-disable rule name to @stylistic/max-len.
awx/ui/src/util/lists.js Updates eslint-disable rule name to import-x.
awx/ui/src/util/dates.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/util/auth.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/setupTests.js Updates eslint rule name and reformats asyncFlush helper.
awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListDenyButton.js Removes unnecessary eslint-disable for “useless fragment”.
awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalListApproveButton.js Removes unnecessary eslint-disable for “useless fragment”.
awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/WorkflowApprovalList.js Removes inline eslint disables and updates hook deps formatting.
awx/ui/src/screens/WorkflowApproval/shared/WorkflowApprovalUtils.test.js Removes make-plural usage; updates Lingui activation in tests.
awx/ui/src/screens/User/UserTeams/UserTeamListItem.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/User/shared/index.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/screens/TopologyView/utils/useZoom__RTL.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/TopologyView/TopologyView__RTL.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/TopologyView/MeshGraph.js Cleans up eslint disables around this usage in D3 callbacks.
awx/ui/src/screens/TopologyView/MeshGraph__RTL.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Visualizer.js Narrows eslint-disable list to remaining relevant rules.
awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.js Removes unnecessary react-hooks/exhaustive-deps disable.
awx/ui/src/screens/Template/shared/PlaybookSelect.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Team/Team.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/screens/Team/shared/index.js Updates eslint-disable rule name to import-x.
awx/ui/src/screens/Setting/shared/SharedFields.test.js Removes deprecated Lingui locale data loading in tests.
awx/ui/src/screens/Organization/shared/index.js Updates eslint-disable rule name to import-x.
awx/ui/src/screens/Organization/OrganizationList/OrganizationListItem.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/NotificationTemplate/constants.js Updates eslint-disable rule name to import-x.
awx/ui/src/screens/Login/Login.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/screens/Job/JobOutput/useJobEvents.js Removes file-level eslint-disable for destructuring assignment.
awx/ui/src/screens/Job/JobOutput/JobOutput.js Removes file-level eslint-disable and removes inline exhaustive-deps suppression.
awx/ui/src/screens/Inventory/shared/Inventory.helptext.js Removes file-level eslint-disable for destructuring assignment.
awx/ui/src/screens/Inventory/shared/ConstructedInventoryHint.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Inventory/InventoryList/InventoryList.js Removes inline exhaustive-deps suppression.
awx/ui/src/screens/Inventory/InventoryHosts/InventoryHostItem.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Inventory/ConstructedInventoryDetail/ConstructedInventorySyncButton.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Inventory/ConstructedInventoryDetail/ConstructedInventoryDetail.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Instances/Shared/RemoveInstanceButton.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Instances/InstanceList/InstanceList.test.js Removes make-plural usage and locale data loading in tests.
awx/ui/src/screens/Credential/shared/CredentialPlugins/CredentialPluginPrompt/MetadataStep.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/screens/Credential/shared/CredentialPlugins/CredentialPluginField.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/screens/Credential/shared/CredentialFormFields/CredentialField.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/locales/zu/messages.po Catalog reformat/reordering consistent with updated Lingui extraction.
awx/ui/src/locales/zh/messages.po Catalog reformat/reordering consistent with updated Lingui extraction.
awx/ui/src/locales/ko/messages.po Catalog reformat/reordering consistent with updated Lingui extraction.
awx/ui/src/locales/fr/messages.po Catalog reformat/reordering consistent with updated Lingui extraction.
awx/ui/src/i18nLoader.js Removes make-plural imports and Lingui locale data loading calls.
awx/ui/src/contexts/Workflow.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/contexts/Kebabified.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/contexts/Config.js Normalizes API imports and removes redundant SettingsAPI import/disable.
awx/ui/src/components/Workflow/WorkflowUtils.test.js Updates layout test to assert geometry only (less coupled to dagre internals).
awx/ui/src/components/Workflow/WorkflowUtils.js Switches from dagre to @dagrejs/dagre; removes unnecessary eslint-disable.
awx/ui/src/components/Sort/Sort.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/components/Search/AdvancedSearch.js Removes inline exhaustive-deps suppression.
awx/ui/src/components/ResourceAccessList/DeleteRoleConfirmationModal.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/components/RelatedTemplateList/relatedTemplateHelpers.js Removes unnecessary eslint-disable for default export preference.
awx/ui/src/components/ListHeader/ListHeader.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/components/FormField/FormField.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/components/ContentError/ContentError.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/components/AppContainer/AppContainer.js Removes now-unneeded i18next/no-literal-string disables for translated attributes.
awx/ui/src/components/AddDropDownButton/AddDropDownButton.js Removes file-level eslint-disable for “useless fragment”.
awx/ui/src/App.js Removes i18next/no-literal-string disable in loading UI.
awx/ui/package.json Upgrades Lingui and ESLint toolchain dependencies; replaces dagre with @dagrejs/dagre; updates lint script.
awx/ui/lingui.config.js Adds Lingui v6 JS config and PO formatter wiring.
awx/ui/eslint.config.mjs Adds ESLint v9 flat configuration (Airbnb-extended, import-x, stylistic, i18next, etc.).
awx/ui/config/webpack.config.js Relaxes fully-specified ESM resolution for node_modules; updates ESLint plugin config handling for flat config.
awx/ui/.linguirc Removes deprecated Lingui v5 JSON config file.
awx/ui/.eslintrc.json Removes legacy ESLint config now replaced by flat config.
awx/ui/.eslintignore Removes legacy ignore file now replaced by flat config ignores.

Comment thread awx/ui/src/App.js
Comment thread awx/ui/src/screens/TopologyView/MeshGraph.js

@cigamit cigamit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and working. We will need to remove the warning in StatusLabel still.

@cigamit
cigamit merged commit e262d34 into ctrliq:main Jun 12, 2026
@blaipr

blaipr commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Checked both comments against the actual flat config rather than assumption — neither directive is needed: npx eslint src/App.js src/screens/TopologyView/MeshGraph.js exits 0 with zero errors and zero warnings on this branch, and the full npm run lint gate reports 0 errors. The i18next/no-literal-string configuration (jsx-text-only mode with the plugin's default excludes spread in) does not flag the Loading... fallback, and react/no-this-in-sfc as configured by airbnb-extended does not flag the d3 callbacks. The directives were removed precisely because ESLint 9 reported them as unused; restoring them would reintroduce the unused-directive warnings. No change made.

@TheWitness

TheWitness commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

@blaipr, we are going to be moving to long lines with Lingui to facilitate machines translations from POEdit. When, you think you are done with major changes, I'll be proceeding to extend the lines in the various JavaScript files that are currently broken with a line break and updating the Lingui configuration to output them.

@blaipr

blaipr commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@TheWitness sounds good. The last big source change is #392 plus one route-tree follow-up after it merges, everything else open is small. I'll keep new strings on single lines, and happy to hold the follow-up if you want to go first.

@TheWitness

Copy link
Copy Markdown
Collaborator

Check the language-updates branch for the lingui.conf.ts file in the awx/ui directory. It looks like the file below. I have removed the .linguirc file and will use the new lingui.config.ts file moving forward. So, to prevent the warnings in POEdit, we have to move multi-line strings to long lines as well. Keeps things cleaner in the end as long as your editor wraps correctly or not, it should not be an issue. If you open any of the message.po files from POEdit, and then perform a machine translation, you will see all the warnings on long lines. If you find any, any we could use the help.

import { defineConfig } from "@lingui/cli";
import { formatter } from "@lingui/format-po";

export default defineConfig({
   catalogs:[{
      path: "<rootDir>/locales/{locale}/messages",
      include: ["<rootDir>"],
      exclude: ["**/node_modules/**"]
   }],
   compileNamespace: "cjs",
   fallbackLocales: { "default": "en"},
   format: "po",
   locales: ["en","es","fr","de","it","ko","nl","zh","ja","zu"],
   orderBy: "messageId",
   rootDir: "./src",
   runtimeConfigModule: ["@lingui/core", "i18n"],
   sourceLocale: "en",
   format: formatter({
      lineNumbers: true, 
      foldLength: 0
   }),
});

@blaipr

blaipr commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Understood — adopted as the working convention going forward: long-line .po entries (foldLength: 0), lingui.config.ts as the config of record once language-updates lands, and no catalog extractions in feature PRs until then so your rework doesn't get churned (flagged the four new msgids in #396 accordingly). Happy to help triage the POEdit machine-translation warnings — I'll take a pass over the language-updates catalogs and report anything I find on that branch.

@blaipr

blaipr commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Took that pass over the language-updates catalogs (ICU-aware placeholder/tag comparison between msgid and msgstr, all locales). 49 entries will misrender or fail to parse at runtime, in three categories:

1. Machine translation translated the ICU syntax itself (~15 entries, worst in de/it). Lingui cannot parse these — the plural keyword and branch names must stay English:

  • de L94/L153/L2046/L2342/L3053/L9426/L10466/L10667: {intervalValue, plural, one {…}} became {Intervallwert, Plural, eins {…}}
  • it L21/L153: {forks, plural, one {# fork}…}{forchette, plurale, una {# forchetta}…}
  • same pattern in es L154, fr L21/L154, ja L21/L154, ko L21/L153, nl L21/L154, zh L154

2. React component tags corrupted (~20 entries, ja/ko/zh mostly): <0> became < 0 > (spaces break Lingui's Trans tag mapping), e.g. ja L3014/L3358/L3717/L4239/L6249, ko L3005/L3341/L3700/L4214/L6208, zh L3019/L3194/L3366/L3725/L4247/L6257. Also it L2400 lost its opening <0> (keeps the closing </0>), and de L2399/L8077 dropped tags entirely.

3. Dropped/renumbered placeholders:

  • Soft delete {pluralizedItemName}? lost the placeholder in es L4000, fr L4015, nl L4005, zh L4016
  • zh L3370: {interval} minutes0分钟 (placeholder replaced by a literal 0)
  • ko L2017: {0, plural, one {{1}} other {{2}}} lost the entire one branch
  • ja L3717: <0>{0}<1>{1}</1></0> → indices renumbered to { 2 }/{ 3 } with spaced braces

Separate observation: every locale still has ~48 folded multi-line entries, but those come from source t template literals that genuinely contain newlines and indentation (e.g. the Grafana URL helper text) — they'll keep re-folding on every extract regardless of foldLength: 0. Collapsing those literals to single lines in the JSX source would clean both the UI strings (embedded indentation) and the catalogs in one shot; happy to do that as a follow-up PR if useful.

(zu is ~1992/1996 untranslated — assuming that's intentional as a pseudo-locale.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants