Skip to content

chore: migrate toolchain to TypeScript 5.5, Vite, Vitest, and ESLint v9#84

Open
mattbodle wants to merge 2 commits intomainfrom
feat/typescript-vite-vitest-migration
Open

chore: migrate toolchain to TypeScript 5.5, Vite, Vitest, and ESLint v9#84
mattbodle wants to merge 2 commits intomainfrom
feat/typescript-vite-vitest-migration

Conversation

@mattbodle
Copy link
Copy Markdown
Collaborator

@mattbodle mattbodle commented Apr 2, 2026

Summary

  • Migrates source from src/Rokt-Kit.js (ES5) to src/Rokt-Kit.ts (TypeScript 5.5, strict mode, class-based)
  • Replaces Rollup v1 with Vite (library mode, explicit target: 'es2020') producing IIFE + CJS bundles + .d.ts type declarations
  • Replaces the legacy manual Mocha HTML runner (test/index.html) with Vitest + jsdom (158 tests, headless — no automated real-browser testing existed before)
  • Replaces ESLint v7 .eslintrc with ESLint v9 flat config (eslint.config.mjs)
  • Updates Prettier to 120-char width, single quotes, trailing commas
  • Bumps .nvmrc to Node 24

Browser baseline: The emitted IIFE targets ES2020 (const, let, class, ??, ?.). This is the first explicit browser target — no baseline existed before. Real-browser smoke testing can be added in a follow-up if needed.

Test plan

  • npm run lint — zero errors
  • npm run build — produces dist/Rokt-Kit.iife.js, dist/Rokt-Kit.common.js, dist/Rokt-Kit.d.ts
  • npm run test — 158/158 tests pass in Vitest/jsdom

🤖 Generated with Claude Code

Replaces the 2019-era ES5/Rollup/Karma/Mocha/Chai stack with a modern
toolchain aligned with the internal sdk-web repo:

- Source: src/Rokt-Kit.js → src/Rokt-Kit.ts (class-based, strict mode)
- Build: Rollup v1 → Vite (IIFE + CJS + .d.ts via vite-plugin-dts)
- Tests: Karma + Mocha/Chai → Vitest + jsdom (158 tests passing)
- Lint: ESLint v7 (.eslintrc) → ESLint v9 flat config (eslint.config.mjs)
- Prettier: updated to 120-char width, single quotes, trailing commas
- Node: .nvmrc bumped to 24

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mattbodle
Copy link
Copy Markdown
Collaborator Author

Background: this PR replaces both the build tool and the test runner for a browser SDK. The description lists the mechanical changes, but it does not say whether browser support changes or what replaces the old real-browser verification.

Please add that context explicitly. Missing in this PR description: the supported browser baseline after the Vite build, whether dropping Chrome and Firefox coverage is intentional, and whether a browser-level smoke test is coming in a follow-up PR.

-Codex

import { resolve } from 'path';

