Skip to content

feat(deps)!: upgrade js-yaml to v5 - #4029

Merged
PierreBrisorgueil merged 4 commits into
masterfrom
feat/js-yaml-v5-migration
Aug 31, 2026
Merged

feat(deps)!: upgrade js-yaml to v5#4029
PierreBrisorgueil merged 4 commits into
masterfrom
feat/js-yaml-v5-migration

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

What

Upgrades js-yaml from 4.3.2 to 5.4.1 and migrates the two call sites that used the ESM default export.

File Change
lib/services/express.js:17 import YAML from 'js-yaml' -> import * as YAML from 'js-yaml'
modules/tasks/tests/tasks.openapi-operationid.unit.tests.js:2 same
MIGRATIONS.md downstream migration entry

Namespace import rather than named, so every YAML.load(...) call site stays as it is — this is the migration path js-yaml's own v4->v5 guide recommends for existing code.

Why it could not just be a version bump

Dependabot's #3909 proposed the same major as a lockfile-only change and was red. v5 removes the ESM default export, so the old import throws SyntaxError: The requested module 'js-yaml' does not provide an export named 'default' at module-load time. Since lib/app.js imports lib/services/express.js, that breaks application boot, not only the test suite.

Schema change — checked, not assumed

v5 switches load() from DEFAULT_SCHEMA to CORE_SCHEMA, which drops merge keys (<<:), implicit timestamps, !!binary, !!omap, !!pairs and !!set; load('') now throws instead of returning undefined.

Grepped every .yml in the repo for <<:, !!, anchors/aliases and unquoted YYYY-MM-DD scalars: no hits, and no spec file is empty. So nothing changes stack-side. The tasks.openapi-operationid test is the real check here — it parses modules/tasks/doc/tasks.yml unmocked, so it exercises the new schema for real.

The three express.docs* tests mock js-yaml and already expose a named load, so they need no change.

Verification

Full unit suite green locally: 171 suites, 2380 tests.

Downstream

MIGRATIONS.md carries the checklist: switch default imports, grep project YAML for merge keys / unquoted dates / !! tags, guard any YAML file that can legitimately be empty. Marked BREAKING CHANGE because a downstream project with its own default import breaks on update.

Supersedes #3909.

Summary by CodeRabbit

  • Bug Fixes

    • OpenAPI YAML files that are empty, whitespace-only, comment-only, or BOM-only are now skipped gracefully with a warning.
    • Malformed non-empty YAML continues to report an error.
    • Valid OpenAPI specifications continue to load and serve normally.
  • Documentation

    • Added migration guidance for upgrading the YAML parser, including import changes and updated parsing behavior.

js-yaml v5 removes the ESM default export, so `import YAML from 'js-yaml'`
now throws at module-load time. Both call sites switch to a namespace import,
which keeps the `YAML.load(...)` call sites unchanged:

- lib/services/express.js (OpenAPI spec parsing at boot)
- modules/tasks/tests/tasks.openapi-operationid.unit.tests.js

v5 also switches load() from DEFAULT_SCHEMA to CORE_SCHEMA, dropping merge
keys, implicit timestamps, !!binary, !!omap, !!pairs and !!set. The stack's
own YAML specs use none of these, so nothing changes stack-side — verified
by the operationId test, which parses modules/tasks/doc/tasks.yml for real
rather than through a mock.

BREAKING CHANGE: downstream projects using `import yaml from 'js-yaml'` must
switch to a namespace or named import, and any project YAML relying on merge
keys, unquoted dates or !! tags changes meaning under CORE_SCHEMA. See
MIGRATIONS.md for the full downstream checklist.
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 49 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f4f30750-251a-4385-b3d6-bdf39706e13f

📥 Commits

Reviewing files that changed from the base of the PR and between 513d548 and 3ad5792.

📒 Files selected for processing (2)
  • lib/services/express.js
  • lib/services/tests/express.docsEmptySpec.unit.tests.js

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8b0d998f-1892-44c5-a15b-2706aa712912

📥 Commits

Reviewing files that changed from the base of the PR and between 477580e and 513d548.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • MIGRATIONS.md
  • lib/services/express.js
  • lib/services/tests/express.docs.unit.tests.js
  • lib/services/tests/express.docsEmptySpec.unit.tests.js
  • lib/services/tests/express.docsEnvGate.unit.tests.js
  • modules/tasks/tests/tasks.openapi-operationid.unit.tests.js
  • package.json
💤 Files with no reviewable changes (2)
  • lib/services/tests/express.docsEnvGate.unit.tests.js
  • lib/services/tests/express.docs.unit.tests.js

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The project upgrades js-yaml from v4 to v5, updates import and mock shapes, documents migration changes, and adjusts OpenAPI loading for content-free YAML documents.

Changes

js-yaml v5 upgrade

Layer / File(s) Summary
js-yaml v5 contract and import updates
package.json, MIGRATIONS.md, lib/services/express.js, modules/tasks/tests/tasks.openapi-operationid.unit.tests.js, lib/services/tests/express.docs.unit.tests.js, lib/services/tests/express.docsEnvGate.unit.tests.js
The dependency uses js-yaml v5. Imports and mocks use the namespace or top-level load export. Migration notes document changed schemas, scalar parsing, YAML 1.1 support, and empty-document errors.
OpenAPI empty-document handling
lib/services/express.js, lib/services/tests/express.docsEmptySpec.unit.tests.js
OpenAPI loading skips empty, whitespace-only, comment-only, and BOM-only files. Other YAML errors propagate. Tests cover skipped files, malformed YAML, and valid specification serving.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 513d5

