Skip to content

Node.js 26 Compatibility #2558

Description

@Sourav-kashyap

Node.js 26 Compatibility Issues Report

Project: loopback4-microservice-catalog
Node Version Tested: 26.2.0


Executive Summary

The project is NOT ready for Node.js 26 due to dependency ecosystem issues. After updating CI workflows and package.json files to support Node 26, testing revealed multiple compatibility issues that prevent successful installation and build.

Root Cause: Node 26 introduced stricter ES module handling and API changes that exposed issues in unmaintained dependencies.


Why Didn't This Happen Before?

Node 22/24 vs Node 26:

Aspect Node 22/24 Node 26
ESM Enforcement Lenient - allowed some inconsistencies STRICT - no exceptions
Module Resolution More forgiving Follows spec exactly
Package Subpath Exports Partial support Full support required
Warning Visibility Fewer warnings More warnings shown

The Key Changes:

  1. ESM Strict Mode - require() illegal in ES modules
  2. Package Exports - Must use proper moduleResolution
  3. API Changes - Dependencies may have breaking updates
  4. More Warnings - Deprecation notices visible

Summary: All Issues Are Dependency-Related

No actual application code changes required (no .ts or .js file changes needed in your source code).

Issue Type What's Broken Fix Location
#1 Dependency yargs package ESM incompatibility package.json (override)
#2 Dependency copyfiles package uses old yargs API External package
#3 Dependency widdershins package uses old yargs API External package
#4 Configuration TypeScript moduleResolution tsconfig.json
#5 Warning localStorage path Node runtime
#6 Warning AWS SDK v2 deprecated External package

The "code changes" needed are only in:

  1. External dependencies (yargs, copyfiles, widdershins)
  2. Build configuration (tsconfig.json, package.json scripts)
  3. NOT in your actual TypeScript/JavaScript source code

Why Problems Now? - Detailed Analysis

The Root Cause Chain

Node 26 Released
  ↓
Stricter ESM enforcement
  ↓
yargs@17.x breaks (ESM incompatibility)
  ↓
Must upgrade to yargs@18.x
  ↓
yargs@18.x has different API
  ↓
Old packages expecting yargs@17 API break:
  - copyfiles ❌
  - widdershins ❌

Complete Issue List with "Why Now" Analysis

Issue #1: yargs ESM Incompatibility

What is yargs?
A Node.js package for parsing command-line arguments. Used by hundreds of npm packages.

How it worked before (Node 22/24):

yargs@17.7.2
├── package.json has "type": "module" (ES module)
├── But code uses require() syntax (CommonJS)
└── Node 22/24: Lenient - allowed this mismatch

Why it breaks now (Node 26):

Node 26: STRICT enforcement
├── "type": "module" means ONLY import/export allowed
├── require() is ILLEGAL in ES modules
└── Throws error immediately

The Error:

ReferenceError: require is not defined in ES module scope
at file:///node_modules/yargs/yargs:3:69

Proper Solution:
Upgrade to yargs@18.x which has proper ES module support.

