build(ui): adopt sonda over rollup-plugin-visualizer to examine bundle

Requires a change to tsconfig module/moduleResolution settings. We were
on old legacy values anyways so good to update it.
This commit is contained in:
psychedelicious
2025-06-25 19:21:12 +10:00
parent 1a39d22b6c
commit 7948bca864
10 changed files with 111 additions and 42 deletions

View File

@@ -14,3 +14,4 @@ static/
src/theme/css/overlayscrollbars.css
src/theme_/css/overlayscrollbars.css
pnpm-lock.yaml
.sonda/

View File

@@ -151,6 +151,7 @@
"openapi-typescript": "^7.6.1",
"prettier": "^3.5.3",
"rollup-plugin-visualizer": "^5.14.0",
"sonda": "^0.8.2",
"storybook": "^8.6.12",
"tsafe": "^1.8.5",
"type-fest": "^4.40.0",

View File

@@ -286,6 +286,9 @@ devDependencies:
rollup-plugin-visualizer:
specifier: ^5.14.0
version: 5.14.0
sonda:
specifier: ^0.8.2
version: 0.8.2
storybook:
specifier: ^8.6.12
version: 8.6.12(prettier@3.5.3)
@@ -3979,6 +3982,13 @@ packages:
ieee754: 1.2.1
dev: true
/bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
dependencies:
run-applescript: 7.0.0
dev: true
/cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
@@ -4446,6 +4456,19 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
/default-browser-id@5.0.0:
resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
engines: {node: '>=18'}
dev: true
/default-browser@5.2.1:
resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
engines: {node: '>=18'}
dependencies:
bundle-name: 4.1.0
default-browser-id: 5.0.0
dev: true
/defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
requiresBuild: true
@@ -4466,6 +4489,11 @@ packages:
engines: {node: '>=8'}
dev: true
/define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
dev: true
/define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
@@ -5792,6 +5820,12 @@ packages:
hasBin: true
dev: true
/is-docker@3.0.0:
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
dev: true
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -5832,6 +5866,14 @@ packages:
is-extglob: 2.1.1
dev: true
/is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'}
hasBin: true
dependencies:
is-docker: 3.0.0
dev: true
/is-interactive@1.0.0:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
@@ -5953,6 +5995,13 @@ packages:
is-docker: 2.2.1
dev: true
/is-wsl@3.1.0:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
engines: {node: '>=16'}
dependencies:
is-inside-container: 1.0.0
dev: true
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
dev: true
@@ -6558,6 +6607,16 @@ packages:
mimic-fn: 2.1.0
dev: true
/open@10.1.2:
resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==}
engines: {node: '>=18'}
dependencies:
default-browser: 5.2.1
define-lazy-prop: 3.0.0
is-inside-container: 1.0.0
is-wsl: 3.1.0
dev: true
/open@8.4.2:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
@@ -7530,6 +7589,11 @@ packages:
'@babel/runtime': 7.27.0
dev: false
/run-applescript@7.0.0:
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
engines: {node: '>=18'}
dev: true
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -7735,6 +7799,15 @@ packages:
- supports-color
dev: false
/sonda@0.8.2:
resolution: {integrity: sha512-OzLlgd4TVjNzhk+Q7NWrdHc4hAU3gchF9u9ZQ4GcQLKk3IX6OTbaf4U7Itq9XssuuCPVVPTl8p0rGAjBHalxsg==}
engines: {node: '>=20.19 || >=22.12'}
hasBin: true
dependencies:
'@ampproject/remapping': 2.3.0
open: 10.1.2
dev: true
/source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}

View File

