Maintenance pass: tsup + vitest migration, CI alignment, multi-form bugfix#6
Merged
Conversation
Replace the webpack + jest + ts-jest + husky toolchain with tsup + vitest, mirroring the @formspark/formson setup. Drops 8 dev dependencies (jest, ts-jest, @types/jest, jest-environment-jsdom, ts-loader, webpack, webpack-cli, husky, pretty-quick) in favor of tsup, vitest, jsdom, and @vitest/coverage-v8. jest-environment-jsdom was previously listed under "dependencies" by mistake and would have shipped to consumers; it is now devDeps-only. Bumps prettier 2.8 -> 3.8 and typescript 4.9 -> 5.9. tsconfig is updated to bundler-mode ES2020 to match formson. The Node engine constraint moves from >=10 (long EOL) to >=20. The package now publishes esm + cjs + iife + dts builds. main, module, and unpkg fields are wired so unpkg.com/@formspark/formtrack still resolves to the IIFE bundle for the documented script-tag usage.
Mirror the @formspark/formson and @formspark/use-formspark setup: check.yml runs build + tests on PRs and via workflow_call (Node 22.x), release.yml runs on push to master, calls check, then bumps the version from commit messages and publishes to npm with provenance (Node 24.x). The previous single workflow ran on master only, so PRs were not checked at all. Action versions are also brought up to date (actions/checkout@v6, actions/setup-node@v6, gh-action-bump-version pinned to v11.0.7 instead of @master).
Existing spec only verified the singleton guard. Added integration tests that drive the full path: URL params -> form input injection, including the standard UTM set, custom data-formtrack-params, the opt-out (no data-formtrack attribute), parameters absent from the URL, and the update-not-duplicate behavior on subsequent polls. Coverage is now 100% statements / branches / functions / lines. README: drop the stale Continuous-deployment badge and fix "refers traffic" -> "drives traffic".
A multi-form integration test surfaced a real bug: appendOrUpdateInput called document.getElementById, so when two <form data-formtrack> elements were on the same page they fought over the same ID and only the first form ever got an injected input. Switch to looking up by name within formElement.elements, which is form-local and matches the semantic the form actually submits with. The id attribute is still written for backwards compatibility with anyone querying it. Adds tests for: multiple forms on a page, whitespace and empty entries in data-formtrack-params, URLs with no matching params, empty-string param values being skipped, late-mounted forms picked up by the next poll cycle (using fake timers), and unregister actually clearing the interval.
The original webpack UMD build used libraryExport: "default" so
window.Formtrack and require("@formspark/formtrack") were both the
registerPoller function directly. Tsup's IIFE and CJS outputs
default to a namespace shape ({ default: fn, __esModule: true }),
which would silently break any consumer relying on the function
surface even though our README only documents the auto-registering
script-tag use case.
Re-export registerPoller as the default of src/index.ts and append a
tiny footer to each bundle that flattens the namespace back to the
function while keeping the .default property and __esModule flag for
ESM/CJS interop:
IIFE: Formtrack = Object.assign(Formtrack.default, Formtrack);
CJS: module.exports = Object.assign(module.exports.default, module.exports);
Also enable minify, which (combined with the polyfill being smaller
under minification) drops the IIFE bundle from 12.79 KB to 5.23 KB.
Verified end-to-end:
- require("./dist/index.js") returns the function, .default still works
- jsdom-loaded IIFE exposes window.Formtrack as the callable function
- 16/16 vitest tests still pass
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.
Summary
Five commits in dependency order:
Migrate to tsup and vitest. Drops 8 dev deps (webpack, webpack-cli, ts-loader, jest, ts-jest, @types/jest, husky, pretty-quick) in favor of tsup + vitest + jsdom + @vitest/coverage-v8, mirroring
@formspark/formson.jest-environment-jsdomwas misclassified underdependenciesand would have shipped to consumers — fixed. Bumps prettier 2.8 → 3.8 and typescript 4.9 → 5.9. Node engine constraint moves from>=10(long EOL) to>=20. tsconfig switched to bundler-mode ES2020.npm auditreports 0 vulnerabilities.Replace
continuous-deployment.ymlwithcheck.yml+release.yml. Mirrorsformson/use-formspark/localview. The previous single workflow only ran on push to master, so PRs were never checked. Action versions brought up to date (actions/checkout@v6,actions/setup-node@v6,phips28/gh-action-bump-versionpinned tov11.0.7instead of@master).npm publish --provenanceenabled.Expanded test coverage and README cleanup. Existing spec only covered the singleton guard. Added integration tests that drive the full URL-params → form-input injection path: standard UTM set, custom
data-formtrack-params, no-data-formtrackopt-out, params absent from URL, and update-not-duplicate behavior. README: drop stale CI badge, fix "refers traffic" → "drives traffic".Scope input lookup to the form, not the whole document — fixes a real bug. A multi-form integration test surfaced that
appendOrUpdateInputcalleddocument.getElementById, so when two<form data-formtrack>elements were on the same page they fought over the same ID and only the first form ever got an injected input. Switched toformElement.elements.namedItem(name)(form-local lookup by submitted name). Theidattribute is still written for any external consumer that queries it. Added 6 more tests pinning: multiple forms, whitespace/empty entries indata-formtrack-params, URLs with no matching params, empty-string param values being skipped, late-mounted forms picked up by the next poll cycle (usingvi.useFakeTimers), andunregister()actually clearing the interval.Preserve the previous UMD surface in the new bundles. The original webpack UMD build used
libraryExport: "default"sowindow.Formtrackandrequire("@formspark/formtrack")were both theregisterPollerfunction directly. Tsup's IIFE and CJS outputs default to a{ default: fn, __esModule: true }namespace — silently breaking any consumer relying on the function surface. Re-exportedregisterPolleras the default ofsrc/index.tsand added a per-format footer that flattens the namespace back to the function while keeping.defaultand__esModulefor ESM/CJS interop:Also enabled
minify: true, which (combined with the polyfill compressing well) drops the IIFE bundle from 12.79 KB to 5.23 KB.Numbers
.d.tsand.d.mtsnpm audit: 0 vulnerabilitiesEnd-to-end verification of the bundles
Built each format and loaded into a fresh JSDOM instance with
?utm_source=google&utm_campaign=spring&utm_medium=cpc, asserting both API shape and behavior. 13/13 assertions pass:<script>from unpkg)window.FormtrackisregisterPollerrequire())registerPollerdirectly, with.defaultback-referenceimport F from)registerPollerTest plan
npm test— 16/16 passnpx vitest run --coverage— 100% across all metricsnpm run build— ESM + CJS + IIFE + DTS build cleanly, minifiednpm audit— 0 vulnerabilitiesphips28/gh-action-bump-version@v11.0.7finds the version-bump trigger in your typical commit conventions, otherwise auto-bump won't fire