@mui/internal-docs-infra 0.11.1-canary.9 → 0.11.1
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/ChunkProvider/ChunkContext.d.mts +10 -0
- package/ChunkProvider/ChunkContext.mjs +15 -0
- package/ChunkProvider/ChunkProvider.d.mts +14 -0
- package/ChunkProvider/ChunkProvider.mjs +38 -0
- package/ChunkProvider/PreloadContext.d.mts +14 -0
- package/ChunkProvider/PreloadContext.mjs +18 -0
- package/ChunkProvider/PreloadProvider.d.mts +13 -0
- package/ChunkProvider/PreloadProvider.mjs +33 -0
- package/ChunkProvider/index.d.mts +7 -0
- package/ChunkProvider/index.mjs +7 -0
- package/ChunkProvider/types.d.mts +23 -0
- package/ChunkProvider/types.mjs +1 -0
- package/ChunkProvider/usePreload.d.mts +8 -0
- package/ChunkProvider/usePreload.mjs +21 -0
- package/CodeControllerContext/CodeControllerContext.d.mts +11 -0
- package/CodeControllerContext/CodeControllerContext.mjs +2 -1
- package/CodeHighlighter/CodeHighlighter.d.mts +15 -1
- package/CodeHighlighter/CodeHighlighter.mjs +97 -319
- package/CodeHighlighter/CodeHighlighterChunk.d.mts +42 -0
- package/CodeHighlighter/CodeHighlighterChunk.mjs +77 -0
- package/CodeHighlighter/CodeHighlighterClient.mjs +597 -128
- package/CodeHighlighter/CodeHighlighterContext.d.mts +57 -1
- package/CodeHighlighter/CodeHighlighterFallbackContext.d.mts +14 -2
- package/CodeHighlighter/CodeHighlighterFallbackContext.mjs +1 -3
- package/CodeHighlighter/CodeInitialSourceLoader.d.mts +10 -0
- package/CodeHighlighter/CodeInitialSourceLoader.mjs +108 -0
- package/CodeHighlighter/CodeSourceLoader.d.mts +11 -0
- package/CodeHighlighter/CodeSourceLoader.mjs +128 -0
- package/CodeHighlighter/buildCodeHighlighterChunkProps.d.mts +47 -0
- package/CodeHighlighter/buildCodeHighlighterChunkProps.mjs +61 -0
- package/CodeHighlighter/buildStringFallback.d.mts +29 -0
- package/CodeHighlighter/buildStringFallback.mjs +42 -0
- package/CodeHighlighter/codeToFallbackProps.d.mts +31 -2
- package/CodeHighlighter/codeToFallbackProps.mjs +347 -42
- package/CodeHighlighter/createClientProps.d.mts +17 -0
- package/CodeHighlighter/createClientProps.mjs +78 -0
- package/CodeHighlighter/errors.d.mts +6 -0
- package/CodeHighlighter/errors.mjs +10 -0
- package/CodeHighlighter/fallbackCompression.d.mts +96 -0
- package/CodeHighlighter/fallbackCompression.mjs +253 -0
- package/CodeHighlighter/fallbackFormat.d.mts +137 -0
- package/CodeHighlighter/fallbackFormat.mjs +422 -0
- package/CodeHighlighter/index.d.mts +4 -1
- package/CodeHighlighter/index.mjs +3 -1
- package/CodeHighlighter/mergeComments.d.mts +38 -0
- package/CodeHighlighter/mergeComments.mjs +80 -0
- package/CodeHighlighter/prepareInitialSource.d.mts +42 -0
- package/CodeHighlighter/prepareInitialSource.mjs +292 -0
- package/CodeHighlighter/resolveFallbackCritical.d.mts +23 -0
- package/CodeHighlighter/resolveFallbackCritical.mjs +44 -0
- package/CodeHighlighter/types.d.mts +272 -8
- package/CodeHighlighter/useCodeFallback.d.mts +94 -0
- package/CodeHighlighter/useCodeFallback.mjs +204 -0
- package/CodeHighlighter/useGrammarsReady.d.mts +18 -0
- package/CodeHighlighter/useGrammarsReady.mjs +45 -0
- package/CodeHighlighter/useSpeculativeCodePreload.d.mts +26 -0
- package/CodeHighlighter/useSpeculativeCodePreload.mjs +40 -0
- package/CodeHighlighter/useSpeculativeEditingPreload.d.mts +33 -0
- package/CodeHighlighter/useSpeculativeEditingPreload.mjs +58 -0
- package/CodeHighlighter/useSpeculativeGrammarPreload.d.mts +23 -0
- package/CodeHighlighter/useSpeculativeGrammarPreload.mjs +31 -0
- package/CodeHighlighter/useSpeculativeUseCodePreload.d.mts +22 -0
- package/CodeHighlighter/useSpeculativeUseCodePreload.mjs +41 -0
- package/CodeProvider/CodeContext.d.mts +47 -12
- package/CodeProvider/CodeContext.mjs +7 -0
- package/CodeProvider/CodeProvider.d.mts +4 -2
- package/CodeProvider/CodeProvider.mjs +40 -102
- package/CodeProvider/CodeProviderLazy.d.mts +40 -0
- package/CodeProvider/CodeProviderLazy.mjs +96 -0
- package/CodeProvider/constants.d.mts +26 -0
- package/CodeProvider/constants.mjs +24 -0
- package/CodeProvider/createParseSourceWorkerClient.d.mts +6 -0
- package/CodeProvider/createParseSourceWorkerClient.mjs +22 -2
- package/CodeProvider/index.d.mts +2 -1
- package/CodeProvider/index.mjs +9 -1
- package/CodeProvider/parseSourceWorker.mjs +33 -0
- package/CodeProvider/useCodeProviderValue.d.mts +54 -0
- package/CodeProvider/useCodeProviderValue.mjs +188 -0
- package/CoordinatedLazy/ChunkServerLoader.d.mts +25 -0
- package/CoordinatedLazy/ChunkServerLoader.mjs +97 -0
- package/CoordinatedLazy/CoordinatedContentContext.d.mts +15 -0
- package/CoordinatedLazy/CoordinatedContentContext.mjs +22 -0
- package/CoordinatedLazy/CoordinatedFallbackContext.d.mts +11 -0
- package/CoordinatedLazy/CoordinatedFallbackContext.mjs +13 -0
- package/CoordinatedLazy/CoordinatedGateContext.d.mts +14 -0
- package/CoordinatedLazy/CoordinatedGateContext.mjs +19 -0
- package/CoordinatedLazy/CoordinatedLazy.d.mts +14 -0
- package/CoordinatedLazy/CoordinatedLazy.mjs +86 -0
- package/CoordinatedLazy/CoordinatedLazyClient.d.mts +24 -0
- package/CoordinatedLazy/CoordinatedLazyClient.mjs +65 -0
- package/CoordinatedLazy/LazyContent.d.mts +26 -0
- package/CoordinatedLazy/LazyContent.mjs +80 -0
- package/CoordinatedLazy/LazyContentServer.d.mts +18 -0
- package/CoordinatedLazy/LazyContentServer.mjs +25 -0
- package/CoordinatedLazy/buildChunkRenderInputs.d.mts +8 -0
- package/CoordinatedLazy/buildChunkRenderInputs.mjs +35 -0
- package/CoordinatedLazy/createCoordinatedLazy.d.mts +32 -0
- package/CoordinatedLazy/createCoordinatedLazy.mjs +127 -0
- package/CoordinatedLazy/index.d.mts +14 -0
- package/CoordinatedLazy/index.mjs +18 -0
- package/CoordinatedLazy/resolveChunkRender.d.mts +26 -0
- package/CoordinatedLazy/resolveChunkRender.mjs +73 -0
- package/CoordinatedLazy/types.d.mts +408 -0
- package/CoordinatedLazy/types.mjs +1 -0
- package/CoordinatedLazy/useChunk.d.mts +30 -0
- package/CoordinatedLazy/useChunk.mjs +135 -0
- package/CoordinatedLazy/useCoordinatedFallback.d.mts +12 -0
- package/CoordinatedLazy/useCoordinatedFallback.mjs +40 -0
- package/CoordinatedLazy/useCoordinatedSwap.d.mts +16 -0
- package/CoordinatedLazy/useCoordinatedSwap.mjs +124 -0
- package/LICENSE +1 -1
- package/abstractCreateDemo/abstractCreateDemo.d.mts +54 -3
- package/abstractCreateDemo/abstractCreateDemo.mjs +47 -7
- package/abstractCreateDemo/resolveDemoFlag.d.mts +20 -0
- package/abstractCreateDemo/resolveDemoFlag.mjs +25 -0
- package/abstractCreateStream/abstractCreateStream.d.mts +18 -0
- package/abstractCreateStream/abstractCreateStream.mjs +45 -0
- package/abstractCreateStream/index.d.mts +2 -0
- package/abstractCreateStream/index.mjs +1 -0
- package/abstractCreateStream/types.d.mts +34 -0
- package/abstractCreateStream/types.mjs +1 -0
- package/abstractCreateTypes/TypeCode.mjs +12 -11
- package/abstractCreateTypes/typesToJsx.mjs +30 -9
- package/cli/ensureDemoClients.mjs +4 -148
- package/cli/ensureDemoPages.d.mts +45 -0
- package/cli/ensureDemoPages.mjs +99 -0
- package/cli/fileUtils/index.d.mts +11 -0
- package/cli/fileUtils/index.mjs +48 -0
- package/cli/findDemoIndexFiles.d.mts +15 -0
- package/cli/findDemoIndexFiles.mjs +121 -0
- package/cli/index.mjs +1 -1
- package/cli/loadNextConfig.d.mts +25 -0
- package/cli/loadNextConfig.mjs +60 -1
- package/cli/runBrowser.mjs +1 -1
- package/cli/runValidate.mjs +44 -1
- package/package.json +84 -4
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasis.mjs +30 -0
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.d.mts +17 -0
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasisLazy.mjs +52 -0
- package/pipeline/hastUtils/frameFallbackFromSpans.d.mts +18 -0
- package/pipeline/hastUtils/frameFallbackFromSpans.mjs +24 -0
- package/pipeline/hastUtils/hast.d.mts +27 -0
- package/pipeline/hastUtils/hastCompression.d.mts +3 -1
- package/pipeline/hastUtils/hastCompression.mjs +9 -1
- package/pipeline/hastUtils/hastDecompress.mjs +10 -4
- package/pipeline/hastUtils/hastDictionary.mjs +9 -0
- package/pipeline/hastUtils/hastUtils.d.mts +4 -3
- package/pipeline/hastUtils/hastUtils.mjs +24 -12
- package/pipeline/hastUtils/index.d.mts +2 -1
- package/pipeline/hastUtils/index.mjs +2 -1
- package/pipeline/hastUtils/stripHighlightingSpans.d.mts +6 -2
- package/pipeline/hastUtils/stripHighlightingSpans.mjs +22 -10
- package/pipeline/lintJavascriptDemoFocus/lintJavascriptDemoFocus.mjs +10 -7
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.d.mts +31 -13
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransform.mjs +50 -55
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.d.mts +78 -0
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs +405 -0
- package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.d.mts +5 -5
- package/pipeline/loadIsomorphicCodeVariant/computeHastDeltas.mjs +36 -66
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.d.mts +23 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeHastSource.mjs +92 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSource.d.mts +19 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSource.mjs +25 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.d.mts +17 -0
- package/pipeline/loadIsomorphicCodeVariant/decodeSourceToText.mjs +26 -0
- package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +26 -2
- package/pipeline/loadIsomorphicCodeVariant/diffHast.mjs +563 -19
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.d.mts +49 -0
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.mjs +152 -0
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.d.mts +51 -0
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.mjs +161 -0
- package/pipeline/loadIsomorphicCodeVariant/flattenCodeVariant.mjs +6 -3
- package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.d.mts +12 -0
- package/pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.mjs +44 -0
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.d.mts +16 -0
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +74 -0
- package/pipeline/loadIsomorphicCodeVariant/loadCodeFallback.mjs +17 -5
- package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +229 -15
- package/pipeline/loadIsomorphicCodeVariant/transformSource.d.mts +2 -2
- package/pipeline/loadIsomorphicCodeVariant/transformSource.mjs +56 -22
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.d.mts +18 -0
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.mjs +11 -7
- package/pipeline/loadServerTypes/hastTypeUtils.d.mts +2 -2
- package/pipeline/loadServerTypes/hastTypeUtils.mjs +4 -4
- package/pipeline/loadServerTypes/loadServerTypes.mjs +1 -1
- package/pipeline/loadServerTypesMeta/extractJSDocText.d.mts +14 -0
- package/pipeline/loadServerTypesMeta/extractJSDocText.mjs +60 -0
- package/pipeline/loadServerTypesMeta/processTypes.mjs +43 -46
- package/pipeline/loadServerTypesText/order.mjs +1 -1
- package/pipeline/loadServerTypesText/parseTypesMarkdown.mjs +3 -1
- package/pipeline/loaderUtils/index.d.mts +0 -1
- package/pipeline/loaderUtils/index.mjs +0 -1
- package/pipeline/loaderUtils/parseImportsAndComments.d.mts +5 -1
- package/pipeline/loaderUtils/parseImportsAndComments.mjs +19 -9
- package/pipeline/loaderUtils/resolveModulePath.mjs +23 -1
- package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.d.mts +12 -0
- package/pipeline/parseCreateFactoryCall/parseCreateFactoryCall.mjs +17 -13
- package/pipeline/parseSource/addLineGutters.mjs +45 -11
- package/pipeline/parseSource/calculateFrameRanges.d.mts +22 -0
- package/pipeline/parseSource/calculateFrameRanges.mjs +69 -25
- package/pipeline/parseSource/detectGrammarScopes.d.mts +13 -0
- package/pipeline/parseSource/detectGrammarScopes.mjs +35 -0
- package/pipeline/parseSource/extendSyntaxTokens.mjs +501 -43
- package/pipeline/parseSource/frameVisibility.d.mts +47 -0
- package/pipeline/parseSource/frameVisibility.mjs +114 -0
- package/pipeline/parseSource/grammarCache.d.mts +33 -0
- package/pipeline/parseSource/grammarCache.mjs +73 -0
- package/pipeline/parseSource/grammarLoaders.d.mts +14 -0
- package/pipeline/parseSource/grammarLoaders.mjs +24 -0
- package/pipeline/parseSource/grammarMaps.d.mts +21 -1
- package/pipeline/parseSource/grammarMaps.mjs +36 -0
- package/pipeline/parseSource/isFrameSpan.d.mts +19 -0
- package/pipeline/parseSource/isFrameSpan.mjs +24 -0
- package/pipeline/parseSource/parseSource.d.mts +41 -6
- package/pipeline/parseSource/parseSource.mjs +184 -36
- 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 +179 -16
- package/pipeline/syncPageIndex/metadataToMarkdown.mjs +6 -2
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.d.mts +26 -0
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs +181 -114
- package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.d.mts +12 -0
- package/pipeline/transformHtmlCodeInline/removeSuffixFromHighlightedNodes.mjs +52 -0
- package/pipeline/transformHtmlCodeInline/transformHtmlCodeInline.mjs +22 -1
- package/pipeline/transformTypescriptToJavascript/removeTypes.d.mts +5 -8
- package/pipeline/transformTypescriptToJavascript/removeTypes.mjs +27 -93
- package/useCode/EditableEngine.d.mts +233 -0
- package/useCode/EditableEngine.mjs +1712 -0
- package/useCode/EditingEngine.d.mts +13 -0
- package/useCode/EditingEngine.mjs +14 -0
- package/useCode/Pre.browser.mjs +5 -1
- package/useCode/Pre.d.mts +127 -1
- package/useCode/Pre.mjs +417 -165
- package/useCode/SourceEditingEngine.d.mts +50 -0
- package/useCode/SourceEditingEngine.mjs +461 -0
- package/useCode/TransformEngine.d.mts +39 -0
- package/useCode/TransformEngine.mjs +208 -0
- package/useCode/editingEngineCache.d.mts +29 -0
- package/useCode/editingEngineCache.mjs +68 -0
- package/useCode/sourceLineCounts.d.mts +80 -0
- package/useCode/sourceLineCounts.mjs +284 -0
- package/useCode/subscribeToggleNudge.d.mts +3 -0
- package/useCode/subscribeToggleNudge.mjs +95 -0
- package/useCode/transformEngineCache.d.mts +21 -0
- package/useCode/transformEngineCache.mjs +60 -0
- package/useCode/useCode.d.mts +140 -1
- package/useCode/useCode.mjs +250 -19
- package/useCode/useCodeUtils.d.mts +131 -20
- package/useCode/useCodeUtils.mjs +267 -194
- package/useCode/useCopyFunctionality.d.mts +13 -1
- package/useCode/useCopyFunctionality.mjs +39 -9
- package/useCode/useEditable.browser.mjs +10 -2
- package/useCode/useEditable.d.mts +27 -106
- package/useCode/useEditable.integration.browser.d.mts +1 -0
- package/useCode/useEditable.integration.browser.mjs +870 -0
- package/useCode/useEditable.mjs +198 -1247
- package/useCode/useEditableUtils.d.mts +50 -1
- package/useCode/useEditableUtils.mjs +29 -0
- package/useCode/useFileNavigation.d.mts +91 -3
- package/useCode/useFileNavigation.mjs +201 -41
- package/useCode/useHighlightGate.d.mts +17 -0
- package/useCode/useHighlightGate.mjs +147 -0
- package/useCode/useSourceEditing.d.mts +8 -0
- package/useCode/useSourceEditing.mjs +158 -314
- package/useCode/useSourceEnhancing.d.mts +5 -1
- package/useCode/useSourceEnhancing.mjs +22 -36
- package/useCode/useTransformManagement.d.mts +93 -5
- package/useCode/useTransformManagement.mjs +496 -28
- package/useCode/useTransitionPhase.d.mts +24 -0
- package/useCode/useTransitionPhase.mjs +49 -0
- package/useCode/useUIState.d.mts +2 -2
- package/useCode/useUIState.mjs +8 -8
- package/useCode/useVariantSelection.d.mts +130 -6
- package/useCode/useVariantSelection.mjs +529 -93
- package/useCodeWindow/useCodeWindow.d.mts +19 -2
- package/useCodeWindow/useCodeWindow.mjs +98 -71
- package/useCoordinated/coordinatePreference.d.mts +439 -0
- package/useCoordinated/coordinatePreference.mjs +951 -0
- package/useCoordinated/coordinatePreference.testUtils.d.mts +21 -0
- package/useCoordinated/coordinatePreference.testUtils.mjs +69 -0
- package/useCoordinated/createSettleGate.d.mts +96 -0
- package/useCoordinated/createSettleGate.mjs +171 -0
- package/useCoordinated/index.d.mts +8 -0
- package/useCoordinated/index.mjs +8 -0
- package/useCoordinated/layoutShiftGate.d.mts +24 -0
- package/useCoordinated/layoutShiftGate.mjs +79 -0
- package/useCoordinated/pageSettleGate.d.mts +11 -0
- package/useCoordinated/pageSettleGate.mjs +13 -0
- package/useCoordinated/scheduleTasks.d.mts +23 -0
- package/useCoordinated/scheduleTasks.mjs +45 -0
- package/useCoordinated/useCoordinated.d.mts +193 -0
- package/useCoordinated/useCoordinated.mjs +469 -0
- package/useCoordinated/useCoordinatedLazy.d.mts +17 -0
- package/useCoordinated/useCoordinatedLazy.mjs +38 -0
- package/useCoordinated/useCoordinatedLocalStorage.d.mts +16 -0
- package/useCoordinated/useCoordinatedLocalStorage.mjs +22 -0
- package/useCoordinated/useCoordinatedPreference.d.mts +20 -0
- package/useCoordinated/useCoordinatedPreference.mjs +26 -0
- package/useCoordinated/useSettleGate.d.mts +11 -0
- package/useCoordinated/useSettleGate.mjs +34 -0
- package/useDemo/exportVariant.d.mts +12 -5
- package/useDemo/exportVariant.mjs +59 -5
- package/useDemo/useDemo.d.mts +5 -2
- package/useScrollAnchor/useScrollAnchor.mjs +28 -5
- package/useStream/index.d.mts +6 -0
- package/useStream/index.mjs +6 -0
- package/useStream/streamChunks.d.mts +23 -0
- package/useStream/streamChunks.mjs +85 -0
- package/useStream/types.d.mts +45 -0
- package/useStream/types.mjs +1 -0
- package/useStream/useStream.d.mts +57 -0
- package/useStream/useStream.mjs +119 -0
- package/useStream/useStreamController.d.mts +15 -0
- package/useStream/useStreamController.mjs +90 -0
- package/withDocsInfra/withDocsInfra.d.mts +19 -0
- package/withDocsInfra/withDocsInfra.mjs +13 -5
- package/pipeline/loaderUtils/convertCommentsToOneIndexed.d.mts +0 -8
- package/pipeline/loaderUtils/convertCommentsToOneIndexed.mjs +0 -16
|
@@ -7,12 +7,32 @@ 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 { resolveFallbackCritical } from "./resolveFallbackCritical.mjs";
|
|
12
|
+
import { decompressResidualFallbacks, residualDictionaryText, scatterResidualFallbacks } from "./fallbackCompression.mjs";
|
|
11
13
|
import { mergeCodeMetadata } from "../pipeline/loadIsomorphicCodeVariant/mergeCodeMetadata.mjs";
|
|
14
|
+
import { getAvailableTransforms } from "../pipeline/loadIsomorphicCodeVariant/getAvailableTransforms.mjs";
|
|
15
|
+
import { useSpeculativeCodePreload } from "./useSpeculativeCodePreload.mjs";
|
|
16
|
+
import { useSpeculativeEditingPreload } from "./useSpeculativeEditingPreload.mjs";
|
|
17
|
+
import { useSpeculativeUseCodePreload } from "./useSpeculativeUseCodePreload.mjs";
|
|
18
|
+
import { useSpeculativeGrammarPreload } from "./useSpeculativeGrammarPreload.mjs";
|
|
19
|
+
import { useGrammarsReady } from "./useGrammarsReady.mjs";
|
|
20
|
+
import { detectGrammarScopes } from "../pipeline/parseSource/detectGrammarScopes.mjs";
|
|
21
|
+
import { useChunk } from "../CoordinatedLazy/useChunk.mjs";
|
|
22
|
+
import { useCoordinatedSwap } from "../CoordinatedLazy/useCoordinatedSwap.mjs";
|
|
23
|
+
import { CoordinatedFallbackContext } from "../CoordinatedLazy/CoordinatedFallbackContext.mjs";
|
|
24
|
+
import { CoordinatedContentContext } from "../CoordinatedLazy/CoordinatedContentContext.mjs";
|
|
25
|
+
import { requestIdle } from "../useCoordinated/scheduleTasks.mjs";
|
|
12
26
|
import * as Errors from "./errors.mjs";
|
|
13
27
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
28
|
const DEBUG = false; // Set to true for debugging purposes
|
|
15
29
|
|
|
30
|
+
// `useChunk` is the chunk loader/renderer, but here we use only its loading
|
|
31
|
+
// engine (load-when-enabled + abort + `refresh()` with stale-while-revalidate),
|
|
32
|
+
// so the content component is an unused placeholder.
|
|
33
|
+
function NoopChunkContent() {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
16
36
|
function useInitialData({
|
|
17
37
|
variants,
|
|
18
38
|
variantName,
|
|
@@ -25,14 +45,15 @@ function useInitialData({
|
|
|
25
45
|
fallbackUsesAllVariants,
|
|
26
46
|
isControlled,
|
|
27
47
|
globalsCode,
|
|
28
|
-
setProcessedGlobalsCode
|
|
48
|
+
setProcessedGlobalsCode,
|
|
49
|
+
handleSetFallbackHasts
|
|
29
50
|
}) {
|
|
30
51
|
const {
|
|
31
52
|
sourceParser,
|
|
32
53
|
loadCodeMeta,
|
|
33
54
|
loadVariantMeta,
|
|
34
55
|
loadSource,
|
|
35
|
-
|
|
56
|
+
loadCodeFallbackLoader,
|
|
36
57
|
sourceEnhancers
|
|
37
58
|
} = useCodeContext();
|
|
38
59
|
const {
|
|
@@ -45,7 +66,11 @@ function useInitialData({
|
|
|
45
66
|
// URL is required for loading fallback data
|
|
46
67
|
throw new Errors.ErrorCodeHighlighterClientMissingUrlForFallback();
|
|
47
68
|
}
|
|
48
|
-
|
|
69
|
+
|
|
70
|
+
// Validate against the loader accessor's presence (synchronously defined
|
|
71
|
+
// whenever a CodeProvider is mounted) - never against the resolved fn, so we
|
|
72
|
+
// don't throw merely because a lazy import is still in flight.
|
|
73
|
+
if (!loadCodeFallbackLoader) {
|
|
49
74
|
throw new Errors.ErrorCodeHighlighterClientMissingLoadFallbackCode(url);
|
|
50
75
|
}
|
|
51
76
|
}
|
|
@@ -53,22 +78,27 @@ function useInitialData({
|
|
|
53
78
|
// Signal to downstream loaders that a fallback fetch is pending. Used to gate
|
|
54
79
|
// `useAllVariants` so it can reuse the data populated by the fallback rather
|
|
55
80
|
// than racing it and re-fetching the same variant.
|
|
56
|
-
const fallbackPending = Boolean(needsFallback && url &&
|
|
81
|
+
const fallbackPending = Boolean(needsFallback && url && loadCodeFallbackLoader);
|
|
57
82
|
|
|
83
|
+
// The fallback load runs through `useChunk` too (same loading engine as the
|
|
84
|
+
// full load) — the body is unchanged (it still calls `setCode` / hoists /
|
|
85
|
+
// `setProcessedGlobalsCode` directly; `code` stays owned by the component).
|
|
86
|
+
// `controlled: !needsFallback` is the gate.
|
|
58
87
|
// TODO: fallbackInitialRenderOnly option? this would mean we can't fetch fallback data on the client side
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// TODO: abort controller
|
|
66
|
-
|
|
67
|
-
(async () => {
|
|
88
|
+
const fallbackSource = React.useMemo(() => ({
|
|
89
|
+
mode: 'data',
|
|
90
|
+
load: async (_options, signal) => {
|
|
91
|
+
if (!url || !loadCodeFallbackLoader) {
|
|
92
|
+
return code ?? {};
|
|
93
|
+
}
|
|
68
94
|
if (DEBUG) {
|
|
69
95
|
// eslint-disable-next-line no-console
|
|
70
96
|
console.log('Loading initial data for CodeHighlighterClient: ', reason);
|
|
71
97
|
}
|
|
98
|
+
|
|
99
|
+
// Lazily resolve the heavy fallback loader (instant under an eager
|
|
100
|
+
// CodeProvider, a deduped fetch under CodeProviderLazy) before loading.
|
|
101
|
+
const loadCodeFallback = await loadCodeFallbackLoader();
|
|
72
102
|
const loaded = await loadCodeFallback(url, variantName, code, {
|
|
73
103
|
shouldHighlight: highlightAfter === 'init',
|
|
74
104
|
fallbackUsesExtraFiles,
|
|
@@ -82,19 +112,47 @@ function useInitialData({
|
|
|
82
112
|
variants,
|
|
83
113
|
globalsCode // Let loadCodeFallback handle processing
|
|
84
114
|
}).catch(error => ({
|
|
85
|
-
error
|
|
115
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
86
116
|
}));
|
|
87
117
|
if ('error' in loaded) {
|
|
88
118
|
console.error(new Errors.ErrorCodeHighlighterClientLoadFallbackFailure(loaded.error));
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
return code ?? {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Fold each variant's highlighted-visible `fallbackCritical` over its plain
|
|
123
|
+
// `fallback` (under `highlightAt: 'init'`) and strip the staging field, so the
|
|
124
|
+
// hoisted loading fallback is already highlighted and nothing leaks to the
|
|
125
|
+
// content. `collapseToEmpty` isn't threaded into the client here, so the `false`
|
|
126
|
+
// form is assumed: under collapse-to-empty this may promote a few frames that are
|
|
127
|
+
// then CSS-hidden, but that is harmless — the promoted text is byte-identical
|
|
128
|
+
// (a valid dictionary) and the frames never paint.
|
|
129
|
+
const resolved = resolveFallbackCritical(loaded.code, highlightAfter, false) ?? loaded.code;
|
|
130
|
+
|
|
131
|
+
// Strip fallbacks from code and hoist them directly
|
|
132
|
+
const {
|
|
133
|
+
strippedCode,
|
|
134
|
+
allFallbackHasts
|
|
135
|
+
} = stripFallbackHastsFromCode(resolved, variantName, fallbackUsesExtraFiles, fallbackUsesAllVariants);
|
|
136
|
+
if (!signal.aborted) {
|
|
137
|
+
setCode(strippedCode);
|
|
138
|
+
for (const [variant, hasts] of Object.entries(allFallbackHasts)) {
|
|
139
|
+
handleSetFallbackHasts(variant, hasts);
|
|
140
|
+
}
|
|
91
141
|
// Store processed globalsCode from loadCodeFallback result
|
|
92
142
|
if (loaded.processedGlobalsCode) {
|
|
93
143
|
setProcessedGlobalsCode(loaded.processedGlobalsCode);
|
|
94
144
|
}
|
|
95
145
|
}
|
|
96
|
-
|
|
97
|
-
|
|
146
|
+
return strippedCode;
|
|
147
|
+
}
|
|
148
|
+
}), [reason, variantName, code, setCode, highlightAfter, url, sourceParser, loadSource, loadVariantMeta, loadCodeMeta, sourceEnhancers, fallbackUsesExtraFiles, fallbackUsesAllVariants, fileName, variants, globalsCode, setProcessedGlobalsCode, loadCodeFallbackLoader, handleSetFallbackHasts]);
|
|
149
|
+
const fallbackConfig = React.useMemo(() => ({
|
|
150
|
+
ChunkContent: NoopChunkContent,
|
|
151
|
+
source: fallbackSource
|
|
152
|
+
}), [fallbackSource]);
|
|
153
|
+
useChunk(fallbackConfig, {
|
|
154
|
+
controlled: !needsFallback
|
|
155
|
+
});
|
|
98
156
|
return {
|
|
99
157
|
fallbackPending
|
|
100
158
|
};
|
|
@@ -115,7 +173,7 @@ function useAllVariants({
|
|
|
115
173
|
loadCodeMeta,
|
|
116
174
|
loadVariantMeta,
|
|
117
175
|
loadSource,
|
|
118
|
-
|
|
176
|
+
loadIsomorphicCodeVariantLoader,
|
|
119
177
|
sourceEnhancers
|
|
120
178
|
} = useCodeContext();
|
|
121
179
|
const needsData = !readyForContent && !isControlled && !fallbackPending;
|
|
@@ -126,7 +184,7 @@ function useAllVariants({
|
|
|
126
184
|
if (!url) {
|
|
127
185
|
throw new Errors.ErrorCodeHighlighterClientMissingUrlForVariants();
|
|
128
186
|
}
|
|
129
|
-
if (!
|
|
187
|
+
if (!loadIsomorphicCodeVariantLoader) {
|
|
130
188
|
throw new Errors.ErrorCodeHighlighterClientMissingLoadVariant(url);
|
|
131
189
|
}
|
|
132
190
|
if (!code && !loadCodeMeta) {
|
|
@@ -152,16 +210,24 @@ function useAllVariants({
|
|
|
152
210
|
throw new Errors.ErrorCodeHighlighterClientMissingLoadSourceForUnloadedUrls();
|
|
153
211
|
}
|
|
154
212
|
}
|
|
155
|
-
}, [code, globalsCode, loadCodeMeta,
|
|
156
|
-
React.useEffect(() => {
|
|
157
|
-
if (!needsData || !url || !loadIsomorphicCodeVariant) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
213
|
+
}, [code, globalsCode, loadCodeMeta, loadIsomorphicCodeVariantLoader, loadSource, needsData, url]);
|
|
160
214
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
215
|
+
// The full-variant load runs through `useChunk` so it inherits the abstraction's
|
|
216
|
+
// load-when-enabled + abort + `refresh()` (stale-while-revalidate) engine. The
|
|
217
|
+
// loader body is unchanged — it still calls `setCode` / `setProcessedGlobalsCode`
|
|
218
|
+
// directly (the chunk's own `data`/`loading` are unused; `code` stays owned by
|
|
219
|
+
// this component). `controlled: !needsData` is the gate: when the data isn't
|
|
220
|
+
// needed the chunk treats itself as already loaded and never runs the loader.
|
|
221
|
+
const fullVariantSource = React.useMemo(() => ({
|
|
222
|
+
mode: 'data',
|
|
223
|
+
load: async (_options, signal) => {
|
|
224
|
+
if (!url || !loadIsomorphicCodeVariantLoader) {
|
|
225
|
+
return code ?? {};
|
|
226
|
+
}
|
|
164
227
|
try {
|
|
228
|
+
// Lazily resolve the heavy variant loader (instant under an eager
|
|
229
|
+
// CodeProvider, a deduped fetch under CodeProviderLazy) before loading.
|
|
230
|
+
const loadIsomorphicCodeVariant = await loadIsomorphicCodeVariantLoader();
|
|
165
231
|
let loadedCode = code;
|
|
166
232
|
if (!loadedCode) {
|
|
167
233
|
if (!loadCodeMeta) {
|
|
@@ -189,7 +255,9 @@ function useAllVariants({
|
|
|
189
255
|
return item;
|
|
190
256
|
}));
|
|
191
257
|
// Store processed globalsCode in state for future use
|
|
192
|
-
|
|
258
|
+
if (!signal.aborted) {
|
|
259
|
+
setProcessedGlobalsCode(globalsCodeObjects);
|
|
260
|
+
}
|
|
193
261
|
}
|
|
194
262
|
|
|
195
263
|
// Load variant data without parsing or transforming
|
|
@@ -210,7 +278,7 @@ function useAllVariants({
|
|
|
210
278
|
name,
|
|
211
279
|
variant
|
|
212
280
|
})).catch(error => ({
|
|
213
|
-
error
|
|
281
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
214
282
|
}));
|
|
215
283
|
}));
|
|
216
284
|
const resultCode = {};
|
|
@@ -222,30 +290,37 @@ function useAllVariants({
|
|
|
222
290
|
resultCode[item.name] = item.variant.code;
|
|
223
291
|
}
|
|
224
292
|
}
|
|
293
|
+
|
|
294
|
+
// Strip the staging `fallbackCritical` before it enters `code` state and
|
|
295
|
+
// reaches the content. The full load runs with `disableParsing`, so the source
|
|
296
|
+
// is a raw string and no `fallbackCritical` is produced here — the strip is
|
|
297
|
+
// purely defensive, hence the strip-only `'idle'` (promotion is `'init'`-gated).
|
|
298
|
+
const resolvedResultCode = resolveFallbackCritical(resultCode, 'idle', false) ?? resultCode;
|
|
225
299
|
if (errors.length > 0) {
|
|
226
300
|
console.error(new Errors.ErrorCodeHighlighterClientLoadVariantsFailure(url, errors));
|
|
227
|
-
} else {
|
|
228
|
-
setCode(
|
|
301
|
+
} else if (!signal.aborted) {
|
|
302
|
+
setCode(resolvedResultCode);
|
|
229
303
|
}
|
|
304
|
+
return resolvedResultCode;
|
|
230
305
|
} catch (error) {
|
|
231
306
|
console.error(new Errors.ErrorCodeHighlighterClientLoadAllVariantsFailure(url, error));
|
|
307
|
+
return code ?? {};
|
|
232
308
|
}
|
|
233
|
-
}
|
|
234
|
-
}, [
|
|
309
|
+
}
|
|
310
|
+
}), [variants, url, code, setCode, loadSource, loadVariantMeta, loadCodeMeta, sourceEnhancers, processedGlobalsCode, globalsCode, setProcessedGlobalsCode, loadIsomorphicCodeVariantLoader]);
|
|
311
|
+
const fullVariantConfig = React.useMemo(() => ({
|
|
312
|
+
ChunkContent: NoopChunkContent,
|
|
313
|
+
source: fullVariantSource
|
|
314
|
+
}), [fullVariantSource]);
|
|
315
|
+
const {
|
|
316
|
+
refresh: refreshAllVariants
|
|
317
|
+
} = useChunk(fullVariantConfig, {
|
|
318
|
+
controlled: !needsData
|
|
319
|
+
});
|
|
235
320
|
return {
|
|
236
|
-
|
|
321
|
+
refresh: refreshAllVariants
|
|
237
322
|
};
|
|
238
323
|
}
|
|
239
|
-
function yieldToMain() {
|
|
240
|
-
if (globalThis.scheduler?.yield) {
|
|
241
|
-
return globalThis.scheduler.yield();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Fall back to yielding with setTimeout.
|
|
245
|
-
return new Promise(resolve => {
|
|
246
|
-
setTimeout(resolve, 0);
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
324
|
function useCodeParsing({
|
|
250
325
|
code,
|
|
251
326
|
readyForContent,
|
|
@@ -262,22 +337,18 @@ function useCodeParsing({
|
|
|
262
337
|
const [isHighlightAllowed, setIsHighlightAllowed] = React.useState(highlightAfter === 'init' || highlightAfter === 'hydration' && isHydrated);
|
|
263
338
|
React.useEffect(() => {
|
|
264
339
|
if (highlightAfter === 'idle') {
|
|
265
|
-
|
|
266
|
-
const cancelIdleCallback = window.cancelIdleCallback ?? clearTimeout;
|
|
267
|
-
const idleRequest = requestIdleCallback(() => {
|
|
268
|
-
setIsHighlightAllowed(true);
|
|
269
|
-
});
|
|
270
|
-
return () => cancelIdleCallback(idleRequest);
|
|
340
|
+
return requestIdle(() => setIsHighlightAllowed(true));
|
|
271
341
|
}
|
|
272
342
|
return undefined;
|
|
273
343
|
}, [highlightAfter]);
|
|
274
344
|
|
|
275
|
-
//
|
|
345
|
+
// Highlight instantly once hydrated, as a non-blocking client transition,
|
|
346
|
+
// rather than deferring to a scheduled task. (`highlightAt: 'idle'` above is
|
|
347
|
+
// the mode that deliberately keeps the unhighlighted first paint and swaps in
|
|
348
|
+
// the highlighted tree on a later idle render.)
|
|
276
349
|
React.useEffect(() => {
|
|
277
350
|
if (highlightAfter === 'hydration' && isHydrated) {
|
|
278
|
-
|
|
279
|
-
// this should run from top to bottom
|
|
280
|
-
yieldToMain().then(() => setIsHighlightAllowed(true));
|
|
351
|
+
React.startTransition(() => setIsHighlightAllowed(true));
|
|
281
352
|
}
|
|
282
353
|
}, [highlightAfter, isHydrated]);
|
|
283
354
|
|
|
@@ -289,9 +360,31 @@ function useCodeParsing({
|
|
|
289
360
|
return isHighlightAllowed;
|
|
290
361
|
}, [readyForContent, isHighlightAllowed]);
|
|
291
362
|
|
|
363
|
+
// Memoize the "every variant is already in HAST form" check so it
|
|
364
|
+
// doesn't re-walk the variant + extraFiles trees on every render.
|
|
365
|
+
// Used both as the short-circuit inside the `parseCode` memo (fully-
|
|
366
|
+
// precomputed sites skip parsing entirely) and as the unmemoized
|
|
367
|
+
// `waitingForParsedCode` gate just below.
|
|
368
|
+
const allVariantsAlreadyHighlighted = React.useMemo(() => code ? hasAllVariants(Object.keys(code), code, true) : false, [code]);
|
|
369
|
+
|
|
370
|
+
// Under `CodeProviderLazy` grammars load per-language and on demand, so the
|
|
371
|
+
// client parse must wait until the grammars for this block's scopes are
|
|
372
|
+
// registered — otherwise `parseSource` falls back to plain text. Gate the
|
|
373
|
+
// parse memo on readiness so the block keeps its fallback until they land (no
|
|
374
|
+
// plain-text flash), then highlights. Synchronously ready when warm (the
|
|
375
|
+
// speculative preload primed them, or under an eager `CodeProvider`), so this
|
|
376
|
+
// adds no delay on the common path.
|
|
377
|
+
const grammarScopes = React.useMemo(() => code ? detectGrammarScopes(code) : [], [code]);
|
|
378
|
+
const grammarsReady = useGrammarsReady(grammarScopes, !!code && shouldHighlight && !allVariantsAlreadyHighlighted);
|
|
379
|
+
|
|
292
380
|
// Parse the internal code state when ready and timing conditions are met
|
|
293
381
|
const parsedCode = React.useMemo(() => {
|
|
294
|
-
if (!code || !shouldHighlight ||
|
|
382
|
+
if (!code || !shouldHighlight || allVariantsAlreadyHighlighted) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
if (!grammarsReady) {
|
|
386
|
+
// Grammars for this block's scopes are still loading; keep the fallback and
|
|
387
|
+
// re-run once `useGrammarsReady` flips (mirrors the `!parseSource` wait).
|
|
295
388
|
return undefined;
|
|
296
389
|
}
|
|
297
390
|
if (!parseSource) {
|
|
@@ -317,54 +410,136 @@ function useCodeParsing({
|
|
|
317
410
|
return undefined;
|
|
318
411
|
}
|
|
319
412
|
return parseCode(code, parseSource);
|
|
320
|
-
}, [code, shouldHighlight, sourceParser, parseSource, parseCode, forceClient, url]);
|
|
321
|
-
|
|
413
|
+
}, [code, shouldHighlight, allVariantsAlreadyHighlighted, grammarsReady, sourceParser, parseSource, parseCode, forceClient, url]);
|
|
414
|
+
|
|
415
|
+
// Keep highlighting deferred until parsed HAST is actually available for the
|
|
416
|
+
// variants that need it. `shouldHighlight` can flip true ~30ms after
|
|
417
|
+
// hydration, but `parseCode` only runs once the async `sourceParser` promise
|
|
418
|
+
// resolves. Without this wait, downstream consumers (e.g. the transform
|
|
419
|
+
// swap) would commit while the visible variant is still rendered from its
|
|
420
|
+
// raw string source, producing a structure swap on the DOM moments later.
|
|
421
|
+
const waitingForParsedCode = shouldHighlight && !!code && !allVariantsAlreadyHighlighted && !parsedCode;
|
|
422
|
+
|
|
423
|
+
// Only signal `deferHighlight` while a highlight pass is actively in
|
|
424
|
+
// flight. When `shouldHighlight` is `false` (e.g. `highlightAt: 'idle'`
|
|
425
|
+
// before the idle window fires, or `'view'` before the block scrolls
|
|
426
|
+
// into view) we render the un-highlighted source as-is — downstream
|
|
427
|
+
// consumers like `useTransformManagement`'s `awaitHighlight` gate must
|
|
428
|
+
// commit eagerly against that source instead of blocking the barrier
|
|
429
|
+
// indefinitely. Once the trigger fires, `shouldHighlight` flips true,
|
|
430
|
+
// `waitingForParsedCode` becomes true while `parseCode` runs, and
|
|
431
|
+
// `deferHighlight` engages for the brief window before the next
|
|
432
|
+
// commit paints the highlighted tree.
|
|
433
|
+
const deferHighlight = waitingForParsedCode;
|
|
434
|
+
|
|
435
|
+
// Render-side readiness gate. `<Pre>` (via `useCode.shouldHighlight`)
|
|
436
|
+
// needs to know whether the published `code` should be rendered as
|
|
437
|
+
// highlighted HAST *now*. That answer is false in two distinct
|
|
438
|
+
// windows that `deferHighlight` deliberately collapses out:
|
|
439
|
+
// 1. The trigger for `highlightAt: 'hydration' | 'idle' | 'visible'`
|
|
440
|
+
// hasn't fired yet — `shouldHighlight` is still false. The
|
|
441
|
+
// precomputed `codeWithGlobals` already contains HAST, so
|
|
442
|
+
// without a render-side gate `<Pre>` would render highlighted
|
|
443
|
+
// spans on the SSR pass and on first client paint, defeating
|
|
444
|
+
// the whole point of deferred highlighting.
|
|
445
|
+
// 2. The trigger has fired (`shouldHighlight = true`) but
|
|
446
|
+
// `parseCode` hasn't resolved yet (`waitingForParsedCode`).
|
|
447
|
+
// Rendering would briefly flash un-highlighted text against
|
|
448
|
+
// the same tree position before the highlighted HAST lands.
|
|
449
|
+
//
|
|
450
|
+
// `highlightReady` is the inverse of the pre-`e7cc08b7` wide
|
|
451
|
+
// `deferHighlight` semantic, exposed separately so the narrow
|
|
452
|
+
// `deferHighlight` (barrier consumers only block on real in-flight
|
|
453
|
+
// work) and the render gate can diverge without coupling.
|
|
454
|
+
const highlightReady = shouldHighlight && !waitingForParsedCode;
|
|
322
455
|
return {
|
|
323
456
|
parsedCode,
|
|
324
|
-
deferHighlight
|
|
457
|
+
deferHighlight,
|
|
458
|
+
highlightReady
|
|
325
459
|
};
|
|
326
460
|
}
|
|
327
461
|
function useCodeTransforms({
|
|
328
462
|
parsedCode,
|
|
463
|
+
loadedCode,
|
|
329
464
|
variantName
|
|
330
465
|
}) {
|
|
331
466
|
const {
|
|
332
467
|
sourceParser,
|
|
333
|
-
|
|
334
|
-
computeHastDeltas
|
|
468
|
+
computeHastDeltasLoader
|
|
335
469
|
} = useCodeContext();
|
|
336
|
-
|
|
470
|
+
// Track which `parsedCode` the cached `transformedCode` was computed from
|
|
471
|
+
// so a fresh `parsedCode` (e.g. a newly-loaded variant being added to the
|
|
472
|
+
// map) re-engages `waitingForTransformedCode` instead of returning the
|
|
473
|
+
// stale output for one render cycle. Storing input + output together lets
|
|
474
|
+
// callers detect staleness with reference equality.
|
|
475
|
+
const [transformedState, setTransformedState] = React.useState({});
|
|
337
476
|
|
|
338
477
|
// Get available transforms from the current variant (separate memo for efficiency)
|
|
339
|
-
const availableTransforms = React.useMemo(() =>
|
|
340
|
-
if (!getAvailableTransforms) {
|
|
341
|
-
return [];
|
|
342
|
-
}
|
|
343
|
-
return getAvailableTransforms(parsedCode, variantName);
|
|
344
|
-
}, [parsedCode, variantName, getAvailableTransforms]);
|
|
478
|
+
const availableTransforms = React.useMemo(() => getAvailableTransforms(parsedCode ?? loadedCode, variantName), [parsedCode, loadedCode, variantName]);
|
|
345
479
|
|
|
346
|
-
// Effect to compute transformations for all variants
|
|
480
|
+
// Effect to compute transformations for all variants. Only runs when the
|
|
481
|
+
// full async pipeline is wired (`parsedCode` + worker + deltas computer);
|
|
482
|
+
// the no-async case is derived during render below instead of being stored,
|
|
483
|
+
// so this effect never publishes a synchronous pass-through state.
|
|
347
484
|
React.useEffect(() => {
|
|
348
|
-
if (!parsedCode || !sourceParser || !
|
|
349
|
-
setTransformedCode(parsedCode);
|
|
485
|
+
if (!parsedCode || !sourceParser || !computeHastDeltasLoader) {
|
|
350
486
|
return;
|
|
351
487
|
}
|
|
352
488
|
|
|
353
489
|
// Process transformations for all variants
|
|
354
490
|
(async () => {
|
|
355
491
|
try {
|
|
356
|
-
|
|
492
|
+
// Resolve the parser and the (lazy) transform-delta computer in parallel
|
|
493
|
+
// before computing deltas. computeHastDeltas pulls jsondiffpatch, so it's
|
|
494
|
+
// kept out of the initial bundle under CodeProviderLazy.
|
|
495
|
+
const [parseSource, computeHastDeltas] = await Promise.all([sourceParser, computeHastDeltasLoader()]);
|
|
357
496
|
const enhanced = await computeHastDeltas(parsedCode, parseSource);
|
|
358
|
-
|
|
497
|
+
setTransformedState({
|
|
498
|
+
input: parsedCode,
|
|
499
|
+
output: enhanced
|
|
500
|
+
});
|
|
359
501
|
} catch (error) {
|
|
360
502
|
console.error(new Errors.ErrorCodeHighlighterClientTransformProcessingFailure(error));
|
|
361
|
-
|
|
503
|
+
setTransformedState({
|
|
504
|
+
input: parsedCode,
|
|
505
|
+
output: parsedCode
|
|
506
|
+
});
|
|
362
507
|
}
|
|
363
508
|
})();
|
|
364
|
-
}, [parsedCode, sourceParser,
|
|
509
|
+
}, [parsedCode, sourceParser, computeHastDeltasLoader]);
|
|
510
|
+
|
|
511
|
+
// When the full async pipeline is wired, expose the cached output regardless
|
|
512
|
+
// of whether `parsedCode` changed since the last computation — falling back
|
|
513
|
+
// to `undefined` here would yank the currently-displayed HAST for a frame
|
|
514
|
+
// while the async pipeline catches up. Staleness is signalled via
|
|
515
|
+
// `waitingForTransformedCode` so downstream gates (e.g.
|
|
516
|
+
// `useTransformManagement` / `useVariantSelection`) hold off committing a
|
|
517
|
+
// swap until fresh deltas land. Without the pipeline, `transformedCode` is a
|
|
518
|
+
// synchronous pass-through of `parsedCode` derived during render.
|
|
519
|
+
const hasAsyncPipeline = !!parsedCode && !!sourceParser && !!computeHastDeltasLoader;
|
|
520
|
+
const transformedCode = hasAsyncPipeline ? transformedState.output : parsedCode;
|
|
521
|
+
|
|
522
|
+
// Async hast-deltas pipeline status. While true, consumers (notably
|
|
523
|
+
// `useTransformManagement`'s `deferHighlight` gate) should treat
|
|
524
|
+
// highlighting as not-yet-settled and hold off committing a transform
|
|
525
|
+
// swap. Without this, the swap can commit after `parsedCode` is ready
|
|
526
|
+
// but *before* `computeHastDeltas` resolves: the incoming tree first
|
|
527
|
+
// renders without the transform deltas, then re-renders a frame or
|
|
528
|
+
// two later when `transformedCode` arrives, producing a visible jump
|
|
529
|
+
// on top of the just-played collapse animation.
|
|
530
|
+
//
|
|
531
|
+
// Only relevant when both a worker (`sourceParser`) and a deltas
|
|
532
|
+
// computer (`computeHastDeltas`) are wired up — environments without
|
|
533
|
+
// them resolve `transformedCode` synchronously to `parsedCode` in the
|
|
534
|
+
// effect above, so the deltas phase is a no-op. We compare the cached
|
|
535
|
+
// `input` against the live `parsedCode` instead of just checking
|
|
536
|
+
// `!transformedCode` so a freshly-arriving variant re-engages the wait
|
|
537
|
+
// until its deltas land.
|
|
538
|
+
const waitingForTransformedCode = hasAsyncPipeline && transformedState.input !== parsedCode;
|
|
365
539
|
return {
|
|
366
540
|
transformedCode,
|
|
367
|
-
availableTransforms
|
|
541
|
+
availableTransforms,
|
|
542
|
+
waitingForTransformedCode
|
|
368
543
|
};
|
|
369
544
|
}
|
|
370
545
|
function useControlledCodeParsing({
|
|
@@ -374,6 +549,7 @@ function useControlledCodeParsing({
|
|
|
374
549
|
preParsedCache
|
|
375
550
|
}) {
|
|
376
551
|
const {
|
|
552
|
+
sourceParser,
|
|
377
553
|
parseSource,
|
|
378
554
|
parseControlledCode
|
|
379
555
|
} = useCodeContext();
|
|
@@ -383,26 +559,30 @@ function useControlledCodeParsing({
|
|
|
383
559
|
if (!code) {
|
|
384
560
|
return undefined;
|
|
385
561
|
}
|
|
386
|
-
if (!parseSource
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
console.error(new Errors.ErrorCodeHighlighterClientMissingParseSource(url, false));
|
|
393
|
-
}
|
|
562
|
+
if (!parseSource) {
|
|
563
|
+
// A CodeProvider is present and its async `sourceParser` promise hasn't
|
|
564
|
+
// resolved yet (e.g. CodeProviderLazy dynamic-importing the engine) — wait
|
|
565
|
+
// for it instead of erroring. The memo re-runs once `parseSource` lands.
|
|
566
|
+
if (sourceParser) {
|
|
567
|
+
return undefined;
|
|
394
568
|
}
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
569
|
+
if (forceClient) {
|
|
570
|
+
console.error(new Errors.ErrorCodeHighlighterClientMissingParseSource(url, true));
|
|
571
|
+
} else {
|
|
572
|
+
console.error(new Errors.ErrorCodeHighlighterClientMissingParseSource(url, false));
|
|
573
|
+
}
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
if (!parseControlledCode) {
|
|
577
|
+
if (forceClient) {
|
|
578
|
+
console.error(new Errors.ErrorCodeHighlighterClientMissingParseControlledCode(url, true));
|
|
579
|
+
} else {
|
|
580
|
+
console.error(new Errors.ErrorCodeHighlighterClientMissingParseControlledCode(url, false));
|
|
401
581
|
}
|
|
402
582
|
return undefined;
|
|
403
583
|
}
|
|
404
584
|
return parseControlledCode(code, parseSource, preParsedCache);
|
|
405
|
-
}, [code, parseSource, parseControlledCode, forceClient, url, preParsedCache]);
|
|
585
|
+
}, [code, sourceParser, parseSource, parseControlledCode, forceClient, url, preParsedCache]);
|
|
406
586
|
return {
|
|
407
587
|
parsedControlledCode
|
|
408
588
|
};
|
|
@@ -420,7 +600,7 @@ function useGlobalsCodeMerging({
|
|
|
420
600
|
loadCodeMeta,
|
|
421
601
|
loadSource,
|
|
422
602
|
loadVariantMeta,
|
|
423
|
-
|
|
603
|
+
loadIsomorphicCodeVariantLoader
|
|
424
604
|
} = useCodeContext();
|
|
425
605
|
|
|
426
606
|
// Set processedGlobalsCode if we have ready Code objects but haven't stored them yet
|
|
@@ -440,7 +620,7 @@ function useGlobalsCodeMerging({
|
|
|
440
620
|
}
|
|
441
621
|
// If not all ready, fall through to loading logic below
|
|
442
622
|
}
|
|
443
|
-
if (!
|
|
623
|
+
if (!loadIsomorphicCodeVariantLoader) {
|
|
444
624
|
console.error(new Errors.ErrorCodeHighlighterClientMissingLoadVariantForGlobals());
|
|
445
625
|
return;
|
|
446
626
|
}
|
|
@@ -448,6 +628,8 @@ function useGlobalsCodeMerging({
|
|
|
448
628
|
// Need to load string URLs or load missing variants
|
|
449
629
|
(async () => {
|
|
450
630
|
try {
|
|
631
|
+
const loadIsomorphicCodeVariant = await loadIsomorphicCodeVariantLoader();
|
|
632
|
+
|
|
451
633
|
// First, load any string URLs into Code objects
|
|
452
634
|
const basicCodeObjects = await Promise.all(globalsCode.map(async item => {
|
|
453
635
|
if (typeof item === 'string') {
|
|
@@ -509,7 +691,7 @@ function useGlobalsCodeMerging({
|
|
|
509
691
|
console.error(new Errors.ErrorCodeHighlighterClientLoadGlobalsCodeFailure(url || 'No URL', error));
|
|
510
692
|
}
|
|
511
693
|
})();
|
|
512
|
-
}, [url, globalsCode, processedGlobalsCode, setProcessedGlobalsCode, loadCodeMeta, loadSource, loadVariantMeta, variants,
|
|
694
|
+
}, [url, globalsCode, processedGlobalsCode, setProcessedGlobalsCode, loadCodeMeta, loadSource, loadVariantMeta, variants, loadIsomorphicCodeVariantLoader]);
|
|
513
695
|
|
|
514
696
|
// Determine globalsCodeObjects to use (prefer processed, fallback to direct if ready)
|
|
515
697
|
const globalsCodeObjects = React.useMemo(() => {
|
|
@@ -657,15 +839,23 @@ export function CodeHighlighterClient(props) {
|
|
|
657
839
|
const isControlled = Boolean(props.code || controlled?.code);
|
|
658
840
|
const [code, setCode] = React.useState(typeof props.precompute === 'object' ? props.precompute : undefined);
|
|
659
841
|
|
|
660
|
-
// Sync code state with precompute prop changes (for hot-reload)
|
|
661
|
-
|
|
842
|
+
// Sync code state with precompute prop changes (for hot-reload). Done with
|
|
843
|
+
// the store-previous-prop render-phase derivation rather than an effect:
|
|
844
|
+
// `code` is genuinely state (also mutated by `useInitialData` via `setCode`
|
|
845
|
+
// for client fallback loading) so it can't be pure derivation, but the
|
|
846
|
+
// re-seed on a new `precompute` is a render-time setState off the previous
|
|
847
|
+
// prop value. Match the original effect's branch logic: only object values
|
|
848
|
+
// re-seed and only an explicit `undefined` clears — any other value (e.g. a
|
|
849
|
+
// loader) leaves `code` untouched.
|
|
850
|
+
const [prevPrecompute, setPrevPrecompute] = React.useState(props.precompute);
|
|
851
|
+
if (props.precompute !== prevPrecompute) {
|
|
852
|
+
setPrevPrecompute(props.precompute);
|
|
662
853
|
if (typeof props.precompute === 'object') {
|
|
663
854
|
setCode(props.precompute);
|
|
664
855
|
} else if (props.precompute === undefined) {
|
|
665
|
-
// Only reset to undefined if precompute is explicitly undefined
|
|
666
856
|
setCode(undefined);
|
|
667
857
|
}
|
|
668
|
-
}
|
|
858
|
+
}
|
|
669
859
|
|
|
670
860
|
// State to store processed globalsCode to avoid duplicate loading
|
|
671
861
|
const [processedGlobalsCode, setProcessedGlobalsCode] = React.useState(undefined);
|
|
@@ -689,8 +879,112 @@ export function CodeHighlighterClient(props) {
|
|
|
689
879
|
highlightAfter,
|
|
690
880
|
enhanceAfter,
|
|
691
881
|
fallbackUsesExtraFiles,
|
|
692
|
-
fallbackUsesAllVariants
|
|
882
|
+
fallbackUsesAllVariants,
|
|
883
|
+
editActivation
|
|
693
884
|
} = props;
|
|
885
|
+
|
|
886
|
+
// Speculative preload: on first render, start fetching the heavy loaders this
|
|
887
|
+
// block is about to need (under CodeProviderLazy) so they're in flight before
|
|
888
|
+
// the content mounts and awaits them. Signals are cheap + accurate, so a
|
|
889
|
+
// precomputed or code-free block preloads nothing.
|
|
890
|
+
// Only the precomputed/loaded (non-controlled) code drives speculative loading.
|
|
891
|
+
const speculativeCode = isControlled ? undefined : code;
|
|
892
|
+
const speculativeGrammarScopes = React.useMemo(() => speculativeCode ? detectGrammarScopes(speculativeCode) : [], [speculativeCode]);
|
|
893
|
+
const speculativeAllPresent = React.useMemo(() => speculativeCode ? hasAllVariants(variants, speculativeCode) : false, [variants, speculativeCode]);
|
|
894
|
+
const speculativeHasTransforms = React.useMemo(() => !!speculativeCode && !hasAllVariants(variants, speculativeCode, true) && getAvailableTransforms(speculativeCode, variantName).length > 0, [variants, speculativeCode, variantName]);
|
|
895
|
+
useSpeculativeCodePreload({
|
|
896
|
+
needsData: !isControlled && !!url && !speculativeAllPresent,
|
|
897
|
+
hasTransforms: speculativeHasTransforms
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// Per-block editing activation: flipped once when the block first engages for
|
|
901
|
+
// editing — threaded down to `useEditable.onActivate` via `CodeHighlighterContext`
|
|
902
|
+
// (immediately in `'eager'`, on hover/focus/click in `'interaction'`). Drives
|
|
903
|
+
// the editable speculative preload below and notifies the CodeControllerContext.
|
|
904
|
+
const [editingActivated, setEditingActivated] = React.useState(false);
|
|
905
|
+
const controllerOnActivate = controlled?.onActivate;
|
|
906
|
+
const handleEditingActivated = React.useCallback(() => {
|
|
907
|
+
setEditingActivated(true);
|
|
908
|
+
controllerOnActivate?.();
|
|
909
|
+
}, [controllerOnActivate]);
|
|
910
|
+
|
|
911
|
+
// Grammar scopes the editable files need for live re-highlighting. Unlike the
|
|
912
|
+
// speculative highlight/transform preloads — which intentionally skip
|
|
913
|
+
// controlled blocks (`speculativeCode` is cleared above) — an editable block
|
|
914
|
+
// DOES re-highlight its edits on the client, so its grammars must load or the
|
|
915
|
+
// edited source falls back to plain text. The editable file set (and thus the
|
|
916
|
+
// scopes) comes from `props.code`: editing changes source *content*, never
|
|
917
|
+
// which files exist, so this stays stable across keystrokes.
|
|
918
|
+
const editableGrammarScopes = React.useMemo(() => {
|
|
919
|
+
const editableCode = props.code ?? code;
|
|
920
|
+
return editableCode ? detectGrammarScopes(editableCode) : [];
|
|
921
|
+
}, [props.code, code]);
|
|
922
|
+
|
|
923
|
+
// When the block is editable (a CodeControllerContext with `setCode` is in
|
|
924
|
+
// scope), warm the live-editing engine, the per-language grammars, and the
|
|
925
|
+
// worker so they're in flight before the user edits. Deduped page-wide. In
|
|
926
|
+
// `editActivation: 'interaction'` mode the warming waits until the block is
|
|
927
|
+
// `activated` (engaged) — that mode defers loading until the reader engages.
|
|
928
|
+
useSpeculativeEditingPreload({
|
|
929
|
+
enabled: Boolean(controlled?.setCode),
|
|
930
|
+
editActivation,
|
|
931
|
+
activated: editingActivated,
|
|
932
|
+
scopes: editableGrammarScopes
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Preload the client-side transform applier (the `jsondiffpatch` chunk) when
|
|
936
|
+
// the code declares transforms — so it is warm before the reader switches a
|
|
937
|
+
// transform, in parallel with the (lazy) content. Broader than the
|
|
938
|
+
// `speculativeHasTransforms` highlight signal above: even a fully-precomputed
|
|
939
|
+
// (already-highlighted) block needs the applier to switch transforms
|
|
940
|
+
// client-side, so this drops the not-yet-highlighted gate. A block with no
|
|
941
|
+
// transforms never pulls the chunk.
|
|
942
|
+
const speculativeHasAnyTransforms = React.useMemo(() => speculativeCode ? getAvailableTransforms(speculativeCode, variantName).length > 0 : false, [speculativeCode, variantName]);
|
|
943
|
+
useSpeculativeUseCodePreload({
|
|
944
|
+
hasTransforms: speculativeHasAnyTransforms
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
// Preload the per-language grammar chunks this block needs, before `useCode`
|
|
948
|
+
// mounts and parses — in parallel with the (lazy) content. Only when the block
|
|
949
|
+
// will actually highlight client-side: it is forced client-side, not yet
|
|
950
|
+
// fully precomputed (so the client must parse), or eagerly editable (live
|
|
951
|
+
// re-highlight). A fully-precomputed read-only block renders its highlighted
|
|
952
|
+
// HAST and never parses, so it loads no grammar at all.
|
|
953
|
+
const willClientHighlight = !!speculativeCode && (Boolean(props.forceClient) || !hasAllVariants(variants, speculativeCode, true) || (editActivation ?? 'eager') !== 'interaction' && Boolean(controlled?.setCode));
|
|
954
|
+
useSpeculativeGrammarPreload({
|
|
955
|
+
scopes: speculativeGrammarScopes,
|
|
956
|
+
enabled: willClientHighlight
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
// ── Fallback hoisting ──
|
|
960
|
+
// State for fallbacks hoisted from ContentLoading via useCodeFallback.
|
|
961
|
+
// Content is stripped from Code on the server and passed to ContentLoading
|
|
962
|
+
// as source/extraSource props. ContentLoading hoists them back here so
|
|
963
|
+
// CodeHighlighterClient can derive text dictionaries for decompression.
|
|
964
|
+
const [hoistedFallbackHasts, setHoistedFallbackHasts] = React.useState({});
|
|
965
|
+
|
|
966
|
+
// Track whether ContentLoading called useCodeFallback via callback. The
|
|
967
|
+
// force-mount-once behavior (mounting the fallback even when the code is
|
|
968
|
+
// already ready, so `useCodeFallback` can hoist the DEFLATE dictionary) is now
|
|
969
|
+
// owned by `useCoordinatedSwap` below; this ref only drives the dev-time
|
|
970
|
+
// validation that ContentLoading wired its hoist hook.
|
|
971
|
+
const hookCalledRef = React.useRef(false);
|
|
972
|
+
const handleHookCalled = React.useCallback(() => {
|
|
973
|
+
hookCalledRef.current = true;
|
|
974
|
+
}, []);
|
|
975
|
+
|
|
976
|
+
// Stable callback for ContentLoading to hoist its fallbacks.
|
|
977
|
+
const handleSetFallbackHasts = React.useCallback((variant, hasts) => {
|
|
978
|
+
setHoistedFallbackHasts(prev => {
|
|
979
|
+
if (prev[variant] === hasts) {
|
|
980
|
+
return prev;
|
|
981
|
+
}
|
|
982
|
+
return {
|
|
983
|
+
...prev,
|
|
984
|
+
[variant]: hasts
|
|
985
|
+
};
|
|
986
|
+
});
|
|
987
|
+
}, []);
|
|
694
988
|
const {
|
|
695
989
|
fallbackPending
|
|
696
990
|
} = useInitialData({
|
|
@@ -705,9 +999,59 @@ export function CodeHighlighterClient(props) {
|
|
|
705
999
|
fallbackUsesAllVariants,
|
|
706
1000
|
isControlled,
|
|
707
1001
|
globalsCode: props.globalsCode,
|
|
708
|
-
setProcessedGlobalsCode
|
|
1002
|
+
setProcessedGlobalsCode,
|
|
1003
|
+
handleSetFallbackHasts
|
|
709
1004
|
});
|
|
710
1005
|
|
|
1006
|
+
// Reverse the server-side residual consolidation, scattering the decompressed
|
|
1007
|
+
// fallbacks back onto the code so every variant carries its own dictionary
|
|
1008
|
+
// (the swap line-count classifier reads `code.fallback`, not the active-only
|
|
1009
|
+
// hoist). The blob is primed with the RENDERED subset's text, which reaches
|
|
1010
|
+
// the client only via the hoist — so wait for that subset to hoist before
|
|
1011
|
+
// decompressing. WHICH variants are rendered depends on `fallbackUsesAllVariants`
|
|
1012
|
+
// (every variant, or just the initial one); gate on THAT subset, never on the
|
|
1013
|
+
// *current* `variantName`, or swapping to a non-rendered variant drops the
|
|
1014
|
+
// scatter and strands the other variants without their dictionary.
|
|
1015
|
+
const residualFallbacks = props.residualFallbacks;
|
|
1016
|
+
const renderedVariant = props.initialVariant || props.defaultVariant || variants[0];
|
|
1017
|
+
const renderedHoisted = fallbackUsesAllVariants ? variants.every(variant => Boolean(hoistedFallbackHasts[variant])) : Boolean(hoistedFallbackHasts[renderedVariant]);
|
|
1018
|
+
const residualMap = React.useMemo(() => {
|
|
1019
|
+
if (!residualFallbacks || !renderedHoisted) {
|
|
1020
|
+
return undefined;
|
|
1021
|
+
}
|
|
1022
|
+
return decompressResidualFallbacks(residualFallbacks, residualDictionaryText(hoistedFallbackHasts));
|
|
1023
|
+
}, [residualFallbacks, renderedHoisted, hoistedFallbackHasts]);
|
|
1024
|
+
|
|
1025
|
+
// Scatter the dictionaries back onto whichever code carries it, so consumers
|
|
1026
|
+
// (the render and the swap line-count classifier) read `code.fallback` for any
|
|
1027
|
+
// variant. Two sources: the decompressed residual blob (`residualMap` — the
|
|
1028
|
+
// non-rendered variants, and under `fallbackUsesAllVariants` the blob is empty)
|
|
1029
|
+
// and the hoist (`hoistedFallbackHasts` — the rendered subset, which is the ONLY
|
|
1030
|
+
// place every variant's dictionary lives under `fallbackUsesAllVariants`). Skip
|
|
1031
|
+
// the hoist under `fallbackCollapsed`, where it is only each file's collapsed
|
|
1032
|
+
// window; the full dictionary comes from the blob there. Memoized so the
|
|
1033
|
+
// freshly-cloned code keeps a stable identity until its inputs change.
|
|
1034
|
+
const restoreFallbacks = React.useCallback(base => {
|
|
1035
|
+
if (!base) {
|
|
1036
|
+
return base;
|
|
1037
|
+
}
|
|
1038
|
+
let restored = residualMap ? scatterResidualFallbacks(base, residualMap) : base;
|
|
1039
|
+
if (!props.fallbackCollapsed) {
|
|
1040
|
+
// `preserveExisting`: never let the hoist overwrite a `fallback` already
|
|
1041
|
+
// on the variant. A fully-loaded `hastCompressed` source carries its own
|
|
1042
|
+
// source-paired (structured) `fallback`, which is the only valid DEFLATE
|
|
1043
|
+
// dictionary. The hoist can be an un-highlighted *raw-string* fallback
|
|
1044
|
+
// whose text keeps a trailing newline `buildRootFallback` drops, so
|
|
1045
|
+
// overwriting the structured one makes `decodeHastSource` throw a
|
|
1046
|
+
// dictionary mismatch. The hoist is the dictionary only when the variant's
|
|
1047
|
+
// own was stripped, so apply it solely where one isn't already present.
|
|
1048
|
+
restored = scatterResidualFallbacks(restored, hoistedFallbackHasts, true);
|
|
1049
|
+
}
|
|
1050
|
+
return restored;
|
|
1051
|
+
}, [residualMap, hoistedFallbackHasts, props.fallbackCollapsed]);
|
|
1052
|
+
const resolvedPropsCode = React.useMemo(() => restoreFallbacks(props.code), [props.code, restoreFallbacks]);
|
|
1053
|
+
const resolvedStateCode = React.useMemo(() => restoreFallbacks(code), [code, restoreFallbacks]);
|
|
1054
|
+
|
|
711
1055
|
// Use useSyncExternalStore to detect hydration
|
|
712
1056
|
const subscribe = React.useCallback(() => () => {}, []);
|
|
713
1057
|
const getSnapshot = React.useCallback(() => true, []);
|
|
@@ -717,22 +1061,18 @@ export function CodeHighlighterClient(props) {
|
|
|
717
1061
|
const [isEnhanceAllowed, setIsEnhanceAllowed] = React.useState(enhanceAfter === 'init' || enhanceAfter === 'hydration' && isHydrated);
|
|
718
1062
|
React.useEffect(() => {
|
|
719
1063
|
if (enhanceAfter === 'idle') {
|
|
720
|
-
|
|
721
|
-
const cancelIdleCallback = window.cancelIdleCallback ?? clearTimeout;
|
|
722
|
-
const idleRequest = requestIdleCallback(() => {
|
|
723
|
-
setIsEnhanceAllowed(true);
|
|
724
|
-
});
|
|
725
|
-
return () => cancelIdleCallback(idleRequest);
|
|
1064
|
+
return requestIdle(() => setIsEnhanceAllowed(true));
|
|
726
1065
|
}
|
|
727
1066
|
return undefined;
|
|
728
1067
|
}, [enhanceAfter]);
|
|
729
1068
|
|
|
730
|
-
//
|
|
1069
|
+
// Enhance instantly once hydrated, as a non-blocking client transition,
|
|
1070
|
+
// rather than deferring to a scheduled task. (`enhanceAfter: 'idle'` above is
|
|
1071
|
+
// the mode that deliberately keeps the un-enhanced first paint and swaps in
|
|
1072
|
+
// the enhanced tree on a later idle render.)
|
|
731
1073
|
React.useEffect(() => {
|
|
732
1074
|
if (enhanceAfter === 'hydration' && isHydrated) {
|
|
733
|
-
|
|
734
|
-
// this should run from top to bottom
|
|
735
|
-
yieldToMain().then(() => setIsEnhanceAllowed(true));
|
|
1075
|
+
React.startTransition(() => setIsEnhanceAllowed(true));
|
|
736
1076
|
}
|
|
737
1077
|
}, [enhanceAfter, isHydrated]);
|
|
738
1078
|
const readyForContent = React.useMemo(() => {
|
|
@@ -757,7 +1097,9 @@ export function CodeHighlighterClient(props) {
|
|
|
757
1097
|
const regularCode = props.code || code;
|
|
758
1098
|
return regularCode ? hasAllVariants(variants, regularCode) : false;
|
|
759
1099
|
}, [activeCode, isEnhanceAllowed, controlled?.code, variants, props.code, code]);
|
|
760
|
-
|
|
1100
|
+
const {
|
|
1101
|
+
refresh: refreshAllVariants
|
|
1102
|
+
} = useAllVariants({
|
|
761
1103
|
readyForContent,
|
|
762
1104
|
variants,
|
|
763
1105
|
isControlled,
|
|
@@ -773,7 +1115,7 @@ export function CodeHighlighterClient(props) {
|
|
|
773
1115
|
// Merge globalsCode with internal state code (fetched data) - this should be stable once ready
|
|
774
1116
|
const stateCodeWithGlobals = useGlobalsCodeMerging({
|
|
775
1117
|
url,
|
|
776
|
-
code,
|
|
1118
|
+
code: resolvedStateCode,
|
|
777
1119
|
// Only use internal state, not props.code
|
|
778
1120
|
globalsCode: props.globalsCode,
|
|
779
1121
|
processedGlobalsCode,
|
|
@@ -784,7 +1126,7 @@ export function CodeHighlighterClient(props) {
|
|
|
784
1126
|
|
|
785
1127
|
// For props.code (controlled), always re-merge when it changes (don't cache in state)
|
|
786
1128
|
const propsCodeWithGlobals = usePropsCodeGlobalsMerging({
|
|
787
|
-
code:
|
|
1129
|
+
code: resolvedPropsCode,
|
|
788
1130
|
globalsCode: props.globalsCode,
|
|
789
1131
|
processedGlobalsCode,
|
|
790
1132
|
variants
|
|
@@ -794,7 +1136,8 @@ export function CodeHighlighterClient(props) {
|
|
|
794
1136
|
const codeWithGlobals = propsCodeWithGlobals || stateCodeWithGlobals;
|
|
795
1137
|
const {
|
|
796
1138
|
parsedCode,
|
|
797
|
-
deferHighlight
|
|
1139
|
+
deferHighlight: deferHighlightForParsing,
|
|
1140
|
+
highlightReady
|
|
798
1141
|
} = useCodeParsing({
|
|
799
1142
|
code: codeWithGlobals,
|
|
800
1143
|
readyForContent: readyForContent || Boolean(props.code),
|
|
@@ -805,18 +1148,90 @@ export function CodeHighlighterClient(props) {
|
|
|
805
1148
|
});
|
|
806
1149
|
const {
|
|
807
1150
|
transformedCode,
|
|
808
|
-
availableTransforms
|
|
1151
|
+
availableTransforms,
|
|
1152
|
+
waitingForTransformedCode
|
|
809
1153
|
} = useCodeTransforms({
|
|
810
1154
|
parsedCode,
|
|
1155
|
+
loadedCode: codeWithGlobals,
|
|
811
1156
|
variantName
|
|
812
1157
|
});
|
|
813
1158
|
|
|
1159
|
+
// Combined highlight-readiness gate consumed via context (notably by
|
|
1160
|
+
// `useTransformManagement`). Stay deferred while either the sync
|
|
1161
|
+
// `parseCode` pass or the async `computeHastDeltas` pass is still in
|
|
1162
|
+
// flight — committing a transform swap with `transformedCode` still
|
|
1163
|
+
// pending causes the incoming pre to first render without the
|
|
1164
|
+
// transform deltas and then re-flow a frame or two later when the
|
|
1165
|
+
// deltas land, producing a visible jump on top of the collapse
|
|
1166
|
+
// animation. The wait only matters for highlighters with at least one
|
|
1167
|
+
// applicable transform; plain (variant-only) highlighters skip it so
|
|
1168
|
+
// their stored-preference resolution doesn't pay the deltas latency.
|
|
1169
|
+
const deferHighlight = deferHighlightForParsing || availableTransforms.length > 0 && waitingForTransformedCode;
|
|
1170
|
+
|
|
1171
|
+
// The fallback↔content swap, generalized into `useCoordinatedSwap`: it owns
|
|
1172
|
+
// the force-mount-once behavior, nested-fallback suppression (via the shared
|
|
1173
|
+
// `CoordinatedFallbackContext`), and registration with the page-wide settle
|
|
1174
|
+
// gate. `holdGate={deferHighlight}` keeps the gate open while the content
|
|
1175
|
+
// stays rendered (the highlighter shows plain text, then highlights in place)
|
|
1176
|
+
// rather than re-showing the fallback. CH keeps its own hoist state, residual
|
|
1177
|
+
// decompression, and `CodeHighlighterContext`/`CodeHighlighterFallbackContext`.
|
|
1178
|
+
const {
|
|
1179
|
+
showFallback,
|
|
1180
|
+
fallbackContext: coordinatedFallbackContext,
|
|
1181
|
+
hoisted
|
|
1182
|
+
} = useCoordinatedSwap({
|
|
1183
|
+
ready: activeCodeReady,
|
|
1184
|
+
holdGate: deferHighlight,
|
|
1185
|
+
hasFallback: !!props.fallback,
|
|
1186
|
+
skipFallback: props.skipFallback
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
// Validate that ContentLoading calls useCodeFallback(props). Child effects
|
|
1190
|
+
// fire before parent effects, so hookCalledRef is set by the time this runs.
|
|
1191
|
+
React.useEffect(() => {
|
|
1192
|
+
if (showFallback && !hookCalledRef.current) {
|
|
1193
|
+
throw new Errors.ErrorCodeHighlighterClientMissingFallbackHoist();
|
|
1194
|
+
}
|
|
1195
|
+
}, [showFallback]);
|
|
1196
|
+
|
|
1197
|
+
// A dynamically-imported content (e.g. `LazyContent`) calls `reportReady` — via
|
|
1198
|
+
// the `CoordinatedContentContext` provided around `children` below — once its
|
|
1199
|
+
// `import()` resolves. Without a `ContentLoading` there is nothing to cover that
|
|
1200
|
+
// load (the slot would flash empty), so fail fast instead.
|
|
1201
|
+
const fallbackProvided = !!props.fallback;
|
|
1202
|
+
const reportContentReady = React.useCallback(() => {
|
|
1203
|
+
if (!fallbackProvided) {
|
|
1204
|
+
throw new Errors.ErrorCodeHighlighterClientDynamicContentRequiresFallback();
|
|
1205
|
+
}
|
|
1206
|
+
}, [fallbackProvided]);
|
|
1207
|
+
|
|
1208
|
+
// Hand the loading `fallback` down to the content so a dynamically-imported
|
|
1209
|
+
// content (`LazyContent`) shows the *same* `ContentLoading` as its Suspense
|
|
1210
|
+
// fallback while its chunk loads - the placeholder the swap showed keeps
|
|
1211
|
+
// covering the load, with no empty flash and no double render.
|
|
1212
|
+
const contentContext = React.useMemo(() => ({
|
|
1213
|
+
hoisted,
|
|
1214
|
+
reportReady: reportContentReady,
|
|
1215
|
+
fallback: props.fallback
|
|
1216
|
+
}), [hoisted, reportContentReady, props.fallback]);
|
|
1217
|
+
|
|
814
1218
|
// Per-highlighter pre-parsed HAST cache. Lives in a ref so the same Map
|
|
815
1219
|
// instance is shared across renders without becoming a React dep. The
|
|
816
1220
|
// editable populates it via `useSourceEditing` (which reads it from
|
|
817
1221
|
// `CodeHighlighterContext`), and `parseControlledCode` consults it on
|
|
818
1222
|
// every render to skip the sync main-thread parse on exact source matches.
|
|
819
1223
|
const [preParsedCache] = React.useState(() => new Map());
|
|
1224
|
+
|
|
1225
|
+
// Client-side refresh: re-run the FULL variant loader (via the chunk's
|
|
1226
|
+
// `refresh()`) and swap in fresh data, keeping the current highlighted output
|
|
1227
|
+
// visible until the new tree lands (stale-while-revalidate, via the existing
|
|
1228
|
+
// `deferHighlight` gate). Invalidate the per-file pre-parsed HAST cache so the
|
|
1229
|
+
// refreshed source re-parses instead of reusing stale entries. A no-op for a
|
|
1230
|
+
// block with no `url` to re-fetch from.
|
|
1231
|
+
const refresh = React.useCallback(() => {
|
|
1232
|
+
preParsedCache.clear();
|
|
1233
|
+
refreshAllVariants();
|
|
1234
|
+
}, [preParsedCache, refreshAllVariants]);
|
|
820
1235
|
const {
|
|
821
1236
|
parsedControlledCode
|
|
822
1237
|
} = useControlledCodeParsing({
|
|
@@ -830,8 +1245,29 @@ export function CodeHighlighterClient(props) {
|
|
|
830
1245
|
const overlaidCode = parsedControlledCode || transformedCode || codeWithGlobals;
|
|
831
1246
|
|
|
832
1247
|
// For fallback context, use the processed code or fall back to non-controlled code
|
|
833
|
-
const codeForFallback = overlaidCode || (controlled?.code ? undefined :
|
|
834
|
-
|
|
1248
|
+
const codeForFallback = overlaidCode || (controlled?.code ? undefined : resolvedPropsCode || resolvedStateCode);
|
|
1249
|
+
|
|
1250
|
+
// Resolve the active variant's fallbacks from the two places one can cross
|
|
1251
|
+
// the server→client boundary: the hoisted copy (from a `ContentLoading`
|
|
1252
|
+
// component, which had it stripped off `Code`) and the variant's own
|
|
1253
|
+
// `fallback` field on `Code` (present without a `ContentLoading`, or scattered
|
|
1254
|
+
// back from the residual blob). For most files only one is populated. When
|
|
1255
|
+
// both are — a `fallbackCollapsed` block hoists the *visible* window but
|
|
1256
|
+
// scatters the *full* fallback onto `Code` — the `Code` copy must win, since
|
|
1257
|
+
// the full text is the DEFLATE dictionary `hastCompressed` needs. So merge
|
|
1258
|
+
// with the derived (`Code`) copy taking precedence.
|
|
1259
|
+
const activeFallbacks = React.useMemo(() => {
|
|
1260
|
+
const merged = {
|
|
1261
|
+
...hoistedFallbackHasts[variantName],
|
|
1262
|
+
...deriveFallbacksFromCode(codeForFallback, variantName)
|
|
1263
|
+
};
|
|
1264
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
1265
|
+
}, [hoistedFallbackHasts, variantName, codeForFallback]);
|
|
1266
|
+
const fallbackContext = React.useMemo(() => ({
|
|
1267
|
+
extraVariants: codeToFallbackProps(variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants).extraVariants,
|
|
1268
|
+
setFallbackHasts: handleSetFallbackHasts,
|
|
1269
|
+
onHookCalled: handleHookCalled
|
|
1270
|
+
}), [variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants, handleSetFallbackHasts, handleHookCalled]);
|
|
835
1271
|
const context = React.useMemo(() => ({
|
|
836
1272
|
code: overlaidCode,
|
|
837
1273
|
// Use processed/transformed code
|
|
@@ -839,23 +1275,56 @@ export function CodeHighlighterClient(props) {
|
|
|
839
1275
|
selection: controlled?.selection || selection,
|
|
840
1276
|
setSelection: controlled?.setSelection || setSelection,
|
|
841
1277
|
components: controlled?.components || props.components,
|
|
842
|
-
|
|
1278
|
+
// Only suppress when an external CodeController owns the code; static
|
|
1279
|
+
// `props.code` still needs the locally-computed list.
|
|
1280
|
+
availableTransforms: controlled?.code ? [] : availableTransforms,
|
|
843
1281
|
url: props.url,
|
|
844
1282
|
deferHighlight,
|
|
1283
|
+
fallbacks: activeFallbacks,
|
|
1284
|
+
highlightReady,
|
|
1285
|
+
highlightAfter,
|
|
1286
|
+
editActivation,
|
|
1287
|
+
onEditingActivated: handleEditingActivated,
|
|
1288
|
+
refresh,
|
|
845
1289
|
preParsedCache
|
|
846
|
-
}), [overlaidCode, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, controlled?.components, props.components,
|
|
1290
|
+
}), [overlaidCode, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, controlled?.components, props.components, controlled?.code, availableTransforms, props.url, deferHighlight, activeFallbacks, highlightReady, highlightAfter, editActivation, handleEditingActivated, refresh, preParsedCache]);
|
|
847
1291
|
if (!props.variants && !props.components && !activeCode) {
|
|
848
1292
|
throw new Errors.ErrorCodeHighlighterClientMissingData();
|
|
849
1293
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1294
|
+
|
|
1295
|
+
// Reset (while the fallback shows) so the validation effect re-checks that
|
|
1296
|
+
// ContentLoading wired the hook; the child's `useCodeFallback` effect sets it
|
|
1297
|
+
// again before that runs.
|
|
1298
|
+
if (showFallback) {
|
|
1299
|
+
// eslint-disable-next-line react-hooks/refs -- dev-only validation flag; reset during render so the child useCodeFallback effect (fires before this parent's validation effect) can re-set it each time the fallback shows
|
|
1300
|
+
hookCalledRef.current = false;
|
|
856
1301
|
}
|
|
857
|
-
|
|
1302
|
+
|
|
1303
|
+
// Provide the generic `CoordinatedFallbackContext` (so a nested CodeHighlighter
|
|
1304
|
+
// detects it via `useCoordinatedSwap` and suppresses its own swap, collapsing
|
|
1305
|
+
// the fallback→content→fallback→content flicker) alongside the CH-specific
|
|
1306
|
+
// fallback context that `useCodeFallback` reads to hoist.
|
|
1307
|
+
const fallbackNode = /*#__PURE__*/_jsx(CoordinatedFallbackContext.Provider, {
|
|
1308
|
+
value: coordinatedFallbackContext,
|
|
1309
|
+
children: /*#__PURE__*/_jsx(CodeHighlighterFallbackContext.Provider, {
|
|
1310
|
+
value: fallbackContext,
|
|
1311
|
+
children: props.fallback
|
|
1312
|
+
})
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
// The content subtree. A dynamically-imported content (`LazyContent`) reads the
|
|
1316
|
+
// loading `fallback` from `CoordinatedContentContext` and shows it as its own
|
|
1317
|
+
// Suspense fallback while its `import()` resolves - so swapping to it never
|
|
1318
|
+
// flashes empty.
|
|
1319
|
+
const contentNode = /*#__PURE__*/_jsx(CodeHighlighterContext.Provider, {
|
|
858
1320
|
value: context,
|
|
859
|
-
children:
|
|
1321
|
+
children: /*#__PURE__*/_jsx(CoordinatedContentContext.Provider, {
|
|
1322
|
+
value: contentContext,
|
|
1323
|
+
children: props.children
|
|
1324
|
+
})
|
|
860
1325
|
});
|
|
1326
|
+
|
|
1327
|
+
// Show the fallback OR the content (swap on data-readiness). The content loads
|
|
1328
|
+
// its own chunk after the swap, covered by the fallback it inherits via context.
|
|
1329
|
+
return showFallback ? fallbackNode : contentNode;
|
|
861
1330
|
}
|