@mui/internal-docs-infra 0.12.1-canary.44 → 0.12.1-canary.45
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/CodeControllerContext/CodeControllerContext.d.mts +2 -2
- package/CodeHighlighter/CodeHighlighter.mjs +2 -1
- package/CodeHighlighter/CodeHighlighterChunk.mjs +2 -1
- package/CodeHighlighter/CodeHighlighterClient.mjs +127 -81
- package/CodeHighlighter/CodeHighlighterContext.d.mts +5 -4
- package/CodeHighlighter/CodeHighlighterFallbackContext.d.mts +2 -0
- package/CodeHighlighter/buildCodeHighlighterChunkProps.d.mts +1 -1
- package/CodeHighlighter/buildCodeHighlighterChunkProps.mjs +1 -1
- package/CodeHighlighter/createClientProps.mjs +1 -0
- package/CodeHighlighter/fallbackFormat.d.mts +1 -2
- package/CodeHighlighter/fallbackFormat.mjs +1 -2
- package/CodeHighlighter/parseControlledCode.mjs +20 -14
- package/CodeHighlighter/scheduleDeferredPrecompute.d.mts +14 -0
- package/CodeHighlighter/scheduleDeferredPrecompute.mjs +57 -0
- package/CodeHighlighter/types.d.mts +31 -28
- package/CodeHighlighter/useCodeFallback.d.mts +5 -0
- package/CodeHighlighter/useCodeFallback.mjs +13 -10
- package/CodeHighlighter/useSpeculativeEditingPreload.d.mts +3 -3
- package/CodeHighlighter/useSpeculativeEditingPreload.mjs +8 -7
- package/CodeProvider/CodeContext.d.mts +12 -18
- package/CodeProvider/CodeContext.mjs +13 -1
- package/CodeProvider/CodeProvider.mjs +2 -3
- package/CodeProvider/CodeProviderLazy.mjs +4 -4
- package/CodeProvider/constants.d.mts +3 -3
- package/CodeProvider/constants.mjs +2 -2
- package/CodeProvider/useCodeProviderValue.d.mts +3 -3
- package/CodeProvider/useCodeProviderValue.mjs +32 -51
- package/abstractCreateDemo/DemoRootContext.d.mts +6 -0
- package/abstractCreateDemo/DemoRootContext.mjs +19 -0
- package/abstractCreateDemo/abstractCreateDemo.d.mts +1 -0
- package/abstractCreateDemo/abstractCreateDemo.mjs +8 -3
- package/abstractCreateDemoClient/abstractCreateDemoClient.mjs +6 -15
- package/abstractCreateTypes/TypeCode.d.mts +1 -1
- package/abstractCreateTypes/TypeCode.mjs +9 -4
- package/abstractCreateTypes/typesToJsx.mjs +10 -7
- package/createDemoData/createDemoData.mjs +1 -0
- package/createDemoData/types.d.mts +2 -0
- package/package.json +7 -16
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasis.mjs +2 -2
- package/pipeline/hastUtils/index.d.mts +1 -1
- package/pipeline/hastUtils/index.mjs +1 -1
- package/pipeline/loadIsomorphicCodeVariant/createEditableSourceProjection.d.mts +5 -0
- package/pipeline/loadIsomorphicCodeVariant/createEditableSourceProjection.mjs +85 -0
- package/pipeline/loadIsomorphicCodeVariant/diffHast.d.mts +1 -1
- package/pipeline/loadIsomorphicCodeVariant/diffHast.mjs +7 -81
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.d.mts +0 -16
- package/pipeline/loadIsomorphicCodeVariant/embedTransforms.mjs +7 -70
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.d.mts +4 -13
- package/pipeline/loadIsomorphicCodeVariant/findExpandingRanges.mjs +3 -40
- package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +30 -1
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.d.mts +4 -0
- package/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.mjs +65 -2
- package/pipeline/parseCreateFactoryCall/replacePrecomputeValue.d.mts +1 -0
- package/pipeline/parseCreateFactoryCall/replacePrecomputeValue.mjs +1 -0
- package/pipeline/parseSource/detectFileTypes.d.mts +2 -2
- package/pipeline/parseSource/detectGrammarScopes.d.mts +2 -2
- package/pipeline/parseSource/detectGrammarScopes.mjs +1 -1
- package/pipeline/parseSource/frameVisibility.mjs +3 -4
- package/pipeline/parseSource/grammarCache.d.mts +2 -0
- package/pipeline/parseSource/grammarCache.mjs +8 -4
- package/pipeline/parseSource/parseSource.mjs +57 -35
- package/useCode/CodeEditor.d.mts +34 -0
- package/useCode/CodeEditor.mjs +241 -0
- package/useCode/CodeEditorLazy.d.mts +12 -0
- package/useCode/CodeEditorLazy.mjs +31 -0
- package/useCode/Pre.browser.mjs +52 -280
- package/useCode/Pre.d.mts +16 -106
- package/useCode/Pre.mjs +71 -462
- package/useCode/SourceEditingEngine.d.mts +12 -2
- package/useCode/SourceEditingEngine.mjs +65 -13
- package/useCode/TransformEngine.d.mts +3 -2
- package/useCode/TransformEngine.mjs +26 -6
- package/useCode/codeEditorCache.d.mts +8 -0
- package/useCode/codeEditorCache.mjs +30 -0
- package/useCode/index.d.mts +2 -1
- package/useCode/index.mjs +2 -1
- package/useCode/resolveActionVariant.d.mts +9 -0
- package/useCode/resolveActionVariant.mjs +121 -0
- package/useCode/sourceEditingEngineCache.d.mts +6 -0
- package/useCode/sourceEditingEngineCache.mjs +25 -0
- package/useCode/sourceLineCounts.d.mts +2 -63
- package/useCode/sourceLineCounts.mjs +0 -175
- package/useCode/useCode.d.mts +27 -113
- package/useCode/useCode.mjs +114 -180
- package/useCode/useCodeUtils.d.mts +3 -91
- package/useCode/useCodeUtils.mjs +2 -205
- package/useCode/useCopyFunctionality.d.mts +13 -1
- package/useCode/useCopyFunctionality.mjs +47 -7
- package/useCode/useFileNavigation.d.mts +19 -51
- package/useCode/useFileNavigation.mjs +61 -65
- package/useCode/useSourceEditing.d.mts +16 -17
- package/useCode/useSourceEditing.mjs +86 -43
- package/useCode/useTransformManagement.d.mts +6 -89
- package/useCode/useTransformManagement.mjs +72 -501
- package/useCode/useUIState.d.mts +3 -1
- package/useCode/useUIState.mjs +11 -6
- package/useCode/useVariantSelection.d.mts +7 -127
- package/useCode/useVariantSelection.mjs +57 -578
- package/useDemo/useDemo.d.mts +7 -4
- package/useDemo/useDemo.mjs +82 -66
- package/useDemoController/instantiateElement.mjs +3 -1
- package/useDemoController/useDemoController.d.mts +3 -7
- package/useDemoController/useDemoController.mjs +26 -21
- package/useDemoController/useRunner.mjs +3 -2
- package/useDemoController/useVariantBuilds.d.mts +1 -1
- package/useDemoController/useVariantBuilds.mjs +14 -2
- package/withDocsInfra/withDeploymentConfig.mjs +6 -7
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.d.mts +0 -16
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +0 -74
- package/useCode/EditableEngine.d.mts +0 -233
- package/useCode/EditableEngine.mjs +0 -1712
- package/useCode/EditingEngine.d.mts +0 -13
- package/useCode/EditingEngine.mjs +0 -14
- package/useCode/cloneRangeWithInlineStyles.d.mts +0 -47
- package/useCode/cloneRangeWithInlineStyles.mjs +0 -123
- package/useCode/editingEngineCache.d.mts +0 -29
- package/useCode/editingEngineCache.mjs +0 -68
- package/useCode/useEditable.browser.d.mts +0 -1
- package/useCode/useEditable.browser.mjs +0 -1471
- package/useCode/useEditable.d.mts +0 -31
- package/useCode/useEditable.integration.browser.d.mts +0 -1
- package/useCode/useEditable.integration.browser.mjs +0 -870
- package/useCode/useEditable.mjs +0 -239
- package/useCode/useEditableUtils.d.mts +0 -132
- package/useCode/useEditableUtils.mjs +0 -402
- package/useCode/useHighlightGate.d.mts +0 -17
- package/useCode/useHighlightGate.mjs +0 -147
- package/useCode/useTransitionPhase.d.mts +0 -24
- package/useCode/useTransitionPhase.mjs +0 -49
- package/useCrossTabState/index.d.mts +0 -2
- package/useCrossTabState/index.mjs +0 -2
- package/useCrossTabState/useCrossTabMirror.d.mts +0 -26
- package/useCrossTabState/useCrossTabMirror.mjs +0 -108
- package/useCrossTabState/useCrossTabState.d.mts +0 -21
- package/useCrossTabState/useCrossTabState.mjs +0 -31
|
@@ -78,8 +78,8 @@ export interface CodeControllerContext {
|
|
|
78
78
|
export type CodeControllerProps<T extends {} = {}> = {
|
|
79
79
|
children: React.ReactNode;
|
|
80
80
|
/**
|
|
81
|
-
* The demo's
|
|
82
|
-
*
|
|
81
|
+
* The demo's URL, supplied by the demo factory or passed explicitly so custom
|
|
82
|
+
* controllers can identify the demo they wrap.
|
|
83
83
|
*/
|
|
84
84
|
url?: string;
|
|
85
85
|
} & T;
|
|
@@ -79,7 +79,8 @@ export function CodeHighlighter(props) {
|
|
|
79
79
|
const renderChunk = options => {
|
|
80
80
|
const userProps = {
|
|
81
81
|
...props,
|
|
82
|
-
code: options.preloaded,
|
|
82
|
+
code: props.loadPrecompute ? undefined : options.preloaded,
|
|
83
|
+
precompute: props.loadPrecompute ? options.preloaded : props.precompute,
|
|
83
84
|
ContentLoading,
|
|
84
85
|
initialVariant: initialKey,
|
|
85
86
|
fallback: options.fallback,
|
|
@@ -28,7 +28,8 @@ function CodeHighlighterChunkContent(props) {
|
|
|
28
28
|
} = props;
|
|
29
29
|
const clientProps = createClientProps({
|
|
30
30
|
...userProps,
|
|
31
|
-
code: data ?? userProps.code
|
|
31
|
+
code: userProps.loadPrecompute ? undefined : data ?? userProps.code,
|
|
32
|
+
precompute: userProps.loadPrecompute ? data ?? userProps.precompute : userProps.precompute
|
|
32
33
|
});
|
|
33
34
|
return /*#__PURE__*/_jsx(CodeHighlighterClient, {
|
|
34
35
|
...clientProps
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { DemoRootContext } from "../abstractCreateDemo/DemoRootContext.mjs";
|
|
5
|
+
import { useCodeContext, useDemandSourceParser } from "../CodeProvider/CodeContext.mjs";
|
|
5
6
|
import { CodeHighlighterContext } from "./CodeHighlighterContext.mjs";
|
|
6
7
|
import { maybeCodeInitialData } from "../pipeline/loadIsomorphicCodeVariant/maybeCodeInitialData.mjs";
|
|
7
8
|
import { hasAllVariants } from "../pipeline/loadIsomorphicCodeVariant/hasAllCodeVariants.mjs";
|
|
@@ -24,10 +25,15 @@ import { useCoordinatedSwap } from "../CoordinatedLazy/useCoordinatedSwap.mjs";
|
|
|
24
25
|
import { CoordinatedFallbackContext } from "../CoordinatedLazy/CoordinatedFallbackContext.mjs";
|
|
25
26
|
import { CoordinatedContentContext } from "../CoordinatedLazy/CoordinatedContentContext.mjs";
|
|
26
27
|
import { requestIdle } from "../useCoordinated/scheduleTasks.mjs";
|
|
28
|
+
import { scheduleDeferredPrecompute } from "./scheduleDeferredPrecompute.mjs";
|
|
27
29
|
import * as Errors from "./errors.mjs";
|
|
28
30
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
29
31
|
const DEBUG = false; // Set to true for debugging purposes
|
|
30
32
|
|
|
33
|
+
// Deferred source loading should stay behind the initial paint without leaving
|
|
34
|
+
// an untouched demo on its server fallback for too long.
|
|
35
|
+
const DEFERRED_PRECOMPUTE_IDLE_TIMEOUT_MS = 2_000;
|
|
36
|
+
|
|
31
37
|
// Safety-net deadline (ms) for the default `'idle'` highlight/enhance swaps. They
|
|
32
38
|
// defer the swap to `requestIdleCallback`, which a busy main thread can starve
|
|
33
39
|
// INDEFINITELY — leaving code stuck at its un-highlighted first paint until a full
|
|
@@ -78,7 +84,7 @@ function useInitialData({
|
|
|
78
84
|
handleSetFallbackHasts
|
|
79
85
|
}) {
|
|
80
86
|
const {
|
|
81
|
-
|
|
87
|
+
loadSourceParser,
|
|
82
88
|
loadCodeMeta,
|
|
83
89
|
loadVariantMeta,
|
|
84
90
|
loadSource,
|
|
@@ -128,11 +134,15 @@ function useInitialData({
|
|
|
128
134
|
// Lazily resolve the heavy fallback loader (instant under an eager
|
|
129
135
|
// CodeProvider, a deduped fetch under CodeProviderLazy) before loading.
|
|
130
136
|
const loadCodeFallback = await loadCodeFallbackLoader();
|
|
137
|
+
|
|
138
|
+
// Only start the parser load when the fallback is highlighted here;
|
|
139
|
+
// `loadCodeFallback` never awaits it otherwise.
|
|
140
|
+
const shouldHighlight = highlightAfter === 'init';
|
|
131
141
|
const loaded = await loadCodeFallback(url, variantName, code, {
|
|
132
|
-
shouldHighlight
|
|
142
|
+
shouldHighlight,
|
|
133
143
|
fallbackUsesExtraFiles,
|
|
134
144
|
fallbackUsesAllVariants,
|
|
135
|
-
sourceParser,
|
|
145
|
+
sourceParser: shouldHighlight ? loadSourceParser?.() : undefined,
|
|
136
146
|
loadSource,
|
|
137
147
|
loadVariantMeta,
|
|
138
148
|
loadCodeMeta,
|
|
@@ -174,7 +184,7 @@ function useInitialData({
|
|
|
174
184
|
}
|
|
175
185
|
return strippedCode;
|
|
176
186
|
}
|
|
177
|
-
}), [reason, variantName, code, setCode, highlightAfter, url,
|
|
187
|
+
}), [reason, variantName, code, setCode, highlightAfter, url, loadSourceParser, loadSource, loadVariantMeta, loadCodeMeta, sourceEnhancers, fallbackUsesExtraFiles, fallbackUsesAllVariants, fileName, variants, globalsCode, setProcessedGlobalsCode, loadCodeFallbackLoader, handleSetFallbackHasts]);
|
|
178
188
|
const fallbackConfig = React.useMemo(() => ({
|
|
179
189
|
ChunkContent: NoopChunkContent,
|
|
180
190
|
source: fallbackSource
|
|
@@ -359,7 +369,7 @@ function useCodeParsing({
|
|
|
359
369
|
url
|
|
360
370
|
}) {
|
|
361
371
|
const {
|
|
362
|
-
|
|
372
|
+
loadSourceParser,
|
|
363
373
|
parseSource,
|
|
364
374
|
parseCode
|
|
365
375
|
} = useCodeContext();
|
|
@@ -407,6 +417,7 @@ function useCodeParsing({
|
|
|
407
417
|
// adds no delay on the common path.
|
|
408
418
|
const grammarScopes = React.useMemo(() => code ? detectGrammarScopes(code) : [], [code]);
|
|
409
419
|
const grammarsReady = useGrammarsReady(grammarScopes, !!code && shouldHighlight && !allVariantsAlreadyHighlighted);
|
|
420
|
+
useDemandSourceParser(loadSourceParser, !!code && shouldHighlight && !allVariantsAlreadyHighlighted && grammarsReady && !parseSource);
|
|
410
421
|
|
|
411
422
|
// Parse the internal code state when ready and timing conditions are met
|
|
412
423
|
const parsedCode = React.useMemo(() => {
|
|
@@ -419,10 +430,10 @@ function useCodeParsing({
|
|
|
419
430
|
return undefined;
|
|
420
431
|
}
|
|
421
432
|
if (!parseSource) {
|
|
422
|
-
// A CodeProvider is present and its
|
|
423
|
-
//
|
|
424
|
-
//
|
|
425
|
-
if (
|
|
433
|
+
// A CodeProvider is present and its parser hasn't loaded yet — wait for
|
|
434
|
+
// it instead of erroring. The memo will re-run once `parseSource` is
|
|
435
|
+
// populated.
|
|
436
|
+
if (loadSourceParser) {
|
|
426
437
|
return undefined;
|
|
427
438
|
}
|
|
428
439
|
if (forceClient) {
|
|
@@ -441,12 +452,11 @@ function useCodeParsing({
|
|
|
441
452
|
return undefined;
|
|
442
453
|
}
|
|
443
454
|
return parseCode(code, parseSource);
|
|
444
|
-
}, [code, shouldHighlight, allVariantsAlreadyHighlighted, grammarsReady,
|
|
455
|
+
}, [code, shouldHighlight, allVariantsAlreadyHighlighted, grammarsReady, loadSourceParser, parseSource, parseCode, forceClient, url]);
|
|
445
456
|
|
|
446
457
|
// Keep highlighting deferred until parsed HAST is actually available for the
|
|
447
458
|
// variants that need it. `shouldHighlight` can flip true ~30ms after
|
|
448
|
-
// hydration, but `parseCode` only runs once the
|
|
449
|
-
// resolves. Without this wait, downstream consumers (e.g. the transform
|
|
459
|
+
// hydration, but `parseCode` only runs once the source parser has loaded. Without this wait, downstream consumers (e.g. the transform
|
|
450
460
|
// swap) would commit while the visible variant is still rendered from its
|
|
451
461
|
// raw string source, producing a structure swap on the DOM moments later.
|
|
452
462
|
const waitingForParsedCode = shouldHighlight && !!code && !allVariantsAlreadyHighlighted && !parsedCode;
|
|
@@ -495,7 +505,7 @@ function useCodeTransforms({
|
|
|
495
505
|
variantName
|
|
496
506
|
}) {
|
|
497
507
|
const {
|
|
498
|
-
|
|
508
|
+
loadSourceParser,
|
|
499
509
|
computeHastDeltasLoader
|
|
500
510
|
} = useCodeContext();
|
|
501
511
|
// Track which `parsedCode` the cached `transformedCode` was computed from
|
|
@@ -513,7 +523,7 @@ function useCodeTransforms({
|
|
|
513
523
|
// the no-async case is derived during render below instead of being stored,
|
|
514
524
|
// so this effect never publishes a synchronous pass-through state.
|
|
515
525
|
React.useEffect(() => {
|
|
516
|
-
if (!parsedCode || !
|
|
526
|
+
if (!parsedCode || !loadSourceParser || !computeHastDeltasLoader) {
|
|
517
527
|
return undefined;
|
|
518
528
|
}
|
|
519
529
|
|
|
@@ -544,7 +554,7 @@ function useCodeTransforms({
|
|
|
544
554
|
// Resolve the parser and the (lazy) transform-delta computer in parallel
|
|
545
555
|
// before computing deltas. computeHastDeltas pulls jsondiffpatch, so it's
|
|
546
556
|
// kept out of the initial bundle under CodeProviderLazy.
|
|
547
|
-
const [parseSource, computeHastDeltas] = await Promise.all([
|
|
557
|
+
const [parseSource, computeHastDeltas] = await Promise.all([loadSourceParser(), computeHastDeltasLoader()]);
|
|
548
558
|
const enhanced = await computeHastDeltas(parsedCode, parseSource);
|
|
549
559
|
commit(enhanced);
|
|
550
560
|
} catch (error) {
|
|
@@ -558,7 +568,7 @@ function useCodeTransforms({
|
|
|
558
568
|
settled = true; // a newer run (or unmount) supersedes this one; ignore late writes
|
|
559
569
|
clearTimeout(timer);
|
|
560
570
|
};
|
|
561
|
-
}, [parsedCode,
|
|
571
|
+
}, [parsedCode, loadSourceParser, computeHastDeltasLoader]);
|
|
562
572
|
|
|
563
573
|
// When the full async pipeline is wired, expose the cached output regardless
|
|
564
574
|
// of whether `parsedCode` changed since the last computation — falling back
|
|
@@ -568,7 +578,7 @@ function useCodeTransforms({
|
|
|
568
578
|
// `useTransformManagement` / `useVariantSelection`) hold off committing a
|
|
569
579
|
// swap until fresh deltas land. Without the pipeline, `transformedCode` is a
|
|
570
580
|
// synchronous pass-through of `parsedCode` derived during render.
|
|
571
|
-
const hasAsyncPipeline = !!parsedCode && !!
|
|
581
|
+
const hasAsyncPipeline = !!parsedCode && !!loadSourceParser && !!computeHastDeltasLoader;
|
|
572
582
|
const transformedCode = hasAsyncPipeline ? transformedState.output : parsedCode;
|
|
573
583
|
|
|
574
584
|
// Async hast-deltas pipeline status. While true, consumers (notably
|
|
@@ -580,7 +590,7 @@ function useCodeTransforms({
|
|
|
580
590
|
// two later when `transformedCode` arrives, producing a visible jump
|
|
581
591
|
// on top of the just-played collapse animation.
|
|
582
592
|
//
|
|
583
|
-
// Only relevant when both a
|
|
593
|
+
// Only relevant when both a parser (`loadSourceParser`) and a deltas
|
|
584
594
|
// computer (`computeHastDeltas`) are wired up — environments without
|
|
585
595
|
// them resolve `transformedCode` synchronously to `parsedCode` in the
|
|
586
596
|
// effect above, so the deltas phase is a no-op. We compare the cached
|
|
@@ -596,26 +606,29 @@ function useCodeTransforms({
|
|
|
596
606
|
}
|
|
597
607
|
function useControlledCodeParsing({
|
|
598
608
|
code,
|
|
609
|
+
grammarScopes,
|
|
599
610
|
forceClient,
|
|
600
611
|
url,
|
|
601
612
|
preParsedCache
|
|
602
613
|
}) {
|
|
603
614
|
const {
|
|
604
|
-
|
|
615
|
+
loadSourceParser,
|
|
605
616
|
parseSource,
|
|
606
617
|
parseControlledCode
|
|
607
618
|
} = useCodeContext();
|
|
619
|
+
const grammarsReady = useGrammarsReady(grammarScopes, Boolean(code));
|
|
620
|
+
useDemandSourceParser(loadSourceParser, Boolean(code) && grammarsReady && !parseSource);
|
|
608
621
|
|
|
609
622
|
// Parse the controlled code separately (no need to check readyForContent)
|
|
610
623
|
const parsedControlledCode = React.useMemo(() => {
|
|
611
|
-
if (!code) {
|
|
624
|
+
if (!code || !grammarsReady) {
|
|
612
625
|
return undefined;
|
|
613
626
|
}
|
|
614
627
|
if (!parseSource) {
|
|
615
|
-
// A CodeProvider is present and its
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
if (
|
|
628
|
+
// A CodeProvider is present and its parser hasn't loaded yet (e.g.
|
|
629
|
+
// CodeProviderLazy dynamic-importing the engine) — wait for it instead of
|
|
630
|
+
// erroring. The memo re-runs once `parseSource` lands.
|
|
631
|
+
if (loadSourceParser) {
|
|
619
632
|
return undefined;
|
|
620
633
|
}
|
|
621
634
|
if (forceClient) {
|
|
@@ -634,7 +647,7 @@ function useControlledCodeParsing({
|
|
|
634
647
|
return undefined;
|
|
635
648
|
}
|
|
636
649
|
return parseControlledCode(code, parseSource, preParsedCache);
|
|
637
|
-
}, [code,
|
|
650
|
+
}, [code, grammarsReady, loadSourceParser, parseSource, parseControlledCode, forceClient, url, preParsedCache]);
|
|
638
651
|
return {
|
|
639
652
|
parsedControlledCode
|
|
640
653
|
};
|
|
@@ -888,8 +901,41 @@ function usePropsCodeGlobalsMerging({
|
|
|
888
901
|
}
|
|
889
902
|
export function CodeHighlighterClient(props) {
|
|
890
903
|
const controlled = useControlledCode();
|
|
904
|
+
const demoRootRef = React.useContext(DemoRootContext);
|
|
891
905
|
const isControlled = Boolean(props.code || controlled?.code);
|
|
892
906
|
const [code, setCode] = React.useState(typeof props.precompute === 'object' ? props.precompute : undefined);
|
|
907
|
+
const [deferredPrecomputeLoaded, setDeferredPrecomputeLoaded] = React.useState(!props.loadPrecompute);
|
|
908
|
+
const [deferredPrecomputeStarted, setDeferredPrecomputeStarted] = React.useState(false);
|
|
909
|
+
React.useEffect(() => {
|
|
910
|
+
if (!props.loadPrecompute || deferredPrecomputeLoaded) {
|
|
911
|
+
return undefined;
|
|
912
|
+
}
|
|
913
|
+
let active = true;
|
|
914
|
+
async function load() {
|
|
915
|
+
setDeferredPrecomputeStarted(true);
|
|
916
|
+
try {
|
|
917
|
+
const loaded = await props.loadPrecompute();
|
|
918
|
+
if (active) {
|
|
919
|
+
React.startTransition(() => {
|
|
920
|
+
setCode(loaded);
|
|
921
|
+
setDeferredPrecomputeLoaded(true);
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
} catch {
|
|
925
|
+
// Keep the server-rendered fallback when the deferred chunk fails.
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
const cancel = scheduleDeferredPrecompute({
|
|
929
|
+
root: demoRootRef?.current,
|
|
930
|
+
enhanceAfter: props.enhanceAfter,
|
|
931
|
+
load,
|
|
932
|
+
timeout: DEFERRED_PRECOMPUTE_IDLE_TIMEOUT_MS
|
|
933
|
+
});
|
|
934
|
+
return () => {
|
|
935
|
+
active = false;
|
|
936
|
+
cancel();
|
|
937
|
+
};
|
|
938
|
+
}, [props.loadPrecompute, props.enhanceAfter, deferredPrecomputeLoaded, demoRootRef]);
|
|
893
939
|
|
|
894
940
|
// Sync code state with precompute prop changes (for hot-reload). Done with
|
|
895
941
|
// the store-previous-prop render-phase derivation rather than an effect:
|
|
@@ -902,7 +948,7 @@ export function CodeHighlighterClient(props) {
|
|
|
902
948
|
const [prevPrecompute, setPrevPrecompute] = React.useState(props.precompute);
|
|
903
949
|
if (props.precompute !== prevPrecompute) {
|
|
904
950
|
setPrevPrecompute(props.precompute);
|
|
905
|
-
if (typeof props.precompute === 'object') {
|
|
951
|
+
if (typeof props.precompute === 'object' && !(props.loadPrecompute && deferredPrecomputeLoaded)) {
|
|
906
952
|
setCode(props.precompute);
|
|
907
953
|
} else if (props.precompute === undefined) {
|
|
908
954
|
setCode(undefined);
|
|
@@ -950,50 +996,10 @@ export function CodeHighlighterClient(props) {
|
|
|
950
996
|
});
|
|
951
997
|
|
|
952
998
|
// Per-block editing activation: flipped once when the block first engages for
|
|
953
|
-
// editing — threaded down to
|
|
999
|
+
// editing — threaded down to the textarea's focus handler via `CodeHighlighterContext`
|
|
954
1000
|
// (immediately in `'eager'`, on hover/focus/click in `'interaction'`). Drives
|
|
955
1001
|
// the editable speculative preload below and notifies the CodeControllerContext.
|
|
956
1002
|
const [editingActivated, setEditingActivated] = React.useState(false);
|
|
957
|
-
const controllerOnActivate = controlled?.onActivate;
|
|
958
|
-
// Which live-editing engine chunks the controller should preload on activation: `js`
|
|
959
|
-
// if the demo has any JS/TS file, `css` if any CSS file. Stable across keystrokes
|
|
960
|
-
// (editing changes source content, never which files exist), like the grammar scopes
|
|
961
|
-
// below.
|
|
962
|
-
const editableFileTypes = React.useMemo(() => {
|
|
963
|
-
const editableCode = props.code ?? code;
|
|
964
|
-
return editableCode ? detectFileTypes(editableCode) : {
|
|
965
|
-
js: false,
|
|
966
|
-
css: false
|
|
967
|
-
};
|
|
968
|
-
}, [props.code, code]);
|
|
969
|
-
const handleEditingActivated = React.useCallback(() => {
|
|
970
|
-
setEditingActivated(true);
|
|
971
|
-
controllerOnActivate?.(editableFileTypes);
|
|
972
|
-
}, [controllerOnActivate, editableFileTypes]);
|
|
973
|
-
|
|
974
|
-
// Grammar scopes the editable files need for live re-highlighting. Unlike the
|
|
975
|
-
// speculative highlight/transform preloads — which intentionally skip
|
|
976
|
-
// controlled blocks (`speculativeCode` is cleared above) — an editable block
|
|
977
|
-
// DOES re-highlight its edits on the client, so its grammars must load or the
|
|
978
|
-
// edited source falls back to plain text. The editable file set (and thus the
|
|
979
|
-
// scopes) comes from `props.code`: editing changes source *content*, never
|
|
980
|
-
// which files exist, so this stays stable across keystrokes.
|
|
981
|
-
const editableGrammarScopes = React.useMemo(() => {
|
|
982
|
-
const editableCode = props.code ?? code;
|
|
983
|
-
return editableCode ? detectGrammarScopes(editableCode) : [];
|
|
984
|
-
}, [props.code, code]);
|
|
985
|
-
|
|
986
|
-
// When the block is editable (a CodeControllerContext with `setCode` is in
|
|
987
|
-
// scope), warm the live-editing engine, the per-language grammars, and the
|
|
988
|
-
// worker so they're in flight before the user edits. Deduped page-wide. In
|
|
989
|
-
// `editActivation: 'interaction'` mode the warming waits until the block is
|
|
990
|
-
// `activated` (engaged) — that mode defers loading until the reader engages.
|
|
991
|
-
useSpeculativeEditingPreload({
|
|
992
|
-
enabled: Boolean(controlled?.setCode),
|
|
993
|
-
editActivation,
|
|
994
|
-
activated: editingActivated,
|
|
995
|
-
scopes: editableGrammarScopes
|
|
996
|
-
});
|
|
997
1003
|
|
|
998
1004
|
// Preload the client-side transform applier (the `jsondiffpatch` chunk) when
|
|
999
1005
|
// the code declares transforms — so it is warm before the reader switches a
|
|
@@ -1060,7 +1066,7 @@ export function CodeHighlighterClient(props) {
|
|
|
1060
1066
|
highlightAfter,
|
|
1061
1067
|
fallbackUsesExtraFiles,
|
|
1062
1068
|
fallbackUsesAllVariants,
|
|
1063
|
-
isControlled,
|
|
1069
|
+
isControlled: isControlled || !deferredPrecomputeLoaded,
|
|
1064
1070
|
globalsCode: props.globalsCode,
|
|
1065
1071
|
setProcessedGlobalsCode,
|
|
1066
1072
|
handleSetFallbackHasts
|
|
@@ -1141,15 +1147,15 @@ export function CodeHighlighterClient(props) {
|
|
|
1141
1147
|
}
|
|
1142
1148
|
}, [enhanceAfter, isHydrated]);
|
|
1143
1149
|
const readyForContent = React.useMemo(() => {
|
|
1144
|
-
if (!code) {
|
|
1150
|
+
if (!code || !deferredPrecomputeLoaded) {
|
|
1145
1151
|
return false;
|
|
1146
1152
|
}
|
|
1147
1153
|
return hasAllVariants(variants, code);
|
|
1148
|
-
}, [code, variants]);
|
|
1154
|
+
}, [code, variants, deferredPrecomputeLoaded]);
|
|
1149
1155
|
|
|
1150
1156
|
// Separate check for activeCode to determine when to show fallback
|
|
1151
1157
|
const activeCodeReady = React.useMemo(() => {
|
|
1152
|
-
if (!activeCode || !isEnhanceAllowed) {
|
|
1158
|
+
if (!activeCode || !isEnhanceAllowed || !deferredPrecomputeLoaded) {
|
|
1153
1159
|
return false;
|
|
1154
1160
|
}
|
|
1155
1161
|
|
|
@@ -1161,13 +1167,13 @@ export function CodeHighlighterClient(props) {
|
|
|
1161
1167
|
// For regular code, use the existing hasAllVariants function
|
|
1162
1168
|
const regularCode = props.code || code;
|
|
1163
1169
|
return regularCode ? hasAllVariants(variants, regularCode) : false;
|
|
1164
|
-
}, [activeCode, isEnhanceAllowed, controlled?.code, variants, props.code, code]);
|
|
1170
|
+
}, [activeCode, isEnhanceAllowed, deferredPrecomputeLoaded, controlled?.code, variants, props.code, code]);
|
|
1165
1171
|
const {
|
|
1166
1172
|
refresh: refreshAllVariants
|
|
1167
1173
|
} = useAllVariants({
|
|
1168
1174
|
readyForContent,
|
|
1169
1175
|
variants,
|
|
1170
|
-
isControlled,
|
|
1176
|
+
isControlled: isControlled || !deferredPrecomputeLoaded,
|
|
1171
1177
|
url,
|
|
1172
1178
|
code,
|
|
1173
1179
|
setCode,
|
|
@@ -1199,6 +1205,40 @@ export function CodeHighlighterClient(props) {
|
|
|
1199
1205
|
|
|
1200
1206
|
// Use props.code result if available, otherwise use state code result
|
|
1201
1207
|
const codeWithGlobals = propsCodeWithGlobals || stateCodeWithGlobals;
|
|
1208
|
+
|
|
1209
|
+
// Union immutable and live metadata: either graph can be partial while editing activates.
|
|
1210
|
+
const editableFileTypes = React.useMemo(() => {
|
|
1211
|
+
const initial = codeWithGlobals ? detectFileTypes(codeWithGlobals) : {
|
|
1212
|
+
js: false,
|
|
1213
|
+
css: false
|
|
1214
|
+
};
|
|
1215
|
+
const live = controlled?.code ? detectFileTypes(controlled.code) : {
|
|
1216
|
+
js: false,
|
|
1217
|
+
css: false
|
|
1218
|
+
};
|
|
1219
|
+
return {
|
|
1220
|
+
js: initial.js || live.js,
|
|
1221
|
+
css: initial.css || live.css
|
|
1222
|
+
};
|
|
1223
|
+
}, [codeWithGlobals, controlled]);
|
|
1224
|
+
const editableGrammarScopes = React.useMemo(() => {
|
|
1225
|
+
const scopes = new Set(codeWithGlobals ? detectGrammarScopes(codeWithGlobals) : []);
|
|
1226
|
+
if (controlled?.code) {
|
|
1227
|
+
detectGrammarScopes(controlled.code).forEach(scope => scopes.add(scope));
|
|
1228
|
+
}
|
|
1229
|
+
return [...scopes];
|
|
1230
|
+
}, [codeWithGlobals, controlled]);
|
|
1231
|
+
const controllerOnActivate = controlled?.onActivate;
|
|
1232
|
+
const handleEditingActivated = React.useCallback(() => {
|
|
1233
|
+
setEditingActivated(true);
|
|
1234
|
+
controllerOnActivate?.(editableFileTypes);
|
|
1235
|
+
}, [controllerOnActivate, editableFileTypes]);
|
|
1236
|
+
useSpeculativeEditingPreload({
|
|
1237
|
+
enabled: Boolean(controlled?.setCode),
|
|
1238
|
+
editActivation,
|
|
1239
|
+
activated: editingActivated,
|
|
1240
|
+
scopes: editableGrammarScopes
|
|
1241
|
+
});
|
|
1202
1242
|
const {
|
|
1203
1243
|
parsedCode,
|
|
1204
1244
|
deferHighlight: deferHighlightForParsing,
|
|
@@ -1301,6 +1341,7 @@ export function CodeHighlighterClient(props) {
|
|
|
1301
1341
|
parsedControlledCode
|
|
1302
1342
|
} = useControlledCodeParsing({
|
|
1303
1343
|
code: controlled?.code,
|
|
1344
|
+
grammarScopes: editableGrammarScopes,
|
|
1304
1345
|
forceClient: props.forceClient,
|
|
1305
1346
|
url: props.url,
|
|
1306
1347
|
preParsedCache
|
|
@@ -1330,9 +1371,10 @@ export function CodeHighlighterClient(props) {
|
|
|
1330
1371
|
}, [hoistedFallbackHasts, variantName, codeForFallback]);
|
|
1331
1372
|
const fallbackContext = React.useMemo(() => ({
|
|
1332
1373
|
extraVariants: codeToFallbackProps(variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants).extraVariants,
|
|
1374
|
+
canLoadContent: !props.loadPrecompute || deferredPrecomputeStarted,
|
|
1333
1375
|
setFallbackHasts: handleSetFallbackHasts,
|
|
1334
1376
|
onHookCalled: handleHookCalled
|
|
1335
|
-
}), [variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants, handleSetFallbackHasts, handleHookCalled]);
|
|
1377
|
+
}), [variantName, codeForFallback, fileName, props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants, props.loadPrecompute, deferredPrecomputeStarted, handleSetFallbackHasts, handleHookCalled]);
|
|
1336
1378
|
|
|
1337
1379
|
// Keep the build-time render for any variant the controller hasn't produced a live
|
|
1338
1380
|
// preview for yet — an in-flight build, a build error, or a variant with no live build
|
|
@@ -1357,16 +1399,17 @@ export function CodeHighlighterClient(props) {
|
|
|
1357
1399
|
for (const variant of Object.keys(live)) {
|
|
1358
1400
|
const buildTime = props.components?.[variant];
|
|
1359
1401
|
const liveNode = injectFallback(live[variant], buildTime);
|
|
1360
|
-
merged[variant] = /*#__PURE__*/
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
},
|
|
1402
|
+
merged[variant] = /*#__PURE__*/_jsx(React.Suspense, {
|
|
1403
|
+
fallback: buildTime ?? null,
|
|
1404
|
+
children: liveNode
|
|
1405
|
+
}, variant);
|
|
1364
1406
|
}
|
|
1365
1407
|
return merged;
|
|
1366
1408
|
}, [controlled?.components, props.components]);
|
|
1367
1409
|
const context = React.useMemo(() => ({
|
|
1368
1410
|
code: overlaidCode,
|
|
1369
1411
|
// Use processed/transformed code
|
|
1412
|
+
initialCode: codeWithGlobals,
|
|
1370
1413
|
setCode: controlled?.setCode,
|
|
1371
1414
|
selection: controlled?.selection || selection,
|
|
1372
1415
|
setSelection: controlled?.setSelection || setSelection,
|
|
@@ -1382,7 +1425,7 @@ export function CodeHighlighterClient(props) {
|
|
|
1382
1425
|
onEditingActivated: handleEditingActivated,
|
|
1383
1426
|
refresh,
|
|
1384
1427
|
preParsedCache
|
|
1385
|
-
}), [overlaidCode, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, bridgedComponents, controlled?.errors, availableTransforms, props.url, deferHighlight, activeFallbacks, highlightReady, highlightAfter, editActivation, handleEditingActivated, refresh, preParsedCache]);
|
|
1428
|
+
}), [overlaidCode, codeWithGlobals, controlled?.setCode, selection, controlled?.selection, controlled?.setSelection, bridgedComponents, controlled?.errors, availableTransforms, props.url, deferHighlight, activeFallbacks, highlightReady, highlightAfter, editActivation, handleEditingActivated, refresh, preParsedCache]);
|
|
1386
1429
|
if (!props.variants && !props.components && !activeCode) {
|
|
1387
1430
|
throw new Errors.ErrorCodeHighlighterClientMissingData();
|
|
1388
1431
|
}
|
|
@@ -1406,6 +1449,9 @@ export function CodeHighlighterClient(props) {
|
|
|
1406
1449
|
children: props.fallback
|
|
1407
1450
|
})
|
|
1408
1451
|
});
|
|
1452
|
+
const contentChildren = props.loadPrecompute && /*#__PURE__*/React.isValidElement(props.children) ? /*#__PURE__*/React.cloneElement(props.children, {
|
|
1453
|
+
code: overlaidCode ?? codeWithGlobals ?? code
|
|
1454
|
+
}) : props.children;
|
|
1409
1455
|
|
|
1410
1456
|
// The content subtree. A dynamically-imported content (`LazyContent`) reads the
|
|
1411
1457
|
// loading `fallback` from `CoordinatedContentContext` and shows it as its own
|
|
@@ -1415,7 +1461,7 @@ export function CodeHighlighterClient(props) {
|
|
|
1415
1461
|
value: context,
|
|
1416
1462
|
children: /*#__PURE__*/_jsx(CoordinatedContentContext.Provider, {
|
|
1417
1463
|
value: contentContext,
|
|
1418
|
-
children:
|
|
1464
|
+
children: contentChildren
|
|
1419
1465
|
})
|
|
1420
1466
|
});
|
|
1421
1467
|
|
|
@@ -14,6 +14,8 @@ export interface PreParsedCacheEntry {
|
|
|
14
14
|
}
|
|
15
15
|
export interface CodeHighlighterContextType {
|
|
16
16
|
code?: Code;
|
|
17
|
+
/** Loaded source before controlled edits are overlaid. */
|
|
18
|
+
initialCode?: Code;
|
|
17
19
|
setCode?: React.Dispatch<React.SetStateAction<ControlledCode | null>>;
|
|
18
20
|
selection?: Selection;
|
|
19
21
|
setSelection?: React.Dispatch<React.SetStateAction<Selection>>;
|
|
@@ -64,15 +66,14 @@ export interface CodeHighlighterContextType {
|
|
|
64
66
|
/**
|
|
65
67
|
* Echo of the `editActivation` prop on the surrounding `CodeHighlighter` /
|
|
66
68
|
* `CodeHighlighterClient`. `useCode` reads it from here and threads it down to
|
|
67
|
-
* `
|
|
68
|
-
* `'interaction'`), so the editing-activation strategy can be configured at
|
|
69
|
+
* `Pre` (which defers the textarea load when `'interaction'`), so the strategy can be configured at
|
|
69
70
|
* the `CodeHighlighter` / demo level rather than inside the content subtree.
|
|
70
71
|
*/
|
|
71
72
|
editActivation?: 'eager' | 'interaction';
|
|
72
73
|
/**
|
|
73
|
-
* Callback `useCode` threads down to
|
|
74
|
+
* Callback `useCode` threads down to the editor's `onActivate`, fired once
|
|
74
75
|
* when the block first engages for editing. `CodeHighlighterClient` supplies it
|
|
75
|
-
* to flip its per-block `activated` state (warming the live-editing
|
|
76
|
+
* to flip its per-block `activated` state (warming the live-editing runtime,
|
|
76
77
|
* grammars, and worker) and to notify the `CodeControllerContext`.
|
|
77
78
|
*/
|
|
78
79
|
onEditingActivated?: () => void;
|
|
@@ -2,6 +2,8 @@ import * as React from 'react';
|
|
|
2
2
|
import type { Fallbacks, ContentLoadingVariant } from "./types.mjs";
|
|
3
3
|
export interface CodeHighlighterFallbackContext {
|
|
4
4
|
extraVariants?: Record<string, ContentLoadingVariant>;
|
|
5
|
+
/** Whether the loading fallback may begin loading independently lazy content. */
|
|
6
|
+
canLoadContent?: boolean;
|
|
5
7
|
/**
|
|
6
8
|
* Callback used by `useCodeFallback` to hoist fallback data
|
|
7
9
|
* back to `CodeHighlighterClient` so it can derive text dictionaries
|
|
@@ -30,7 +30,7 @@ export interface CodeHighlighterChunkProps {
|
|
|
30
30
|
forceClient: boolean;
|
|
31
31
|
}
|
|
32
32
|
/** The `CodeHighlighter` props this decision reads. */
|
|
33
|
-
type BuildCodeHighlighterChunkPropsInput<T extends {}> = Pick<CodeHighlighterProps<T>, 'code' | 'precompute' | 'components' | 'variants' | 'variant' | 'initialVariant' | 'defaultVariant' | 'controlled' | 'forceClient' | 'highlightAfter' | 'fallbackUsesExtraFiles' | 'fallbackUsesAllVariants' | 'loadCodeMeta' | 'loadVariantMeta' | 'loadSource' | 'sourceParser' | 'sourceTransformers' | 'url' | 'fileName'>;
|
|
33
|
+
type BuildCodeHighlighterChunkPropsInput<T extends {}> = Pick<CodeHighlighterProps<T>, 'code' | 'precompute' | 'loadPrecompute' | 'components' | 'variants' | 'variant' | 'initialVariant' | 'defaultVariant' | 'controlled' | 'forceClient' | 'highlightAfter' | 'fallbackUsesExtraFiles' | 'fallbackUsesAllVariants' | 'loadCodeMeta' | 'loadVariantMeta' | 'loadSource' | 'sourceParser' | 'sourceTransformers' | 'url' | 'fileName'>;
|
|
34
34
|
/**
|
|
35
35
|
* Map `CodeHighlighter`'s props onto the generic chunk render-decision inputs.
|
|
36
36
|
* This replaces the bespoke `renderCodeHighlighter`/`renderWithInitialSource`
|
|
@@ -34,7 +34,7 @@ export function buildCodeHighlighterChunkProps(props) {
|
|
|
34
34
|
// Every variant already highlighted (or controlled editing) -> render the
|
|
35
35
|
// client directly with no loading.
|
|
36
36
|
const allVariantsLoaded = Boolean(code && hasAllVariants(variants, code, true));
|
|
37
|
-
const controlled = Boolean(props.controlled) || allVariantsLoaded;
|
|
37
|
+
const controlled = !props.loadPrecompute && (Boolean(props.controlled) || allVariantsLoaded);
|
|
38
38
|
|
|
39
39
|
// No loading functions (or the caller forced the client) -> the server cannot
|
|
40
40
|
// load, so route to a client/content branch.
|
|
@@ -127,8 +127,7 @@ export declare function redistributeRootFallback(root: HastRoot, fallback: Fallb
|
|
|
127
127
|
* When `collapsesToEmpty` is `true` the source records `focusedLines === 0`
|
|
128
128
|
* (the `oversizedFocus: 'hide'` collapse-to-nothing case): the collapsed window
|
|
129
129
|
* is intentionally empty, so the first-frame fallback is skipped and an empty
|
|
130
|
-
* array is returned. Mirrors the runtime rule in `Pre.tsx
|
|
131
|
-
* `getInitialVisibleSourceLines`.
|
|
130
|
+
* array is returned. Mirrors the runtime rule in `Pre.tsx`.
|
|
132
131
|
*
|
|
133
132
|
* Used by `fallbackCollapsed` to paint only the on-screen lines while the
|
|
134
133
|
* file's full fallback rides along compressed (see the prop-compression
|
|
@@ -384,8 +384,7 @@ function fallbackFrameType(frame) {
|
|
|
384
384
|
* When `collapsesToEmpty` is `true` the source records `focusedLines === 0`
|
|
385
385
|
* (the `oversizedFocus: 'hide'` collapse-to-nothing case): the collapsed window
|
|
386
386
|
* is intentionally empty, so the first-frame fallback is skipped and an empty
|
|
387
|
-
* array is returned. Mirrors the runtime rule in `Pre.tsx
|
|
388
|
-
* `getInitialVisibleSourceLines`.
|
|
387
|
+
* array is returned. Mirrors the runtime rule in `Pre.tsx`.
|
|
389
388
|
*
|
|
390
389
|
* Used by `fallbackCollapsed` to paint only the on-screen lines while the
|
|
391
390
|
* file's full fallback rides along compressed (see the prop-compression
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isGrammarRegistered } from "../pipeline/parseSource/grammarCache.mjs";
|
|
2
|
+
import { resolveGrammarScope } from "../pipeline/parseSource/grammarMaps.mjs";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Cache key for a parsed file. Qualified by variant so two variants that share a
|
|
3
6
|
* file name (e.g. both have `Demo.tsx`) get independent entries instead of
|
|
@@ -28,8 +31,12 @@ export function parseControlledCode(controlledCode, parseSource, preParsedCache)
|
|
|
28
31
|
*/
|
|
29
32
|
const resolveSource = (variant, fileName, source) => {
|
|
30
33
|
const key = preParsedCacheKey(variant, fileName);
|
|
34
|
+
const grammarScope = resolveGrammarScope(fileName);
|
|
35
|
+
// An existing parser missing this file's grammar returns valid-looking plain HAST.
|
|
36
|
+
// Do not retain that temporary fallback across the grammar-readiness rerender.
|
|
37
|
+
const cacheable = !grammarScope || isGrammarRegistered(grammarScope) !== false;
|
|
31
38
|
const entry = preParsedCache?.get(key);
|
|
32
|
-
if (entry) {
|
|
39
|
+
if (entry && cacheable) {
|
|
33
40
|
if (entry.source === source) {
|
|
34
41
|
return entry.hast;
|
|
35
42
|
}
|
|
@@ -37,10 +44,12 @@ export function parseControlledCode(controlledCode, parseSource, preParsedCache)
|
|
|
37
44
|
}
|
|
38
45
|
try {
|
|
39
46
|
const hast = parseSource(source, fileName);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
if (cacheable) {
|
|
48
|
+
preParsedCache?.set(key, {
|
|
49
|
+
source,
|
|
50
|
+
hast
|
|
51
|
+
});
|
|
52
|
+
}
|
|
44
53
|
return hast;
|
|
45
54
|
} catch {
|
|
46
55
|
return source;
|
|
@@ -84,26 +93,23 @@ export function parseControlledCode(controlledCode, parseSource, preParsedCache)
|
|
|
84
93
|
const fileSourceToProcess = fileData.source === null ? '' : fileData.source;
|
|
85
94
|
if (typeof fileSourceToProcess === 'string') {
|
|
86
95
|
parsedExtraFiles[fileName] = {
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
...fileData,
|
|
97
|
+
source: resolveSource(variant, fileName, fileSourceToProcess)
|
|
89
98
|
};
|
|
90
99
|
} else {
|
|
91
100
|
// Keep other values as-is
|
|
92
101
|
parsedExtraFiles[fileName] = {
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
...fileData,
|
|
103
|
+
source: fileSourceToProcess
|
|
95
104
|
};
|
|
96
105
|
}
|
|
97
106
|
}
|
|
98
107
|
extraFiles = parsedExtraFiles;
|
|
99
108
|
}
|
|
100
109
|
parsed[variant] = {
|
|
101
|
-
|
|
102
|
-
url: variantCode.url,
|
|
110
|
+
...variantCode,
|
|
103
111
|
source: mainSource,
|
|
104
|
-
extraFiles
|
|
105
|
-
filesOrder: variantCode.filesOrder,
|
|
106
|
-
comments: variantCode.comments
|
|
112
|
+
extraFiles
|
|
107
113
|
};
|
|
108
114
|
}
|
|
109
115
|
}
|