This upgrades js-yaml and updates the affected imports while preserving existing endpoint gating and parser failure behavior; schema differences and downstream migration steps are documented, and the full unit suite is reported green. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant OpenAPIFile
  participant initApiSpec
  participant jsYaml
  participant Logger
  participant Express
  initApiSpec->>OpenAPIFile: read raw YAML content
  initApiSpec->>jsYaml: load raw YAML
  alt content-free document
    jsYaml-->>initApiSpec: empty-document error
    initApiSpec->>Logger: warn and skip document
  else valid document
    jsYaml-->>initApiSpec: parsed OpenAPI document
    initApiSpec->>Express: register specification route
  else malformed document
    jsYaml-->>initApiSpec: parsing error
    initApiSpec-->>OpenAPIFile: propagate loading failure
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the upgrade, migration changes, schema risks, testing, and downstream impact. It does not follow the repository template and omits the required Scope, Guardrails check, and No… Rewrite the description using the repository template. Add Summary, Scope, Validation, Guardrails check, and Notes sections. Mark each validation and guardrail item, state the impacted modules, cross-module impact, risk level, security cons…
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: a breaking upgrade of js-yaml to version 5.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description explains the upgrade, migration changes, schema risks, testing, and downstream impact. It does not follow the repository template and omits the required Scope, Guardrails check, and Notes sections, plus explicit checklist status for lint, tests, and manual checks.

Resolution

Rewrite the description using the repository template. Add Summary, Scope, Validation, Guardrails check, and Notes sections. Mark each validation and guardrail item, state the impacted modules, cross-module impact, risk level, security considerations, mergeability considerations, and related issue or explicitly state that none applies.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/js-yaml-v5-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.07%. Comparing base (477580e) to head (3ad5792).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4029   +/-   ##
=======================================
  Coverage   94.06%   94.07%           
=======================================
  Files         170      170           
  Lines        5833     5837    +4     
  Branches     1867     1869    +2     
=======================================
+ Hits         5487     5491    +4     
  Misses        283      283           
  Partials       63       63           
Flag Coverage Δ
integration 62.25% <80.00%> (+<0.01%) ⬆️
unit 77.59% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 477580e...3ad5792. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Review gate caught two things the v5 migration missed.

1. `load('')` throws in v5 where v4 returned undefined, so an empty (or
   whitespace-only) OpenAPI file went from 'skip with a warning' to a rethrown
   error — i.e. a boot failure for any downstream project with an empty spec.
   express.js now checks the raw content before calling load(), which routes
   the empty case back through the existing not-a-plain-object guard. Locked
   by a test that fails exactly the way boot would if the guard is removed.

2. MIGRATIONS.md pointed downstream at `{ schema: DEFAULT_SCHEMA }` to restore
   YAML 1.1 behaviour, but v5 removed that export. The replacement is
   YAML11_SCHEMA. Verified by executing the package, not by reading docs.

Also drops the now-dead `default:` keys from the three js-yaml jest mocks: v5
has no default export, so a mock that supplies one would let a reintroduced
default import pass tests and still fail at boot.
…ion notes

Independent review (CodeRabbit was rate-limited on this public repo) found the
previous commit's guard incomplete and three factual errors in MIGRATIONS.md.
Every claim below was checked by executing js-yaml 5.4.1.

The guard: `raw.trim()` is truthy for a file containing only a comment, but v5
raises 'expected a document, but the input is empty' for that too — as it does
for a BOM-only file. Since config.files.openapi globs modules/*/doc/*.yml, a
downstream module shipping a stubbed or commented-out spec still bricked boot.
express.js now lets load() run and discriminates on err.reason, so all four
content-free shapes skip with a warning while real parse errors still propagate.

The tests: the mocked case is replaced by express.docsEmptySpec.unit.tests.js,
which runs the REAL parser over zero-byte, whitespace-only, comment-only and
BOM-only input, asserts the warning is emitted, and adds two controls — a
malformed document must still fail loudly, a valid one must still be served.
A mock could only ever throw for the inputs its author imagined, which is
exactly how the comment-only case slipped through. Mutation-checked: removing
the guard fails all four cases.

MIGRATIONS.md corrections:
- a '<<:' merge key does NOT raise under CORE_SCHEMA, it is silently kept as a
  literal '<<' key. Saying it raises would send a downstream maintainer looking
  for a failure that never comes.
- YAML11_SCHEMA is not a drop-in for v4's DEFAULT_SCHEMA. It also restores YAML
  1.1 scalar rules v4 did not apply (12:30 -> 750, 014 -> 12, 0o14 -> '0o14',
  key y -> true). Documented as a last resort with the divergences listed.
- '!!set' does not load as a native Set under the default schema, it throws
  unknown tag; the Set only appears under YAML11_SCHEMA, and serialises to {}.
- the content-free trigger set and the recommended guard now match the code.
Follow-up nits from the independent review.

A multi-document file is the closest sibling of the guarded condition: v5 raises
'expected a single document in the stream, but found more' from the same function
over the same documents array, two lines below the empty-document throw. It is
therefore the input any future loosening of the reason check would swallow first,
so it earns an explicit control alongside the malformed-syntax one.

Also records why loadAll() was not used. It looks like the tidier fix — it returns
[] for content-free input and drops the message-string dependency entirely — but it
accepts a multi-document file that both v4 and v5 reject, trading a loud failure for
a silent partial parse.

Drops an HTML entity from the test file's JSDoc that only existed to avoid closing
the block comment.
@PierreBrisorgueil
PierreBrisorgueil merged commit b3de6b6 into master Aug 31, 2026
8 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the feat/js-yaml-v5-migration branch August 31, 2026 10:22
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