Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ updates:
interval: "daily"
cooldown:
default-days: 1 # zizmor: ignore[dependabot-cooldown] — fast cooldown is intentional
groups:
vite-plus-and-vitest:
patterns:
- "vite"
- "vite-plus"
- "@voidzero-dev/vite-plus-*"
- "vitest"
- "@vitest/*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand Down
11 changes: 4 additions & 7 deletions apps/web/app/benchmarks/components/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,29 @@ export function TrendChart({
const searchParams = useSearchParams();
const svgRef = useRef<SVGSVGElement>(null);
const [hiddenSeries, setHiddenSeries] = useState<Set<string>>(() => new Set());
const [showIndividualRuns, setShowIndividualRuns] = useState(true);
const [tooltip, setTooltip] = useState<{
x: number;
y: number;
content: string;
pointId: string;
} | null>(null);
const hasTrend = series.some((item) => hasRollingMedian(item.values, TREND_WINDOW));
const requestedVisibility = resolveIndividualRunsVisibilityFromSearch(searchParams.toString());
const showIndividualRuns = hasTrend ? requestedVisibility : true;

useEffect(() => {
const requestedVisibility = resolveIndividualRunsVisibilityFromSearch(window.location.search);
setShowIndividualRuns(hasTrend ? requestedVisibility : true);

if (!hasTrend && !requestedVisibility) {
router.replace(
individualRunsVisibilityUrl(
pathname,
new URLSearchParams(window.location.search),
new URLSearchParams(searchParams.toString()),
true,
window.location.hash,
),
{ scroll: false },
);
}
}, [hasTrend, pathname, router, searchParams]);
}, [hasTrend, pathname, requestedVisibility, router, searchParams]);