BUT → This creates new problems (Issues #2 and #3)

Status: 🟡 Partially Fixed (upgraded to 18.x, but created downstream issues)

Error:

ReferenceError: require is not defined in ES module scope
at file:///node_modules/yargs/yargs:3:69

Description: yargs@17.7.2 has "type": "module" in package.json but uses require() syntax, which is invalid in ES modules with Node 26's stricter enforcement.

Fix Applied: Upgraded to yargs@18.0.0 via package.json override

"overrides": {
  "yargs": "^18.0.0"
}

Result: Fixed yargs itself, but broke dependent packages (see issues #2 and #3).


Issue #2: copyfiles Package - yargs API Incompatibility

Status: 🔴 BLOCKING

What is copyfiles?
A package that copies files in your project. Used by @sourceloop/cli to copy template files.

How it worked before (Node 22/24):

// copyfiles@2.4.1 code
const yargs = require("yargs"); // Gets yargs object

// This line works with yargs 17.x:
yargs.alias("u", "up"); // ✅ Method exists on yargs object

Why it breaks now (Node 26 + yargs 18.x):

// With yargs 18.x
const yargs = require("yargs"); // Gets a FUNCTION, not an object

// This line now fails:
yargs.alias("u", "up"); // ❌ Error: alias is not a function
// Functions don't have .alias() method

The API Change:

// yargs 17.x API (old) - copyfiles uses this
const yargs = require("yargs"); // Returns object
yargs.alias("u", "up"); // ✅ Works

// yargs 18.x API (new) - required for Node 26
const yargs = require("yargs")(); // Must CALL the function
yargs.alias("u", "up"); // ✅ Works after calling

The Error:

/Users/sourav.kashyap/Desktop/Mcp/loopback4-microservice-catalog/node_modules/copyfiles/copyfiles:6
args.alias('u', 'up')
     ^

TypeError: args.alias is not a function

Why copyfiles breaks:
copyfiles was written 3+ years ago and expects the old API. The package is unmaintained - no updates.

Affected Package: @sourceloop/cli@12.2.6 build process

Code Location: packages/cli/package.json

"copydeps": "copyfiles -a --up 1 src/**/templates/** lib"

Package Status: ⚠️ Unmaintained (last update 3+ years ago)

Build Command Affected: npm run afterinstall, lerna run build

Proper Solution:
Replace copyfiles with maintained alternative like cpy.

Options:

  1. Fork and patch copyfiles for yargs 18 compatibility
  2. Replace with alternative package (cpy)
  3. Remove dependency on copyfiles

Issue #3: widdershins Package - yargs API Incompatibility

Status: 🔴 BLOCKING

What is widdershins?
Converts OpenAPI JSON specs to Markdown documentation for API docs.

How it worked before (Node 22/24):

// widdershins@4.0.1 code
const yargs = require("yargs");

// This works with yargs 17.x:
yargs.usage("widdershins [options]..."); // ✅ Method exists

Why it breaks now (Node 26 + yargs 18.x):

// With yargs 18.x
const yargs = require("yargs"); // Gets a FUNCTION

// This line fails:
yargs.usage("widdershins [options]..."); // ❌ Error: usage is not a function

The Error:

/Users/sourav.kashyap/Desktop/Mcp/loopback4-microservice-catalog/node_modules/widdershins/widdershins.js:14
    .usage('widdershins [options] {input-file|url} [[-o] output markdown]')
     ^

TypeError: require(...).usage is not a function

Why widdershins breaks:
Same problem as copyfiles - expects yargs 17.x API, package is unmaintained.

Package Status: ⚠️ Unmaintained

Build Command Affected: lerna run build, npm run afterinstall

Affected Services (16 total):

  • @sourceloop/audit-service
  • @sourceloop/authentication-service
  • @sourceloop/bpmn-service
  • @sourceloop/chat-service
  • @sourceloop/in-mail-service
  • @sourceloop/notification-service
  • @sourceloop/oidc-service
  • @sourceloop/payment-service
  • @sourceloop/reporting-service
  • @sourceloop/scheduler-service
  • @sourceloop/search-service
  • @sourceloop/survey-service
  • @sourceloop/task-service
  • @sourceloop/user-tenant-service
  • @sourceloop/video-conferencing-service
  • And others...

Code Pattern (in each service):

"apidocs": "npx widdershins --language_tabs 'javascript:JavaScript:request' 'javascript--nodejs:Node.JS' --summary openapi.json -o openapi.md"

Proper Solution:
Either:

  1. Replace with alternative (no direct replacement exists)
  2. Skip apidocs generation (use OpenAPI JSON directly)
  3. Fork and patch widdershins

Options:

  1. Fork and patch widdershins for yargs 18 compatibility
  2. Replace with alternative OpenAPI to Markdown converter
  3. Skip apidocs generation temporarily

Issue #4: OpenTelemetry Module Path Issue

Status:

What is this?
TypeScript trying to find @opentelemetry/semantic-conventions/incubating module.

How it worked before (Node 22/24):

TypeScript with CommonJS moduleResolution
├── Looks for files with .js extension
├── Doesn't understand package.exports field well
└── Node 22/24: More lenient path resolution

Why it breaks now (Node 26):

Node 26 + newer TypeScript
├── package.json has subpath export:
│   "./incubating": "./build/src/index-incubating.js"
├── Old moduleResolution: "node" - ignores exports field
└── Can't find the module

The Package Export Structure:

// @opentelemetry/semantic-conventions package.json
{
  "exports": {
    ".": "./build/src/index.js",
    "./incubating": "./build/src/index-incubating.js" // ← This path
  }
}

Original Error:

src/profiles/otlp.profile.ts(15,43): error TS2307: Cannot find module '@opentelemetry/semantic-conventions/incubating' or its corresponding type declarations.

The Import:

// packages/observability/src/profiles/otlp.profile.ts
import { ATTR_DEPLOYMENT_ENVIRONMENT } from "@opentelemetry/semantic-conventions/incubating";

Affected Package: @sourceloop/observability@0.0.1

Code Location: packages/observability/src/profiles/otlp.profile.ts:15

Fix Applied: Updated packages/observability/tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "Node16",
    "module": "Node16"
  }
}

