You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fusion Framework — The Zero-Warning Lint Milestone 🧹
There's no shiny new component in this one, no headline API. What we're shipping instead is something less glamorous but arguably more valuable: we paid off a chunk of lint debt that had been quietly accumulating across the monorepo, and we used the exercise to make fusion-lint itself more configurable in the process.
If you've ever opened a PR and had to scroll past a hundred pre-existing warnings to find the one you actually introduced, this release is for you.
Why we did this
Two lint signals had been drifting for a while: Biome's noExplicitAny/noConfusingVoidType checks in packages/*, and fusion-lint's TSDoc / intent-comment / file-convention checks across both packages/* and cookbooks/*. None of it was breaking anything, which is exactly the problem — it's the kind of debt that's easy to ignore until it quietly buries a real signal.
Cleaning it up properly (rather than just suppressing warnings) meant we couldn't keep fusion-lint's rule engine as rigid as it was. Rules were built as fixed instances with a check(source, filePath) signature and an awkward allowMultipleIn escape hatch — good enough for a handful of built-in rules, not good enough for a team that wants to configure or extend them. So lint-core, lint-rules, and lint-config got a real rework: a LintContext object, a RuleDef/resolveMatch factory API, and a consistent options.match shape for include/exclude patterns. That's the reason those three packages carry a 1.0.0 this time — see the breaking changes section below if you touch those APIs directly.
The numbers, verified twice
We didn't want to just copy the "before" numbers from an old draft of the PR description and call it a day — a few of them turned out to be stale by the time the branch had grown to 169 commits. So we re-ran everything as a clean main vs. branch comparison (via a throwaway git worktree checkout of origin/main, same lint commands on both sides):
That last one is worth a footnote: the React Router v7 route-module files (where a clientLoader, an action, a page component, and an ErrorElement legitimately live in one file, per framework convention) used to show up as 17 "accepted" warnings. They're now fully exempted via fusion-lint.config.json's excludePattern, so they don't show up as noise at all — which is the more honest way to model an intentional exception.
Eating our own dog food 🐕
A fair chunk of the work in this PR was less "fix the warning" and more "restructure the file so the rule stops complaining, and make sure nothing downstream notices." A few examples:
cookbooks/app-react-router-legacy's ErrorElementPage.tsx had three exports crammed into one file (a single-export-per-file violation). It's now split into components/ErrorElementPage.tsx, ErrorElementBoundary.tsx, client-loader.ts, and a small barrel index.ts.
@equinor/fusion-framework-module-context's errors.ts didn't match filename-convention (a generic filename hiding a specific class, FusionContextSearchError). It moved to src/errors/FusionContextSearchError.ts with an errors/index.ts barrel.
Several cookbook files got renamed to kebab-case or split up for the same reason: Router.tsx → AppRouter.tsx, chartsData.ts → charts-data.ts, Styled.tsx → flex-grid.ts + flex-grid-column.ts, and so on.
We're calling this out deliberately: this PR is meant to double as a working example of what bringing a package into fusion-lint compliance actually looks like — the config changes, the file splits, the import updates — not just a number going to zero in CI. If you're planning to clean up a package of your own, the commit history here is a reasonable reference.
One thing that didn't go smoothly: moving module-context's errors.ts briefly broke the build. Its package.json had a ./errors.js subpath export pointing at dist/esm/errors.js, and after the file moved to dist/esm/errors/index.js, every downstream consumer (react, react-app, react-module-context, react-module-bookmark, react-components-bookmark, react-components-people-provider) failed to build with TS2307: Cannot find module. CI caught it, we fixed the subpath, and pnpm build:packages is green again. The lesson, which is now filed away for next time: when a file backing a subpath export moves, the exports/typesVersions map has to move with it — tsc -b on the package itself won't tell you, only the downstream consumers will.
⚠️ Breaking changes — but only if you write custom lint rules
If you only use fusion-lint through its CLI and config file, nothing changes for you. This only affects code that constructs custom rules or calls the lint-core/lint-rules/lint-config APIs directly.
@equinor/fusion-framework-lint-core — Rule.check(source, filePath, severity) becomes Rule.check(source, ctx: LintContext). Read ctx.filePath and ctx.severity off the context object instead of taking them as separate arguments.
@equinor/fusion-framework-lint-rules — rules now export factory functions instead of pre-built Rule instances, and allowMultipleIn is gone in favor of the shared options.match:
Note that single-export-per-file's default barrel exemption (index.ts, index.tsx, etc.) is now only a fallback. If you supply your own options.match, it fully replaces the default rather than merging with it — so re-add the barrel patterns yourself if you still want them exempted.
@equinor/fusion-framework-lint-config — CustomRuleDefinition.check follows the same new check(source, ctx) shape as lint-core.
🐛 A friendly heads-up
This PR touched ~1,320 files. Most of it is mechanical — renames, TSDoc, comments, config — but at that scale, something occasionally slips through review that neither pnpm build, pnpm test, nor a human skim catches. If you upgrade and something behaves differently than before (an import path that moved, a type that got stricter than it should have, anything odd), please open an issue and tag it back to this release. We'd genuinely rather hear about it now than have it turn into a mystery six months from now.
📦 Released packages
82 packages went out in this release. Headline entries first, full list below.
Changesets release PR — #5164 (merged, published to npm)
🤖 Beep Boppp — release notes assembled by Fusion Assistant. 2,507 warnings walked into a bar. None walked out. If your app walks in weird after upgrading, though, please tell us — we'd rather hear about the one thing we missed than have you find it the hard way.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Fusion Framework — The Zero-Warning Lint Milestone 🧹
There's no shiny new component in this one, no headline API. What we're shipping instead is something less glamorous but arguably more valuable: we paid off a chunk of lint debt that had been quietly accumulating across the monorepo, and we used the exercise to make
fusion-lintitself more configurable in the process.If you've ever opened a PR and had to scroll past a hundred pre-existing warnings to find the one you actually introduced, this release is for you.
Why we did this
Two lint signals had been drifting for a while: Biome's
noExplicitAny/noConfusingVoidTypechecks inpackages/*, andfusion-lint's TSDoc / intent-comment / file-convention checks across bothpackages/*andcookbooks/*. None of it was breaking anything, which is exactly the problem — it's the kind of debt that's easy to ignore until it quietly buries a real signal.Cleaning it up properly (rather than just suppressing warnings) meant we couldn't keep
fusion-lint's rule engine as rigid as it was. Rules were built as fixed instances with acheck(source, filePath)signature and an awkwardallowMultipleInescape hatch — good enough for a handful of built-in rules, not good enough for a team that wants to configure or extend them. Solint-core,lint-rules, andlint-configgot a real rework: aLintContextobject, aRuleDef/resolveMatchfactory API, and a consistentoptions.matchshape for include/exclude patterns. That's the reason those three packages carry a1.0.0this time — see the breaking changes section below if you touch those APIs directly.The numbers, verified twice
We didn't want to just copy the "before" numbers from an old draft of the PR description and call it a day — a few of them turned out to be stale by the time the branch had grown to 169 commits. So we re-ran everything as a clean
mainvs. branch comparison (via a throwawaygit worktreecheckout oforigin/main, same lint commands on both sides):fusion-lintonpackages/*: 2,507 problems (73 errors, 2,434 warnings) → 0noExplicitAny/noConfusingVoidTypeonpackages/*: 147 warnings → 0fusion-lintoncookbooks/*: 224 problems (7 errors, 217 warnings) → 0That last one is worth a footnote: the React Router v7 route-module files (where a
clientLoader, anaction, a page component, and anErrorElementlegitimately live in one file, per framework convention) used to show up as 17 "accepted" warnings. They're now fully exempted viafusion-lint.config.json'sexcludePattern, so they don't show up as noise at all — which is the more honest way to model an intentional exception.Eating our own dog food 🐕
A fair chunk of the work in this PR was less "fix the warning" and more "restructure the file so the rule stops complaining, and make sure nothing downstream notices." A few examples:
cookbooks/app-react-router-legacy'sErrorElementPage.tsxhad three exports crammed into one file (asingle-export-per-fileviolation). It's now split intocomponents/ErrorElementPage.tsx,ErrorElementBoundary.tsx,client-loader.ts, and a small barrelindex.ts.@equinor/fusion-framework-module-context'serrors.tsdidn't matchfilename-convention(a generic filename hiding a specific class,FusionContextSearchError). It moved tosrc/errors/FusionContextSearchError.tswith anerrors/index.tsbarrel.Router.tsx→AppRouter.tsx,chartsData.ts→charts-data.ts,Styled.tsx→flex-grid.ts+flex-grid-column.ts, and so on.We're calling this out deliberately: this PR is meant to double as a working example of what bringing a package into
fusion-lintcompliance actually looks like — the config changes, the file splits, the import updates — not just a number going to zero in CI. If you're planning to clean up a package of your own, the commit history here is a reasonable reference.One thing that didn't go smoothly: moving
module-context'serrors.tsbriefly broke the build. Itspackage.jsonhad a./errors.jssubpath export pointing atdist/esm/errors.js, and after the file moved todist/esm/errors/index.js, every downstream consumer (react,react-app,react-module-context,react-module-bookmark,react-components-bookmark,react-components-people-provider) failed to build withTS2307: Cannot find module. CI caught it, we fixed the subpath, andpnpm build:packagesis green again. The lesson, which is now filed away for next time: when a file backing a subpath export moves, theexports/typesVersionsmap has to move with it —tsc -bon the package itself won't tell you, only the downstream consumers will.If you only use
fusion-lintthrough its CLI and config file, nothing changes for you. This only affects code that constructs custom rules or calls thelint-core/lint-rules/lint-configAPIs directly.@equinor/fusion-framework-lint-core—Rule.check(source, filePath, severity)becomesRule.check(source, ctx: LintContext). Readctx.filePathandctx.severityoff the context object instead of taking them as separate arguments.@equinor/fusion-framework-lint-rules— rules now export factory functions instead of pre-builtRuleinstances, andallowMultipleInis gone in favor of the sharedoptions.match:Note that
single-export-per-file's default barrel exemption (index.ts,index.tsx, etc.) is now only a fallback. If you supply your ownoptions.match, it fully replaces the default rather than merging with it — so re-add the barrel patterns yourself if you still want them exempted.@equinor/fusion-framework-lint-config—CustomRuleDefinition.checkfollows the same newcheck(source, ctx)shape aslint-core.🐛 A friendly heads-up
This PR touched ~1,320 files. Most of it is mechanical — renames, TSDoc, comments, config — but at that scale, something occasionally slips through review that neither
pnpm build,pnpm test, nor a human skim catches. If you upgrade and something behaves differently than before (an import path that moved, a type that got stricter than it should have, anything odd), please open an issue and tag it back to this release. We'd genuinely rather hear about it now than have it turn into a mystery six months from now.📦 Released packages
82 packages went out in this release. Headline entries first, full list below.
@equinor/fusion-framework-lint-core@equinor/fusion-framework-lint-rules@equinor/fusion-framework-lint-config@equinor/fusion-lintFull list of released packages (82)
@equinor/fusion-framework@equinor/fusion-framework-app@equinor/fusion-framework-cli@equinor/fusion-framework-cli-plugin-ai-base@equinor/fusion-framework-cli-plugin-ai-chat@equinor/fusion-framework-cli-plugin-ai-index@equinor/fusion-framework-cli-plugin-copilot@equinor/fusion-framework-dev-portal@equinor/fusion-framework-dev-server@equinor/fusion-framework-lint-config@equinor/fusion-framework-lint-core@equinor/fusion-framework-lint-lsp@equinor/fusion-framework-lint-rules@equinor/fusion-lint@equinor/fusion-framework-module-ag-grid@equinor/fusion-framework-module-ai@equinor/fusion-framework-module-analytics@equinor/fusion-framework-module-app@equinor/fusion-framework-module-azure-identity@equinor/fusion-framework-module-bookmark@equinor/fusion-framework-module-context@equinor/fusion-framework-module-event@equinor/fusion-framework-module-feature-flag@equinor/fusion-framework-module-http@equinor/fusion-framework-module@equinor/fusion-framework-module-msal@equinor/fusion-framework-module-msal-node@equinor/fusion-framework-module-navigation@equinor/fusion-framework-module-service-discovery@equinor/fusion-framework-module-services@equinor/fusion-framework-module-signalr@equinor/fusion-framework-module-telemetry@equinor/fusion-framework-module-widget@equinor/fusion-framework-react@equinor/fusion-framework-react-ag-grid@equinor/fusion-framework-react-app@equinor/fusion-framework-react-components-bookmark@equinor/fusion-framework-react-components-people-provider@equinor/fusion-framework-react-module@equinor/fusion-framework-react-module-bookmark@equinor/fusion-framework-react-module-context@equinor/fusion-framework-react-module-event@equinor/fusion-framework-react-module-http@equinor/fusion-framework-react-module-signalr@equinor/fusion-framework-react-router@equinor/fusion-imports@equinor/fusion-load-env@equinor/fusion-log@equinor/fusion-observable@equinor/fusion-query@equinor/fusion-framework-vite-plugin-api-service@equinor/fusion-framework-vite-plugin-markdown@equinor/fusion-framework-vite-plugin-raw-imports@equinor/fusion-framework-vite-plugin-routes-dsl@equinor/fusion-framework-vite-plugin-spa@equinor/fusion-framework-widget@equinor/fusion-framework-cookbook-app-react@equinor/fusion-framework-cookbook-app-react-ag-grid@equinor/fusion-framework-cookbook-app-react-ai@equinor/fusion-framework-cookbook-app-react-apploader@equinor/fusion-framework-cookbook-app-react-assets@equinor/fusion-framework-cookbook-app-react-bookmark@equinor/fusion-framework-cookbook-app-react-bookmark-advanced@equinor/fusion-framework-cookbook-app-react-charts@equinor/fusion-framework-cookbook-app-react-context@equinor/fusion-framework-cookbook-app-react-context-custom-error@equinor/fusion-framework-cookbook-app-react-environment-variables@equinor/fusion-framework-cookbook-app-react-feature-flag@equinor/fusion-framework-cookbook-app-react-module@equinor/fusion-framework-cookbook-app-react-msal@equinor/fusion-framework-cookbook-app-react-people@equinor/fusion-framework-cookbook-app-react-router@equinor/fusion-framework-cookbook-app-react-router-legacy@equinor/fusion-framework-cookbook-app-react-settings@equinor/fusion-framework-cookbook-app-react-stylingpoc-portalportalportal-analyticsfusion-ts-lint-vscode🔗 Key PRs
fusion-lintAPI rework — #5163🤖 Beep Boppp — release notes assembled by Fusion Assistant. 2,507 warnings walked into a bar. None walked out. If your app walks in weird after upgrading, though, please tell us — we'd rather hear about the one thing we missed than have you find it the hard way.
All reactions