Skip to content

Modernize dave to 0.3.0: ESM, promise API, current deps, GitHub Actions - #1

Merged
aa8y merged 13 commits into
masterfrom
aa8y/modernize/2026/6
Jun 6, 2026
Merged

Modernize dave to 0.3.0: ESM, promise API, current deps, GitHub Actions#1
aa8y merged 13 commits into
masterfrom
aa8y/modernize/2026/6

Conversation

@aa8y

@aa8y aa8y commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary

Long-overdue modernization pass on dave. Twelve focused commits, each
atomic and green, no big-bang changes. Bumps the package to 0.3.0.

Public API — breaking (pre-1.0):

  • main() and runCommand() return promises; callback signatures removed.
  • Package is now ESM ("type": "module"). Consumers must import (not require).

Dependencies — all on current majors:

  • yargs 13 → 18 (parseAsync, string: true on the tags option so 1.0
    doesn't collapse to a Number).
  • chai 4 → 6.
  • mocha, eslint, js-yaml, mustache: latest within current major.
  • Dropped async (a for…of + await replaces eachLimit(_, 1, _)).
  • Dropped lodash (single use of _.merge replaced by a 13-line
    deepMerge with matching semantics).
  • serialize-javascript pinned via npm overrides to clear the only
    audit finding mocha 10 hasn't picked up.

Tooling / CI:

  • istanbulc8 (istanbul's CJS hook can't load mocha's now-ESM yargs
    on Node ≥ 22; this was the blocker that started the modernization).
  • Travis → GitHub Actions (Node 22 + 24 matrix, codecov upload).
  • .nvmrc: 22.3.0 → 24.
  • Removed legacy .eslintrc.yml; flat config in eslint.config.mjs is
    the single source of truth.

Tests:

  • All 36 existing tests preserved and ported to async/await.
  • 4 new tests for previously-uncovered failure paths (getMetadata ENOENT
    and YAMLException; main() propagation of manifest read errors).
  • 49 passing, 96.36 % statement coverage, 100 % on lib/yargs and
    lib/manifest.

Audit: npm audit reports 0 vulnerabilities (down from 12).

README: dropped the always-aspirational pull and template lines;
replaced the TravisCI example with a GitHub Actions one.

Test plan

  • nvm use && npm ci && npm test — 49 passing, lint clean.
  • npm audit — 0 vulnerabilities.
  • ./index.js build --context . --tags begin end --manifest ./test/manifest.yml — exits 0.
  • CI green on the Node 22 and 24 matrix legs.
  • Codecov report uploads from the Node 24 leg.

aa8y added 13 commits June 6, 2026 11:55
CLAUDE.md captures repo orientation for future sessions; the plan file
under .claude/plans/ tracks the modernization tasks for this branch.
istanbul 0.4.5 is unmaintained; its CJS hook fails on current Node
because mocha's bundled yargs is now ESM. Swap to c8, which reads V8's
native coverage. Test script becomes:

  c8 mocha 'test/**/*Test.js' && npm run lint

Verified: 45 tests pass; coverage report intact; lint clean.
- Add .github/workflows/ci.yml with a Node 22/24 matrix.
- Drop .travis.yml.
- Swap the build badge in the README.
- Emit lcov from c8 so codecov has something to consume.
- Remove .eslintrc.yml; eslint 9 uses the flat config in
  eslint.config.mjs exclusively and ignores the legacy file.
- Pin .nvmrc to Node 24 (current active LTS as of June 2026).
Bring every dep (other than yargs, which is on v13 latest and gets a
major bump in its own commit) up to its current major's latest:

- async   3.2.5  -> 3.2.6
- chai    4.4.1  -> 4.5.0
- eslint  9.5.0  -> 9.39.4
- js-yaml 4.1.0  -> 4.2.0  (clears the prototype-pollution advisory)
- lodash  4.17.21 -> 4.18.1 (clears the unset/omit/template advisories)
- mocha   10.4.0 -> 10.8.2
main() and runCommand() now return promises. Internals use async/await
throughout (fs/promises in lib/manifest, a Promise wrapper around yargs
parse callback, util.promisify(exec) for runCommand). The async package
is no longer needed — async.eachLimit(_, 1, _) collapses into a plain
for...of with await.

eslint config caught up to the new code: ecmaVersion bumped to 'latest'
and space-before-function-paren allows the required space between
'async' and '(' on arrow functions.

BREAKING: callback-based callers of main() / runCommand() must switch
to .then() or await. Pre-1.0 so this is an intentional API churn.
- package.json: type: "module".
- All source and test files use import/export.
- ESM import paths include the .js extension as required.
- eslint sourceType bumped to "module".
- Bin guard switches from `module === require.main` to comparing
  process.argv[1] against fileURLToPath(import.meta.url).

Reordered before the yargs bump because yargs 18 is ESM-only and can
no longer be require()'d. CJS consumers must migrate too — pre-1.0.
API changes:
- yargs is now a factory: `yargs(args).command(...).parseAsync()`.
- parseAsync() replaces the callback Promise wrapper.
- Tags option needs `string: true` because yargs 18 coerces
  numeric-looking array values to Numbers before custom .coerce()
  runs (1.0 was collapsing to 1). With `string: true` the original
  token is preserved and the old .coerce() round-trip is unneeded.
ESM migration in the previous commit unblocks chai 5+ (ESM-only).
Existing assert.* usage works unchanged on chai 6.
Lodash was a 70KB dep used in exactly two call sites for _.merge.
Inline a 13-line deepMerge in lib/manifest.js with matching
semantics (plain-object recursion, source-wins-on-overlap, null
source skipped) and drop lodash from dependencies.
npm audit fix took care of flatted, minimatch, and picomatch. Only
remaining was serialize-javascript: mocha 10.x pins ^6.0.2 and the
advisory ceiling is <=7.0.4, so npm audit fix offered to downgrade
mocha to 8.1.3 — unacceptable. Pin via an npm override to ^7.0.5;
mocha's HTML reporter doesn't exercise the API surface that changed.

npm audit: 0 vulnerabilities.
Adds three small tests:
- getMetadata loads a real manifest (happy path was previously
  untested at this layer; only exercised transitively via main).
- getMetadata rejects with ENOENT for a missing path.
- getMetadata rejects with YAMLException for malformed YAML.
- main propagates ENOENT when the manifest path is bad.

test/malformed.yml is a fixture for the YAML failure case. The
runCommand failure path is already covered by 'can fail running
a command.' on the runCommand suite.
- Bump package.json version to 0.3.0 to reflect the breaking API
  surface (ESM + promises) and dependency churn on this branch.
- README: drop pull/template from the supported-commands list (they
  were never implemented), trim the stale 0.1.0 sentence, replace
  the TravisCI example with a GitHub Actions one, and drop the
  pull/template line items from Future Work.
- CLAUDE.md: update internal notes to match the modernized code
  (deepMerge, yargs 18 + string:true, c8 + lcov, no .eslintrc.yml).
@aa8y
aa8y merged commit 7a95e3f into master Jun 6, 2026
2 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