Migrate to Vite 8 / ESM TypeScript, add test suite, SSR-safe manual-activation ESM build (v2.0.0)#27
Open
yellowby wants to merge 7 commits into
Open
Migrate to Vite 8 / ESM TypeScript, add test suite, SSR-safe manual-activation ESM build (v2.0.0)#27yellowby wants to merge 7 commits into
yellowby wants to merge 7 commits into
Conversation
- Replace webpack 5 + ejs-loader with Vite 8 (Rolldown) driven from scripts/build.js: five single-entry IIFE builds emit the classic unhashed bundles (embed.js, embed-options.js, options.js, options.i18n.de/fr.js + .min.js variants), plus a multi-entry ESM build in dist/esm/ and tsc-generated declarations in dist/types/. - Rewrite all src/ as strict-TypeScript ES modules. Side-effectful modules now export boot() called by the entries in the original CJS execution order under the _loaded guard, so double-loading bundles still boots exactly once despite ESM import hoisting. - Replace the six EJS templates with template-literal functions in options/renderer.ts. Fixes the group template, which compiled to _.escape() without lodash bundled and threw at runtime, and adds HTML-escaping of scalar interpolations (previously raw). - Drop legacy-browser support: ActiveX/Flash detection, attachEvent, vendor-prefixed rAF/setImmediate, IE styleSheet.cssText, innerText fallbacks, and the Chrome-77 UA sniff (lazy-loading-native.js removed; SUPPORT_IFRAME_LOADING_ATTR hardcoded true). Build targets Vite's baseline-widely-available. - package.json: type=module, v2.0.0, exports map (., ./options, ./i18n/de, ./i18n/fr) pointing at dist/esm + dist/types; webpack and ejs devDependencies removed; flat ESLint config with typescript-eslint; delete stale dist/embed-autoplay*. - Add demo/ smoke-test pages used to verify script-src config parsing, API surface parity, options form + i18n, double-load, and ESM consumption in a real browser. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Unit tests (tests/unit/, jsdom): utils (query-string handling, endpoint building, extendOptions normalization, widget wrappers, setStyles), events pub/sub, messaging (postMessage origin trimming, findIframe incl. shadow roots, message listener + pym fallback), theme, options renderer (escaping semantics, all six templates), form-generator, form-builder, and translator. 72 tests. - Browser tests (tests/e2e/, Playwright + chromium): script-src config parsing, anchor unfurl to the exact API endpoint, resize and cancelWidget message flows, link recovery on cancel, pre-migration API surface snapshot, options form + German i18n second-script flow, double-load boots once, ESM build singleton identity. The Iframely endpoint is route-stubbed, so tests need no network or API key. - demo/ pages double as the e2e fixtures and a manual playground served by the new scripts/serve.mjs (pnpm serve); Playwright starts it automatically. Replace stale demo/old.html with demo/cancel-recover.html. - Fix Iframely.setTheme type to include its optional container param. - Scripts: test, test:watch, test:e2e, serve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Demo pages use an {{IFRAMELY_API_KEY}} placeholder in script srcs;
scripts/serve.mjs substitutes it at request time from the
environment or a git-ignored .env at the repo root, falling back to
the inert TEST placeholder. The key is only injected into locally
served responses and never written to committed files.
- Add .env.example; git-ignore .env.
- Pin the Playwright web server to IFRAMELY_API_KEY=TEST and disable
reuseExistingServer so tests never assert against a dev server that
is running with a real key.
- Document the workflow in the README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- TypeScript 5.9 -> 7.0 (native compiler) for typecheck and declaration emit; tsgo requires an explicit rootDir in tsconfig.build.json for the dist/types layout. The typescript-eslint dependency was what pinned TS to 5.x. - Replace ESLint (+ @eslint/js, globals, typescript-eslint) with oxlint: correctness category as errors plus the carried-over no-console/no-empty rules, configured in oxlint.config.ts. - Add oxfmt for formatting, configured in oxfmt.config.ts: tabs (width 4), single quotes, no trailing commas; dist/, demo/ and markdown excluded. One-time reformat of the codebase to tabs. - Scripts: lint -> oxlint, new format/format:check; prebuild now runs typecheck + lint + format:check. - Pin Node 24.18 via .node-version: the *.config.ts files rely on Node native type stripping, which the previous default Node 22.16 lacks. No engines field so npm consumers are unaffected. - Rewrite the events test this-binding assertion via vi.fn mock contexts (oxlint no-this-alias). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ESM entries (main.ts, main-options.ts) no longer auto-run on
DOMContentLoaded: config.autorun defaults to false, and consumers
activate embeds explicitly with iframely.load(). Pre-set
window.iframely = { config: { autorun: true } } before importing to
restore the script-tag behavior. IIFE script bundles are unchanged.
- Importing is now safe in isomorphic environments (SvelteKit, etc.):
on the server iframely.ts exports a detached, stateless no-op stub;
dom-ready and the entry bootstraps skip without browser globals, so
a server pass neither throws nor runs side effects.
- Rename extendOptions to iframely.configure; extendOptions stays as
a deprecated alias registered in deprecated.ts and marked
@deprecated in the types. Internal callers switched.
- Tests: new node-environment SSR suite (imports every public entry,
asserts inert no-op behavior), new e2e spec proving no auto-run on
import and activation via load(), surface snapshot gains the
`configure` key, alias identity pinned in unit tests.
- Document the ESM/isomorphic usage flow in the README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- unload(container?) removes the widgets the library built (domain- matched iframes and pending data-iframely-url lazy iframes with the proper wrapper structure; foreign iframes untouched), firing unload-widget per widget and a final unload event. - Modules release their state through those events: intersection unobserves removed iframes and drops all observers on a full unload (observers held strong refs - the actual leak in long-lived SPAs), lazy-img-placeholder clears pending waiting-widget timers, import resets transient import state. - Not covered by design: restoring consumed anchors and import/shadow widgets; documented in types and README. SSR stub gains a no-op. - Tests: unload unit suite, e2e load->unload round trip, surface snapshot + SSR no-op coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
buildOptionsForm now checks at call time that the embed bundle has provided iframely.trigger and iframely.addEventListener; without them it logs a console.warn and returns instead of throwing from form-builder. Checked per call, not at boot, so loading options.js before embed.js keeps working. Covered by a unit test booting only the options module and an e2e spec with an options.js-only fixture page asserting zero page errors, an empty container, and the warning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Modernizes embed.js end to end: webpack 5 + EJS is replaced with Vite 8 and strict ESM TypeScript, a full unit + browser test suite is added, and the new ESM build becomes manual-activation and SSR-safe with a richer public API (
configure,load/unload). Classic IIFE script bundles keep their filenames and behavior. Version bumps to 2.0.0.Changes by commit
Build & toolchain
scripts/build.js: five single-entry IIFE builds emit the classic unhashed bundles (embed.js,embed-options.js,options.js,options.i18n.de/fr.js+.min.js), plus an ESM build indist/esm/and declarations indist/types/.src/rewritten as strict-TypeScript ES modules; side-effectful modules exportboot()so double-loading a bundle still boots exactly once._.escape()without lodash and threw at runtime) and adds HTML-escaping of scalar interpolations.attachEvent, vendor-prefixed rAF, IE fallbacks, Chrome-77 UA sniff); targets Vite's baseline-widely-available.package.json:type: module, exports map (.,./options,./i18n/de,./i18n/fr)..node-versionfor native type stripping of*.config.ts; noenginesfield, so npm consumers are unaffected.Tests & demo workflow
demo/pages double as e2e fixtures and a manual playground served byscripts/serve.mjs(pnpm serve).serve.mjssubstitutes{{IFRAMELY_API_KEY}}at request time from the environment or a git-ignored the key is never written to committed filee inertTESTkey.ESM public API
iframely.configure(1f11c44)DOMContentLoaded; consumers calliframely.load()explicitly (window.iframely = { config: { autorun: true } }restores script-tag behavior). IIFE bundles are unchanged.extendOptionsrenamed toiframely.configure, withextendOptionskept as a deprecated alias.iframely.unload()as the inverse ofload()(7196103)unload(container?)removes library-built widgets (foreign iframes untouched), firingunload-widgetper widget and a finalunloadevent; modules release observers and timers through those events — fixing an IntersectionObserver leak in long-lived SPbuildOptionsFormnow warns and returns instead of throwing when the embed bundle isn't present.Breaking changes
type: module) with an exports map; deep imports of old paths won't resolve.iframely.load()or opt in toautorun.extendOptionsis deprecated in favor ofiframely.configure(alias still works).🤖 Generated with Claude Code