export default defineConfig({
build: {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Background: this build now ships a materially different browser target from the current release. The emitted dist/Rokt-Kit.iife.js contains const, let, class, arrow functions and ??, while the previous bundle was ES5-style. For a kit that is injected into merchant pages, that is a breaking runtime change unless we have explicitly raised the browser baseline.

Please set an explicit supported build target and verify the published IIFE against it before merging.

-Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — added target: 'es2020' to vite.config.ts. The IIFE now has an explicit browser baseline. Added a note to the PR description clarifying this is the first explicit target (no baseline existed before).

"build": "vite build",
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
"lint:fix": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --fix",
"test": "vitest run",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Background: after this change the executed suite is test/src/**/*.spec.ts, but the old Karma harness still stays in-tree: test/src/tests.js, test/index.html, test/config.js and test/lib/mockhttprequest.js. That leaves a second, dead test path in the repo and adds maintenance cost to a migration PR that is supposed to simplify the toolchain.

Delete the legacy harness in the same PR, or call out clearly why it needs to stay.

-Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — deleted test/src/tests.js, test/index.html, test/config.js, and test/lib/mockhttprequest.js in commit 7d03487.

src/Rokt-Kit.ts Outdated

const selection = this.launcher!.selectPlacements(selectPlacementsOptions);

// After selection resolves, sync the Rokt session ID back to mParticle
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Background: this works, but the same side-effect is repeated in four branches, which makes the success and failure flow harder to scan than it needs to be. Normalising the result to one promise and logging once in a terminal branch would read more cleanly.

For example:

const logSelection = () => this.logSelectPlacementsEvent(selectPlacementsAttributes);

void Promise.resolve(selection)
  .then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))
  .catch(() => undefined)
  .finally(logSelection);

That keeps the monadic flow in one place and makes the side-effects explicit.

-Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — adopted the single Promise.resolve() chain with .finally() for the log, as suggested. Fixed in 7d03487.

- Set explicit build target (es2020) in vite.config.ts
- Refactor selectPlacements to use a single functional promise chain
- Delete legacy Mocha HTML test harness (tests.js, index.html, config.js, mockhttprequest.js)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mattbodle
Copy link
Copy Markdown
Collaborator Author

Re: @mattbodle's comment: The old test harness was a manual Mocha HTML page (test/index.html) — no automated browser testing existed before, so no Chrome/Firefox coverage is being dropped. The PR description has been updated to reflect this and to call out that es2020 is the first explicit browser target.


> `npm run test` runs `npm run build && npm run build:test && karma start test/karma.config.js` — it builds the source, builds the test bundle, and runs Karma with Mocha/Chai in Chrome and Firefox.
> `npm run test` runs Vitest in headless jsdom mode.
> `npm run build` runs Vite and produces IIFE and CommonJS bundles + TypeScript type declarations.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit - we should add an esm module as well. it was missing from like 1/2 our npm packages and we started to fix that in the mono repo

Comment on lines +208 to +219
function mergeObjects(...args: Record<string, unknown>[]): Record<string, unknown> {
const resObj: Record<string, unknown> = {};
for (const obj of args) {
if (obj && typeof obj === 'object') {
const keys = Object.keys(obj);
for (const key of keys) {
resObj[key] = obj[key];
}
}
}
return resObj;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is it intentional to leave this in here to ensure that we are doing a strict js --> ts as opposed to starting to incorporate ts features, like spread operators which would would remove the need for this method?

Comment on lines +571 to +574
window.Rokt.__event_stream__!(this.enrichEvent(queuedEvents[i]));
}
}
window.Rokt.__event_stream__!(this.enrichEvent(event));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Both these are inside the check for it on line 566, so no ! needed

Suggested change
window.Rokt.__event_stream__!(this.enrichEvent(queuedEvents[i]));
}
}
window.Rokt.__event_stream__!(this.enrichEvent(event));
window.Rokt.__event_stream__(this.enrichEvent(queuedEvents[i]));
}
}
window.Rokt.__event_stream__(this.enrichEvent(event));

Comment on lines +754 to +765
generateLauncherScript: generateLauncherScript,
extractRoktExtensions: extractRoktExtensions,
hashEventMessage: hashEventMessage,
parseSettingsString: parseSettingsString,
generateMappedEventLookup: generateMappedEventLookup,
generateMappedEventAttributeLookup: generateMappedEventAttributeLookup,
sendAdBlockMeasurementSignals: sendAdBlockMeasurementSignals,
createAutoRemovedIframe: createAutoRemovedIframe,
djb2: djb2,
setAllowedOriginHashes: (hashes: number[]) => {
RoktKit._allowedOriginHashes = hashes;
},
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Future improvement - we may be able to simply export these individual methods and not need this annoying helper anymore

return;
}

const hashedEvent = hashEventMessage(event.EventDataType ?? 0, event.EventCategory ?? 0, event.EventName ?? '');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

EventDataType and EventCategory will never be falsey, so we shoudl remove the ?? 0 afterwards and update the hashEventMessage definition to make them required. having a 0 would likely mess up the hash

Comment on lines +917 to +920
void Promise.resolve(selection)
.then((sel) => sel?.context?.sessionId?.then((sessionId) => this.setRoktSessionId(sessionId)))
.catch(() => undefined)
.finally(logSelection);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

❤️ huge improvement over this monstrosity

// Types
// ============================================================

interface KitSettings {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: These should be RoktKitSettings as I can see a case in the future where we have a more generic KitSettings interface.

getUserIdentities?: () => { userIdentities: Record<string, string> };
}

interface KitFilters {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can this be imported from the core SDK type definitions?

filteredUser?: FilteredUser | null;
}

interface RoktManager {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can this be imported from the core SDK type definitions?

// Types
// ============================================================

interface KitSettings {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

At this point, can we consider breaking up this single file into smaller modules?


return {
EventName: eventName,
EventDataType: 14, // MessageType.Profile
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we just use the type enums?

"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why do we need a ui test?

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