Files
sencho/frontend
Anso b843b89ca4 feat(frontend): add LazyBoundary for chunk-load failure recovery (#875)
* feat(frontend): add LazyBoundary for chunk-load failure recovery

When a lazy chunk fetch fails, the existing top-level ErrorBoundary shows
"Something went wrong" with a "Try again" CTA. "Try again" cannot succeed
against a chunk URL that no longer exists on the server (typical post-
deploy case where the user's tab was opened against an older bundle).
The right remedy is to reload the tab so the browser fetches the new
hashed chunks emitted by the current build.

Add LazyBoundary, a section-local error boundary that:

- Detects chunk-load errors via a substring union covering Chrome / Edge
  ("Failed to fetch dynamically imported module"), Safari ("Importing a
  module script failed"), Firefox ("disallowed MIME type" thrown when a
  deploy serves SPA index.html for a missing chunk URL), older Webpack
  ("Error loading dynamically imported module"), and Vite ("Loading
  chunk/CSS chunk N failed").
- For chunk errors, renders a glass-card matching the LockCard aesthetic
  with an AlertTriangle icon, a "This part of Sencho needs a reload"
  message, and a Reload CTA that calls window.location.reload().
- For non-chunk runtime errors, falls back to "Something went wrong" +
  the error message + a Try again CTA. Try again is safe on this path
  because the lazy import has already resolved before the render error
  fires.
- Logs to console.error in componentDidCatch so the underlying failure
  is still observable.
- Has role="alert" so screen readers announce the failure.

Wrap every existing Suspense site (1 in SettingsPage, 7 in EditorLayout
including the security-history overlay, 1 in ResourcesView) with
LazyBoundary. The top-level ErrorBoundary remains the catch-all for
errors that escape the section-local boundary.

Includes a unit test enumerating each browser's documented chunk-load
message so a regression in any one runtime is caught early.

* fix(frontend): move isChunkLoadError to its own file to satisfy react-refresh/only-export-components
2026-05-02 03:40:45 -04:00
..

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])