ci: add build/test pipeline and modernize the toolchain - #194
Merged
Conversation
The package could not be built from a clean checkout: `typescript` was missing from devDependencies entirely, and `prepublishOnly` invoked a `build` script that did not exist. - switch the lockfile to pnpm, matching the scripts that already shelled out to it, and pin the version via `packageManager` - add the missing `typescript` dependency - replace the deprecated ESLint 7 setup with ESLint 9 flat config and typescript-eslint 8 - add Prettier with the settings that were already declared in package.json, correcting the deprecated `jsxBracketSameLine` key to `bracketSameLine` so the existing JSX style is preserved - give the package a real script set: build, clean, type-check, lint, format, test. The Windows-only `start` and `clear:lib` scripts are replaced with cross-platform equivalents. `build` now reproduces exactly what was published for 2.0.2: a webpack UMD bundle plus tsc-generated declarations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SVGGeometryElement.getTotalLength is not implemented by jsdom, and is
absent under server-side rendering. Both call sites used optional
chaining, which guards a null ref but not an element that exists without
the method, so any attempt to render Xarrow in a test environment threw:
TypeError: lineRef.current?.getTotalLength is not a function
This is why the project has no meaningful test suite - the component
could not be mounted in one. Adds a small helper that falls back to 0,
which leaves the draw animation inert rather than throwing.
Also splits two `let` destructuring blocks so that only the bindings that
are actually reassigned stay mutable, and declares `children` explicitly
on XarrowProvider now that React 18 types no longer imply it.
Fixes #110
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the previous placeholder test, which imported a component from examples/ and had its only assertion commented out, with tests that actually mount the component. Runs against a bare jsdom with no SVG geometry polyfills, so the code paths that fail under SSR stay covered rather than being stubbed away. Includes a skipped regression test for the NaN geometry bug (#139, #171, #192), which reproduces reliably with curveness={0}. It is skipped rather than deleted so the fix has a test waiting for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repository had no CI at all - .github contained only issue templates, so nothing was built, linted or tested on push or PR. ci.yml runs format, lint, type-check, test and build across Node 20, 22 and 24, then packs the tarball and asserts the entry point and type declarations are present, so a broken `files` field cannot reach npm unnoticed. publish.yml releases on a version change to package.json. It uses npm Trusted Publishing, so no NPM_TOKEN secret is needed and provenance is attached automatically. The run is a no-op when the version is already on the registry, making it safe to re-run. Also adds a monthly dependabot config for npm and github-actions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
Specifying both `version: 10` in the action and `packageManager` in package.json makes the action abort with ERR_PNPM_BAD_PM_VERSION. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Node 20 reached end-of-life in April 2026, and jsdom 30 depends on the undici build that ships with Node 22 or newer, so the test step fails there with 'webidl.util.markAsUncloneable is not a function'. Also drops the engines field. The published artifact is an ES5 UMD bundle with no runtime Node requirement, and the package did not declare engines before, so adding one would newly constrain consumers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The v4 actions run on the deprecated Node 20 runtime and are being force-migrated by the runner. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffing the built artifact against master revealed that the Xwrapper edit had also dropped `const log = console.log`. It is dead code, but removing it was not intentional and Xarrow.tsx still has the same binding, so it is restored to keep the runtime diff limited to the deliberate change. Also collapses the CI matrix to a single Node 22 LTS job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
webpack-dev-server, html-webpack-plugin, style-loader and css-loader were only referenced by webpack.exampleConfig.js, which has been commented out of webpack.config.js for years. The examples app builds with react-scripts and does not use them either. webpack-dev-server 3 was the sole source of every remaining audit finding, so removing it takes the tree from 27 vulnerabilities (12 high, 13 moderate, 2 low) to zero. The library build is untouched - it only uses babel-loader, ts-loader and file-loader. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sets up CI for the first time. The repository had no workflows at all —
.github/contained only issue templates, so nothing has been built, linted or tested on push or PR since the project started.Why this is more than a YAML file
The package could not be built from a clean checkout:
typescriptwas not in devDependencies at all, sotsconly worked on machines that happened to have it globally.prepublishOnlyranpnpm run build, but there was nobuildscript — onlybuild:prod,build:devandbuild:ts.startandclear:libwere Windows-only (del /q,for /d %x, backslash paths), so no contributor on macOS or Linux could run the repo as documented.examples/and had its only assertion commented out.A workflow on top of that would have been red on the first run, so this PR fixes the underlying package before wiring the pipeline.
What is here
Toolchain
packageManagerpackage.json. The deprecatedjsxBracketSameLinekey was being silently ignored; corrected tobracketSameLineso the existing JSX style is preservedbuild,clean,type-check,lint,format,testWorkflows
ci.yml— format, lint, type-check, test, build across Node 22 and 24, then packs the tarball and assertslib/index.jsandlib/index.d.tsare present so a brokenfilesfield cannot reach npm unnoticedpublish.yml— publishes on a version change inpackage.json, using npm Trusted Publishing (noNPM_TOKENsecret, provenance attached automatically), then cuts the matching GitHub release. No-op when the version is already on the registry, so it is safe to re-rundependabot.yml— monthly npm and github-actions updates, groupedOne source fix, because CI needs it
SVGGeometryElement.getTotalLengthis not implemented by jsdom and is absent under SSR. Both call sites used optional chaining, which guards a null ref but not an element that exists without the method:This is precisely why the project has no test suite — the component could not be mounted in one. Fixed with a small helper that falls back to
0. Closes #110.Tests
Five smoke tests that actually mount the component, running against a bare jsdom with no SVG geometry polyfills, so the SSR-sensitive paths stay honest rather than being stubbed away.
Also included is a skipped regression test for the NaN geometry bug (#139, #171, #192), which reproduces reliably with
curveness={0}:It is skipped rather than deleted so the fix has a test already waiting for it.
Artifact diff against master
Master's
srchas moved on since 2.0.2 was published, so the npm tarball is the wrong baseline. Instead I built master HEAD with its own declared toolchain (TS 4.9 +@types/react16) and diffed the emitted output against this branch, holding the compiler constant so source effects and toolchain effects could be told apart.Typing — public surface unchanged.
index.d.ts,Xarrow.d.ts,Xwrapper.d.ts,useXarrow.d.ts,constants.d.ts,anchors.d.ts,propTypes.d.ts,useXarrowProps.d.tsandprivateTypes.d.tsare byte-identical. Three files change:types.d.tstypeof cPaths[number]→(typeof cPaths)[number]×4utils/GetPosition.d.tslineLength: any→lineLength: numberutils/index.d.tsgetTotalLengthRuntime — one intentional behaviour change. Comparing unminified
tscoutput (same compiler both sides) the entire delta is four items, three of which are provably equivalent:animateDrawinghoisted out of the destructureanimDirectionsplit out of a comma-declarationvar, same value, never reassignedpath/startAnchorPositionsplit into their own statementsgetTotalLengthguardXwrapper.jsis byte-identical.One caveat, stated plainly: the shipped bundle is compiled by TypeScript 5.9 rather than 4.9, and TS changed its own
__importStarhelper in between (it now enumerates withObject.getOwnPropertyNamesinstead offor...in). That is TypeScript's runtime helper rather than this project's code, but it is genuinely different bytes in the published file.Merging this PR publishes nothing.
versionstays at2.0.2.publish.ymlwill run on merge becausepackage.jsonchanged, but the already-published check short-circuits it before any publish step.Verification
Cloned the branch fresh and ran the full pipeline locally, and CI is green on the branch:
pnpm install --frozen-lockfileformat:checklinttype-checktestbuildNotes
prefer-constsweep and Prettier pass touchsrc/. That is mechanical (auto-fix plus formatting) and is isolated in its own commits.anyusage and unused vars, deliberately left as warnings rather than silently disabled.examples/is untouched and still does not build (it has its own yarn.lock and aworkspace:dependency with no workspace). Out of scope here; relates to Demo on codesandbox.io not working #172.@types/react16 → 18 surfaced a genuine type error inXwrapper, fixed by declaringchildrenexplicitly.enginesfield was added. The published artifact is ES5 UMD with no runtime Node requirement, and the package never declared one, so adding it would newly constrain consumers.Vulnerability cleanup
webpack-dev-server3 was the only source of every remaining audit finding, and it was dead weight: referenced solely bywebpack.exampleConfig.js, which has been commented out ofwebpack.config.jsfor years. The examples app builds with react-scripts and never used it.Removing it along with
html-webpack-plugin,style-loaderandcss-loadertakespnpm auditfrom 27 findings (12 high, 13 moderate, 2 low) to zero, for both--prodand dev. The library build only ever used babel-loader, ts-loader and file-loader, so the bundle is unaffected.Follow-ups this unblocks
🤖 Generated with Claude Code