@chris-c-brine/vite-version-update-helper 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @Chris-C-Brine/vite-version-update-helper
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@chris-c-brine/vite-version-update-helper.svg)](https://www.npmjs.com/package/@chris-c-brine/mrt-csv-export)
4
+ [![License: AAL](https://img.shields.io/badge/License-AAL-blue.svg)](https://github.com/Chris-C-Brine/vite-version-update-helper/blob/main/LICENSE)
5
+
6
+
7
+ ## Usage
8
+ 1. Add this package to your project
9
+ 2. Add ViteChunkErrorHandler to your root component
10
+ 3. Check your index.html headers
11
+ 4. Check your server config (nginx provided)
12
+
13
+ ### For Nginx (nginx.conf):
14
+ ```editorconfig
15
+ location / {
16
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
17
+ try_files $uri $uri/ /index.html;
18
+ }
19
+ ```
20
+ OR ALT:
21
+ ```editorconfig
22
+ server {
23
+ listen 80;
24
+ server_name yourdomain.com ;
25
+ root /var/www/your-vite-app/dist ; # Path to your Vite build folder
26
+ index index.html ;
27
+
28
+ # 1. Force index.html to always revalidate (NEVER CACHE)
29
+ location / {
30
+ try_files $uri $uri/ /index.html;
31
+
32
+ # Kill caching for the main HTML entryway
33
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
34
+ expires off;
35
+ }
36
+
37
+ # 2. Cache Vite's hashed assets aggressively (1 year)
38
+ location ~* \.(js|mjs|css)$ {
39
+ expires 1y;
40
+ add_header Cache-Control "public, immutable";
41
+ access_log off;
42
+ }
43
+
44
+ # 3. Cache static media assets decently long
45
+ location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
46
+ expires 30d;
47
+ add_header Cache-Control "public";
48
+ access_log off;
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### For <head> (index.html):
54
+ ```html
55
+ <head>
56
+ <meta charset="UTF-8" />
57
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
58
+ <meta http-equiv="Pragma" content="no-cache" />
59
+ <meta http-equiv="Expires" content="0" />
60
+ ...
61
+ </head>
62
+ ```
63
+
64
+
65
+ ### Example
66
+ ```tsx
67
+ createRoot(document.getElementById('root')!).render(
68
+ <StrictMode>
69
+ <ViteVersionErrorHandler>
70
+ <App/>
71
+ </ViteVersionErrorHandler>
72
+ </StrictMode>,
73
+ )
74
+ ```
75
+
76
+ Note: Wrap <App/> inside ErrorBoundary
77
+
78
+ [AAL](./public/LICENSE) © Christopher C. Brine
package/dist/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Attribution Assurance License
2
+
3
+ Copyright (c) 2026 Christopher Brine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+ 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
10
+ "This product includes software developed by Christopher Brine (https://github.com/Chris-C-Brine/vite-version-update-helper)"
11
+ Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.
12
+ 4. Except as contained in this notice, the name of Christopher Brine shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Christopher Brine.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react"),t=require("react/jsx-runtime");var n=(e,t=`log`)=>n=>{if([`library`].includes(`development`)){let r=`Dev Only - ${e}`;t===`table`?(console.log(r),console[t](n)):console[t](r,n)}};function r(){let e=sessionStorage.getItem(`chunk_error_reload`),t=Date.now();return{now:t,errorOccurredAgo:e?t-parseInt(e,10):0}}function i({duration:n}){let[r,i]=(0,e.useState)(n);return(0,e.useEffect)(()=>{if(r<=0)return;let e=setInterval(()=>{i(e=>e-1)},1e3);return()=>clearInterval(e)},[r]),(0,t.jsx)(t.Fragment,{children:(e=>{let t=Math.floor(e/60),n=e%60;return`${String(t).padStart(2,`0`)}:${String(n).padStart(2,`0`)}`})(r)})}var a=n(`ViteChunkErrorHandler caught an error`,`table`),o=n(`ViteChunkErrorHandler: Chunk error detected, but reloading is blocked.`),s=class extends e.Component{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,t){a([{error:e,errorInfo:t}]);let n=this.props.bounceRate??1e4,i=e.message||``;if(i.includes(`Failed to fetch dynamically imported module`)||i.includes(`Loading chunk`)||e.name===`ChunkLoadError`){let{errorOccurredAgo:e,now:t}=r();e>n?(sessionStorage.setItem(`chunk_error_reload`,t.toString()),window.location.reload()):(setTimeout(()=>{sessionStorage.setItem(`chunk_error_reload`,t.toString()),window.location.reload()},n-e),o(`Waiting ${e} milliseconds before retrying.`))}}render(){let{errorOccurredAgo:e}=r(),n=this.props.bounceRate??1e4;return this.state.hasError?this.props.fallback??(0,t.jsxs)(`div`,{style:{padding:`2rem`,textAlign:`center`,fontFamily:`sans-serif`},children:[(0,t.jsx)(`h3`,{children:`Updating application...`}),(0,t.jsx)(`p`,{children:`We are loading the latest version of the app.`}),(0,t.jsxs)(`p`,{children:[`Retrying in `,(0,t.jsx)(i,{duration:Math.floor((n-e)/1e3)}),` seconds...`]})]}):this.props.children}};exports.ViteVersionErrorHandler=s,exports.devLog=n,exports.getErrorOccurredAgo=r;
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,73 @@
1
+ import { Component as e, useEffect as t, useState as n } from "react";
2
+ import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
3
+ //#region src/lib/utils.ts
4
+ var o = (e, t = "log") => (n) => {
5
+ if (["library"].includes("development")) {
6
+ let r = `Dev Only - ${e}`;
7
+ t === "table" ? (console.log(r), console[t](n)) : console[t](r, n);
8
+ }
9
+ };
10
+ function s() {
11
+ let e = sessionStorage.getItem("chunk_error_reload"), t = Date.now();
12
+ return {
13
+ now: t,
14
+ errorOccurredAgo: e ? t - parseInt(e, 10) : 0
15
+ };
16
+ }
17
+ //#endregion
18
+ //#region src/lib/components/CountdownTimer.tsx
19
+ function c({ duration: e }) {
20
+ let [a, o] = n(e);
21
+ return t(() => {
22
+ if (a <= 0) return;
23
+ let e = setInterval(() => {
24
+ o((e) => e - 1);
25
+ }, 1e3);
26
+ return () => clearInterval(e);
27
+ }, [a]), /* @__PURE__ */ i(r, { children: ((e) => {
28
+ let t = Math.floor(e / 60), n = e % 60;
29
+ return `${String(t).padStart(2, "0")}:${String(n).padStart(2, "0")}`;
30
+ })(a) });
31
+ }
32
+ //#endregion
33
+ //#region src/lib/components/ViteVersionErrorHandler.tsx
34
+ var l = o("ViteChunkErrorHandler caught an error", "table"), u = o("ViteChunkErrorHandler: Chunk error detected, but reloading is blocked."), d = class extends e {
35
+ state = { hasError: !1 };
36
+ static getDerivedStateFromError() {
37
+ return { hasError: !0 };
38
+ }
39
+ componentDidCatch(e, t) {
40
+ l([{
41
+ error: e,
42
+ errorInfo: t
43
+ }]);
44
+ let n = this.props.bounceRate ?? 1e4, r = e.message || "";
45
+ if (r.includes("Failed to fetch dynamically imported module") || r.includes("Loading chunk") || e.name === "ChunkLoadError") {
46
+ let { errorOccurredAgo: e, now: t } = s();
47
+ e > n ? (sessionStorage.setItem("chunk_error_reload", t.toString()), window.location.reload()) : (setTimeout(() => {
48
+ sessionStorage.setItem("chunk_error_reload", t.toString()), window.location.reload();
49
+ }, n - e), u(`Waiting ${e} milliseconds before retrying.`));
50
+ }
51
+ }
52
+ render() {
53
+ let { errorOccurredAgo: e } = s(), t = this.props.bounceRate ?? 1e4;
54
+ return this.state.hasError ? this.props.fallback ?? /* @__PURE__ */ a("div", {
55
+ style: {
56
+ padding: "2rem",
57
+ textAlign: "center",
58
+ fontFamily: "sans-serif"
59
+ },
60
+ children: [
61
+ /* @__PURE__ */ i("h3", { children: "Updating application..." }),
62
+ /* @__PURE__ */ i("p", { children: "We are loading the latest version of the app." }),
63
+ /* @__PURE__ */ a("p", { children: [
64
+ "Retrying in ",
65
+ /* @__PURE__ */ i(c, { duration: Math.floor((t - e) / 1e3) }),
66
+ " seconds..."
67
+ ] })
68
+ ]
69
+ }) : this.props.children;
70
+ }
71
+ };
72
+ //#endregion
73
+ export { d as ViteVersionErrorHandler, o as devLog, s as getErrorOccurredAgo };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@chris-c-brine/vite-version-update-helper",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "main": "dist/index.cjs",
9
+ "module": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+
12
+ "scripts": {
13
+ "dev": "vite --open",
14
+ "build": "tsc -b && vite build",
15
+ "build:lib": "tsc && vite build --mode library",
16
+ "lint": "eslint .",
17
+ "preview": "vite preview",
18
+ "prepublishOnly": "npm run build:lib"
19
+ },
20
+ "peerDependencies": {
21
+ "react": ">=18.0.0",
22
+ "react-dom": ">=18.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@eslint/js": "^10.0.1",
26
+ "@types/node": "^24.13.2",
27
+ "@types/react": "^19.2.17",
28
+ "@types/react-dom": "^19.2.3",
29
+ "@vitejs/plugin-react": "^6.0.2",
30
+ "eslint": "^10.5.0",
31
+ "eslint-plugin-react-dom": "^5.10.0",
32
+ "eslint-plugin-react-hooks": "^7.1.1",
33
+ "eslint-plugin-react-refresh": "^0.5.3",
34
+ "eslint-plugin-react-x": "^5.10.0",
35
+ "globals": "^17.6.0",
36
+ "typescript": "~6.0.2",
37
+ "typescript-eslint": "^8.61.0",
38
+ "vite": "^8.1.0",
39
+ "vite-plugin-dts": "^5.0.3"
40
+ }
41
+ }