Skip to content

Modernize monorepo: ESM, Node 24, npm workspaces#397

Open
veged wants to merge 12 commits intomasterfrom
claude/update-dependencies-plan-ifYsx
Open

Modernize monorepo: ESM, Node 24, npm workspaces#397
veged wants to merge 12 commits intomasterfrom
claude/update-dependencies-plan-ifYsx

Conversation

@veged
Copy link
Copy Markdown
Member

@veged veged commented Feb 25, 2026

Summary

  • CJS → ESM: Migrated ~300+ files across all 22 packages to native ES modules
  • Node 24 + npm workspaces: Replaced Lerna with npm workspaces, set engines: ">=24", type: "module"
  • ~35 dependencies replaced with native Node.js APIs (only 3 production deps remain: cosmiconfig, fast-xml-parser, change-case)
  • Test infrastructure updated: mocha 11, chai 6, sinon 21, esmock (replaces proxyquire), c8 (replaces nyc)
  • ESLint 10 flat config (replaces ESLint 4 + tslint)
  • GitHub Actions CI (replaces Travis CI + AppVeyor)

Key dependency replacements

Removed Replacement
pinkie-promise, es6-promisify Native Promise / node:fs/promises
graceful-fs, mz/fs node:fs/promises
lodash.* (8 packages) Native APIs (structuredClone, isDeepStrictEqual, etc.)
glob, is-glob node:fs globSync
debug node:util debuglog
depd process.emitWarning
betterc cosmiconfig
stringify-object node:util inspect
proxyquire esmock
nyc c8

Test plan

  • All 1153 tests passing, 0 failures
  • 0 ESLint errors
  • Verify CI passes on GitHub Actions

🤖 Generated with Claude Code

claude and others added 12 commits February 23, 2026 17:21
Analyze all 22 packages in the monorepo, compare current vs latest
versions of all dependencies, and document a phased update strategy:
- Phase 0: Migrate from Lerna to npm workspaces, Travis/AppVeyor to GH Actions
- Phase 1-2: Update test/lint tooling (mocha, eslint, replace tslint)
- Phase 3: Update prod deps, replace 16 packages with native Node.js 18+ APIs
- Phase 4: Update sub-package dev deps
- Phase 5: Optional CJS→ESM migration

https://claude.ai/code/session_011Gd38T7faQEEqyRNjyU6ha
Add CRITICAL INSTRUCTION marker and bilingual reinforcement to ensure
the model consistently responds in Russian from the start of every session.

https://claude.ai/code/session_011Gd38T7faQEEqyRNjyU6ha
User prefers to keep project CLAUDE.md minimal and handle language
settings via global config/hooks instead.

https://claude.ai/code/session_011Gd38T7faQEEqyRNjyU6ha
…deps

- Lerna → npm workspaces, Travis/Appveyor → GitHub Actions CI
- CJS → ESM: all 300+ files converted (import/export, node: prefix, .js extensions)
- Replace ~35 dependencies with native Node.js APIs:
  pinkie-promise, es6-promisify, graceful-fs, mz/fs, es6-error, async-each,
  hash-set, ho-iter, depd, debug, lodash.*, glob, is-glob, betterc→cosmiconfig,
  node-eval→node:vm, stringify-object→node:util, json5→inline,
  xamel→fast-xml-parser, camel-case+pascal-case→change-case
- Dev stack: mocha 11, chai 6, sinon 21, esmock (replaces proxyquire),
  c8 (replaces nyc), ESLint 10 flat config (replaces eslint 4 + tslint)
- TypeScript 5.9, .d.ts files updated to direct exports
- Only 3 production deps remain: cosmiconfig, fast-xml-parser, change-case

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix react.js preset: restore correct base (origin-react) and pattern
- Add Symbol.for('nodejs.util.inspect.custom') to BemCell and BemFile
- Fix merge.js: skip undefined values to match lodash.mergeWith behavior
- Fix save.test.js: writeFile stub must return Promise (resolves())
- Fix walkers.test.js: include sdk stub in mock default export
- Fix config library tests: use .bemrc.json mock instead of betterc injection
- Add naming.presets exports field for ESM subpath imports
- Fix workspace version refs for @bem/sdk.naming.presets
- Remove invalid mocha imports (describe/it are globals)
- Fix chai 6 imports (no default export)
- Add chai-subset dev dependency