@@ -4,8 +4,8 @@ import { RGB_COLOR_SWATCHES } from 'common/components/ColorPicker/swatches';
import { rgbColorToString } from 'common/util/colorCodeTransformers';
import type { CSSProperties } from 'react';
import { memo, useCallback } from 'react';
import type { RgbColor } from 'react-colorful';
import { RgbColorPicker as ColorfulRgbColorPicker } from 'react-colorful';
import type { RgbColor } from 'react-colorful/dist/types';
import { useTranslation } from 'react-i18next';
type Props = {

View File

@@ -4,8 +4,8 @@ import { RGBA_COLOR_SWATCHES } from 'common/components/ColorPicker/swatches';
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
import type { CSSProperties } from 'react';
import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
import { RgbaColorPicker as ColorfulRgbaColorPicker } from 'react-colorful';
import type { RgbaColor } from 'react-colorful/dist/types';
import { useTranslation } from 'react-i18next';
type Props = {

View File

@@ -14,9 +14,8 @@ import { useStore } from '@nanostores/react';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { typedMemo } from 'common/util/typedMemo';
import { NO_DRAG_CLASS, NO_WHEEL_CLASS } from 'features/nodes/types/constants';
import type { AnyStore, ReadableAtom, Task, WritableAtom } from 'nanostores';
import type { ReadableAtom, WritableAtom } from 'nanostores';
import { atom, computed } from 'nanostores';
import type { StoreValues } from 'nanostores/computed';
import type { ChangeEvent, MouseEventHandler, PropsWithChildren, RefObject } from 'react';
import React, {
createContext,
@@ -472,17 +471,6 @@ const useKeyboardNavigation = <T extends object>() => {
return keyboardNavProps;
};
const useAtom = <T,>(initialValue: T) => {
return useState(() => atom<T>(initialValue))[0];
};
const useComputed = <Value, OriginStores extends AnyStore[]>(
stores: [...OriginStores],
cb: (...values: StoreValues<OriginStores>) => Task<Value> | Value
) => {
return useState(() => computed(stores, cb))[0];
};
const countOptions = <T extends object>(optionsOrGroups: OptionOrGroup<T>[]) => {
let count = 0;
for (const optionOrGroup of optionsOrGroups) {
@@ -515,18 +503,20 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
const rootRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const { $groupStatusMap, $areAllGroupsDisabled, toggleGroup } = useTogglableGroups(optionsOrGroups);
const $activeOptionId = useAtom(getFirstOptionId(optionsOrGroups, getOptionId));
const $compactView = useAtom(true);
const $optionsOrGroups = useAtom(optionsOrGroups);
const $totalOptionCount = useComputed([$optionsOrGroups], countOptions);
const $filteredOptions = useAtom<OptionOrGroup<T>[]>([]);
const $flattenedFilteredOptions = useComputed([$filteredOptions], flattenOptions);
const $hasOptions = useComputed([$totalOptionCount], (count) => count > 0);
const $filteredOptionsCount = useComputed([$flattenedFilteredOptions], (options) => options.length);
const $hasFilteredOptions = useComputed([$filteredOptionsCount], (count) => count > 0);
const $selectedItem = useAtom<T | undefined>(undefined);
const $searchTerm = useAtom('');
const $selectedItemId = useComputed([$selectedItem], (item) => (item ? getOptionId(item) : undefined));
const $activeOptionId = useState(() => atom(getFirstOptionId(optionsOrGroups, getOptionId)))[0];
const $compactView = useState(() => atom(true))[0];
const $optionsOrGroups = useState(() => atom(optionsOrGroups))[0];
const $totalOptionCount = useState(() => computed([$optionsOrGroups], countOptions))[0];
const $filteredOptions = useState(() => atom<OptionOrGroup<T>[]>([]))[0];
const $flattenedFilteredOptions = useState(() => computed([$filteredOptions], flattenOptions))[0];
const $hasOptions = useState(() => computed([$totalOptionCount], (count) => count > 0))[0];
const $filteredOptionsCount = useState(() => computed([$flattenedFilteredOptions], (options) => options.length))[0];
const $hasFilteredOptions = useState(() => computed([$filteredOptionsCount], (count) => count > 0))[0];
const $selectedItem = useState(() => atom<T | undefined>(undefined))[0];
const $searchTerm = useState(() => atom(''))[0];
const $selectedItemId = useState(() =>
computed([$selectedItem], (item) => (item ? getOptionId(item) : undefined))
)[0];
const onSelectById = useCallback(
(id: string) => {
@@ -809,15 +799,17 @@ SearchInput.displayName = 'SearchInput';
const GroupToggleButtons = typedMemo(<T extends object>() => {
const { $optionsOrGroups, $groupStatusMap, $areAllGroupsDisabled } = usePickerContext<T>();
const { t } = useTranslation();
const $groups = useComputed([$optionsOrGroups], (optionsOrGroups) => {
const _groups: Group<T>[] = [];
for (const optionOrGroup of optionsOrGroups) {
if (isGroup(optionOrGroup)) {
_groups.push(optionOrGroup);
const $groups = useState(() =>
computed([$optionsOrGroups], (optionsOrGroups) => {
const _groups: Group<T>[] = [];
for (const optionOrGroup of optionsOrGroups) {
if (isGroup(optionOrGroup)) {
_groups.push(optionOrGroup);
}
}
}
return _groups;
});
return _groups;
})
)[0];
const groups = useStore($groups);
const areAllGroupsDisabled = useStore($areAllGroupsDisabled);

View File

@@ -11,7 +11,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"moduleResolution": "bundler",
// TODO: Disabled for IDE performance issues with our translation JSON
// "resolveJsonModule": true,
"noUncheckedIndexedAccess": true,

View File

@@ -1,8 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.mts"]

View File

@@ -1,8 +1,7 @@
/// <reference types="vitest" />
import react from '@vitejs/plugin-react-swc';
import path from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import type { PluginOption } from 'vite';
import Sonda from 'sonda/vite';
import { defineConfig } from 'vite';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import dts from 'vite-plugin-dts';
@@ -17,7 +16,6 @@ export default defineConfig(({ mode }) => {
react(),
eslint(),
tsconfigPaths(),
visualizer() as unknown as PluginOption,
dts({
insertTypesEntry: true,
}),
@@ -70,9 +68,10 @@ export default defineConfig(({ mode }) => {
react(),
mode !== 'test' && eslint({ failOnError: mode === 'production', failOnWarning: mode === 'production' }),
tsconfigPaths(),
visualizer() as unknown as PluginOption,
Sonda(),
],
build: {
sourcemap: true,
chunkSizeWarningLimit: 1500,
},
server: {