Build Command Affected: npm run afterinstall, lerna run build

Result:

Note: Node16 moduleResolution properly handles package.exports field.


Issue #5: localStorage Warning

Status: ⚠️ WARNING (Non-blocking)

What is this?
Node 26 Web Storage API warning for localStorage.

How it worked before (Node 22/24):

localStorage available by default
├── No explicit configuration needed
└── No warnings shown

Why warning appears now (Node 26):

Node 26 Web Storage API changes
├── localStorage requires explicit configuration
├── Must specify --localstorage-file path
└── Shows warning if not configured

The Warning:

ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
(Use `node --trace-warnings ...` to show where the warning was created.)

Description: Node 26 Web Storage API requires explicit file path for localStorage.

Affected: Services using node ./dist/openapi-spec command

Severity: LOW - Warning only, does not block builds

Build Commands Affected: npm run afterinstall, lerna run build

Proper Solution:
Either ignore (warning only) or add --localstorage-file flag to Node commands.


Issue #6: AWS SDK v2 End-of-Support Warning

Status: ⚠️ WARNING (Non-blocking)

What is this?
AWS SDK for JavaScript v2 has reached end-of-support.

How it worked before (Node 22/24):

AWS SDK v2 worked fine
├── No deprecation warnings
└── Node didn't show EOL notices

Why warning appears now (Node 26):

Node 26 shows more warnings
├── AWS SDK v2 is deprecated
└── Node 26 displays the EOL notice

The Warning:

(node:XXXX) NOTE: The AWS SDK for JavaScript (v2) has reached end-of-support.
It will no longer receive updates or releases.

Please migrate your code to use AWS SDK for JavaScript (v3).
For more information, check the blog post at https://a.co/cUPnyil

Description: AWS SDK v2 has reached end-of-life.

Severity: LOW - Deprecation warning only

Recommendation: Plan migration to AWS SDK v3

Build Commands Affected: Services using AWS SDK

Proper Solution:
Plan migration to AWS SDK v3 (not urgent).


Issue Status Summary

Issue Package Status Severity Fix Required
#1 yargs 17.x 🟡 Partially Fixed HIGH Created downstream issues
#2 copyfiles ❌ BLOCKING HIGH Replace or patch
#3 widdershins ❌ BLOCKING HIGH Replace or skip
#4 OpenTelemetry ❌ BLOCKING - ModuleResolution updated
#5 localStorage ⚠️ Warning LOW Optional fix
#6 AWS SDK v2 ⚠️ Warning LOW Migration to v3

