@mui/internal-docs-infra 0.11.1-canary.13 → 0.11.1-canary.14
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/CodeHighlighter/CodeHighlighter.mjs +47 -4
- package/CodeHighlighter/CodeHighlighterClient.mjs +128 -11
- package/CodeHighlighter/CodeHighlighterContext.d.mts +8 -1
- package/CodeHighlighter/CodeHighlighterFallbackContext.d.mts +14 -2
- package/CodeHighlighter/CodeHighlighterFallbackContext.mjs +1 -3
- package/CodeHighlighter/codeToFallbackProps.d.mts +28 -2
- package/CodeHighlighter/codeToFallbackProps.mjs +253 -52
- package/CodeHighlighter/errors.d.mts +3 -0
- package/CodeHighlighter/errors.mjs +5 -0
- package/CodeHighlighter/fallbackCompression.d.mts +79 -0
- package/CodeHighlighter/fallbackCompression.mjs +230 -0
- package/CodeHighlighter/fallbackFormat.d.mts +93 -0
- package/CodeHighlighter/fallbackFormat.mjs +323 -0
- package/CodeHighlighter/index.d.mts +2 -0
- package/CodeHighlighter/index.mjs +1 -0
- package/CodeHighlighter/types.d.mts +48 -18
- package/CodeHighlighter/useCodeFallback.d.mts +51 -0
- package/CodeHighlighter/useCodeFallback.mjs +132 -0
- package/abstractCreateTypes/typesToJsx.mjs +18 -2
- package/cli/runBrowser.mjs +1 -1
- package/package.json +2 -2
- package/pipeline/hastUtils/frameFallbackFromSpans.d.mts +18 -0
- package/pipeline/hastUtils/frameFallbackFromSpans.mjs +24 -0
- package/pipeline/hastUtils/hastUtils.d.mts +4 -3
- package/pipeline/hastUtils/hastUtils.mjs +18 -6
- package/pipeline/hastUtils/index.d.mts +2 -1
- package/pipeline/hastUtils/index.mjs +2 -1
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.d.mts +5 -4
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.mjs +56 -9
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.d.mts +9 -1
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs +45 -4
- package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +1 -1
- package/pipeline/loadIsomorphicCodeVariant/diffHast.mjs +89 -0
- package/pipeline/loadIsomorphicCodeVariant/flattenCodeVariant.mjs +4 -2
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +2 -7
- package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +72 -3
- package/pipeline/loadServerTypes/hastTypeUtils.d.mts +2 -2
- package/pipeline/loadServerTypes/hastTypeUtils.mjs +4 -4
- package/pipeline/parseSource/addLineGutters.mjs +18 -16
- package/pipeline/parseSource/frameVisibility.d.mts +6 -0
- package/pipeline/parseSource/frameVisibility.mjs +20 -0
- package/pipeline/parseSource/redistributeFrameFallbacks.d.mts +40 -0
- package/pipeline/parseSource/redistributeFrameFallbacks.mjs +138 -0
- package/pipeline/parseSource/restructureFrames.d.mts +5 -0
- package/pipeline/parseSource/restructureFrames.mjs +75 -2
- package/useCode/Pre.browser.mjs +1 -1
- package/useCode/Pre.d.mts +3 -0
- package/useCode/Pre.mjs +18 -9
- package/useCode/sourceLineCounts.d.mts +2 -1
- package/useCode/sourceLineCounts.mjs +8 -8
- package/useCode/useCode.mjs +5 -0
- package/useCode/useCodeUtils.d.mts +4 -3
- package/useCode/useCodeUtils.mjs +10 -5
- package/useCode/useCopyFunctionality.d.mts +13 -1
- package/useCode/useCopyFunctionality.mjs +36 -8
- package/useCode/useEditable.mjs +85 -21
- package/useCode/useFileNavigation.d.mts +9 -1
- package/useCode/useFileNavigation.mjs +53 -6
- package/useCode/useSourceEditing.mjs +13 -5
- package/useCode/useSourceEnhancing.d.mts +5 -1
- package/useCode/useSourceEnhancing.mjs +16 -7
- package/useCode/useTransformManagement.mjs +7 -5
- package/useCoordinated/index.d.mts +3 -1
- package/useCoordinated/index.mjs +3 -1
- package/useCoordinated/layoutShiftGate.d.mts +24 -0
- package/useCoordinated/layoutShiftGate.mjs +159 -0
- package/useCoordinated/useCoordinated.mjs +40 -1
- package/useCoordinated/useCoordinatedLazy.d.mts +17 -0
- package/useCoordinated/useCoordinatedLazy.mjs +38 -0
|
@@ -6,7 +6,8 @@ import { maybeCodeInitialData } from "../pipeline/loadIsomorphicCodeVariant/mayb
|
|
|
6
6
|
import { hasAllVariants } from "../pipeline/loadIsomorphicCodeVariant/hasAllCodeVariants.mjs";
|
|
7
7
|
import { getFileNameFromUrl, getLanguageFromExtension } from "../pipeline/loaderUtils/index.mjs";
|
|
8
8
|
import { replaceUrlPrefix } from "../pipeline/loaderUtils/applyUrlPrefix.mjs";
|
|
9
|
-
import { codeToFallbackProps } from "./codeToFallbackProps.mjs";
|
|
9
|
+
import { codeToFallbackProps, stripFallbackHastsFromCode } from "./codeToFallbackProps.mjs";
|
|
10
|
+
import { collapseRenderedFallbacks, compressResidualFallbacks, extractResidualFallbacks, mergeResidualFallbacks, residualDictionaryText } from "./fallbackCompression.mjs";
|
|
10
11
|
import * as Errors from "./errors.mjs";
|
|
11
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
13
|
const DEBUG = false; // Set to true for debugging purposes
|
|
@@ -45,6 +46,7 @@ function createClientProps(props) {
|
|
|
45
46
|
enhanceAfter: enhanceAfter || 'idle',
|
|
46
47
|
skipFallback: props.skipFallback,
|
|
47
48
|
controlled: props.controlled,
|
|
49
|
+
residualFallbacks: props.residualFallbacks,
|
|
48
50
|
name: props.name,
|
|
49
51
|
slug: props.slug,
|
|
50
52
|
// Use processedGlobalsCode if available, otherwise fall back to raw globalsCode
|
|
@@ -198,14 +200,44 @@ function renderWithInitialSource(props) {
|
|
|
198
200
|
code,
|
|
199
201
|
initialFilename,
|
|
200
202
|
fallbackUsesExtraFiles,
|
|
201
|
-
fallbackUsesAllVariants
|
|
203
|
+
fallbackUsesAllVariants,
|
|
204
|
+
fallbackCollapsed
|
|
202
205
|
} = props;
|
|
203
206
|
|
|
207
|
+
// Strip fallbackHast entries from Code — they move to ContentLoading props
|
|
208
|
+
// as source/extraSource instead of being serialized on Code.
|
|
209
|
+
const {
|
|
210
|
+
strippedCode,
|
|
211
|
+
allFallbackHasts
|
|
212
|
+
} = stripFallbackHastsFromCode(code, initialVariant, fallbackUsesExtraFiles, fallbackUsesAllVariants);
|
|
213
|
+
|
|
204
214
|
// Rewrite the top-level URL before it reaches the loading fallback so the
|
|
205
215
|
// browser never sees `file://` URLs. See `createClientProps` for the same
|
|
206
216
|
// rewrite on the regular client path.
|
|
207
217
|
const url = props.urlPrefix && props.url ? replaceUrlPrefix(props.url, props.urlPrefix) : props.url;
|
|
208
|
-
|
|
218
|
+
|
|
219
|
+
// `fallbackCollapsed` paints only each file's collapsed window in the loading
|
|
220
|
+
// UI; the full fallbacks defer into the blob. Otherwise the loading UI gets
|
|
221
|
+
// the full rendered subset, as usual.
|
|
222
|
+
const contentLoadingHasts = fallbackCollapsed ? collapseRenderedFallbacks(allFallbackHasts) : allFallbackHasts;
|
|
223
|
+
const fallbackProps = codeToFallbackProps(initialVariant, strippedCode, initialFilename, fallbackUsesExtraFiles, fallbackUsesAllVariants, contentLoadingHasts);
|
|
224
|
+
|
|
225
|
+
// Consolidate every fallback the loading UI won't render into a single DEFLATE
|
|
226
|
+
// blob, primed with the rendered (collapsed, when `fallbackCollapsed`) text so
|
|
227
|
+
// it dedupes against what's already on the client. That's everything still on
|
|
228
|
+
// `strippedCode` after hoisting — plus, when `fallbackCollapsed`, each
|
|
229
|
+
// rendered file's *full* fallback (the loading UI only painted its collapsed
|
|
230
|
+
// window, so the rest must travel here). The blob crosses once; `wireCode`
|
|
231
|
+
// carries no inline fallbacks, and the client scatters them back after the
|
|
232
|
+
// rendered text hoists. When there's nothing worth compressing, keep the
|
|
233
|
+
// plain inline fallbacks unchanged.
|
|
234
|
+
const {
|
|
235
|
+
wireCode,
|
|
236
|
+
residual
|
|
237
|
+
} = extractResidualFallbacks(strippedCode);
|
|
238
|
+
const fullResidual = fallbackCollapsed ? mergeResidualFallbacks(residual, allFallbackHasts) : residual;
|
|
239
|
+
const residualFallbacks = compressResidualFallbacks(fullResidual, residualDictionaryText(contentLoadingHasts));
|
|
240
|
+
const codeForClient = residualFallbacks ? wireCode : strippedCode;
|
|
209
241
|
|
|
210
242
|
// Get the component for the selected variant
|
|
211
243
|
const component = props.components?.[initialVariant];
|
|
@@ -221,7 +253,12 @@ function renderWithInitialSource(props) {
|
|
|
221
253
|
initialFilename,
|
|
222
254
|
initialVariant,
|
|
223
255
|
component,
|
|
224
|
-
components
|
|
256
|
+
components,
|
|
257
|
+
// Signals the ContentLoading that `source` is only the collapsed window,
|
|
258
|
+
// so it can disable any expand control until the full content swaps in.
|
|
259
|
+
...(fallbackCollapsed ? {
|
|
260
|
+
fallbackCollapsed: true
|
|
261
|
+
} : undefined)
|
|
225
262
|
};
|
|
226
263
|
const fallback = /*#__PURE__*/_jsx(ContentLoading, {
|
|
227
264
|
...contentProps
|
|
@@ -232,6 +269,9 @@ function renderWithInitialSource(props) {
|
|
|
232
269
|
children: /*#__PURE__*/_jsx(CodeHighlighterSuspense, {
|
|
233
270
|
children: renderCodeHighlighter({
|
|
234
271
|
...props,
|
|
272
|
+
code: codeForClient,
|
|
273
|
+
precompute: residualFallbacks ? codeForClient : props.precompute,
|
|
274
|
+
residualFallbacks,
|
|
235
275
|
fallback,
|
|
236
276
|
skipFallback: props.enhanceAfter === 'stream'
|
|
237
277
|
})
|
|
@@ -240,6 +280,9 @@ function renderWithInitialSource(props) {
|
|
|
240
280
|
}
|
|
241
281
|
return renderCodeHighlighter({
|
|
242
282
|
...props,
|
|
283
|
+
code: codeForClient,
|
|
284
|
+
precompute: residualFallbacks ? codeForClient : props.precompute,
|
|
285
|
+
residualFallbacks,
|
|
243
286
|
fallback
|
|
244
287
|
});
|
|
245
288
|
}
|
|
@@ -7,9 +7,11 @@ import { maybeCodeInitialData } from "../pipeline/loadIsomorphicCodeVariant/mayb
|
|
|
7
7
|
import { hasAllVariants } from "../pipeline/loadIsomorphicCodeVariant/hasAllCodeVariants.mjs";
|
|
8
8
|
import { CodeHighlighterFallbackContext } from "./CodeHighlighterFallbackContext.mjs";
|
|
9
9
|
import { useControlledCode } from "../CodeControllerContext/index.mjs";
|
|
10
|
-
import { codeToFallbackProps } from "./codeToFallbackProps.mjs";
|
|
10
|
+
import { codeToFallbackProps, deriveFallbacksFromCode, stripFallbackHastsFromCode } from "./codeToFallbackProps.mjs";
|
|
11
|
+
import { decompressResidualFallbacks, residualDictionaryText, scatterResidualFallbacks } from "./fallbackCompression.mjs";
|
|
11
12
|
import { mergeCodeMetadata } from "../pipeline/loadIsomorphicCodeVariant/mergeCodeMetadata.mjs";
|
|
12
13
|
import { getAvailableTransforms } from "../pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.mjs";
|
|
14
|
+
import { useCoordinatedLazy } from "../useCoordinated/useCoordinatedLazy.mjs";
|
|
13
15
|
import * as Errors from "./errors.mjs";
|
|
14
16
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
17
|
const DEBUG = false; // Set to true for debugging purposes
|
|
@@ -26,7 +28,8 @@ function useInitialData({
|
|
|
26
28
|
fallbackUsesAllVariants,
|
|
27
29
|
isControlled,
|
|
28
30
|
globalsCode,
|
|
29
|
-
setProcessedGlobalsCode
|
|
31
|
+
setProcessedGlobalsCode,
|
|
32
|
+
handleSetFallbackHasts
|
|
30
33
|
}) {
|
|
31
34
|
const {
|
|
32
35
|
sourceParser,
|
|
@@ -88,14 +91,22 @@ function useInitialData({
|
|
|
88
91
|
if ('error' in loaded) {
|
|
89
92
|
console.error(new Errors.ErrorCodeHighlighterClientLoadFallbackFailure(loaded.error));
|
|
90
93
|
} else {
|
|
91
|
-
|
|
94
|
+
// Strip fallbacks from code and hoist them directly
|
|
95
|
+
const {
|
|
96
|
+
strippedCode,
|
|
97
|
+
allFallbackHasts
|
|
98
|
+
} = stripFallbackHastsFromCode(loaded.code, variantName, fallbackUsesExtraFiles, fallbackUsesAllVariants);
|
|
99
|
+
setCode(strippedCode);
|
|
100
|
+
for (const [variant, hasts] of Object.entries(allFallbackHasts)) {
|
|
101
|
+
handleSetFallbackHasts(variant, hasts);
|
|
102
|
+
}
|
|
92
103
|
// Store processed globalsCode from loadCodeFallback result
|
|
93
104
|
if (loaded.processedGlobalsCode) {
|
|
94
105
|
setProcessedGlobalsCode(loaded.processedGlobalsCode);
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
})();
|
|
98
|
-
}, [initialData, reason, needsFallback, variantName, code, setCode, highlightAfter, url, sourceParser, loadSource, loadVariantMeta, loadCodeMeta, sourceEnhancers, fallbackUsesExtraFiles, fallbackUsesAllVariants, fileName, variants, globalsCode, setProcessedGlobalsCode, loadCodeFallback]);
|
|
109
|
+
}, [initialData, reason, needsFallback, variantName, code, setCode, highlightAfter, url, sourceParser, loadSource, loadVariantMeta, loadCodeMeta, sourceEnhancers, fallbackUsesExtraFiles, fallbackUsesAllVariants, fileName, variants, globalsCode, setProcessedGlobalsCode, loadCodeFallback, handleSetFallbackHasts]);
|
|
99
110
|
return {
|
|
100
111
|
fallbackPending
|
|
101
112
|
};
|
|
@@ -777,6 +788,40 @@ export function CodeHighlighterClient(props) {
|
|
|
777
788
|
fallbackUsesExtraFiles,
|
|
778
789
|
fallbackUsesAllVariants
|
|
779
790
|
} = props;
|
|
791
|
+
|
|
792
|
+
// ── Fallback hoisting ──
|
|
793
|
+
// State for fallbacks hoisted from ContentLoading via useCodeFallback.
|
|
794
|
+
// Content is stripped from Code on the server and passed to ContentLoading
|
|
795
|
+
// as source/extraSource props. ContentLoading hoists them back here so
|
|
796
|
+
// CodeHighlighterClient can derive text dictionaries for decompression.
|
|
797
|
+
const [hoistedFallbackHasts, setHoistedFallbackHasts] = React.useState({});
|
|
798
|
+
|
|
799
|
+
// Track whether ContentLoading called useCodeFallback via callback.
|
|
800
|
+
const hookCalledRef = React.useRef(false);
|
|
801
|
+
// Whether the fallback (ContentLoading) has mounted at least once. Until it
|
|
802
|
+
// has, we force it to mount even when the code is already ready, so its
|
|
803
|
+
// `useCodeFallback` effect can hoist the root fallback (the DEFLATE
|
|
804
|
+
// dictionary needed to decompress `hastCompressed`). Without this, a
|
|
805
|
+
// server-rendered, fully-loaded highlighter would skip the fallback branch
|
|
806
|
+
// entirely and Content could never decode the compressed HAST.
|
|
807
|
+
const [fallbackMounted, setFallbackMounted] = React.useState(false);
|
|
808
|
+
const handleHookCalled = React.useCallback(() => {
|
|
809
|
+
hookCalledRef.current = true;
|
|
810
|
+
setFallbackMounted(true);
|
|
811
|
+
}, []);
|
|
812
|
+
|
|
813
|
+
// Stable callback for ContentLoading to hoist its fallbacks.
|
|
814
|
+
const handleSetFallbackHasts = React.useCallback((variant, hasts) => {
|
|
815
|
+
setHoistedFallbackHasts(prev => {
|
|
816
|
+
if (prev[variant] === hasts) {
|
|
817
|
+
return prev;
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
...prev,
|
|
821
|
+
[variant]: hasts
|
|
822
|
+
};
|
|
823
|
+
});
|
|
824
|
+
}, []);
|
|
780
825
|
const {
|
|
781
826
|
fallbackPending
|
|
782
827
|
} = useInitialData({
|
|
@@ -791,9 +836,31 @@ export function CodeHighlighterClient(props) {
|
|
|
791
836
|
fallbackUsesAllVariants,
|
|
792
837
|
isControlled,
|
|
793
838
|
globalsCode: props.globalsCode,
|
|
794
|
-
setProcessedGlobalsCode
|
|
839
|
+
setProcessedGlobalsCode,
|
|
840
|
+
handleSetFallbackHasts
|
|
795
841
|
});
|
|
796
842
|
|
|
843
|
+
// Reverse the server-side residual consolidation. The blob is primed with the
|
|
844
|
+
// rendered subset's text, which only reaches the client via the hoist — so we
|
|
845
|
+
// wait for `hoistedFallbackHasts[variantName]` (the rendered initial variant,
|
|
846
|
+
// hoisted atomically) before decompressing. Residual files are hidden until a
|
|
847
|
+
// post-hoist swap, so deferring costs nothing; if the hoist never arrives the
|
|
848
|
+
// existing fallback-hoist check throws anyway.
|
|
849
|
+
const residualFallbacks = props.residualFallbacks;
|
|
850
|
+
const renderedHoist = hoistedFallbackHasts[variantName];
|
|
851
|
+
const residualMap = React.useMemo(() => {
|
|
852
|
+
if (!residualFallbacks || !renderedHoist) {
|
|
853
|
+
return undefined;
|
|
854
|
+
}
|
|
855
|
+
return decompressResidualFallbacks(residualFallbacks, residualDictionaryText(hoistedFallbackHasts));
|
|
856
|
+
}, [residualFallbacks, renderedHoist, hoistedFallbackHasts]);
|
|
857
|
+
|
|
858
|
+
// Scatter the residual back onto whichever code carries it. Memoized so the
|
|
859
|
+
// freshly-cloned code keeps a stable identity until the residual or its base
|
|
860
|
+
// changes (the downstream merges/parses are keyed on it).
|
|
861
|
+
const resolvedPropsCode = React.useMemo(() => props.code && residualMap ? scatterResidualFallbacks(props.code, residualMap) : props.code, [props.code, residualMap]);
|
|
862
|
+
const resolvedStateCode = React.useMemo(() => code && residualMap ? scatterResidualFallbacks(code, residualMap) : code, [code, residualMap]);
|
|
863
|
+
|
|
797
864
|
// Use useSyncExternalStore to detect hydration
|
|
798
865
|
const subscribe = React.useCallback(() => () => {}, []);
|
|
799
866
|
const getSnapshot = React.useCallback(() => true, []);
|
|
@@ -843,6 +910,20 @@ export function CodeHighlighterClient(props) {
|
|
|
843
910
|
const regularCode = props.code || code;
|
|
844
911
|
return regularCode ? hasAllVariants(variants, regularCode) : false;
|
|
845
912
|
}, [activeCode, isEnhanceAllowed, controlled?.code, variants, props.code, code]);
|
|
913
|
+
|
|
914
|
+
// Whether the fallback branch will actually mount this render. A fallback
|
|
915
|
+
// that exists but hasn't hoisted yet is forced to mount once (regardless of
|
|
916
|
+
// `activeCodeReady`) so `useCodeFallback` can hoist the root fallback.
|
|
917
|
+
const isFallbackRendered = !!props.fallback && !props.skipFallback && (!activeCodeReady || !fallbackMounted);
|
|
918
|
+
|
|
919
|
+
// Validate that ContentLoading calls useCodeFallback(props).
|
|
920
|
+
// Child effects fire before parent effects, so hookCalledRef is
|
|
921
|
+
// guaranteed to be set by the time this effect runs.
|
|
922
|
+
React.useEffect(() => {
|
|
923
|
+
if (isFallbackRendered && !hookCalledRef.current) {
|
|
924
|
+
throw new Errors.ErrorCodeHighlighterClientMissingFallbackHoist();
|
|
925
|
+
}
|
|
926
|
+
}, [isFallbackRendered]);
|
|
846
927
|
useAllVariants({
|
|
847
928
|
readyForContent,
|
|
848
929
|
variants,
|
|
@@ -859,7 +940,7 @@ export function CodeHighlighterClient(props) {
|
|
|
859
940
|
// Merge globalsCode with internal state code (fetched data) - this should be stable once ready
|
|
860
941
|
const stateCodeWithGlobals = useGlobalsCodeMerging({
|
|
861
942
|
url,
|
|
862
|
-
code,
|
|
943
|
+
code: resolvedStateCode,
|
|
863
944
|
// Only use internal state, not props.code
|
|
864
945
|
globalsCode: props.globalsCode,
|
|
865
946
|
processedGlobalsCode,
|
|
@@ -870,7 +951,7 @@ export function CodeHighlighterClient(props) {
|
|
|
870
951
|
|
|
871
952
|
// For props.code (controlled), always re-merge when it changes (don't cache in state)
|
|
872
953
|
const propsCodeWithGlobals = usePropsCodeGlobalsMerging({
|
|
873
|
-
code:
|
|
954
|
+
code: resolvedPropsCode,
|
|
874
955
|
globalsCode: props.globalsCode,
|
|
875
956
|
processedGlobalsCode,
|
|
876
957
|
variants
|
|
@@ -912,6 +993,14 @@ export function CodeHighlighterClient(props) {
|
|
|
912
993
|
// their stored-preference resolution doesn't pay the deltas latency.
|
|
913
994
|
const deferHighlight = deferHighlightForParsing || availableTransforms.length > 0 && waitingForTransformedCode;
|
|
914
995
|
|
|
996
|
+
// Declare this block to the page-wide layout-shift gate. While any block is
|
|
997
|
+
// still mid-swap, `useCoordinated` holds its layout-shifting commits, so the
|
|
998
|
+
// first transform/variant change lands as one unified update instead of a
|
|
999
|
+
// cascade as blocks swap in at staggered idle times. Settled once the block
|
|
1000
|
+
// has finished its initial fallback→content swap (no longer rendering the
|
|
1001
|
+
// fallback, and not mid-highlight); released on unmount.
|
|
1002
|
+
useCoordinatedLazy(!isFallbackRendered && !deferHighlight);
|
|
1003
|
+
|
|
915
1004
|
// Per-highlighter pre-parsed HAST cache. Lives in a ref so the same Map
|
|
916
1005
|
// instance is shared across renders without becoming a React dep. The
|
|
917
1006
|
// editable populates it via `useSourceEditing` (which reads it from
|
|
@@ -931,8 +1020,29 @@ export function CodeHighlighterClient(props) {
|
|
|
931
1020
|
const overlaidCode = parsedControlledCode || transformedCode || codeWithGlobals;
|
|
932
1021
|
|
|
933
1022
|
// For fallback context, use the processed code or fall back to non-controlled code
|
|
934
|
-
const codeForFallback = overlaidCode || (controlled?.code ? undefined :
|
|
935
|
-
|
|
1023
|
+
const codeForFallback = overlaidCode || (controlled?.code ? undefined : resolvedPropsCode || resolvedStateCode);
|
|
1024
|
+
|
|
1025
|
+
// Resolve the active variant's fallbacks from the two places one can cross
|
|
1026
|
+
// the server→client boundary: the hoisted copy (from a `ContentLoading`
|
|
1027
|
+
// component, which had it stripped off `Code`) and the variant's own
|
|
1028
|
+
// `fallback` field on `Code` (present without a `ContentLoading`, or scattered
|
|
1029
|
+
// back from the residual blob). For most files only one is populated. When
|
|
1030
|
+
// both are — a `fallbackCollapsed` block hoists the *visible* window but
|
|
1031
|
+
// scatters the *full* fallback onto `Code` — the `Code` copy must win, since
|
|
1032
|
+
// the full text is the DEFLATE dictionary `hastCompressed` needs. So merge
|
|
1033
|
+
// with the derived (`Code`) copy taking precedence.
|
|
1034
|
+
const activeFallbacks = React.useMemo(() => {
|
|
1035
|
+
const merged = {
|
|
1036
|
+
...hoistedFallbackHasts[variantName],
|
|
1037
|
+
...deriveFallbacksFromCode(codeForFallback, variantName)
|
|
1038
|
+
};
|
|
1039
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
1040
|
+
}, [hoistedFallbackHasts, variantName, codeForFallback]);
|
|
1041
|
+
const fallbackContext = React.useMemo(() => ({
|
|
1042
|
+
extraVariants: codeToFallbackProps(variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants).extraVariants,
|
|
1043
|
+
setFallbackHasts: handleSetFallbackHasts,
|
|
1044
|
+
onHookCalled: handleHookCalled
|
|
1045
|
+
}), [variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants, handleSetFallbackHasts, handleHookCalled]);
|
|
936
1046
|
const context = React.useMemo(() => ({
|
|
937
1047
|
code: overlaidCode,
|
|
938
1048
|
// Use processed/transformed code
|
|
@@ -945,14 +1055,21 @@ export function CodeHighlighterClient(props) {
|
|
|
945
1055
|
availableTransforms: controlled?.code ? [] : availableTransforms,
|
|
946
1056
|
url: props.url,
|
|
947
1057
|
deferHighlight,
|
|
1058
|
+
fallbacks: activeFallbacks,
|
|
948
1059
|
highlightReady,
|
|
949
1060
|
highlightAfter,
|
|
950
1061
|
preParsedCache
|
|
951
|
-
}), [overlaidCode, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, controlled?.components, props.components, controlled?.code, availableTransforms, props.url, deferHighlight, highlightReady, highlightAfter, preParsedCache]);
|
|
1062
|
+
}), [overlaidCode, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, controlled?.components, props.components, controlled?.code, availableTransforms, props.url, deferHighlight, activeFallbacks, highlightReady, highlightAfter, preParsedCache]);
|
|
952
1063
|
if (!props.variants && !props.components && !activeCode) {
|
|
953
1064
|
throw new Errors.ErrorCodeHighlighterClientMissingData();
|
|
954
1065
|
}
|
|
955
1066
|
|
|
1067
|
+
// Reset only when fallback is actually rendered so the flag isn't sticky across cycles.
|
|
1068
|
+
// The child's effect will set it again if useCodeFallback(props) is called.
|
|
1069
|
+
if (isFallbackRendered) {
|
|
1070
|
+
hookCalledRef.current = false;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
956
1073
|
// If this CodeHighlighter is nested inside another CodeHighlighter that is
|
|
957
1074
|
// currently rendering its fallback, hold our own fallback->full transition
|
|
958
1075
|
// until the outer one swaps. Otherwise, when the outer swaps from its
|
|
@@ -964,7 +1081,7 @@ export function CodeHighlighterClient(props) {
|
|
|
964
1081
|
const outerFallbackContext = React.useContext(CodeHighlighterFallbackContext);
|
|
965
1082
|
const isNestedInsideOuterFallback = outerFallbackContext !== undefined;
|
|
966
1083
|
const fallback = props.fallback;
|
|
967
|
-
if (fallback && !props.skipFallback && (!activeCodeReady || isNestedInsideOuterFallback)) {
|
|
1084
|
+
if (fallback && !props.skipFallback && (!activeCodeReady || isNestedInsideOuterFallback || !fallbackMounted)) {
|
|
968
1085
|
return /*#__PURE__*/_jsx(CodeHighlighterFallbackContext.Provider, {
|
|
969
1086
|
value: fallbackContext,
|
|
970
1087
|
children: fallback
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type Code, type ControlledCode, type HastRoot } from "./types.mjs";
|
|
2
|
+
import { type Code, type ControlledCode, type Fallbacks, type HastRoot } from "./types.mjs";
|
|
3
3
|
import { type Selection } from "../CodeControllerContext/index.mjs";
|
|
4
4
|
/**
|
|
5
5
|
* One cached pre-parsed file. Stored per-fileName: each new write replaces
|
|
@@ -21,6 +21,13 @@ export interface CodeHighlighterContextType {
|
|
|
21
21
|
availableTransforms?: string[];
|
|
22
22
|
url?: string;
|
|
23
23
|
deferHighlight?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
/**
|
|
26
|
+
* Compact fallback data for the active variant, keyed by fileName.
|
|
27
|
+
* Used by `Pre` to both render the fallback and derive text dictionaries
|
|
28
|
+
* for decompressing `hastCompressed` payloads.
|
|
29
|
+
*/
|
|
30
|
+
fallbacks?: Fallbacks;
|
|
24
31
|
/**
|
|
25
32
|
* Render-side readiness gate. `true` once the highlight trigger
|
|
26
33
|
* (`init` / `hydration` / `idle` / `visible`) has fired *and* the
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export interface CodeHighlighterFallbackContext
|
|
2
|
+
import type { Fallbacks, ContentLoadingVariant } from "./types.mjs";
|
|
3
|
+
export interface CodeHighlighterFallbackContext {
|
|
4
4
|
extraVariants?: Record<string, ContentLoadingVariant>;
|
|
5
|
+
/**
|
|
6
|
+
* Callback used by `useCodeFallback` to hoist fallback data
|
|
7
|
+
* back to `CodeHighlighterClient` so it can derive text dictionaries
|
|
8
|
+
* for decompressing `hastCompressed` payloads.
|
|
9
|
+
*/
|
|
10
|
+
setFallbackHasts?: (variantName: string, hasts: Fallbacks) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Callback invoked by `useCodeFallback` in an effect to signal that
|
|
13
|
+
* the hook was used. Allows the parent to detect when a ContentLoading
|
|
14
|
+
* component forgets to call the hook.
|
|
15
|
+
*/
|
|
16
|
+
onHookCalled?: () => void;
|
|
5
17
|
}
|
|
6
18
|
export declare const CodeHighlighterFallbackContext: React.Context<CodeHighlighterFallbackContext | undefined>;
|
|
7
19
|
export declare function useCodeHighlighterFallbackContext(): CodeHighlighterFallbackContext;
|
|
@@ -9,6 +9,4 @@ export function useCodeHighlighterFallbackContext() {
|
|
|
9
9
|
throw new Error('CodeHighlighterFallbackContext is missing. `useCodeHighlighterFallbackContext` must be used within a `CodeHighlighter` component.');
|
|
10
10
|
}
|
|
11
11
|
return context;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// TODO: rename to ContentMinimal
|
|
12
|
+
}
|
|
@@ -1,2 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function codeToFallbackProps(variant: string, code?: Code,
|
|
1
|
+
import type { BaseContentLoadingProps, Code, Fallbacks } from "./types.mjs";
|
|
2
|
+
export declare function codeToFallbackProps(variant: string, code?: Code, _fileName?: string, _needsAllFiles?: boolean, needsAllVariants?: boolean, allFallbackHasts?: Record<string, Fallbacks>): BaseContentLoadingProps;
|
|
3
|
+
/**
|
|
4
|
+
* Read a variant's per-file fallbacks straight off its `VariantCode` `fallback`
|
|
5
|
+
* fields (main + extra files), returning a `Fallbacks` map keyed by file name.
|
|
6
|
+
*
|
|
7
|
+
* The fallback crosses the server→client boundary exactly once: either on the
|
|
8
|
+
* `VariantCode` (no `ContentLoading`) or — after `stripFallbackHastsFromCode`
|
|
9
|
+
* moves it — on the `ContentLoading` props. This reads the former location, so
|
|
10
|
+
* the client can resolve the DEFLATE dictionary for `hastCompressed` without a
|
|
11
|
+
* hoist when there's no `ContentLoading`. Returns `undefined` when the variant
|
|
12
|
+
* carries no fallback (a string variant, a live-HAST source, or one whose
|
|
13
|
+
* fallbacks were stripped for a `ContentLoading` component) — in which case the
|
|
14
|
+
* hoisted copy is used instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function deriveFallbacksFromCode(code: Code | undefined, variantName: string): Fallbacks | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Strip `fallback` entries from a `Code` object and return the
|
|
19
|
+
* stripped Code alongside the extracted fallbacks grouped by variant → fileName.
|
|
20
|
+
*
|
|
21
|
+
* Used on the server to separate the fallback data from the Code
|
|
22
|
+
* so Code is sent to CodeHighlighterClient without fallbacks, and
|
|
23
|
+
* the data is passed to ContentLoading as source/extraSource props.
|
|
24
|
+
*/
|
|
25
|
+
export declare function stripFallbackHastsFromCode(code: Code | undefined, variantName: string, fallbackUsesExtraFiles?: boolean, fallbackUsesAllVariants?: boolean): {
|
|
26
|
+
strippedCode: Code;
|
|
27
|
+
allFallbackHasts: Record<string, Fallbacks>;
|
|
28
|
+
};
|