@exxatdesignux/ui 0.5.11 → 0.5.12
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/CHANGELOG.md +26 -0
- package/bin/cli.mjs +70 -1
- package/bin/init.mjs +18 -4
- package/bin/sync-extras.mjs +28 -4
- package/consumer-extras/README.md +41 -5
- package/consumer-extras/cursor-rules/exxat-accessibility.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-board-cards.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +4 -2
- package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-command-menu.mdc +3 -2
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +7 -0
- package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +6 -5
- package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +6 -2
- package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +4 -3
- package/consumer-extras/cursor-rules/exxat-no-image-pixel-copy.mdc +25 -14
- package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +8 -2
- package/consumer-extras/cursor-rules/exxat-no-toast.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-no-vaul.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-page-header-actions.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +2 -1
- package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-sidebar-shell.mdc +13 -7
- package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +5 -3
- package/consumer-extras/cursor-rules/exxat-table-row-preview.mdc +1 -0
- package/consumer-extras/cursor-rules/exxat-tabs-chrome.mdc +6 -4
- package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +6 -0
- package/consumer-extras/cursor-rules/exxat-ux-discovery-protocol.mdc +28 -0
- package/consumer-extras/cursor-rules/exxat-ux-principles.mdc +1 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-kpi-trends/SKILL.md +5 -3
- package/consumer-extras/patterns/command-menu-pattern.md +2 -2
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +1 -1
- package/consumer-extras/patterns/jobs/README.md +1 -1
- package/consumer-extras/patterns/perf-memory-pattern.md +115 -150
- package/consumer-extras/scripts/dev-guard.mjs +156 -0
- package/consumer-extras/templates/README.md +23 -0
- package/consumer-extras/templates/handoff.md +190 -0
- package/package.json +2 -2
- package/{template → template-vite}/.claude/skills/exxat-ds-skill/SKILL.md +184 -23
- package/template-vite/.cursor/rules/exxat-accessibility.mdc +40 -0
- package/template-vite/.cursor/rules/exxat-board-cards.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-breadcrumbs-no-back.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-card-vs-list-rows.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-centralized-list-dataset.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-collaboration-access.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-command-menu.mdc +5 -5
- package/template-vite/.cursor/rules/exxat-data-tables.mdc +47 -0
- package/template-vite/.cursor/rules/exxat-dedicated-search-surfaces.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-drawer-vs-dialog.mdc +23 -0
- package/template-vite/.cursor/rules/exxat-ds-agents.mdc +87 -0
- package/template-vite/.cursor/rules/exxat-fontawesome-icons.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-hub-supported-views.mdc +56 -0
- package/{template → template-vite}/.cursor/rules/exxat-kbd-shortcuts.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-kpi-flat-band.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-kpi-max-four.mdc +22 -0
- package/template-vite/.cursor/rules/exxat-kpi-trends.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-library-hub-header.mdc +29 -0
- package/template-vite/.cursor/rules/exxat-list-page-connected-views.mdc +28 -0
- package/template-vite/.cursor/rules/exxat-list-page-view-shells.mdc +32 -0
- package/{template → template-vite}/.cursor/rules/exxat-mono-ids.mdc +1 -0
- package/template-vite/.cursor/rules/exxat-nav-single-active.mdc +32 -0
- package/template-vite/.cursor/rules/exxat-no-image-pixel-copy.mdc +46 -0
- package/template-vite/.cursor/rules/exxat-no-slds-leakage.mdc +84 -0
- package/{template → template-vite}/.cursor/rules/exxat-no-toast.mdc +2 -2
- package/template-vite/.cursor/rules/exxat-no-vaul.mdc +26 -0
- package/template-vite/.cursor/rules/exxat-page-header-actions.mdc +33 -0
- package/{template → template-vite}/.cursor/rules/exxat-page-vs-drawer.mdc +5 -3
- package/template-vite/.cursor/rules/exxat-person-identity-display.mdc +48 -0
- package/template-vite/.cursor/rules/exxat-primary-nav-secondary-panel.mdc +53 -0
- package/template-vite/.cursor/rules/exxat-reuse-before-custom.mdc +37 -0
- package/template-vite/.cursor/rules/exxat-sidebar-shell.mdc +41 -0
- package/template-vite/.cursor/rules/exxat-table-properties-drawer.mdc +79 -0
- package/template-vite/.cursor/rules/exxat-table-row-preview.mdc +25 -0
- package/template-vite/.cursor/rules/exxat-tabs-chrome.mdc +33 -0
- package/template-vite/.cursor/rules/exxat-token-discipline.mdc +109 -0
- package/template-vite/.cursor/rules/exxat-ux-discovery-protocol.mdc +202 -0
- package/template-vite/.cursor/rules/exxat-ux-principles.mdc +187 -0
- package/template-vite/.cursor/skills/exxat-accessibility/SKILL.md +282 -0
- package/template-vite/.cursor/skills/exxat-board-cards/SKILL.md +68 -0
- package/template-vite/.cursor/skills/exxat-card-vs-list-rows/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-centralized-list-dataset/SKILL.md +99 -0
- package/template-vite/.cursor/skills/exxat-collaboration-access/SKILL.md +35 -0
- package/template-vite/.cursor/skills/exxat-dedicated-search-surfaces/SKILL.md +45 -0
- package/template-vite/.cursor/skills/exxat-drawer-vs-dialog/SKILL.md +20 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/SKILL.md +893 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/accessibility.md +142 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/coach-marks.md +169 -0
- package/template-vite/.cursor/skills/exxat-ds-skill/references/data-table-pattern.md +392 -0
- package/template-vite/.cursor/skills/exxat-fontawesome-icons/SKILL.md +31 -0
- package/template-vite/.cursor/skills/exxat-kpi-flat-band/SKILL.md +38 -0
- package/template-vite/.cursor/skills/exxat-kpi-max-four/SKILL.md +19 -0
- package/template-vite/.cursor/skills/exxat-kpi-trends/SKILL.md +29 -0
- package/template-vite/.cursor/skills/exxat-list-page-view-shells/SKILL.md +36 -0
- package/template-vite/.cursor/skills/exxat-mono-ids/SKILL.md +56 -0
- package/template-vite/.cursor/skills/exxat-primary-nav-secondary-panel/SKILL.md +49 -0
- package/template-vite/.cursor/skills/exxat-senior-ux/SKILL.md +198 -0
- package/template-vite/.cursor/skills/exxat-token-economy/SKILL.md +287 -0
- package/template-vite/.cursor/skills/exxat-ux-audit/SKILL.md +303 -0
- package/{template → template-vite}/components/ask-leo-sidebar.tsx +10 -8
- package/{template → template-vite}/components/command-menu.tsx +1 -1
- package/{template → template-vite}/components/data-views/library-folder-tree-branch.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-recents.tsx +1 -1
- package/{template → template-vite}/components/dedicated-search-url-composer.tsx +1 -1
- package/{template → template-vite}/components/library-client.tsx +1 -1
- package/{template → template-vite}/components/library-hub-client.tsx +2 -2
- package/{template → template-vite}/components/library-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/library-table.tsx +35 -27
- package/{template → template-vite}/components/new-library-item-form.tsx +1 -1
- package/{template → template-vite}/components/page-breadcrumb-trail.tsx +1 -1
- package/{template → template-vite}/components/settings-client.tsx +1 -1
- package/{template → template-vite}/components/sidebar/app-sidebar.tsx +2 -2
- package/{template → template-vite}/components/sidebar/nav-main.tsx +1 -1
- package/{template → template-vite}/components/sidebar/nav-user.tsx +1 -1
- package/{template → template-vite}/components/sidebar/secondary-nav.tsx +1 -1
- package/{template → template-vite}/components/system-banner-slot.tsx +1 -1
- package/{template → template-vite}/components/templates/discovery-hub-template.tsx +2 -2
- package/{template → template-vite}/components/templates/new-focus-template.tsx +1 -1
- package/{template → template-vite}/components/tokens-secondary-nav.tsx +2 -2
- package/{template → template-vite}/components/tokens-themes-client.tsx +1 -1
- package/{template → template-vite}/hooks/use-secondary-panel-hub-nav.ts +1 -1
- package/template-vite/index.html +49 -0
- package/template-vite/lib/next-compat.tsx +98 -0
- package/{template → template-vite}/package.json +15 -27
- package/template-vite/scripts/port-next-imports.mjs +70 -0
- package/template-vite/src/App.tsx +103 -0
- package/template-vite/src/main.tsx +50 -0
- package/{template/app/(app)/error.tsx → template-vite/src/pages/_error.tsx} +12 -24
- package/{template/app/(app)/loading.tsx → template-vite/src/pages/_loading.tsx} +4 -2
- package/template-vite/src/pages/_not-found.tsx +17 -0
- package/template-vite/src/pages/dashboard.tsx +48 -0
- package/{template/app/(app)/help/page.tsx → template-vite/src/pages/help.tsx} +3 -2
- package/{template/app/(app)/library/layout.tsx → template-vite/src/pages/library/_layout.tsx} +18 -16
- package/{template/app/(app)/library/all/page.tsx → template-vite/src/pages/library/all.tsx} +1 -1
- package/{template/app/(app)/library/new/page.tsx → template-vite/src/pages/library/new.tsx} +12 -18
- package/template-vite/src/routes.tsx +72 -0
- package/{template/app → template-vite/src/styles}/globals.css +6 -2
- package/{template → template-vite}/tsconfig.json +5 -14
- package/template-vite/vite.config.ts +52 -0
- package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +0 -53
- package/template/.agents/skills/shadcn/SKILL.md +0 -242
- package/template/.agents/skills/shadcn/agents/openai.yml +0 -5
- package/template/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/template/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/template/.agents/skills/shadcn/cli.md +0 -257
- package/template/.agents/skills/shadcn/customization.md +0 -202
- package/template/.agents/skills/shadcn/evals/evals.json +0 -47
- package/template/.agents/skills/shadcn/mcp.md +0 -94
- package/template/.agents/skills/shadcn/rules/base-vs-radix.md +0 -306
- package/template/.agents/skills/shadcn/rules/composition.md +0 -195
- package/template/.agents/skills/shadcn/rules/forms.md +0 -192
- package/template/.agents/skills/shadcn/rules/icons.md +0 -101
- package/template/.agents/skills/shadcn/rules/styling.md +0 -162
- package/template/.cursor/rules/exxat-accessibility.mdc +0 -33
- package/template/.cursor/rules/exxat-data-tables.mdc +0 -32
- package/template/.cursor/rules/exxat-ds-agents.mdc +0 -26
- package/template/.cursor/rules/exxat-list-page-connected-views.mdc +0 -16
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +0 -40
- package/template/.nvmrc +0 -1
- package/template/.prettierignore +0 -7
- package/template/Logo/Exxat_Prism.svg +0 -39
- package/template/Logo/Exxat_one.svg +0 -36
- package/template/app/(app)/dashboard/loading.tsx +0 -18
- package/template/app/(app)/dashboard/page.tsx +0 -36
- package/template/app/(app)/layout.tsx +0 -77
- package/template/app/global-error.tsx +0 -63
- package/template/app/layout.tsx +0 -133
- package/template/app/page.tsx +0 -9
- package/template/docs/HANDBOOK.md +0 -187
- package/template/docs/blueprints/README.md +0 -86
- package/template/docs/blueprints/_template.md +0 -91
- package/template/docs/blueprints/board-card.md +0 -123
- package/template/docs/blueprints/data-table.md +0 -139
- package/template/docs/blueprints/key-metrics.md +0 -128
- package/template/docs/blueprints/list-page-template.md +0 -123
- package/template/docs/blueprints/page-header.md +0 -130
- package/template/docs/card-vs-rows-pattern.md +0 -36
- package/template/docs/collaboration-access-pattern.md +0 -116
- package/template/docs/command-menu-pattern.md +0 -45
- package/template/docs/component-selection-guide.md +0 -224
- package/template/docs/components-audit-2026-05.md +0 -158
- package/template/docs/consumer-upgrade-checklist.md +0 -52
- package/template/docs/data-views-pattern.md +0 -185
- package/template/docs/drawer-vs-dialog-pattern.md +0 -50
- package/template/docs/glossary.md +0 -59
- package/template/docs/hub-supported-views-pattern.md +0 -53
- package/template/docs/jobs/README.md +0 -59
- package/template/docs/jobs/record-detail.md +0 -177
- package/template/docs/kpi-flat-band-pattern.md +0 -57
- package/template/docs/kpi-strip-max-four-pattern.md +0 -30
- package/template/docs/kpi-trend-pattern.md +0 -58
- package/template/docs/large-dataset-strategy.md +0 -155
- package/template/docs/library-hub-header-pattern.md +0 -25
- package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +0 -95
- package/template/docs/migrations/0002-exxat-token-namespace.md +0 -154
- package/template/docs/migrations/0003-globals-css-canonical.md +0 -110
- package/template/docs/migrations/README.md +0 -100
- package/template/docs/migrations/_template.md +0 -64
- package/template/docs/modern-saas-patterns.md +0 -165
- package/template/docs/perf-memory-pattern.md +0 -206
- package/template/docs/reference-implementations.md +0 -153
- package/template/docs/shell-surface-elevation-pattern.md +0 -52
- package/template/docs/token-taxonomy.md +0 -416
- package/template/docs/voice-and-tone.md +0 -262
- package/template/ecosystem.config.cjs +0 -32
- package/template/next.config.mjs +0 -216
- package/template/postcss.config.mjs +0 -8
- package/template/public/favicon/favicon.ico +0 -0
- package/template/tests/setup.ts +0 -26
- package/template/vitest.config.ts +0 -18
- /package/{template → template-vite}/.cursor/rules/exxat-dashboard-view-charts.mdc +0 -0
- /package/{template → template-vite}/.prettierrc +0 -0
- /package/{template → template-vite}/AGENTS.md +0 -0
- /package/{template → template-vite}/README.md +0 -0
- /package/{template → template-vite}/components/.gitkeep +0 -0
- /package/{template → template-vite}/components/ask-leo-composer.tsx +0 -0
- /package/{template → template-vite}/components/brand-color-picker.tsx +0 -0
- /package/{template → template-vite}/components/chart-area-interactive.tsx +0 -0
- /package/{template → template-vite}/components/charts-overview.tsx +0 -0
- /package/{template → template-vite}/components/collaboration-access-flow.tsx +0 -0
- /package/{template → template-vite}/components/columns-client.tsx +0 -0
- /package/{template → template-vite}/components/columns-showcase.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-promo-banner.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-quota-progress-card.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-report-charts.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-section-heading.tsx +0 -0
- /package/{template → template-vite}/components/dashboard-tabs.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-date-calendar.tsx +0 -0
- /package/{template → template-vite}/components/data-table/filter-text-value-input.tsx +0 -0
- /package/{template → template-vite}/components/data-table/index.tsx +0 -0
- /package/{template → template-vite}/components/data-table/pagination.tsx +0 -0
- /package/{template → template-vite}/components/data-table/types.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.test.ts +0 -0
- /package/{template → template-vite}/components/data-table/use-table-state.ts +0 -0
- /package/{template → template-vite}/components/data-views/board-card-primitives.tsx +0 -0
- /package/{template → template-vite}/components/data-views/data-row-list.tsx +0 -0
- /package/{template → template-vite}/components/data-views/finder-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/folder-grid-view.tsx +0 -0
- /package/{template → template-vite}/components/data-views/hub-table.tsx +0 -0
- /package/{template → template-vite}/components/data-views/index.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-card.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-board-template.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-connected-view-body.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-details-placeholder.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-chrome.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-split-hub-tokens.ts +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-column-header.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-tree-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/data-views/list-page-view-frame.tsx +0 -0
- /package/{template → template-vite}/components/data-views/os-folder-glyph.tsx +0 -0
- /package/{template → template-vite}/components/data-views/outline-tree-menu.tsx +0 -0
- /package/{template → template-vite}/components/data-views/table-cells.tsx +0 -0
- /package/{template → template-vite}/components/dev-chunk-load-recovery.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.test.tsx +0 -0
- /package/{template → template-vite}/components/export-drawer.tsx +0 -0
- /package/{template → template-vite}/components/exxat-product-logo.tsx +0 -0
- /package/{template → template-vite}/components/folder-details-shell.tsx +0 -0
- /package/{template → template-vite}/components/form-layout-01.tsx +0 -0
- /package/{template → template-vite}/components/hub-tree-panel-view.tsx +0 -0
- /package/{template → template-vite}/components/invite-collaborators-drawer.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics-ask-leo-bridge.tsx +0 -0
- /package/{template → template-vite}/components/key-metrics.tsx +0 -0
- /package/{template → template-vite}/components/leo-insight-indicator.tsx +0 -0
- /package/{template → template-vite}/components/leo-typing-dots.tsx +0 -0
- /package/{template → template-vite}/components/library-board-view.tsx +0 -0
- /package/{template → template-vite}/components/library-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/library-favorite-button.tsx +0 -0
- /package/{template → template-vite}/components/library-new-folder-sheet.tsx +0 -0
- /package/{template → template-vite}/components/library-os-folder-view.tsx +0 -0
- /package/{template → template-vite}/components/library-page-header.tsx +0 -0
- /package/{template → template-vite}/components/library-panel-activator.tsx +0 -0
- /package/{template → template-vite}/components/list-hub-status-badge.tsx +0 -0
- /package/{template → template-vite}/components/list-page-dashboard-charts.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/getting-started.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/index.ts +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-01.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-02.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-03.tsx +0 -0
- /package/{template → template-vite}/components/onboarding/onboarding-04.tsx +0 -0
- /package/{template → template-vite}/components/page-header.tsx +0 -0
- /package/{template → template-vite}/components/product-switcher.tsx +0 -0
- /package/{template → template-vite}/components/product-wordmark.tsx +0 -0
- /package/{template → template-vite}/components/settings-appearance-card.tsx +0 -0
- /package/{template → template-vite}/components/settings-form-row.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/app-sidebar-dynamic.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/index.ts +0 -0
- /package/{template → template-vite}/components/sidebar/nav-documents.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/nav-secondary.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/secondary-panel.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-collapse.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-auto-open.tsx +0 -0
- /package/{template → template-vite}/components/sidebar/sidebar-shell.tsx +0 -0
- /package/{template → template-vite}/components/site-header.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/column-row.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/draggable-list.ts +0 -0
- /package/{template → template-vite}/components/table-properties/drawer-button.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/drawer.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/filter-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/index.ts +0 -0
- /package/{template → template-vite}/components/table-properties/sort-card.tsx +0 -0
- /package/{template → template-vite}/components/table-properties/types.ts +0 -0
- /package/{template → template-vite}/components/task-list-panel.tsx +0 -0
- /package/{template → template-vite}/components/task-priority-badge.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-landing-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/dedicated-search-results-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/list-page.tsx +0 -0
- /package/{template → template-vite}/components/templates/nested-secondary-panel-shell.tsx +0 -0
- /package/{template → template-vite}/components/templates/primary-page-template.tsx +0 -0
- /package/{template → template-vite}/components/templates/secondary-panel-hub-template.tsx +0 -0
- /package/{template → template-vite}/components/theme-color-sync.tsx +0 -0
- /package/{template → template-vite}/components/theme-provider.tsx +0 -0
- /package/{template → template-vite}/components/tinted-icon-disc.tsx +0 -0
- /package/{template → template-vite}/components/tokens-hub-auxiliary-views.tsx +0 -0
- /package/{template → template-vite}/components/tokens-themes-section.tsx +0 -0
- /package/{template → template-vite}/components/ui/accordion.tsx +0 -0
- /package/{template → template-vite}/components/ui/ai-thinking-surface.tsx +0 -0
- /package/{template → template-vite}/components/ui/alert-dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/avatar.tsx +0 -0
- /package/{template → template-vite}/components/ui/badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/banner.tsx +0 -0
- /package/{template → template-vite}/components/ui/breadcrumb.tsx +0 -0
- /package/{template → template-vite}/components/ui/button.tsx +0 -0
- /package/{template → template-vite}/components/ui/calendar.tsx +0 -0
- /package/{template → template-vite}/components/ui/card.tsx +0 -0
- /package/{template → template-vite}/components/ui/chart.tsx +0 -0
- /package/{template → template-vite}/components/ui/checkbox.tsx +0 -0
- /package/{template → template-vite}/components/ui/coach-mark.tsx +0 -0
- /package/{template → template-vite}/components/ui/collapsible.tsx +0 -0
- /package/{template → template-vite}/components/ui/command.tsx +0 -0
- /package/{template → template-vite}/components/ui/context-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/date-picker-field.tsx +0 -0
- /package/{template → template-vite}/components/ui/dialog.tsx +0 -0
- /package/{template → template-vite}/components/ui/dot-pattern.tsx +0 -0
- /package/{template → template-vite}/components/ui/drag-handle-grip.tsx +0 -0
- /package/{template → template-vite}/components/ui/dropdown-menu.tsx +0 -0
- /package/{template → template-vite}/components/ui/field.tsx +0 -0
- /package/{template → template-vite}/components/ui/form.tsx +0 -0
- /package/{template → template-vite}/components/ui/hover-card.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/input-mask.tsx +0 -0
- /package/{template → template-vite}/components/ui/input.tsx +0 -0
- /package/{template → template-vite}/components/ui/kbd.tsx +0 -0
- /package/{template → template-vite}/components/ui/label.tsx +0 -0
- /package/{template → template-vite}/components/ui/leo-icon.tsx +0 -0
- /package/{template → template-vite}/components/ui/payment-card-fields.tsx +0 -0
- /package/{template → template-vite}/components/ui/popover.tsx +0 -0
- /package/{template → template-vite}/components/ui/radio-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/resizable.tsx +0 -0
- /package/{template → template-vite}/components/ui/scroll-area.tsx +0 -0
- /package/{template → template-vite}/components/ui/select.tsx +0 -0
- /package/{template → template-vite}/components/ui/selection-tile-grid.tsx +0 -0
- /package/{template → template-vite}/components/ui/separator.tsx +0 -0
- /package/{template → template-vite}/components/ui/sheet.tsx +0 -0
- /package/{template → template-vite}/components/ui/sidebar.tsx +0 -0
- /package/{template → template-vite}/components/ui/skeleton.tsx +0 -0
- /package/{template → template-vite}/components/ui/slider.tsx +0 -0
- /package/{template → template-vite}/components/ui/sonner.tsx +0 -0
- /package/{template → template-vite}/components/ui/status-badge.tsx +0 -0
- /package/{template → template-vite}/components/ui/table.tsx +0 -0
- /package/{template → template-vite}/components/ui/tabs.tsx +0 -0
- /package/{template → template-vite}/components/ui/textarea.tsx +0 -0
- /package/{template → template-vite}/components/ui/tip.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-group.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle-switch.tsx +0 -0
- /package/{template → template-vite}/components/ui/toggle.tsx +0 -0
- /package/{template → template-vite}/components/ui/tooltip.tsx +0 -0
- /package/{template → template-vite}/components/ui/view-segmented-control.tsx +0 -0
- /package/{template → template-vite}/components.json +0 -0
- /package/{template → template-vite}/contexts/chart-variant-context.tsx +0 -0
- /package/{template → template-vite}/contexts/command-menu-context.tsx +0 -0
- /package/{template → template-vite}/contexts/dashboard-view-context.tsx +0 -0
- /package/{template → template-vite}/contexts/product-context.tsx +0 -0
- /package/{template → template-vite}/contexts/system-banner-context.tsx +0 -0
- /package/{template → template-vite}/eslint.config.mjs +0 -0
- /package/{template → template-vite}/fontawesome-subset.manifest.json +0 -0
- /package/{template → template-vite}/hooks/.gitkeep +0 -0
- /package/{template → template-vite}/hooks/use-app-theme.ts +0 -0
- /package/{template → template-vite}/hooks/use-coach-mark.ts +0 -0
- /package/{template → template-vite}/hooks/use-location-hash.ts +0 -0
- /package/{template → template-vite}/hooks/use-mobile.ts +0 -0
- /package/{template → template-vite}/hooks/use-mod-key-label.ts +0 -0
- /package/{template → template-vite}/hooks/use-sidebar-reflow-zoom.ts +0 -0
- /package/{template → template-vite}/lib/.gitkeep +0 -0
- /package/{template → template-vite}/lib/ask-leo-route-context.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.test.ts +0 -0
- /package/{template → template-vite}/lib/chart-keyboard-selection.ts +0 -0
- /package/{template → template-vite}/lib/chart-line-dash.ts +0 -0
- /package/{template → template-vite}/lib/chunk-load-error.ts +0 -0
- /package/{template → template-vite}/lib/coach-mark-registry.ts +0 -0
- /package/{template → template-vite}/lib/collaborator-access.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-config.ts +0 -0
- /package/{template → template-vite}/lib/command-menu-search-data.ts +0 -0
- /package/{template → template-vite}/lib/conditional-rule-match.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-customize-coach-mark.ts +0 -0
- /package/{template → template-vite}/lib/dashboard-layout-merge.ts +0 -0
- /package/{template → template-vite}/lib/data-list-display-options.ts +0 -0
- /package/{template → template-vite}/lib/data-list-persistence.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-registry.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view-surface.ts +0 -0
- /package/{template → template-vite}/lib/data-list-view.ts +0 -0
- /package/{template → template-vite}/lib/data-view-dashboard-storage.ts +0 -0
- /package/{template → template-vite}/lib/date-filter.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-recents.ts +0 -0
- /package/{template → template-vite}/lib/dedicated-search-url.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.test.ts +0 -0
- /package/{template → template-vite}/lib/dev-log.ts +0 -0
- /package/{template → template-vite}/lib/discovery-hub.ts +0 -0
- /package/{template → template-vite}/lib/editable-target.ts +0 -0
- /package/{template → template-vite}/lib/exxat-palette.json +0 -0
- /package/{template → template-vite}/lib/exxat-palette.ts +0 -0
- /package/{template → template-vite}/lib/floating-sheet-panel.ts +0 -0
- /package/{template → template-vite}/lib/full-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/hub-connected-view-renderers.ts +0 -0
- /package/{template → template-vite}/lib/initials-from-name.ts +0 -0
- /package/{template → template-vite}/lib/library-authoring.ts +0 -0
- /package/{template → template-vite}/lib/library-dedicated-search.ts +0 -0
- /package/{template → template-vite}/lib/library-hub-search.ts +0 -0
- /package/{template → template-vite}/lib/library-nav.ts +0 -0
- /package/{template → template-vite}/lib/library-recent-searches.ts +0 -0
- /package/{template → template-vite}/lib/library-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-hub-supported-views.ts +0 -0
- /package/{template → template-vite}/lib/list-page-table-properties.ts +0 -0
- /package/{template → template-vite}/lib/list-status-badges.ts +0 -0
- /package/{template → template-vite}/lib/logo-dev.ts +0 -0
- /package/{template → template-vite}/lib/mailto.ts +0 -0
- /package/{template → template-vite}/lib/mock/dashboard.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-folders.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-header-collaborators.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-inspector.ts +0 -0
- /package/{template → template-vite}/lib/mock/library-kpi.ts +0 -0
- /package/{template → template-vite}/lib/mock/library.ts +0 -0
- /package/{template → template-vite}/lib/mock/navigation.tsx +0 -0
- /package/{template → template-vite}/lib/motion-ui.ts +0 -0
- /package/{template → template-vite}/lib/product-brand.ts +0 -0
- /package/{template → template-vite}/lib/raf-throttle.ts +0 -0
- /package/{template → template-vite}/lib/row-height.ts +0 -0
- /package/{template → template-vite}/lib/sidebar-state-cookie.ts +0 -0
- /package/{template → template-vite}/lib/stock-portrait.ts +0 -0
- /package/{template → template-vite}/lib/table-state-lifecycle.ts +0 -0
- /package/{template → template-vite}/lib/utils.test.ts +0 -0
- /package/{template → template-vite}/lib/utils.ts +0 -0
- /package/{template → template-vite}/public/.gitkeep +0 -0
- /package/{template → template-vite}/public/Illustration/Rotation.svg +0 -0
- /package/{template → template-vite}/public/avatars/user.svg +0 -0
- /package/{template/public → template-vite/public/favicon}/favicon.ico +0 -0
- /package/{template/app → template-vite/public}/favicon.ico +0 -0
- /package/{template → template-vite}/public/folders/icons8-folder-windows-11.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-one.svg +0 -0
- /package/{template → template-vite}/public/logos/exxat-prism.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/emory.svg +0 -0
- /package/{template → template-vite}/public/mock-schools/rush.svg +0 -0
- /package/{template → template-vite}/scripts/fontawesome-subset-audit.mjs +0 -0
- /package/{template → template-vite}/scripts/pm2-startup-macos.sh +0 -0
- /package/{template → template-vite}/skills-lock.json +0 -0
- /package/{template/app/(app)/columns/page.tsx → template-vite/src/pages/columns.tsx} +0 -0
- /package/{template/app/(app)/library/find/page.tsx → template-vite/src/pages/library/find.tsx} +0 -0
- /package/{template/app/(app)/library/page.tsx → template-vite/src/pages/library/index.tsx} +0 -0
- /package/{template/app/(app)/library/list/page.tsx → template-vite/src/pages/library/list.tsx} +0 -0
- /package/{template/app/(app)/settings/page.tsx → template-vite/src/pages/settings.tsx} +0 -0
- /package/{template/app/(app)/tokens-themes/page.tsx → template-vite/src/pages/tokens-themes.tsx} +0 -0
- /package/{template → template-vite}/stores/app-store.ts +0 -0
- /package/{template → template-vite}/types/react-payment-inputs.d.ts +0 -0
|
@@ -1,206 +1,171 @@
|
|
|
1
|
-
# Dev memory tuning for
|
|
1
|
+
# Dev memory tuning for Vite + Node 24
|
|
2
2
|
|
|
3
3
|
> **Audience:** humans + AI agents.
|
|
4
|
-
> **Companion to:** [`HANDBOOK.md`](./HANDBOOK.md). Read this when `
|
|
5
|
-
> RSS climbs past ~
|
|
6
|
-
>
|
|
4
|
+
> **Companion to:** [`HANDBOOK.md`](./HANDBOOK.md). Read this when `pnpm dev`
|
|
5
|
+
> RSS climbs past ~1.5 GB or when two designers are running dev servers on
|
|
6
|
+
> one machine.
|
|
7
7
|
|
|
8
|
-
A fresh `
|
|
9
|
-
the settings below. Without them
|
|
10
|
-
|
|
8
|
+
A fresh `pnpm dev` against this app stabilizes around **~250–500 MB RSS**
|
|
9
|
+
with the settings below. Without them — or running multiple Vite
|
|
10
|
+
instances against the same monorepo — total system memory drifts to
|
|
11
|
+
1.5 GB+ per process and designers' laptops start to swap.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
> **History:** before PR-6 (May 2026) `apps/web` ran on Next.js + Turbopack
|
|
14
|
+
> with a 1.4–4 GB baseline per dev server. Migrating to Vite + react-router
|
|
15
|
+
> dropped the steady-state RSS by **8×** and cold start from 8–12 s to
|
|
16
|
+
> 150–300 ms. The full migration story is in commits `49e9adf` (PR-1) →
|
|
17
|
+
> `d16ec77` (PR-6).
|
|
18
|
+
|
|
19
|
+
## 1. The four knobs that matter
|
|
13
20
|
|
|
14
21
|
| # | Knob | Why it matters | Where it lives |
|
|
15
22
|
|---|------|----------------|----------------|
|
|
16
|
-
| 1 | `
|
|
17
|
-
| 2 | `
|
|
18
|
-
| 3 | `
|
|
19
|
-
| 4 | `
|
|
20
|
-
| 5 | `experimental.webpackMemoryOptimizations: true` | Drops large in-memory webpack caches at the cost of slightly slower rebuilds. **Only the `pnpm dev:webpack` fallback uses webpack** — Turbopack ignores this flag. Keep it on for the rare cases where the webpack path is needed. | `next.config.mjs` |
|
|
21
|
-
| 6 | `target: ES2022` (tsconfig) | The TS compiler emits less polyfill scaffolding for `async/await`, optional chaining, nullish coalescing, etc. tsserver in-memory AST shrinks proportionally. Safe with React 19 + Next 16 + Node 24. | `tsconfig.json` |
|
|
22
|
-
|
|
23
|
-
## 2. NODE_OPTIONS propagation
|
|
23
|
+
| 1 | `dev-guard` predev hook | Blocks a second `pnpm dev` from booting against the same checkout. The #1 source of memory inflation in practice — two parallel Vite servers double total system RSS for zero throughput. | `package.json` `predev*` scripts → `exxat-ui dev-guard` (in `packages/ui/consumer-extras/scripts/dev-guard.mjs`) |
|
|
24
|
+
| 2 | `optimizeDeps.include` for hot deps | Pre-bundles `react`, `react-dom`, `react-router-dom` so the first request doesn't trigger a 600+ ms esbuild dep-scan. Drops first-paint TTFB by ~30%. | `vite.config.ts` |
|
|
25
|
+
| 3 | `optimizeDeps.exclude: ["@exxatdesignux/ui"]` | The shared UI package is a pnpm workspace package. Eager pre-bundling fights pnpm's strict node_modules layout (esbuild can't resolve `radix-ui`, `cmdk`, etc. when crawling the package's own `dist/`). Excluding it lets Vite resolve through the consumer's module resolver — which sees peer deps correctly. | `vite.config.ts` |
|
|
26
|
+
| 4 | `resolve.dedupe: ["react", "react-dom", "react-router-dom"]` | Prevents two React instances from being bundled when `apps/web` and `packages/ui` resolve different versions in pnpm. The classic "Invalid hook call" runtime error and a hidden 30+ MB heap duplicate. | `vite.config.ts` |
|
|
24
27
|
|
|
25
|
-
`NODE_OPTIONS` is
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
`NODE_OPTIONS` is **not** required under Vite — V8's default heap is fine
|
|
29
|
+
for a 250–500 MB process. The Next-era `--max-old-space-size=6144` was
|
|
30
|
+
fighting Turbopack's mmap'd FS cache; that whole class of problem is gone.
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
because Next dev is only run from POSIX shells.
|
|
31
|
-
- **PM2 daemon:** set in `ecosystem.config.cjs` `env` (which becomes the child
|
|
32
|
-
environment).
|
|
33
|
-
- **CI:** set in the workflow `env:` block on the same step that runs `next`.
|
|
34
|
-
- **VS Code / Cursor terminals:** these inherit the parent shell env, so the
|
|
35
|
-
`package.json` script is enough.
|
|
32
|
+
## 2. Don't run two Vite servers against the same checkout
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
A single `vite` process (Vite + the React plugin + Tailwind plugin)
|
|
35
|
+
stabilizes at ~280 MB RSS once the route tree is warm. Two parallel
|
|
36
|
+
servers stabilize at ~560 MB, three at ~840 MB — they don't share dep
|
|
37
|
+
caches across processes.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The `predev` hook (`exxat-ui dev-guard`) detects an already-running
|
|
40
|
+
`vite` instance under the same checkout and exits with a friendly
|
|
41
|
+
message instead of starting a second one. Unconditionally honour it —
|
|
42
|
+
do not bypass with `--no-predev` unless you've explicitly killed the
|
|
43
|
+
other server.
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
To check what's running:
|
|
42
46
|
|
|
43
47
|
```bash
|
|
44
|
-
|
|
45
|
-
# 3.2G .next
|
|
48
|
+
ps aux | grep -E "vite/bin/vite" | grep -v grep
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
**Do not disable** the FS cache (`turbopackFileSystemCacheForDev: false`) — cold-start dev becomes painful (~15–30 s every restart on this app). Instead, **bust the cache** when it grows past 1–2 GB:
|
|
51
|
+
If you legitimately need two designer servers (e.g. comparing two
|
|
52
|
+
branches side-by-side), use the dedicated alternate ports:
|
|
51
53
|
|
|
52
54
|
```bash
|
|
53
|
-
pnpm
|
|
54
|
-
pnpm dev:
|
|
55
|
+
pnpm dev # http://localhost:3000
|
|
56
|
+
pnpm dev:3001 # http://localhost:3001
|
|
57
|
+
pnpm dev:3005 # http://localhost:3005
|
|
55
58
|
```
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
These all share the same `dev-guard` so a third can't sneak in.
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
- `.next` is > 2 GB and dev memory is climbing.
|
|
61
|
-
- HMR starts skipping updates or compilation gets stuck on a stale module.
|
|
62
|
+
## 3. Clearing the dep cache
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
Vite caches pre-bundled deps under `node_modules/.vite/`. The cache is
|
|
65
|
+
small (10–50 MB typical) but can grow stale after major dependency
|
|
66
|
+
changes:
|
|
64
67
|
|
|
65
68
|
```bash
|
|
66
|
-
pnpm clean
|
|
69
|
+
pnpm clean:cache # removes node_modules/.vite
|
|
70
|
+
pnpm dev:fresh # clean + restart
|
|
67
71
|
```
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
Bust the cache when:
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
- A `pnpm install` changed `@exxatdesignux/ui` or any framework dep.
|
|
76
|
+
- HMR starts skipping updates.
|
|
77
|
+
- The browser console shows `[vite] (client) Re-optimizing dependencies` looping repeatedly.
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
If you see two `next-server` lineages in `ps`, check:
|
|
79
|
+
For a full nuke (build artefacts too):
|
|
76
80
|
|
|
77
81
|
```bash
|
|
78
|
-
|
|
79
|
-
lsof -p <pid> | grep '\.next/dev/cache' | head -5 # which checkout owns it
|
|
82
|
+
pnpm clean
|
|
80
83
|
```
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Scenario | Symptom | Fix |
|
|
85
|
-
|----------|---------|-----|
|
|
86
|
-
| Two checkouts of the same monorepo (e.g. `DS_Workspace/` and `Exxat-DS-Workspace/`) both running `pnpm dev:web` | Two `next-server` parents, both in `apps/web/.next/...` paths but on different absolute roots | Quit one. Pin to a single checkout per machine. |
|
|
87
|
-
| A customer app (e.g. `test-9`) + the monorepo `apps/web` running at the same time | Different cache hashes (`ee6e79b1/`) under different roots | Stop whichever you're not actively touching: `pm2 stop exxat-ds` or `Ctrl+C` |
|
|
88
|
-
| A stale pm2 daemon from a prior `nvm use 22` session left running after upgrading to Node 24 | One Node-22 + one Node-24 dev server | `pm2 delete exxat-ds` then `pnpm dev:daemon` to re-launch under Node 24 |
|
|
89
|
-
| `next build` running in another tab while `next dev` is up | Three or four `next-server` for the duration of the build | Wait for the build, or kill the dev server until the build is done |
|
|
90
|
-
|
|
91
|
-
## 5. Two `next-server` processes per app is normal
|
|
85
|
+
## 4. Diagnose a memory regression
|
|
92
86
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- **`next-server` (main)** — HTTP entry, watcher, router.
|
|
96
|
-
- **`next-server` (render worker)** — RSC + SSR pipeline.
|
|
97
|
-
|
|
98
|
-
Both inherit `NODE_OPTIONS`, so the 6 GB cap applies to **each**. With the
|
|
99
|
-
config in this app the totals stabilize around **~1.4 GB + ~0.6 GB ≈ 2 GB**.
|
|
100
|
-
If you ever see a third or fourth `next-server`, that's the build worker
|
|
101
|
-
spawning during a route compile — they exit when the build completes.
|
|
102
|
-
|
|
103
|
-
## 6. Diagnose a memory regression
|
|
104
|
-
|
|
105
|
-
When dev RSS climbs past 4 GB and stays there:
|
|
87
|
+
When dev RSS climbs past 1 GB and stays there:
|
|
106
88
|
|
|
107
89
|
```bash
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
ls -lt .next/diagnostics | head -3
|
|
90
|
+
# Check the boring stuff first
|
|
91
|
+
ps aux | grep -E "vite" | grep -v grep | wc -l # > 1 = duplicate dev servers
|
|
92
|
+
du -sh node_modules/.vite # > 200 MB = bust the cache
|
|
93
|
+
|
|
94
|
+
# Then check the active dep graph
|
|
95
|
+
# Vite ships a built-in module-graph endpoint at /__inspect/ when
|
|
96
|
+
# `vite-plugin-inspect` is installed; not currently wired in apps/web,
|
|
97
|
+
# but a one-line `pnpm add -D vite-plugin-inspect` plus the plugin
|
|
98
|
+
# import gives instant visibility for one-off investigations.
|
|
118
99
|
```
|
|
119
100
|
|
|
120
|
-
Common culprits
|
|
101
|
+
Common culprits:
|
|
121
102
|
|
|
122
103
|
| Symptom | Likely cause | Fix |
|
|
123
104
|
|---------|--------------|-----|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
| Heap grows on every HMR cycle, never shrinks |
|
|
129
|
-
|
|
|
130
|
-
| tsserver alone is > 1.5 GB | TS strict + large lib check | `skipLibCheck: true` (already on), drop `allowJs` if not needed |
|
|
131
|
-
| Turbopack worker RSS keeps growing past 4 GiB | `turbopack.memoryLimit` not set | Apply knob 2 |
|
|
105
|
+
| Two or more `vite` processes in `ps` | Dual dev server across checkouts | Stop the duplicate (see §2) |
|
|
106
|
+
| `[vite] (client) Re-optimizing dependencies` looping every save | Dep cache invalidated on every change | `pnpm clean:cache && pnpm dev` |
|
|
107
|
+
| Initial paint + first navigation feel slow but steady-state is fine | `optimizeDeps.include` is missing a hot dep | Add it to the list (knob 2) |
|
|
108
|
+
| `Invalid hook call` runtime error | Two React copies in the bundle | Verify `dedupe` (knob 4) is set |
|
|
109
|
+
| Heap grows on every HMR cycle, never shrinks | Module-level `Map` / `Set` that never gets cleared | Move to request-scoped storage |
|
|
110
|
+
| tsserver alone is > 1.5 GB | TS strict + large lib check | `skipLibCheck: true` is already on; drop `allowJs` if not needed |
|
|
132
111
|
|
|
133
|
-
##
|
|
112
|
+
## 5. Node 24 features we leverage
|
|
134
113
|
|
|
135
114
|
Node 24 (LTS-track) is required by `engines.node` in `package.json` and
|
|
136
115
|
pinned in `.nvmrc`. Specifically:
|
|
137
116
|
|
|
138
|
-
- **V8 13.6 with Maglev JIT default-on** — faster startup, lower base
|
|
139
|
-
Steady-state RSS for the
|
|
140
|
-
- **Improved incremental marking GC** — fewer long pauses during HMR;
|
|
141
|
-
perceived "stutter" when saving a large file is gone.
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
- **`--heap-prof` / `--cpu-prof` always-on** — the `dev:profile` script in
|
|
150
|
-
`package.json` uses these to drop snapshots into `.next/diagnostics/`
|
|
151
|
-
without any third-party profiler dependency.
|
|
152
|
-
- **`--experimental-strip-types`** — Node 24 can run `.ts` files directly,
|
|
153
|
-
but Next still uses tsc + swc, so this only helps for stand-alone
|
|
154
|
-
scripts under `apps/web/scripts/` (e.g. `fa:subset-audit`) if they're
|
|
155
|
-
converted from `.mjs` to `.ts`.
|
|
156
|
-
- **Smaller initial heap allocations** — V8 13.6 starts with ~50 MB less
|
|
157
|
-
reserved arena vs V8 12.x. Most visible in fast CI test runs.
|
|
158
|
-
|
|
159
|
-
## 8. Anti-patterns
|
|
117
|
+
- **V8 13.6 with Maglev JIT default-on** — faster startup, lower base
|
|
118
|
+
heap. Steady-state RSS for the Vite parent is ~12% lower vs Node 22.
|
|
119
|
+
- **Improved incremental marking GC** — fewer long pauses during HMR;
|
|
120
|
+
the perceived "stutter" when saving a large file is gone.
|
|
121
|
+
- **`node --run <script>`** — replaces `npm run` for one-off scripts
|
|
122
|
+
with ~30 ms less per-invocation overhead. Use it in any CI step that
|
|
123
|
+
runs a workspace script directly: `node --run typecheck`.
|
|
124
|
+
- **Smaller initial heap allocations** — V8 13.6 starts with ~50 MB
|
|
125
|
+
less reserved arena vs V8 12.x. Most visible in fast CI test runs.
|
|
126
|
+
|
|
127
|
+
## 6. Anti-patterns
|
|
160
128
|
|
|
161
129
|
| Anti-pattern | Why it's wrong |
|
|
162
130
|
|--------------|----------------|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
| `NODE_OPTIONS=--inspect` in normal dev | Allocates an extra inspector arena per process (~200 MB). Use only when actively debugging. |
|
|
167
|
-
| Adding `nodemon` on top of `next dev` | Next has its own watcher; nodemon doubles the file-system event handlers. |
|
|
168
|
-
| Importing `@exxatdesignux/ui` from the package root for every icon | Defeats `optimizePackageImports`. Always import from the leaf path the DS exposes. |
|
|
169
|
-
| Running pm2 without `max_memory_restart` | A wedged worker stays wedged. The 7 GB ceiling lets pm2 recycle before the OS swaps. |
|
|
170
|
-
| Disabling `turbopackFileSystemCacheForDev` because "cache is the problem" | Cold starts go from ~1s to ~15–30s every restart. Bust with `pnpm clean:cache` instead. |
|
|
131
|
+
| Bypassing `dev-guard` with `npm run dev --ignore-scripts` | Defeats the only protection against parallel servers. The `predev` hook is the cheapest possible safety net. |
|
|
132
|
+
| Adding `nodemon` on top of `vite` | Vite has its own watcher; nodemon doubles the file-system event handlers. |
|
|
133
|
+
| Importing `@exxatdesignux/ui` from the package root for every icon | Bypasses tree-shaking. Always import from the leaf path the DS exposes (e.g. `@exxatdesignux/ui/components/button`). |
|
|
171
134
|
| Two checkouts of the same monorepo both running dev | Caches don't share — 2× total RSS. Pin to one checkout per machine. |
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
1
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
3. `
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
`
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
135
|
+
| Running `vite dev` with `--inspect` in normal dev | Allocates an extra inspector arena per process (~200 MB). Use only when actively debugging. |
|
|
136
|
+
|
|
137
|
+
## 7. Upgrading an existing customer app to the Vite stack
|
|
138
|
+
|
|
139
|
+
If your customer repo was scaffolded before `@exxatdesignux/ui@0.5.10`
|
|
140
|
+
on the legacy Next stack, the migration path is the one this repo took
|
|
141
|
+
in PR-1 → PR-6:
|
|
142
|
+
|
|
143
|
+
1. **PR-1**: add `dev-guard` predev hook + bump Node engines to 24.
|
|
144
|
+
2. **PR-2**: scaffold `template-vite/` alongside Next; designers move first.
|
|
145
|
+
3. **PR-3**: set up `vite.config.ts` with `next/*` aliases pointing at
|
|
146
|
+
shims so existing component code keeps compiling.
|
|
147
|
+
4. **PR-4**: smoke `pnpm build:vite` and fix any cross-stack env vars
|
|
148
|
+
(`process.env.NEXT_PUBLIC_*` ⇄ `import.meta.env.VITE_*`).
|
|
149
|
+
5. **PR-5**: flip `pnpm dev` from Next to Vite; demote Next variants to `:next` slot.
|
|
150
|
+
6. **PR-6**: codemod all `next/*` imports to `react-router-dom` direct,
|
|
151
|
+
delete `app/`, `next.config.mjs`, `next-env.d.ts`, the shim directory,
|
|
152
|
+
and remove `next` + `eslint-config-next` + `@next/bundle-analyzer`
|
|
153
|
+
from `package.json`.
|
|
154
|
+
|
|
155
|
+
A reusable codemod for step 6 lives in commit `d16ec77` under
|
|
156
|
+
`apps/web/scripts/codemod-next-to-rr.mjs` (deleted in the same commit
|
|
157
|
+
to keep the tree clean — pull it from git history if you need it).
|
|
158
|
+
|
|
159
|
+
The full sequence took roughly an afternoon of focused work end-to-end
|
|
160
|
+
on the canonical app. For repos with significant Next-only surfaces
|
|
161
|
+
(RSC server actions, `next/image` heavy use, custom Next middleware),
|
|
162
|
+
expect to spend additional time on those specific subsystems — they
|
|
163
|
+
were not present in `apps/web`.
|
|
199
164
|
|
|
200
165
|
## See also
|
|
201
166
|
|
|
202
167
|
- [`HANDBOOK.md`](./HANDBOOK.md) — workspace orientation
|
|
203
168
|
- [`consumer-upgrade-checklist.md`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/packages/ui/consumer-extras/patterns/consumer-upgrade-checklist.md) — what to do after `pnpm add @exxatdesignux/ui@latest`
|
|
204
|
-
- [
|
|
169
|
+
- [Vite — Performance](https://vite.dev/guide/performance.html)
|
|
205
170
|
- [Node.js — Diagnostics](https://nodejs.org/api/cli.html#--heap-profheap_dir)
|
|
206
171
|
- [V8 — Maglev](https://v8.dev/blog/maglev)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* dev-guard — catches the #1 cause of memory exhaustion before a second
|
|
4
|
+
* dev server boots and lifts total system RSS unnecessarily.
|
|
5
|
+
*
|
|
6
|
+
* Detects Vite parents under the same `pnpm dev` lineage. A single Vite
|
|
7
|
+
* dev server stabilizes at ~250–500 MB; stacking two doubles total RSS,
|
|
8
|
+
* confuses HMR (two ports, two websockets), and creates "which one am I
|
|
9
|
+
* actually viewing?" mistakes when designers compare branches.
|
|
10
|
+
*
|
|
11
|
+
* (History: until PR-6 (May 2026) this guard also detected `next-server`
|
|
12
|
+
* parents because `apps/web` ran on Next + Turbopack. After Next was
|
|
13
|
+
* removed, the Next branch became dead code — this version is Vite-only.)
|
|
14
|
+
*
|
|
15
|
+
* Run as `predev` hook (or any time you want to verify environment health):
|
|
16
|
+
* "predev": "exxat-ui dev-guard"
|
|
17
|
+
*
|
|
18
|
+
* Behavior:
|
|
19
|
+
* - Lists every running `vite` parent.
|
|
20
|
+
* - Exits 0 silently if zero or one is found (the one is *us* — fine).
|
|
21
|
+
* - Prints a guided warning if 2+ are found, names the cwd of each,
|
|
22
|
+
* and asks the user whether to abort or continue.
|
|
23
|
+
* - In CI (`CI=true`) or with `--quiet`, exits 0 without prompting.
|
|
24
|
+
*
|
|
25
|
+
* Tested against macOS / Linux. Windows users: PowerShell `Get-Process` would
|
|
26
|
+
* need a separate branch; not implemented yet because the team is mac/linux.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { execFileSync } from "node:child_process"
|
|
30
|
+
import { createInterface } from "node:readline/promises"
|
|
31
|
+
|
|
32
|
+
const QUIET = process.argv.includes("--quiet") || process.env.CI === "true"
|
|
33
|
+
|
|
34
|
+
/** @typedef {{ pid: number, cwd: string, rss: number }} ViteProc */
|
|
35
|
+
|
|
36
|
+
/** @returns {ViteProc[]} */
|
|
37
|
+
function listDevServers() {
|
|
38
|
+
let out = ""
|
|
39
|
+
try {
|
|
40
|
+
out = execFileSync(
|
|
41
|
+
"ps",
|
|
42
|
+
["-axo", "pid=,rss=,command="],
|
|
43
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
|
|
44
|
+
)
|
|
45
|
+
} catch {
|
|
46
|
+
// ps unavailable (very unusual). Fail open — better to let dev start.
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const matches = []
|
|
51
|
+
for (const line of out.split("\n")) {
|
|
52
|
+
const m = line.match(/^\s*(\d+)\s+(\d+)\s+(.+)$/)
|
|
53
|
+
if (!m) continue
|
|
54
|
+
const [, pid, rss, command] = m
|
|
55
|
+
|
|
56
|
+
// Vite — bin/vite.js or `node ... vite ...` parents. Skip vitest, esbuild
|
|
57
|
+
// children, vite preview workers. We require the literal `vite` token
|
|
58
|
+
// either at end of path (.../bin/vite[.js]) or as a subcommand argv.
|
|
59
|
+
const isViteDev =
|
|
60
|
+
/(\/vite(\.js|\.mjs)?|[\s/]vite\s)\b/.test(command) &&
|
|
61
|
+
!/\bvitest\b/.test(command) &&
|
|
62
|
+
!/\bvite\s+preview\b/.test(command) &&
|
|
63
|
+
!/\besbuild\b/.test(command)
|
|
64
|
+
|
|
65
|
+
if (!isViteDev) continue
|
|
66
|
+
matches.push({
|
|
67
|
+
pid: Number(pid),
|
|
68
|
+
cwd: cwdOfPid(Number(pid)) ?? "(cwd unknown)",
|
|
69
|
+
rss: Number(rss), // KB
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
return matches
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Best-effort cwd lookup. macOS has lsof; Linux has /proc/<pid>/cwd. */
|
|
76
|
+
function cwdOfPid(pid) {
|
|
77
|
+
try {
|
|
78
|
+
if (process.platform === "darwin") {
|
|
79
|
+
const out = execFileSync("lsof", ["-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
80
|
+
encoding: "utf8",
|
|
81
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
82
|
+
})
|
|
83
|
+
const line = out.split("\n").find(l => l.startsWith("n"))
|
|
84
|
+
return line ? line.slice(1) : null
|
|
85
|
+
}
|
|
86
|
+
if (process.platform === "linux") {
|
|
87
|
+
const out = execFileSync("readlink", ["-f", `/proc/${pid}/cwd`], {
|
|
88
|
+
encoding: "utf8",
|
|
89
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
90
|
+
})
|
|
91
|
+
return out.trim() || null
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function fmtMB(kb) {
|
|
100
|
+
return (kb / 1024).toFixed(0) + " MB"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function main() {
|
|
104
|
+
const servers = listDevServers()
|
|
105
|
+
if (servers.length <= 1) {
|
|
106
|
+
if (!QUIET) {
|
|
107
|
+
console.log(
|
|
108
|
+
servers.length === 0
|
|
109
|
+
? "[dev-guard] No other dev server running. Safe to start."
|
|
110
|
+
: `[dev-guard] One vite already running (pid ${servers[0].pid}, ${fmtMB(servers[0].rss)}). Reusing it.`,
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
process.exit(0)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Two or more — this is the bad path.
|
|
117
|
+
console.error("")
|
|
118
|
+
console.error("⚠ dev-guard: multiple Vite dev-server processes detected.")
|
|
119
|
+
console.error("")
|
|
120
|
+
console.error("Two parallel Vite dev servers stabilize at ~500–800 MB combined")
|
|
121
|
+
console.error("and confuse HMR (two ports, two websockets, components from two")
|
|
122
|
+
console.error("workspace roots). Stop the one you're not actively touching.")
|
|
123
|
+
console.error("")
|
|
124
|
+
for (const s of servers) {
|
|
125
|
+
console.error(
|
|
126
|
+
` pid ${String(s.pid).padEnd(6)} ${fmtMB(s.rss).padEnd(8)} ${s.cwd}`,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
console.error("")
|
|
130
|
+
console.error("Recommended: stop the one you're not actively touching.")
|
|
131
|
+
console.error(" • macOS / Linux: kill <pid>")
|
|
132
|
+
console.error(" • pm2 daemon: pm2 stop exxat-ds (or pm2 delete exxat-ds)")
|
|
133
|
+
console.error("")
|
|
134
|
+
|
|
135
|
+
if (QUIET) {
|
|
136
|
+
// CI / quiet mode — don't block, just warn.
|
|
137
|
+
process.exit(0)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
141
|
+
const ans = (await rl.question("Continue starting another dev server anyway? [y/N] ")).trim().toLowerCase()
|
|
142
|
+
rl.close()
|
|
143
|
+
|
|
144
|
+
if (ans === "y" || ans === "yes") {
|
|
145
|
+
console.error("[dev-guard] Continuing.")
|
|
146
|
+
process.exit(0)
|
|
147
|
+
}
|
|
148
|
+
console.error("[dev-guard] Aborted. Stop the other server, then re-run dev.")
|
|
149
|
+
process.exit(1)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main().catch(err => {
|
|
153
|
+
// Never let a guard bug block dev. Log and pass through.
|
|
154
|
+
console.error("[dev-guard] internal error, allowing dev to start:", err?.message ?? err)
|
|
155
|
+
process.exit(0)
|
|
156
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Exxat DS — templates
|
|
2
|
+
|
|
3
|
+
Markdown templates the agent fills in (or copies into a PR / docs folder) at
|
|
4
|
+
specific points of a design task.
|
|
5
|
+
|
|
6
|
+
| File | When the agent emits it |
|
|
7
|
+
|------|------------------------|
|
|
8
|
+
| `handoff.md` | After every design task that creates or rebuilds a surface, before the engineer wires real data. Source of truth for the design → engineering handoff. |
|
|
9
|
+
|
|
10
|
+
These ship via `@exxatdesignux/ui` and are written into the consumer repo at:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
docs/exxat-ds/templates/<file>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
…by `exxat-ui sync-extras`.
|
|
17
|
+
|
|
18
|
+
The agent locates the template via the `exxat-ux-discovery-protocol` rule
|
|
19
|
+
(see `.cursor/rules/exxat-ux-discovery-protocol.mdc`) and fills it in as
|
|
20
|
+
the closing artifact of a build.
|
|
21
|
+
|
|
22
|
+
Engineers do not edit these templates by hand. They open the filled-in copy
|
|
23
|
+
attached to a PR / saved at `docs/exxat-ds/handoff/<surface-slug>.md`.
|