Skip to content

APNEWDRAW-8300 refresh dependencies and toolchain (esbuild + Vitest + oxlint)#13

Draft
linhmtran168 wants to merge 13 commits into
masterfrom
APNEWDRAW-8300-refresh-deps-toolchain
Draft

APNEWDRAW-8300 refresh dependencies and toolchain (esbuild + Vitest + oxlint)#13
linhmtran168 wants to merge 13 commits into
masterfrom
APNEWDRAW-8300-refresh-deps-toolchain

Conversation

@linhmtran168
Copy link
Copy Markdown

@linhmtran168 linhmtran168 commented Jun 1, 2026

Summary

  • Drop Node 18 support; set engines.node to ^20.19.0 || >=22.12.0
  • Replace Grunt/Browserify/Babel/Jasmine build and test pipeline with esbuild (IIFE/ES2020 bundles) and Vitest + Playwright (browser tests); drop dist/es5/ and all polyfill deps
  • Replace ESLint/prettier-eslint with oxlint + oxfmt; add lint CI job; run codebase-wide reformat
  • Security & maintenance dependency bumps including archiver@8 (with API migration) and uuid@14; production audit: 0 vulnerabilities

Jira: https://88-oct.atlassian.net/browse/APNEWDRAW-8300

Review guide

The diff is ~188 files, but most of it is mechanical reformatting from the prettier-eslint → oxfmt swap. Focus review on the Real rows; the formatting row can be skimmed (use git diff -w master to suppress whitespace-only noise).

