Cache-bust strategy for Vanilla-Grid
This document describes the small, deterministic cache-busting strategy for the vanilla-grid component. It focuses on two modes of operation:
- Development: always force reloads to make iterative editing predictable (use
Date.now()behavior). - Production / build: use a stable token derived from the package version so browsers and CDNs can cache artifacts until a new version is published (use build-time token
__VANILLA_COMPONENTS_VERSION__).
The strategy deliberately avoids adding or relying on globals attached to window (e.g. window.VANILLA_GRID_CACHE_BUST).
Actual identifiers: the pseudo-code below uses the generic names
resolveCacheBustToken()/appendCacheToken(). The shipped implementations are per-file-prefixed:_vnGridResolveCacheBustToken/_vnGridAppendCacheTokeninvanilla-grid-element.js, and_vnToolbarResolveCacheBustToken/_vnToolbarAppendCacheTokeninvanilla-grid-toolbar.js. Grep for those names, not the generic ones, when tracing this behavior in the source.
Goals
- Ensure theme CSS, base CSS and the auto-loaded feature/data scripts can be cache-busted.
- Keep development experience fast and predictable (assets reloaded when changed).
- Allow production builds to be cache-friendly using the library version as a stable token.
- Keep the implementation minimal and easy to inject at build time.
Scope
Apply cache-busting to the following vanilla-grid runtime assets only:
- Base stylesheet (vanilla-grid.css)
- Theme stylesheet(s) injected by the element (
themes/vn-grid-*.css) - Auto-loaded feature & data-manager script files injected by
vanilla-grid.js
This does NOT change bundling strategy (content-hashed filenames are still the recommended long-term approach). This covers a query-token strategy implemented inside the runtime code and resolved at build time.
Resolution algorithm (simple)
When an asset URL must be written, resolve the token as follows (priority order):
- If the build-time placeholder
__VANILLA_COMPONENTS_VERSION__is present and non-empty, use it as the token (string). - Otherwise (development/unbuilt), use a fresh timestamp:
String(Date.now())(generated at the moment of injection).
Notes:
- The implementation must test the placeholder with
typeof(to avoid a ReferenceError in environments where the identifier was not replaced during build). - The code should not rely on any
window-level variable.
Helper utilities (pseudocode)
Add a tiny helper pair (can be duplicated in the two files or factored into a shared module):
// Build-time token placeholder. Build tooling will replace __VANILLA_COMPONENTS_VERSION__
// with the actual version string (e.g. '1.2.3'). When not replaced, typeof check prevents
// reference errors.
function resolveCacheBustToken() {
if (typeof __VANILLA_COMPONENTS_VERSION__ !== 'undefined' && __VANILLA_COMPONENTS_VERSION__) {
return String(__VANILLA_COMPONENTS_VERSION__);
}
// Dev fallback — generated when called so repeated calls produce fresh values.
return String(Date.now());
}
function appendCacheToken(url, token) {
return url + (url.indexOf('?') === -1 ? '?v=' : '&v=') + encodeURIComponent(token);
}
Implementation notes:
- In production the token will be stable (package version) so
appendCacheToken()yields?v=1.2.3. - In development
resolveCacheBustToken()returns a timestamp; call it at the moment you need a fresh token. - For assets that are injected together at startup (autoloader + base CSS) it's acceptable to call
resolveCacheBustToken()once and reuse the token for those injections so they share the same token for the current page load. For theme switching you typically want a fresh token in dev, so call it again on theme change.
Where it is implemented
src/vanilla-grid/vanilla-grid-element.js—_vnGridResolveCacheBustToken()/_vnGridAppendCacheToken()at file top. The base stylesheet (_injectBaseStylesheet()) and every theme stylesheet (_updateThemeStylesheet()) get a token; the theme token is resolved at switch time, so development fetches fresh CSS on every switch while a build uses the stable version.src/vanilla-grid/vanilla-grid.js(autoloader) — resolves one token (_vgCacheBustToken) per autoloader run and appends it to every injected feature/data-manager script via_vgAppendCacheToken(), so the whole runtime set is cached coherently.src/vanilla-grid-toolbar/vanilla-grid-toolbar.js— the same strategy for the toolbar's own base and theme stylesheets (_vnToolbarResolveCacheBustToken()/_vnToolbarAppendCacheToken()).
Each file carries its own small copy of the helpers (no shared module), so every script stays self-contained.
Build-time injection
build.js substitutes the placeholder: injectVersionPlaceholder() replaces every __VANILLA_COMPONENTS_VERSION__ with a JSON string literal of the root package.json version, and processJs() calls it before any minifier/obfuscator pass, so downstream tooling sees a plain string (and can drop the dev Date.now() branch). Unbuilt sources leave the identifier undeclared; the typeof guard falls back to Date.now().
Testing & verification
Dev (no build-time token injected)
- Start the app without running replacement injection.
- Observe that injected
link.hrefand scriptsrcvalues contain a?v=<timestamp>token (use Network panel). The timestamp should change across reloads and — for theme switches — should change when_updateThemeStylesheetruns.
Prod (build-time token injected)
- Run the build that replaces
__VANILLA_COMPONENTS_VERSION__with the package version (e.g.1.2.3). - Serve the
distassets and open the app. Verify that injectedlink.hrefand scriptsrcinclude?v=1.2.3and that value does not change on reload or theme switch.
Edge cases
- Confirm that existing query parameters are preserved by
appendCacheToken()(helper appends&v=if?already present). - Confirm that CSP rules allow query strings and that CDNs (if in path) respect query-string cache keys.
Recommendations & trade-offs
- Query tokens are simple and effective for this project. For stronger cache control consider moving to content-hashed filenames at build time (recommended for large production deployments / CDNs).
- Replacing a placeholder at build time is less error-prone than attaching globals to
windowand is compatible with modern bundlers. - Keep the token resolution logic intentionally small and local to the two files to limit coupling.
Appendix — concise code snippet (complete)
// top of file (vanilla-grid-element.js / vanilla-grid.js)
function resolveCacheBustToken() {
if (typeof __VANILLA_COMPONENTS_VERSION__ !== 'undefined' && __VANILLA_COMPONENTS_VERSION__) {
return String(__VANILLA_COMPONENTS_VERSION__);
}
return String(Date.now());
}
function appendCacheToken(url, token) {
return url + (url.indexOf('?') === -1 ? '?v=' : '&v=') + encodeURIComponent(token);
}
// usage examples:
// base stylesheet
link.href = appendCacheToken(_vnGridBaseUrl + 'vanilla-grid.css', resolveCacheBustToken());
// autoloader scripts (one token for the autoloader run)
const autoloaderToken = resolveCacheBustToken();
s.src = appendCacheToken(base + relSrc, autoloaderToken);
// theme switch — fresh token per switch in dev, stable in prod
link.href = appendCacheToken(href, resolveCacheBustToken());