Repository navigation
USWDS - JS: Add ESLint rules that can detect CommonJS/ESM mistakes #6849
Description
Summary
eslint.config.mjs currently sets languageOptions.sourceType: "module"
for every file in packages/, and injects Node's require/module
globals for those same files -- so ESLint currently cannot distinguish a
CommonJS file from an ESM file, and cannot catch the most common mistake
that will come up while converting files from one to the other (see the
parent issue #6845 for full context). This issue adds the rules that make that
distinction possible, ahead of any actual source conversion.
The specific mistake this needs to catch
When a CommonJS module does module.exports = { prefix: "usa" }, converting
its importers to import { prefix } from "./config.js" looks correct but
throws at runtime: SyntaxError: Named export 'prefix' not found. The
correct, universally-safe form is
import config from "./config.js"; const { prefix } = config; (default
import, then destructure). This is exactly the class of error eslint-plugin-import-x's
import/named rule is designed to catch.
Scope
Restructure eslint.config.mjs into (at least) these scoped blocks, adding
rules only where they apply:
- ESM source block (
files: ["packages/**/*.js", "packages/**/*.mjs"],
excludingpackages/usa-icon/src/usa-icons.config.js-- see note
below): add
-import/named: "error"
-import/no-cycle: "error"(there are currently zero import cycles
inpackages/source; this rule keeps it that way, which matters
because a later part of this migration relies on the dependency
graph staying acyclic)
-import/extensions: ["warn", "ignorePackages", { js: "always", mjs: "always" }]
-- land this as a warning, not an error, since it will fire on
every extensionless relativerequire()call currently in the
codebase (there are many). A shrinking warning count in CI output
over the course of this migration is a useful progress signal for
reviewers. It gets flipped to"error"in the final cleanup issue of this migration (USWDS - JS: Migration cleanup -- delete the Storybook CJS-to-ESM transform plugin, tighten lint rules, fix stale docs #6869), once no CommonJS source remains.
-import/no-commonjs: leave this off in this issue. It gets
turned on incrementally, one directory at a time, by each later
conversion issue in this migration, as that directory'sfiles
glob -- turning it on globally now would just fail on every
unconverted file and add no signal. - Node-tooling block (
gulpfile.js,tasks/**/*.js(but not
tasks/**/*.mjs, which are already ESM),.storybook/**/*.js,
svgo.config.js,webpack.twig.config.js,
packages/usa-icon/src/usa-icons.config.js): explicitly set
languageOptions.sourceType: "commonjs"and keep Node globals. This
block should haveimport/no-commonjsexplicitly disabled (these
files are meant to stay CommonJS). - Note:
packages/usa-icon/src/usa-icons.config.jslives inside
packages/but is a Node-side build config
(tasks/svg-sprite.jsdoesrequire("../packages/usa-icon/src/usa-icons.config")),
not browser-facing component source. It must be excluded from the
"ESM source" block above and included in the "Node-tooling" block, or
it will be mistakenly targeted by later conversion work. -
npm run lint:jspasses with only the expectedimport/extensions
warnings (no new errors).
Verification
npm run lint:js
Confirm the warning count roughly matches the number of extensionless
relative require()/import specifiers currently in packages/ (order of
magnitude: over 100).
Depends on
Should land after "Tooling hygiene ahead of the ESM migration" (#6846; which
widens lint:js's scope to the whole repo -- this issue's Node-tooling
block needs that widened scope to have any effect).
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTriage
Activity
ravitejapioneerblaze-code commentedon Sep 19, 2026
Hi team, I'd like to work on this issue and will submit a PR as outlined. Please let me know if it's already being handled.