Skip to content

refactor: migrate to shared eslint config#3661

Merged
eablack merged 26 commits intomainfrom
eb/move-to-shared-eslint
Apr 17, 2026
Merged

refactor: migrate to shared eslint config#3661
eablack merged 26 commits intomainfrom
eb/move-to-shared-eslint

Conversation

@eablack
Copy link
Copy Markdown
Contributor

@eablack eablack commented Apr 14, 2026

Summary

Migrates the project from ESLint 8 with legacy .eslintrc.cjs to ESLint 9 with flat config (eslint.config.js), adopting the shared eslint configuration from @heroku-cli/test-utils. This reduces duplication across Heroku CLI projects and ensures consistent linting rules.

This PR primarily focuses on:

  • ESLint 9 migration and shared config adoption
  • Migrating test helpers to use utilities from @heroku-cli/test-utils (runCommand, expectOutput, etc.)
  • Applying linting fixes across unit tests (test/unit directory)
  • Adding semi: ['warn', 'never'] rule to enforce no-semicolon style
  • Fixing ESLint getter-return conflicts with proper TypeScript patterns

Note: Test helper, fixture, acceptance, and integration test linting changes are being handled in a separate PR (#3671).

Key changes:

  • Removed legacy .eslintrc.cjs and .eslintignore files
  • Created new eslint.config.js using ESLint 9 flat config format
  • Added @heroku-cli/test-utils dependency for shared linting rules and test utilities
  • Removed redundant eslint plugin dependencies (now handled by shared config)
  • Updated test helpers to import from @heroku-cli/test-utils instead of local implementations
  • Applied linting fixes to ~300 unit test files
  • Updated minor source file linting issues (dyno.ts, members/add.ts, etc.)
  • Updated dependencies (chai 4→5, nock 13→14, typescript-eslint 8.57→8.58)

Type of Change

Patch Updates (patch semver update)

  • refactor: Refactoring existing code without changing behavior

Testing

Notes:

  • All existing tests should pass with the new linting configuration
  • Linting rules are now consistent with other Heroku CLI projects
  • No functional changes - only formatting, linting, and test helper migration

Steps:

  1. Run npm run lint to verify all linting rules pass
  2. Run npm test to ensure all tests pass
  3. Verify CI checks pass (Passing CI suffices)

@eablack eablack requested a review from a team as a code owner April 14, 2026 16:09
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 16:09 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 16:09 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 16:09 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 16:09 — with GitHub Actions Inactive
@eablack eablack force-pushed the eb/move-to-shared-eslint branch from 8ee63aa to 303d032 Compare April 14, 2026 17:14
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:14 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:14 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:14 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:14 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:33 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:33 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:33 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 17:33 — with GitHub Actions Inactive
@eablack eablack marked this pull request as draft April 14, 2026 18:08
@eablack eablack force-pushed the eb/move-to-shared-eslint branch from bcac179 to 4904a1c Compare April 14, 2026 21:06
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:07 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:07 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:07 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:07 — with GitHub Actions Inactive
@eablack eablack force-pushed the eb/move-to-shared-eslint branch from 4904a1c to 3b6e983 Compare April 14, 2026 21:59
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:59 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 14, 2026 21:59 — with GitHub Actions Inactive
eablack added 9 commits April 16, 2026 09:04
- Rename test/helpers/runCommand.ts to legacy-run-command.ts for old-style tests
- Replace local run-command.ts with shared implementation from @heroku-cli/test-utils
- Update 226+ test files to import runCommand from @heroku-cli/test-utils
- Rename helper files to kebab-case: testInstances.ts -> test-instances.ts, uxStub.ts -> ux-stub.ts
- Consolidate test utilities to use shared @heroku-cli/test-utils package

This reduces code duplication and ensures all tests use the same command runner implementation.
- Replace local test/helpers/utils/expectOutput.ts with shared implementation
- Update 57 test files to import expectOutput from @heroku-cli/test-utils
- Use named import syntax for consistency with other test-utils exports

This continues the consolidation of test utilities into the shared package.
Remove eslint plugins that are now handled by shared config. Also fix URL construction in exec.ts to avoid duplicate slashes in the API path.
The shared eslint config requires eslint-import-resolver-typescript v4.x
for ESLint 9 flat config compatibility. Without it explicitly installed,
npm resolves to v3.x from eslint-config-oclif, which has an incompatible
interface and causes "invalid interface loaded as resolver" errors.
The bin/run.js file imports from the dist folder, which needs to exist
before linting can resolve those imports. Running build before lint
ensures the TypeScript compiler generates the dist folder first.
Use ternary expression with explicit return to satisfy getter-return rule
while avoiding no-useless-return. Added explicit return type annotation
for clarity.
- Add semi: ['warn', 'never'] to eslint config for no semicolons preference
- Fix unicorn/prefer-ternary warning in members/add.ts
- Remove unnecessary no-var from eslint-disable comment
- Use String.raw for better backslash escaping in tests
- Reorder imports and alphabetize mock properties
@eablack eablack force-pushed the eb/move-to-shared-eslint branch from e72a630 to c4eb1d7 Compare April 16, 2026 16:05
@eablack eablack temporarily deployed to AcceptanceTests April 16, 2026 16:05 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 16, 2026 16:05 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 16, 2026 16:05 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests April 16, 2026 16:05 — with GitHub Actions Inactive
Replace try-catch blocks with error property destructuring from runCommand
across 88 test cases in 23 test files. This simplifies error handling by
using the error returned directly from runCommand instead of relying on
catch blocks.
@eablack eablack temporarily deployed to AcceptanceTests April 16, 2026 21:45 — with GitHub Actions Inactive
eablack added 6 commits April 16, 2026 14:51
Fix 6 test files where error handling was incorrectly using expect.fail,
try-catch blocks, or expect().to.be.rejected instead of properly checking
the error property returned by runCommand. This resolves 9 test failures.
Import HTTPError type and cast error to access body property,
resolving TypeScript compilation error.
Replace stdout-stderr package with captureOutput from @heroku-cli/test-utils
for cleaner and more consistent test output handling. Converted 7 test files
and removed the stdout-stderr dependency from package.json.

Files converted:
- lib/confirm-command.unit.test.ts
- lib/spaces/peering.unit.test.ts
- lib/spaces/spaces.unit.test.ts
- lib/run/log-displayer.unit.test.ts
- lib/spaces/hosts.unit.test.ts
- lib/data/display-quota.unit.test.ts
- lib/spaces/vpn-connections.unit.test.ts
The log-displayer tests that expect errors were wrapping displayer.display()
in captureOutput, which was causing the tests to hang. Since these tests are
only checking error messages and not stdout/stderr output, we can remove the
captureOutput wrapper and catch the errors directly in try-catch blocks.
The MockEventSource was creating setTimeout calls that weren't being cleaned up.
These orphaned timers kept the Node.js event loop alive, causing the test suite
to hang with a 30+ second timeout. Now we track all timeout IDs and clear them
in the close() method to ensure proper cleanup.
The container/release test was calling stdMocks.use() to hijack stdout/stderr
but never calling stdMocks.restore(). This left stdout/stderr in a broken state,
causing the test runner to malfunction and exit after the release tests completed,
preventing subsequent test files from running.

Added stdMocks.flush() and stdMocks.restore() calls to properly clean up after
capturing output.
Copy link
Copy Markdown
Contributor

@michaelmalave michaelmalave left a comment

Choose a reason for hiding this comment

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

Left one comment but otherwise this looks good.

Comment thread src/commands/members/add.ts
eablack added 7 commits April 16, 2026 16:53
The container/release test was using stdMocks.use() to capture stdout/stderr,
but then checking the output from runCommand() which already captures output.
This was redundant and the missing stdMocks.restore() was breaking the test
runner by leaving stdout/stderr hijacked, causing the test suite to exit early.

Removed:
- stdMocks usage from container/release test since runCommand() handles capture
- std-mocks dependency from package.json (no longer used anywhere)
The tests that expect errors to be thrown shouldn't use captureOutput
because captureOutput doesn't return an error property. When an error
is thrown inside captureOutput, it gets swallowed and the test fails
with the expect.fail() message instead of properly catching the error.

Removed captureOutput wrapper from the two tests that expect errors,
since we don't need to capture stdout/stderr when we're just checking
that an error is thrown.
Fixed various linting warnings including:
- Adding explicit type imports (e.g., SinonStub)
- Addressing other ESLint warnings
- Cleaning up test file patterns

Updated 65 test files across commands, lib, and unit test directories.
Fixed linting warnings related to sinon usage across test files:
- Added explicit type imports for sinon types
- Cleaned up sinon stub and spy patterns
- Fixed other sinon-related ESLint warnings

Updated 17 test files.
Fixed additional linting warnings related to sinon usage:
- Added explicit type imports for sinon types
- Cleaned up sinon stub, spy, and sandbox patterns
- Fixed sinon-related ESLint warnings

Updated 19 test files across commands, hooks, and lib directories.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants