Skip to content

fix: guard window.modules access for clay build (esbuild/ESM) compatibility#1571

Closed
jjpaulino wants to merge 10 commits intomasterfrom
jordan/yolo-update
Closed

fix: guard window.modules access for clay build (esbuild/ESM) compatibility#1571
jjpaulino wants to merge 10 commits intomasterfrom
jordan/yolo-update

Conversation

@jjpaulino
Copy link
Copy Markdown
Member

@jjpaulino jjpaulino commented Feb 27, 2026

What this PR does

A small defensive fix to lib/preloader/actions.js that makes Clay Kiln's preloader compatible with the new clay build (esbuild + native ESM) pipeline while remaining fully backward-compatible with the legacy clay compile (Browserify) pipeline.

Why

Clay Kiln's preloader (getComponentModels and getComponentKilnjs) reads component registrations from the Browserify runtime:

// before
Object.keys(window.modules)

This works with clay compile because Browserify populates window.modules at page load.

With clay build (esbuild), window.modules is never defined — the new pipeline uses native ESM and a generated _kiln-edit-init.js bundle that populates window.kiln.componentModels and window.kiln.componentKilnjs directly. Calling Object.keys(undefined) throws a TypeError and crashes the entire Kiln preload phase before any component editing UI renders.

The fix

// after
Object.keys(window.modules || {})

The || {} short-circuit makes both functions safely return an empty object when window.modules is absent.

Why this is correct for clay build: by the time DOMContentLoaded fires (when Kiln's preload runs), the _kiln-edit-init ESM bundle has already executed and populated window.kiln.componentModels and window.kiln.componentKilnjs. The existing if (_.isEmpty(...)) guard at the top of each function detects the already-populated registries and skips the window.modules loop entirely — so the || {} path is never actually iterated.

Backward compatibility

No behavior change for the legacy Browserify build. When window.modules is defined, window.modules || {} evaluates to window.modules and the loop runs exactly as before.

Related

## Why this change exists

Clay Kiln's preloader reads component models and kiln.js files from the
Browserify runtime registry (`window.modules`) via `window.require()`. This
works when the site is built with the legacy `clay compile` (Browserify)
pipeline, which populates `window.modules` at page load.

When the site is built with the new `clay pack-next` (esbuild / ESM) pipeline,
`window.modules` is never defined. Calling `Object.keys(undefined)` throws a
`TypeError` and the entire Kiln preload phase crashes before any component
editing UI is rendered.

## What changed (`lib/preloader/actions.js`)

`getComponentModels()` and `getComponentKilnjs()` both called:

  Object.keys(window.modules)

This has been changed to:

  Object.keys(window.modules || {})

The `|| {}` short-circuit makes both functions safely return an empty object
when `window.modules` is not present. In pack-next mode this is the correct
behaviour: by the time DOMContentLoaded fires (when Kiln's preload runs),
the `_kiln-edit-init` ESM bundle has already executed and populated
`window.kiln.componentModels` and `window.kiln.componentKilnjs` directly.
The `if (_.isEmpty(...))` guard at the top of each function therefore skips
the `window.modules` loop entirely and uses the already-populated registries.

## Backwards compatibility

No behaviour change for the legacy Browserify build. When `window.modules`
is defined (Browserify build), `window.modules || {}` evaluates to
`window.modules` and the loop runs exactly as before.

Made-with: Cursor
@jjpaulino jjpaulino changed the title 🍕 Guard window.modules access for ESM / pack-next compatibility fix: guard window.modules access for clay build (esbuild/ESM) compatibility Mar 1, 2026
jjpaulino and others added 9 commits March 9, 2026 10:11
dist/ is gitignored (not published to npm via files field), but when
installing from a GitHub branch npm gets the source without the built
files. Force-commit dist so sites can install directly from
jordan/yolo-update without needing a prepare build step.

Includes the window.modules || {} guard fix in getComponentModels and
getComponentKilnjs so patch-package is no longer needed in sites.

Made-with: Cursor
- Delete .circleci/ entirely — stops CircleCI from triggering on push
- Update build-dist workflow to run on all branches, not just jordan/yolo-update

Made-with: Cursor
Replaces all CircleCI jobs with equivalent GitHub Actions workflows:

- test.yml       — npm test on Node 12 + 14, all branches/tags, Coveralls upload
- build-dist.yml — rebuild and commit dist/ after tests pass, all branches
- deploy-docs.yml — build website/ and sync to S3 on master push or stg tag
- publish.yml    — npm publish on version tags; prerelease tag if version has suffix

Secrets required (Settings → Secrets → Actions):
  COVERALLS_REPO_TOKEN, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, NPM_TOKEN

Made-with: Cursor
node-sass v4 requires a native C++ addon (libsass) that fails to build
on Node 20 — no Python, no pre-built binary. It is also end-of-life.

Replace with sass (Dart Sass) which is pure JS and needs no compilation.
Bump sass-loader from ^6 to ^7 which supports sass as a drop-in without
requiring the implementation option.

This unblocks npm ci on Node 20 in Docker.

Made-with: Cursor
Add a build:dist script (scripts/build-dist.js + git add -f dist/)
and register it in the pre-commit hook alongside lint. Every commit
on Node <17 now includes the freshly-built dist/ automatically.

On Node 17+ (where Webpack 3 hangs), the hook prints a warning and
exits cleanly so the commit still lands. The GitHub Actions
build-dist workflow (Node 14) remains as the safety net in that case.

Remove scripts/build-dist.js once migrated to Vite (roadmap 6.15).

Made-with: Cursor
template.js: getComponentName() returns null (not a throw) for bare
URIs like 'foo'. Fall back to the uri string itself so componentVariation
is never set to null.

actions.js: addToQueue().catch() was fire-and-forget — its rejection
leaked as an unhandled promise rejection. Node 15+ crashes on these;
Node 14 only warned. Return the chain so the outer .catch() handles
all errors consistently and the test assertions execute correctly.

test.yml: drop Node 12 (EOL Apr 2022, incompatible with sass@^1.79)
and remove the redundant Build dist step (pre-commit hook covers it).

Made-with: Cursor
@jjpaulino jjpaulino closed this Mar 10, 2026
@jjpaulino jjpaulino deleted the jordan/yolo-update branch March 10, 2026 07:31
@jjpaulino jjpaulino restored the jordan/yolo-update branch April 24, 2026 06:42
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