Bucket Files Nature
package-lock.json + package.json 2 Real — toolchain swap (~16k of the raw line count)
Root config ~18 Real — deleting .babelrc/.eslintrc/gruntfile.js/.prettier, adding oxfmt/oxlint config, tsconfig.json, CI yml, etc.
lib/ + spec/ + test/ ~168 Pure formatting. oxfmt cannot byte-match the old prettier-eslint output (e.g. function ( spacing, escaped-quote style), so a reformat diff is unavoidable. One unintended .sort().toSorted() autofix was caught and reverted (f68ac85).

Browser test suite — Vitest + Playwright (new)

The old Jasmine browser suite (run via grunt-contrib-jasmine + PhantomJS) was replaced with a real-browser harness:

File Role
spec/browser/vitest.config.mjs Vitest config — uses @vitest/browser-playwright as the browser provider, headless Chromium
spec/browser/setup.js beforeAll hook that injects dist/exceljs.bare.js as an IIFE <script> into the page, exposing ExcelJS as a global — requires npm run build to run first
spec/browser/exceljs.spec.js 3 integration tests running in real Chromium: xlsx binary buffer round-trip, xlsx base64 round-trip, CSV write buffer
scripts/browser-process-shim.mjs Minimal process + Buffer shims injected by esbuild; replaces Browserify's auto-injected polyfills
lib/exceljs.browser.js Now a thin re-export of exceljs.bare.js (polyfills stripped); kept for back-compat with consumers importing the path directly

Key design point: the browser tests load the pre-built dist bundle into a real Chromium instance (not jsdom). This means they validate the actual browser distribution, including the esbuild output and process/Buffer shims. Any change to scripts/build.mjs or the shim should re-run npm run test:browser to confirm the bundle loads cleanly.

Test Plan

  • npm install succeeds on Node 20.19+ and 22.12+
  • npm run build produces dist/exceljs[.bare]{,.min}.js{,.map} and dist/LICENSE (no dist/es5/)
  • node -e "require('./excel.js').Workbook" succeeds
  • npm run test:unit — 883 passing
  • npm run test:integration — 195 passing
  • npm run test:end-to-end — 1 passing
  • npm run test:dist — 5 passing
  • npm run test:typescript — 2 passing
  • npm run test:browser — 3 passing (Vitest + Playwright/Chromium; requires npx playwright install chromium --with-deps first)
  • npm run lint — exits 0 (warnings only)
  • npm audit --omit=dev — 0 vulnerabilities
  • CI matrix passes on Node 20.x, 22.x, 24.x (Ubuntu + Windows)

🤖 Generated with Claude Code

…drop ES5

- New build pipeline: scripts/build.mjs using esbuild (IIFE, ES2020 target)
- Browser tests: spec/browser/vitest.config.mjs + Playwright/Chromium
- lib/exceljs.browser.js simplified to re-export exceljs.bare.js
- spec/utils/verquire.js: drop EXCEL_BUILD=es5 branch
- Delete index.ts (dist/es5 entry), gruntfile.js
- Remove grunt/babel/browserify/core-js/regenerator-runtime deps
- README: remove ES5-imports section

Adds buffer/crypto-browserify/util shim deps + a small process/Buffer inject
shim (scripts/browser-process-shim.mjs) — esbuild does not auto-polyfill
Node built-ins the way browserify did, and the lib/ source reaches for them.
`stream` is aliased to the already-present `readable-stream` runtime dep so
the bundle ships one stream implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- .oxlintrc.json: correctness+suspicious categories, project rules
- .oxfmtrc.json: JS/TS only, singleQuote+semi matching prior style
- lint: oxlint .; lint:fix: oxfmt . && oxlint --fix .
- lint-staged: oxfmt + oxlint --fix on *.{js,ts}
- Add lint CI job (Node 22)
- Delete .eslintrc, .eslintignore, .prettier, .prettierignore and all per-dir .eslintrc files
- Fix: empty catch params in date-xform.js and defined-name-xform.js
- Fix: redundant x&&x pattern in test-conditional-formatting-sheet.js
- Note: max-len:120 not in oxlint scope; follow-up if needed
- Note: quotes/semi/no-trailing-spaces not in oxlint (formatter territory)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Apply codebase-wide canonical formatting via oxfmt. No logic changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- @types/chai: ^5.2.3 → ^4.3 (fix type/runtime mismatch with chai@4)
- @types/node: ^24.10.8 → ^24.12.4 (patch)
- dayjs: ^1.11.19 → ^1.11.21 (patch)
- fast-csv: ^5.0.5 → ^5.0.7 (patch)
- mocha: ^11.7.5 → ^11.7.6 (patch)
- rimraf: ^6.1.2 → ^6.1.3 (patch)
- tmp: ^0.2.5 → ^0.2.7 (security)
- archiver: ^7.0.1 → ^8.0.0 (major; API changed to ZipArchive class)
- uuid: ^11.1.0 → ^14.0.0 (major; v4 export stable)

Not bumped:
- lint-staged: keep ^16.2.7 (lint-staged@17 requires Node >=22.22.1; project supports Node ^20.19.0)
- got: keep ^11.8.2 (got@12+ ESM-only, tests are CJS)
- chai: keep ^4.5.0 (chai@5 ESM-only, chai-xml incompatible)
- dirty-chai: keep ^2.0.1 (chai@4 family)
- typescript: keep ^5.9.3 (v6 not stable enough for library)

Production audit: 0 vulnerabilities (npm audit --omit=dev)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Set printWidth 100 and trailingComma "all" to match the old .prettier
config exactly, then reran the oxfmt + oxlint --fix pipeline. Reduces
the formatter-swap diff (~450 fewer churn lines vs 120/es5).

oxfmt cannot byte-match prettier-eslint output, so residual reformat
diff remains; this is the closest faithful translation of the old config.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The `oxlint --fix` pipeline silently rewrote 5 `Array#sort()` calls to
`Array#toSorted()` via unicorn/no-array-sort. That is a semantic change
(in-place mutation vs. returning a copy) and out of scope for a
formatting alignment. Disabled the rule and restored the original
`.sort()` calls to match master behavior exactly.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
`npm run install-build` was removed in the toolchain migration (it was
`npm install && grunt build`). Point the asset-size reporter at the
current scripts so it can produce dist/*.js for the size comparison.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The asset-size action runs build-assets without a shell, so the
`npm install && npm run build` form was parsed with `&&` as a package
name (EINVALIDTAGNAME). Restore a single `install-build` npm script
(npm runs scripts in a shell, where `&&` is valid) and have the
workflow call `npm run install-build` as before the grunt removal.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Referenced in CLAUDE.md but dropped during the grunt removal. Mirrors
master: clean dist/ + build/ then run the esbuild build.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Under setup-takumi-guard-npm's registry proxy, `npm install` rewrites
`resolved` URLs in package-lock.json, leaving the tree dirty. The
asset-size action then fails its `git checkout <base>` step. `npm ci`
installs from the lockfile without ever writing to it, so the tree
stays clean for the base-ref checkout.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Files that got Bigger 🚨:

File raw gzip
dist/exceljs.bare.js +491 kB -28.2 kB
dist/exceljs.bare.min.js +158 kB +59.5 kB
dist/exceljs.js +299 kB -68.4 kB
dist/exceljs.min.js +72.1 kB +37.9 kB

@linhmtran168 linhmtran168 changed the title chore: refresh dependencies and toolchain (esbuild + Vitest + oxlint) APNEWDRAW-8300 refresh dependencies and toolchain (esbuild + Vitest + oxlint) Jun 1, 2026
Babel was dropped from the toolchain (no @babel deps, no build/test/CI
references), but .babelrc was left behind and only got reformatted.
Nothing reads it now that esbuild handles transpilation.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Files that got Bigger 🚨:

File raw gzip
dist/exceljs.bare.js +491 kB -28.2 kB
dist/exceljs.bare.min.js +158 kB +59.5 kB
dist/exceljs.js +299 kB -68.4 kB
dist/exceljs.min.js +72.1 kB +37.9 kB

ESLint was replaced by oxlint; the node/no-unsupported-features rule
is now enforced by oxlint, not ESLint.

Refs: APNEWDRAW-8300

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Files that got Bigger 🚨:

File raw gzip
dist/exceljs.bare.js +491 kB -28.2 kB
dist/exceljs.bare.min.js +158 kB +59.5 kB
dist/exceljs.js +299 kB -68.4 kB
dist/exceljs.min.js +72.1 kB +37.9 kB

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