Dependency Chain Problem

Your Project (Node 26)
  │
  ├─> yargs 17.x ❌ (ESM incompatibility)
  │     └─> yargs 18.x ✅ (Node 26 compatible)
  │           BUT ↓
  │     Breaks packages expecting yargs 17 API
  │
  ├─> @sourceloop/cli
  │     └─> copyfiles ❌ (expects yargs 17 API)
  │
  └─> All services (16)
        └─> widdershins ❌ (expects yargs 17 API)

The JavaScript/npm dependency ecosystem creates chains. When yargs upgraded to 18.x for Node 26 compatibility, it broke packages using the old API.


yargs API Changes (17.x → 18.x)

The upgrade from yargs 17 to 18 introduced breaking API changes:

// yargs 17.x (old API)
const yargs = require("yargs");
yargs.alias("v", "version"); // ✅ Works
yargs.usage("Description"); // ✅ Works

// yargs 18.x (new API)
const yargs = require("yargs");
yargs.alias("v", "version"); // ❌ Error: alias is not a function
yargs.usage("Description"); // ❌ Error: usage is not a function

// Correct usage in 18.x:
const yargs = require("yargs")(); // Note: () - call as function
yargs.alias("v", "version"); // ✅ Now works
yargs.usage("Description"); // ✅ Now works

Packages like copyfiles and widdershins need to be updated to call yargs() as a function first.


Resolution Options

Option 1: Replace Dependencies 🔄

Effort: High

copyfiles replacement:

  • cpy - Active maintenance
  • Custom Node.js script

widdershins alternatives:

  • @apidevtools/swagger-parser + custom markdown generator
  • redoc for HTML documentation
  • Skip apidocs, use JSON OpenAPI spec only

Pros: Modern, maintained packages
Cons: Significant code changes required


Option 2: Skip apidocs Generation 📝

Effort: Low

  • Comment out apidocs script in all affected services
  • Keep openapi-spec (JSON generation works fine)
  • Revisit later when ecosystem improves

Pros: Unblocks builds immediately
Cons: No markdown documentation


Option 3: Stay on Node 22/24 ✅

Effort: None

  • Revert yargs override
  • Keep Node 22 and 24 support only
  • Revisit Node 26 in 12 months

Pros: Everything works now
Cons: No Node 26 support


Files Modified for Node 26 Support

GitHub Actions Workflows (5 files)

  • .github/workflows/main.yml - Updated matrix to [22, 24, 26]
  • .github/workflows/audit.yml - Updated matrix to [22, 24, 26]
  • .github/workflows/docs.yml - Updated to Node 26.x
  • .github/workflows/pre-release.yml - Updated to Node 26.x
  • .github/workflows/release.yml - Updated to Node 26.x

Root Package Configuration

  • package.json - Added yargs override to ^18.0.0

Service package.json (16 files)

  • All services in services/ directory - Updated engines to ">=22"

Package package.json (7 files)

  • All packages in packages/ directory - Updated engines to ">=22"
  • packages/observability/tsconfig.json - Updated moduleResolution to "Node16"

Sandbox package.json (32 files)

  • All examples in sandbox/ directory - Updated engines to ">=22"

CLI Templates (2 files)

  • packages/cli/src/generators/project/templates/package.json.ejs
  • packages/cli/src/generators/project/templates/package.plain.json.ejs

Test Commands

# Node version check
node -v  # v26.2.0

# Command 1: Installation and build
npm run afterinstall

# Command 2: Build all packages/services
lerna run build

# Command 3: Run all tests
lerna run test

# Command 4: Clean install
rm -rf node_modules package-lock.json
npm ci

Current Node.js Support Status

Version Status Notes
Node 20 ❌ Removed Support removed as part of this update
Node 22 ✅ Working Tested and compatible
Node 24 ✅ Working Tested and compatible
Node 26 ❌ BLOCKED Issues #2, #3 blocking builds

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions