Skip to content

Make lint runnable, cover the shipped lib/, and run it in CI - #458

Merged
TimelordUK merged 1 commit into
masterfrom
feat/wire-up-lint
Sep 13, 2026
Merged

TimelordUK merged 1 commit into
masterfrom
feat/wire-up-lint

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

npm run lint now exits 0, lints the shipped JavaScript for the first time, and runs as its own CI job.

Three separate problems, in increasing order of how much they mattered.

1. Nothing ran lint

The lint script existed but no workflow called it — which is exactly how the TypeScript bump left typescript-eslint refusing to load for weeks with no red build. Lint that nothing runs is lint that rots.

Added a lint job to test.yml. It needs no SQL Server, no ODBC driver and no native build, so npm ci --ignore-scripts is enough and it finishes in under a minute.

2. The shipped code was never linted

This is the one worth knowing about. eslint.config.mjs targets lib/**/*.ts — and the only .ts file in lib/ is index.d.ts, which the very same config block then ignores via ignores: ['**/*.d.ts']. lib/ is 26 .js files.

So the config looked like it covered the product code while covering none of it, and npm run lint only ever checked 11 files in test/ and samples/.

Added a lib/**/*.js block and widened the lint scripts. Stylistic rules only — eslint-config-love is type-aware and wants a tsconfig project, which plain .js here can't supply.

The good news: the shipped code was in decent shape — 4 violations, all whitespace, all auto-fixed. git diff -w on lib/ is empty, so nothing changed but layout.

3. The 75 pre-existing violations

62 went with --fix. Of the remainder:

Rule Count Action
no-promise-executor-return 3 Fixed — new Promise(resolve => setTimeout(...)) returns the timer handle out of the executor. Braced.
no-floating-promises 1 Fixed — the async IIFE ending logger-example.ts had no catch, so any failure became an unhandled rejection with a bare stack trace. Now logs and sets exitCode.
only-throw-error 11 Disabled, reason recorded in config
no-non-null-assertion 9 Disabled, reason recorded in config

On the two disables: only-throw-error can't be satisfied without rewriting harness code that rethrows driver callback errors. QueryCb types err as Error | undefined, but the overloaded query(sqlOrQuery, paramsOrCb?, cb?) signature leaves it unresolvable at some call sites, and cmd-test.ts declares several of its own handlers as err: any. The second is res![0] in tests that just inserted the row they select. Both are consistent with the ~15 no-unsafe-* rules already off for the same underlying reason — the driver's own surface is loosely typed.

One autofix I had to revert

prefer-object-has-own rewrote Object.prototype.hasOwnProperty.call(o, k) → Object.hasOwn(o, k), which does not compile: tsconfig sets target: es6 with no explicit lib, and Object.hasOwn is ES2022, so tsc failed with TS2550.

Rule disabled, change reverted. Raising the target is the better fix — lib/*.js already uses Object.hasOwn at runtime, so only the TS lib setting lags — but it changes emitted dist/ output and the generated declarations, so it wants its own PR rather than riding along with lint wiring.

Verified

  • npm run lint → exit 0
  • npm run build and npm run build:test → both exit 0
  • driver still loads, 46 exports
  • connection-pool suite 42 passing; query + connect suites 26 passing — covering notifier.js and sql-client.js, the two lib/ files touched

🤖 Generated with Claude Code

https://claude.ai/code/session_01K5wuZia58hUzEqN7CvHtz9

`npm run lint` now exits 0, lints the shipped JavaScript for the first time,
and runs as its own CI job.

Three separate problems, in increasing order of how much they mattered.

1. Nothing ran lint.

The `lint` script existed but no workflow called it, so breaking it cost
nothing - which is how a TypeScript major bump left typescript-eslint refusing
to load for weeks without a single red build. Added a `lint` job to test.yml.
It needs no SQL Server, no ODBC driver and no native build, so
`npm ci --ignore-scripts` is enough and it finishes in under a minute.

2. The shipped code was never linted.

eslint.config.mjs targets `lib/**/*.ts`, and the only .ts file in lib/ is
index.d.ts, which the very same block then ignores via `ignores: ['**/*.d.ts']`.
lib/ is 26 .js files. So the config looked like it covered the product code
while covering none of it, and `npm run lint` only ever checked 11 files in
test/ and samples/.

Added a lib/**/*.js block with the same stylistic rules, and widened the lint
scripts to include it. Stylistic rules only: eslint-config-love is type-aware
and expects a tsconfig project, which plain .js files here cannot supply. The
shipped code turned out to be in good shape - 4 violations, all whitespace,
all auto-fixed. `git diff -w` on lib/ is empty, so nothing changed but layout.

3. The 75 pre-existing violations.

62 went with --fix. Of the rest:

  - 3 x no-promise-executor-return: `new Promise(resolve => setTimeout(...))`
    returns the timer handle out of the executor. Braced.
  - 1 x no-floating-promises: the async IIFE at the end of
    samples/typescript/logger-example.ts had no catch, so any failure in the
    sample became an unhandled rejection with a bare stack trace. Given a
    catch that logs and sets exitCode.
  - 11 x only-throw-error and 9 x no-non-null-assertion: turned off, with the
    reasons recorded in the config. The first cannot be satisfied without
    rewriting harness code that rethrows driver callback errors - QueryCb types
    err as `Error | undefined`, but the overloaded query() signature leaves it
    unresolvable at some call sites and cmd-test.ts declares several handlers as
    `err: any`. The second is `res![0]` in tests that just inserted the row they
    select. Both are consistent with the ~15 no-unsafe-* rules already off for
    the same reason: the driver's own surface is loosely typed.

One autofix had to be reverted. `prefer-object-has-own` rewrote
`Object.prototype.hasOwnProperty.call(o, k)` to `Object.hasOwn(o, k)`, which
does not compile: tsconfig sets target es6 with no explicit lib, and
Object.hasOwn is es2022, so tsc failed with TS2550. Rule disabled and the
change reverted. Raising the target is the better fix, but it changes emitted
dist/ output and the generated declarations, so it belongs in its own change
rather than riding along with lint wiring.

Verified: npm run lint exits 0; npm run build and npm run build:test both exit
0; the driver still loads; connection-pool suite 42 passing and
query/connect suites 26 passing, which covers notifier.js and sql-client.js,
the two lib files touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5wuZia58hUzEqN7CvHtz9
@TimelordUK
TimelordUK merged commit ab47964 into master Sep 13, 2026
12 checks passed
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.

1 participant