Upgrade all Javascript dependencies

Changes which were needed after the upgrade:

- Replace vite-plugin-virtual (vite 8 incompatible) with vite's
  built-in `define` for the buildTimeConfig injection

- tsconfig: drop deprecated `baseUrl` (TS 5+ resolves `paths`
  relative to tsconfig)

- Vitest 4: import `defineConfig` from `vitest/config` so `test`
  is valid in vite configs

- Zod 4: rename `.errors` to `.issues`, make `ZodSchema` a
  type-only import, drop the removed `SafeParseReturnType`

- Tighten/loosen generic casts in localStore to match stricter
  zod 4 inference

- ESLint 9 `no-useless-assignment`: remove redundant initializers
  and dead reassignments; disable the rule where Svelte's reactive
  blocks confuse the analyser

- Svelte/eslint `prefer-svelte-reactivity`: disable for legacy
  Svelte usages of URL / Set

- Misc naming-convention and prefer-const fixes
This commit is contained in:
Rūdolfs Ošiņš
2026-04-22 17:48:45 +02:00
parent 9ca1d7973e
commit b2f74ebd15
24 changed files with 1958 additions and 2110 deletions
Vendored
+2 -2
View File
@@ -1,9 +1,9 @@
import type { Config } from "./module.d.ts";
declare global {
// eslint-disable-next-line no-var, @typescript-eslint/naming-convention
// eslint-disable-next-line @typescript-eslint/naming-convention
var __CONFIG__: Config;
// eslint-disable-next-line no-var, @typescript-eslint/naming-convention
// eslint-disable-next-line @typescript-eslint/naming-convention
var __PLAYWRIGHT__: boolean;
}
+1 -1
View File
@@ -144,7 +144,7 @@ export class Fetcher {
params.method,
responseBody,
info.version,
result.error.errors,
result.error.issues,
params.path,
);
}
+1 -1
View File
@@ -2,6 +2,7 @@ import type { Fetcher, RequestOptions } from "./fetcher.js";
import type { Commit, Commits } from "./repo/commit.js";
import type { Issue } from "./repo/issue.js";
import type { Patch } from "./repo/patch.js";
import type { ZodSchema } from "zod";
import {
array,
@@ -14,7 +15,6 @@ import {
string,
union,
z,
ZodSchema,
} from "zod";
import {
+2 -2
View File
@@ -194,7 +194,7 @@ type Diff = z.infer<typeof diffSchema>;
type ChangesetWithDiff = z.infer<typeof changesetWithDiffSchema>;
type ChangesetWithoutDiff = z.infer<typeof changesetWithoutDiffSchema>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-useless-assignment
const changesetWithDiffSchema = union([
diffAddedChangesetSchema,
diffDeletedChangesetSchema,
@@ -202,7 +202,7 @@ const changesetWithDiffSchema = union([
diffMovedWithModificationsChangesetSchema,
diffCopiedWithModificationsChangesetSchema,
]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-useless-assignment
const changesetWithoutDiffSchema = union([
diffMovedChangesetSchema,
diffCopiedChangesetSchema,
+4 -7
View File
@@ -1,14 +1,11 @@
import nodeConfig from "config";
import path from "node:path";
import virtual from "vite-plugin-virtual";
import { defineConfig } from "vite";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
virtual({
"virtual:config": nodeConfig.util.toObject(),
}),
],
define: {
buildTimeConfig: JSON.stringify(nodeConfig.util.toObject()),
},
test: {
environment: "node",
include: ["http-client/tests/*.test.ts"],
Vendored
+15 -19
View File
@@ -1,20 +1,16 @@
declare module "virtual:*" {
const config: {
nodes: {
requiredApiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultLocalHttpdPort: number;
defaultHttpdScheme: string;
};
source: {
commitsPerPage: number;
};
deploymentId: string | null;
reactions: string[];
supportWebsite: string;
preferredSeeds: BaseUrl[];
declare const buildTimeConfig: {
nodes: {
requiredApiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultLocalHttpdPort: number;
defaultHttpdScheme: string;
};
export default config;
}
source: {
commitsPerPage: number;
};
deploymentId: string | null;
reactions: string[];
supportWebsite: string;
preferredSeeds: BaseUrl[];
};
+1807 -1974
View File
File diff suppressed because it is too large Load Diff
+38 -40
View File
@@ -25,60 +25,58 @@
"node": ">=24"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
"@playwright/test": "1.59.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tsconfig/svelte": "^5.0.4",
"@types/config": "^3.3.5",
"@types/katex": "^0.16.7",
"@types/lodash": "^4.17.17",
"@types/md5": "^2.3.5",
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.59.1",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tsconfig/svelte": "^5.0.8",
"@types/katex": "^0.16.8",
"@types/lodash": "^4.17.24",
"@types/md5": "^2.3.6",
"@types/node": "^24",
"@types/wait-on": "^5.3.4",
"@typescript-eslint/parser": "^8.32.1",
"chalk": "^5.4.1",
"config": "^4.0.0",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.0",
"execa": "^9.5.3",
"get-port": "^7.1.0",
"globals": "^16.1.0",
"happy-dom": "^17.4.7",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"rimraf": "^6.1.2",
"svelte-check": "^4.2.1",
"svelte-eslint-parser": "^1.2.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.32.1",
"vite": "^6.3.5",
"vite-plugin-virtual": "^0.4.0",
"vitest": "^3.1.4",
"wait-on": "^8.0.3"
"@typescript-eslint/parser": "^8.59.0",
"chalk": "^5.6.2",
"config": "^4.4.1",
"eslint": "^10.2.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.17.1",
"execa": "^9.6.1",
"get-port": "^7.2.0",
"globals": "^17.5.0",
"happy-dom": "^20.9.0",
"prettier": "^3.8.3",
"prettier-plugin-svelte": "^3.5.1",
"rimraf": "^6.1.3",
"svelte-check": "^4.4.6",
"svelte-eslint-parser": "^1.6.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.0",
"vite": "^8.0.9",
"vitest": "^4.1.5",
"wait-on": "^9.0.5"
},
"dependencies": {
"@radicle/gray-matter": "4.1.0",
"@wooorm/starry-night": "^3.7.0",
"@wooorm/starry-night": "^3.9.0",
"async-mutex": "^0.5.0",
"baconjs": "^3.0.23",
"bs58": "^6.0.0",
"buffer": "^6.0.3",
"compare-versions": "^6.1.1",
"dompurify": "^3.2.6",
"dompurify": "^3.4.1",
"fuzzysort": "^3.1.0",
"hast-util-to-dom": "^4.0.1",
"hast-util-to-html": "^9.0.5",
"lodash": "^4.17.21",
"lru-cache": "^11.1.0",
"marked": "^15.0.12",
"marked-emoji": "^2.0.0",
"marked-footnote": "^1.2.4",
"marked-katex-extension": "^5.1.4",
"marked-linkify-it": "^3.1.12",
"lodash": "^4.18.1",
"lru-cache": "^11.3.5",
"marked": "^18.0.2",
"marked-emoji": "^2.0.3",
"marked-footnote": "^1.4.0",
"marked-katex-extension": "^5.1.8",
"marked-linkify-it": "^3.1.15",
"md5": "^2.3.0",
"svelte": "^5.30.0",
"svelte": "^5.55.4",
"twemoji": "^14.0.2",
"zod": "^3.25.16"
"zod": "^4.3.6"
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
type Rect = { x: number; y: number; opacity: number };
let rects: Rect[] = $state([]);
const heightWithPadding = viewBoxHeight + 16;
const heightWithPadding = $derived.by(() => viewBoxHeight + 16);
let cellSize: number = $state(0);
let rows: number = 0;
+1
View File
@@ -114,6 +114,7 @@
// If the anchor is an oid embed
if (anchorHref && isCommit(anchorHref)) {
// eslint-disable-next-line svelte/prefer-svelte-reactivity
const url = new URL(rawPath);
// deprecated with httpd 0.18.1
// For older httpd versions we still pass the mime type.
+1
View File
@@ -17,6 +17,7 @@
import { onDestroy } from "svelte";
let observer: IntersectionObserver | undefined = undefined;
// eslint-disable-next-line svelte/prefer-svelte-reactivity
let filesVisibility = new Set<string>();
if ("IntersectionObserver" in window) {
+1 -1
View File
@@ -828,7 +828,7 @@ function renderUserAvatar(key: string): string {
const sectorIdx = Math.floor(a / ((2 * Math.PI) / petals));
if (!sectorGate(a, petals, sectorMask)) continue;
let inside = false;
let inside: boolean;
if (shapeModel === "rose") inside = shapeRose(a, petals, tol);
else if (shapeModel === "starburst")
inside = shapeStarburst(a, petals, softness);
-1
View File
@@ -1,4 +1,3 @@
import buildTimeConfig from "virtual:config";
import mergeWith from "lodash/mergeWith";
import { isArray } from "lodash";
+8 -9
View File
@@ -1,4 +1,3 @@
import type { SafeParseReturnType } from "zod";
import type { Writable } from "svelte/store";
import { writable, get } from "svelte/store";
@@ -32,9 +31,7 @@ export default function storedWritable<
> & { clear: () => void } {
const stored = !disableLocalStorage ? localStorage.getItem(key) : null;
const parseFromJson = (
content: string,
): SafeParseReturnType<string, T["_output"]> => {
const parseFromJson = (content: string) => {
return z
.string()
.transform((_, ctx) => {
@@ -57,17 +54,19 @@ export default function storedWritable<
window?.addEventListener("storage", event => {
if (event.key === key) {
if (event.newValue === null) {
w.set(initialValue);
w.set(initialValue as S);
return;
}
const { success, data } = parseFromJson(event.newValue);
w.set(success ? data : initialValue);
w.set(success ? (data as S) : (initialValue as S));
}
});
}
const parsed = parseFromJson(stored || initialValue);
const w = writable<S>(parsed?.success ? parsed.data : initialValue);
const parsed = stored ? parseFromJson(stored) : undefined;
const w = writable<S>(
parsed?.success ? (parsed.data as S) : (initialValue as S),
);
/**
* Set writable value and inform subscribers. Updates the writeable's stored data in
@@ -91,7 +90,7 @@ export default function storedWritable<
* Delete any data saved for this StoredWritable in localstorage.
*/
function clear() {
w.set(initialValue);
w.set(initialValue as S);
if (!disableLocalStorage) localStorage.removeItem(key);
}
-1
View File
@@ -179,7 +179,6 @@ export function lineNumbersGutter(tree: Root) {
// Prepend text from a partial matched earlier text.
if (startTextRemainder) {
line.unshift({ type: "text", value: startTextRemainder });
startTextRemainder = "";
}
if (line.length > 0) {
+1 -1
View File
@@ -17,7 +17,7 @@ export function parseNodeId(
): { prefix: string; pubkey: string } | undefined {
const match = /^(did:key:)?(z[a-zA-Z0-9]+)$/.exec(nid);
if (match) {
let hex: Uint8Array | undefined = undefined;
let hex: Uint8Array;
try {
hex = bs58.decode(match[2].substring(1));
} catch (error) {
+23 -27
View File
@@ -13,33 +13,29 @@
sheet.href === null || sheet.href.startsWith(window.location.origin),
)
.reduce<string[]>(
(acc, sheet) =>
(acc = [
...acc,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...Array.from(sheet.cssRules).reduce(
(def, rule) =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
(def =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
rule.selectorText === ":root"
? [
...def,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...Array.from(rule.style).filter(name =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
name.startsWith(variableName),
),
]
: def),
[],
),
]),
(acc, sheet) => [
...acc,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...Array.from(sheet.cssRules).reduce(
(def, rule) =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
rule.selectorText === ":root"
? [
...def,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...Array.from(rule.style).filter(name =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
name.startsWith(variableName),
),
]
: def,
[],
),
],
[],
);
}
+5 -6
View File
@@ -146,12 +146,11 @@
return patchReviews;
}
let revisionId: string;
$: if (view.name === "diff") {
revisionId = patch.revisions[patch.revisions.length - 1].id;
} else {
revisionId = view.revision;
}
// eslint-disable-next-line no-useless-assignment
$: revisionId =
view.name === "diff"
? patch.revisions[patch.revisions.length - 1].id
: view.revision;
$: uniqueEmbeds = uniqBy(
patch.revisions.flatMap(({ discussions }) =>
+2 -4
View File
@@ -50,8 +50,7 @@
$: fetchCommitCount(repo.rid, commit);
let selectedBranch: string | undefined;
let commitButtonVariant: ComponentProps<CommitButton>["variant"] | undefined =
undefined;
let commitButtonVariant: ComponentProps<CommitButton>["variant"] | undefined;
// Revision may be a commit ID, a branch name or `undefined` which means the
// default branch. We assign `selectedBranch` accordingly.
@@ -65,8 +64,7 @@
$: lastCommit = tree.lastCommit;
$: onCanonical = Boolean(
!peer &&
selectedBranch ===
repo.payloads["xyz.radicle.project"].data.defaultBranch,
selectedBranch === repo.payloads["xyz.radicle.project"].data.defaultBranch,
);
$: if (onCanonical) {
commitButtonVariant = "right";
@@ -174,6 +174,7 @@
} else if (!selectedBranch) {
selectedTab = "branches";
}
// eslint-disable-next-line no-useless-assignment
lastSelectedBranch = selectedBranch;
}
}
+6 -6
View File
@@ -1,12 +1,12 @@
// Test adapter for configuration in Node.js environment (Playwright tests).
//
// The app uses "virtual:config" (defined in vite.config.ts via
// vite-plugin-virtual), which is populated at build time with
// config.util.toObject() from the "config" npm package. Since Vite's virtual
// module system doesn't work in Node.js, this file provides the same
// configuration data by directly importing and converting the "config" package.
// The app reads `buildTimeConfig`, which Vite replaces at build time (via
// the `define` option) with config.util.toObject() from the "config" npm
// package. Since Vite's `define` replacement doesn't run in Node.js, this file
// provides the same configuration data by directly importing and converting
// the "config" package.
import nodeConfig from "config";
const config = nodeConfig.util.toObject();
const config = nodeConfig.util.toObject() as typeof buildTimeConfig;
export default config;
-1
View File
@@ -8,7 +8,6 @@
"module": "es2022",
"types": ["vite/client"],
"sourceMap": true,
"baseUrl": "./",
"moduleResolution": "bundler",
"strict": true,
"resolveJsonModule": true,
+37 -5
View File
@@ -1,19 +1,18 @@
import config from "config";
import path from "node:path";
import virtual from "vite-plugin-virtual";
import { defineConfig } from "vite";
import { defineConfig } from "vitest/config";
import { svelte } from "@sveltejs/vite-plugin-svelte";
export default defineConfig({
define: {
buildTimeConfig: JSON.stringify(config.util.toObject()),
},
test: {
environment: "happy-dom",
include: ["tests/unit/**/*.test.ts"],
reporters: "verbose",
},
plugins: [
virtual({
"virtual:config": config.util.toObject(),
}),
svelte({
// Reference: https://github.com/sveltejs/vite-plugin-svelte/issues/270#issuecomment-1033190138
dynamicCompileOptions({ filename }) {
@@ -54,6 +53,39 @@ export default defineConfig({
},
},
],
optimizeDeps: {
include: [
"@wooorm/starry-night",
"@wooorm/starry-night/etc",
"@wooorm/starry-night/go.mod",
"@wooorm/starry-night/go.sum",
"@wooorm/starry-night/source.batchfile",
"@wooorm/starry-night/source.dockerfile",
"@wooorm/starry-night/source.dotenv",
"@wooorm/starry-night/source.editorconfig",
"@wooorm/starry-night/source.erlang",
"@wooorm/starry-night/source.git-revlist",
"@wooorm/starry-night/source.gitattributes",
"@wooorm/starry-night/source.gitconfig",
"@wooorm/starry-night/source.gitignore",
"@wooorm/starry-night/source.groovy.gradle",
"@wooorm/starry-night/source.haproxy-config",
"@wooorm/starry-night/source.ini.npmrc",
"@wooorm/starry-night/source.json",
"@wooorm/starry-night/source.nix",
"@wooorm/starry-night/source.sass",
"@wooorm/starry-night/source.solidity",
"@wooorm/starry-night/source.svelte",
"@wooorm/starry-night/source.toml",
"@wooorm/starry-night/source.tsx",
"@wooorm/starry-night/source.zig",
"@wooorm/starry-night/text.html.asciidoc",
"@wooorm/starry-night/text.html.django",
"@wooorm/starry-night/text.html.vue",
"@wooorm/starry-night/text.robots-txt",
"@wooorm/starry-night/text.zone_file",
],
},
server: {
host: "localhost",
port: 3000,
+1 -1
View File
@@ -704,7 +704,7 @@ function userAvatarSvg(key, size = 64) {
const tol = angleTolNear * (1 - t) + angleTolFar * t;
const sectorIdx = Math.floor(a / ((2 * Math.PI) / petals));
if (!_sectorGate(a, petals, sectorMask)) continue;
let inside = false;
let inside;
if (shapeModel === "rose") inside = _shapeRose(a, petals, tol);
else if (shapeModel === "starburst")
inside = _shapeStarburst(a, petals, softness);