All 1153 tests passing, 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace obj.hasOwnProperty() with Object.hasOwn() (10 files)
- Remove useless variable assignments (7 files)
- Fix unused variables: prefix with _ or remove empty catch params
- Remove unnecessary try/catch wrapper in keyset
- Attach cause to re-thrown errors in keyset
- Fix async promise executor in deps/gather.js
- Replace xit() with it.skip() in bemjson-to-jsx test
- Add structuredClone and xit to ESLint globals

0 errors, 1153 tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Old @types/node@8.x is blocked on npm registry (403 Forbidden).
Removed per-package devDependencies (covered by root), updated root
to match Node 24 target.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ueue

- Remove @types/node ^8.0 from packages (blocked on npm registry)
- Update root @types/node to ^24.0.0 to match Node target
- Update c8 to ^11.0.0
- Override yocto-queue to ^1.2.0 (0.1.0 blocked on npm)
- Regenerate package-lock.json with clean dependency tree

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Overriding yocto-queue directly broke p-limit@3's API (Queue is not
a constructor). Instead, override p-limit to ^4.0.0 which natively
uses yocto-queue ^1.0.0, avoiding the blocked 0.1.0 version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitHub Actions blocks old transitive deps with known CVEs (403 on
emoji-regex@8, yocto-queue@0.1.0, etc). These come from deep chains
in mocha/c8 that can't be cleanly overridden. Using npm install lets
npm resolve fresh versions. Also removed p-limit override.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use escaped double quotes for mocha glob in package.json so
  PowerShell passes the pattern to mocha without mangling it
- Delete .eslintignore (ESLint 10 uses ignores in eslint.config.js)
- Remove all 45 unused eslint-disable directives via --fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qfox
Copy link
Copy Markdown
Member

qfox commented Mar 23, 2026

So we can merge it as is but im not sure it is a good idea.

Current state of this monorepo is pretty old but stable and understandable piece. I can understand if we will decide to move to the new world without human ownership of code but I don't like this PR. There are a lot of little nits which will be very painful on upgrading so this PR becomes sense-less.

To use modern shit properly for upgrading purposes we should split this PR to few separate chunks and merge it one by one without hurry. Are you ready for that?

My suggestion:

  • fix styling things, and other minor preparations for migration
  • upgrade to node24 without esm
  • drop mocha
  • merge and patch release for each package
  • add system prompts for agents
  • migrate to esm
  • do the rest
  • merge and major release for each package

What you think? Sounds good or soso? @veged

const expect = require('chai').expect;

const BemjsonNode = require('../..');
import BemjsonNode from '../../index.js';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@veged не хотим поменять на ./, чтобы он брал из package.json?

Comment on lines +5 to +6
import bemjsonToDecl from '../index.js';
const parse = bemjsonToDecl.convert;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

import { convert as parse } from '../'; ?

Comment on lines +1 to +4
import BemEntity from '@bem/sdk.entity-name';
import { inspect } from 'node:util';

const b_ = require('@bem/sdk.entity-name').create;
const util = require('util');
const b_ = BemEntity.create;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

import BemEntity, { create as b_ } from '@bem/sdk.entity-name';
import { inspect as utilInspect } from 'node:util';

const inspect = (el) => utilInspect(el, { breakLength: Infinity, maxArrayLength: null, depth: null });

With patching head like that you no need to patch all these tests, prob it's better.
Not sure if your Agent can optimize diff size for easier migration and faster delivery

{ block: 'icon', mods: { type: 'right' }}
]})).to.equal(
`[
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Patches like these better to move to separate PR

"dependencies": {
"@bem/sdk.decl": "^0.3.10",
"@bem/sdk.entity-name": "^0.2.11",
"stringify-object": "^3.2.0"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You removing dependency here…

| **node-eval** | 1.1.0 / ^2.0.0 | 2.0.0 | → **2.0.0** везде | Унифицировать версию |
| **camel-case** | ^3.0.0 | 5.0.0 | → **4.x** (CJS) или **5.x** (ESM) | v4+ — breaking API. Используется в bemjson-to-jsx |
| **pascal-case** | ^2.0.1 | 4.0.0 | → **3.x** (CJS) или **4.x** (ESM) | Аналогично camel-case |
| **stringify-object** | ^3.2.0 | 6.0.0 | → **5.x** (CJS) или **6.x** (ESM) | Используется в bemjson-to-decl |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

…but here you chatting about it, why?

"chai-subset": "^1.6.0",
"eslint": "^10.0.0",
"esmock": "^2.6.0",
"mocha": "^11.0.0",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why?

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.

3 participants