// Collect all non-null values to determine y-axis bounds
const allValues = series.flatMap((s) => s.values.filter((v): v is number => v !== null));
Expand Down Expand Up @@ -319,7 +317,6 @@ export function TrendChart({
onClick={() => {
setTooltip(null);
const visible = !showIndividualRuns;
setShowIndividualRuns(visible);
router.replace(
individualRunsVisibilityUrl(
pathname,
Expand Down
6 changes: 4 additions & 2 deletions apps/web/app/benchmarks/components/performance-comparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ export function FlameGraph({
ariaLabel: string;
}) {
const fullGraph = flameGraph;
const [previousGraph, setPreviousGraph] = useState(fullGraph);
const [categoryFilters, setCategoryFilters] = useState<Set<TraceCategory> | null>(
defaultTraceFilters,
);
Expand All @@ -293,14 +294,15 @@ export function FlameGraph({
const rowHeight = 24;
const height = (maxDepth + 1) * rowHeight;

useEffect(() => {
if (previousGraph !== fullGraph) {
setPreviousGraph(fullGraph);
const nextFilters = defaultTraceFilters();
const nextRoot = graphForFilters(fullGraph, nextFilters);
setCategoryFilters(nextFilters);
setFocusPath(nextRoot ? [nextRoot] : []);
setFrameQuery("");
setHovered(null);
}, [fullGraph]);
}

useEffect(() => {
const viewport = graphViewportRef.current;
Expand Down
10 changes: 2 additions & 8 deletions apps/web/app/benchmarks/components/performance-results.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import type { CSSProperties, ReactNode } from "react";
import { useEffect, useMemo, useState } from "react";
import { useMemo } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Badge } from "@cloudflare/kumo/components/badge";
import { Tabs } from "@cloudflare/kumo/components/tabs";
Expand Down Expand Up @@ -207,12 +207,7 @@ export function PerformanceTrends({ runs }: { runs: PerformanceRun[] }) {
[latest],
);
const scenarioIds = useMemo(() => scenarios.map((scenario) => scenario.scenarioId), [scenarios]);
const [activeScenario, setActiveScenario] = useState(scenarioIds[0] ?? "");

useEffect(() => {
const selected = resolveSelectedBenchmarkFromSearch(scenarioIds, window.location.search);
if (selected) setActiveScenario(selected);
}, [scenarioIds, searchParams]);
const activeScenario = resolveSelectedBenchmarkFromSearch(scenarioIds, searchParams.toString());

const selectedScenario =
scenarios.find((scenario) => scenario.scenarioId === activeScenario) ?? scenarios[0];
Expand All @@ -229,7 +224,6 @@ export function PerformanceTrends({ runs }: { runs: PerformanceRun[] }) {
}))}
value={selectedScenario.scenarioId}
onValueChange={(benchmarkId) => {
setActiveScenario(benchmarkId);
router.replace(
benchmarkSelectionUrl(
pathname,
Expand Down
4 changes: 3 additions & 1 deletion packages/cloudflare/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export default defineConfig({
entry: ["src/**/*.ts", "src/**/*.tsx", "!src/**/*.d.ts"],
clean: true,
deps: {
skipNodeModulesBundle: true,
resolveDepSubpath: true,
neverBundle: true,
},
dts: {
generator: "tsgo",
tsgo: { path: getTscPath() },
},
fixedExtension: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/create-vinext-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export default defineConfig({
entry: ["src/**/*.ts", "!src/**/*.d.ts"],
clean: true,
deps: {
resolveDepSubpath: true,
alwaysBundle: bundledDeps,
neverBundle: (id) =>
id.includes("node_modules") && !bundledDeps.some((dep) => id.includes(dep)),
},
dts: {
generator: "tsgo",
tsgo: { path: getTscPath() },
},
fixedExtension: false,
Expand Down
10 changes: 6 additions & 4 deletions packages/vinext/src/client/dev-error-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,13 @@ function DevErrorOverlay({
: [],
[error.stack, error.ignoredStackFrames, error.projectRoot, isBuildError],
);
const [previousErrorId, setPreviousErrorId] = useState(error.id);
const [showIgnoredFrames, setShowIgnoredFrames] = useState(false);
// Reset only frame visibility so pagination buttons retain their DOM nodes and focus.
if (previousErrorId !== error.id) {
setPreviousErrorId(error.id);
setShowIgnoredFrames(false);
}
const hasVisibleFrame = frames.some((frame) => !frame.ignored);
const ignoredFramesTally = hasVisibleFrame
? frames.reduce((tally, frame) => tally + (frame.ignored ? 1 : 0), 0)
Expand All @@ -511,10 +517,6 @@ function DevErrorOverlay({
? frames
: frames.filter((frame) => !frame.ignored);

useEffect(() => {
setShowIgnoredFrames(false);
}, [error.id]);

// Esc minimizes, ←/→ navigate between errors. Esc no longer dismisses
// outright — once a developer wants the overlay gone they can hit the ×
// button. Listener is attached on the window so it works regardless of
Expand Down
1 change: 1 addition & 0 deletions packages/vinext/src/server/app-browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,7 @@ function BrowserRoot({
// avoid a stale-read window between commit and layout effects. This mirrors
// the same render-phase ref update pattern used by Next.js's own router.
const stateRef = useRef(treeState);
// oxlint-disable-next-line react/refs -- navigation from child layout effects needs this render's state
stateRef.current = treeState;

// Publish the stable ref object and dispatch during layout commit. This keeps
Expand Down
4 changes: 2 additions & 2 deletions packages/vinext/src/shims/document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function Html(
return <html {...props} />;
}

// oxlint-disable-next-line typescript/consistent-type-definitions, typescript/no-unsafe-declaration-merging -- type-only class augmentation avoids emitting a Babel-incompatible declare field
// oxlint-disable-next-line no-redeclare, typescript/consistent-type-definitions, typescript/no-unsafe-declaration-merging -- type-only class augmentation avoids emitting a Babel-incompatible declare field
export interface Head {
context: HtmlProps;
}
Expand Down Expand Up @@ -102,7 +102,7 @@ export function Main(): React.ReactElement {
return <div id="__next" dangerouslySetInnerHTML={{ __html: "__NEXT_MAIN__" }} />;
}

// oxlint-disable-next-line typescript/consistent-type-definitions, typescript/no-unsafe-declaration-merging -- type-only class augmentation avoids emitting a Babel-incompatible declare field
// oxlint-disable-next-line no-redeclare, typescript/consistent-type-definitions, typescript/no-unsafe-declaration-merging -- type-only class augmentation avoids emitting a Babel-incompatible declare field
export interface NextScript {
context: HtmlProps;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/vinext/src/shims/dynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ function dynamic<P = {}>(
loader,
InitialLazyComponent,
);
// Keep the first hydration render equal to the server's loading fallback.
// oxlint-disable-next-line react/set-state-in-effect -- client-only loading starts after mount
React.useEffect(() => setMounted(true), []);

if (!mounted) {
Expand Down
4 changes: 3 additions & 1 deletion packages/vinext/src/shims/layout-segment-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ export function LayoutSegmentProvider({
}) {
const previousSegmentMap = useRef<SegmentMap | null>(null);
const ctx = getLayoutSegmentContext();
// oxlint-disable-next-line react/refs -- merge against the last committed segment map
const committedSegmentMap = previousSegmentMap.current;
const previousSegmentMapForProvider =
previousSegmentMap.current ??
committedSegmentMap ??
(providerId ? (committedSegmentMapsByProviderId.get(providerId) ?? null) : null);
const mergedSegmentMap = mergeLayoutSegmentMap(previousSegmentMapForProvider, segmentMap);
useEffect(() => {
Expand Down
33 changes: 17 additions & 16 deletions packages/vinext/src/shims/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1173,12 +1173,9 @@ const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
const pendingPagesIntentPrefetchRef = useRef<(() => void) | null>(null);
// Stable setter so the global navigation registry can reset this link's
// pending state from another navigation without depending on render identity.
const setPendingRef = useRef<PendingLinkSetter | null>(null);
if (setPendingRef.current === null) {
setPendingRef.current = (next: boolean) => {
if (mountedRef.current) setPending(next);
};
}
const setPendingRef = useRef<PendingLinkSetter>((next: boolean) => {
if (mountedRef.current) setPending(next);
});
useEffect(() => {
mountedRef.current = true;
const setter = setPendingRef.current;
Expand Down Expand Up @@ -1557,15 +1554,18 @@ const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
(childRef as React.MutableRefObject<HTMLAnchorElement | null>).current = node;
}
};
// Next.js also clones the legacy child and merges its ref during render.
// oxlint-disable-next-line react/refs -- preserve the Next.js legacy Link contract
const dangerousChild = React.cloneElement(child, {
ref: setDangerousRefs,
onClick: (event: MouseEvent<HTMLAnchorElement>) => {
if (childOnClick) childOnClick(event);
reportBlockedDangerousNavigation();
},
});
return (
<LinkStatusContext.Provider value={linkStatusValue}>
{React.cloneElement(child, {
ref: setDangerousRefs,
onClick: (event: MouseEvent<HTMLAnchorElement>) => {
if (childOnClick) childOnClick(event);
reportBlockedDangerousNavigation();
},
})}
{dangerousChild}
</LinkStatusContext.Provider>
);
}
Expand Down Expand Up @@ -1657,10 +1657,11 @@ const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
if (shouldForwardHref) {
clonedProps.href = fullHref;
}
// Next.js also clones the legacy child and merges its ref during render.
// oxlint-disable-next-line react/refs -- preserve the Next.js legacy Link contract
const clonedChild = React.cloneElement(child, clonedProps);
return (
<LinkStatusContext.Provider value={linkStatusValue}>
{React.cloneElement(child, clonedProps)}
</LinkStatusContext.Provider>
<LinkStatusContext.Provider value={linkStatusValue}>{clonedChild}</LinkStatusContext.Provider>
);
}

Expand Down
10 changes: 5 additions & 5 deletions packages/vinext/src/shims/slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,11 @@ function useBfcacheSlotEntries(activeEntry: BfcacheSlotEntry): BfcacheSlotEntry[
const snapshotsByStateKey = React.useRef(new Map<string, BfcacheSlotEntry>());
const [entryOrder, setEntryOrder] = React.useState<string[]>(() => [activeEntry.stateKey]);

const staged = stageBfcacheSlotEntryForRender(
snapshotsByStateKey.current,
entryOrder,
activeEntry,
);
// The cache contains only committed snapshots. Reading it here lets a new
// Activity render preserve entries without publishing speculative writes.
// oxlint-disable-next-line react/refs -- this read is the committed input to a staged render
const committedSnapshots = snapshotsByStateKey.current;
const staged = stageBfcacheSlotEntryForRender(committedSnapshots, entryOrder, activeEntry);
const nextOrder = staged.order;
const orderChanged = !haveSameBfcacheSlotEntryOrder(entryOrder, nextOrder);

Expand Down
2 changes: 2 additions & 0 deletions packages/vinext/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default defineConfig({
entry: ["src/**/*.ts", "src/**/*.tsx", "!src/**/*.d.ts"],
clean: true,
deps: {
resolveDepSubpath: true,
// Agent detection and image dimension extraction are build-time
// implementation details, so inline them rather than requiring vinext
// consumers to install them. Same for pathslash: it is our own ~90-line
Expand All @@ -98,6 +99,7 @@ export default defineConfig({
chunkFileNames: renameBundledDepsOutput,
},
dts: {
generator: "tsgo",
tsgo: { path: getTscPath() },
},
copy: [
Expand Down
2 changes: 2 additions & 0 deletions packages/workers-response-store/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export default defineConfig({
entry: ["src/**/*.ts"],
clean: true,
deps: {
resolveDepSubpath: true,
neverBundle: true,
},
dts: {
generator: "tsgo",
tsgo: { path: getTscPath() },
},
fixedExtension: false,
Expand Down
Loading
Loading