@mui/internal-docs-infra 0.11.1-canary.17 → 0.11.1-canary.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CodeHighlighter/CodeHighlighterClient.mjs +9 -1
- package/CodeHighlighter/buildStringFallback.d.mts +29 -0
- package/CodeHighlighter/buildStringFallback.mjs +42 -0
- package/CodeHighlighter/codeToFallbackProps.d.mts +4 -1
- package/CodeHighlighter/codeToFallbackProps.mjs +94 -11
- package/CodeHighlighter/createClientProps.mjs +10 -1
- package/CodeHighlighter/fallbackCompression.d.mts +19 -2
- package/CodeHighlighter/fallbackCompression.mjs +26 -3
- package/CodeHighlighter/fallbackFormat.d.mts +7 -1
- package/CodeHighlighter/fallbackFormat.mjs +11 -5
- package/CodeHighlighter/mergeComments.mjs +1 -1
- package/CodeHighlighter/prepareInitialSource.mjs +145 -4
- package/CodeHighlighter/types.d.mts +92 -12
- package/CodeHighlighter/useCodeFallback.d.mts +45 -2
- package/CodeHighlighter/useCodeFallback.mjs +82 -10
- package/abstractCreateDemo/abstractCreateDemo.d.mts +52 -3
- package/abstractCreateDemo/abstractCreateDemo.mjs +35 -1
- package/abstractCreateDemo/resolveDemoFlag.d.mts +20 -0
- package/abstractCreateDemo/resolveDemoFlag.mjs +25 -0
- package/package.json +2 -2
- package/pipeline/enhanceCodeEmphasis/enhanceCodeEmphasis.mjs +11 -0
- package/pipeline/hastUtils/hastDictionary.mjs +9 -0
- package/pipeline/hastUtils/stripHighlightingSpans.mjs +2 -7
- package/pipeline/loadIsomorphicCodeVariant/applyCodeTransformWithComments.mjs +2 -1
- package/pipeline/loadIsomorphicCodeVariant/getInitialVisibleSourceLines.mjs +10 -2
- package/pipeline/loadIsomorphicCodeVariant/loadCodeFallback.mjs +17 -5
- package/pipeline/loadIsomorphicCodeVariant/loadIsomorphicCodeVariant.mjs +74 -14
- package/pipeline/loadIsomorphicCodeVariant/transformSource.mjs +3 -6
- package/pipeline/loadServerTypesText/order.mjs +1 -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/parseSource/addLineGutters.mjs +2 -1
- package/pipeline/parseSource/calculateFrameRanges.d.mts +22 -0
- package/pipeline/parseSource/calculateFrameRanges.mjs +69 -25
- package/pipeline/parseSource/frameVisibility.d.mts +26 -1
- package/pipeline/parseSource/frameVisibility.mjs +42 -1
- package/pipeline/parseSource/isFrameSpan.d.mts +19 -0
- package/pipeline/parseSource/isFrameSpan.mjs +24 -0
- package/pipeline/parseSource/parseSource.d.mts +16 -0
- package/pipeline/parseSource/parseSource.mjs +17 -0
- package/pipeline/parseSource/restructureFrames.mjs +2 -1
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.d.mts +26 -0
- package/pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs +37 -3
- package/useCode/Pre.d.mts +19 -0
- package/useCode/Pre.mjs +99 -20
- package/useCode/sourceLineCounts.d.mts +3 -3
- package/useCode/sourceLineCounts.mjs +38 -20
- package/useCode/useCode.d.mts +0 -1
- package/useCode/useCode.mjs +15 -2
- package/useCode/useFileNavigation.d.mts +7 -0
- package/useCode/useFileNavigation.mjs +26 -4
- package/useCode/useUIState.d.mts +2 -2
- package/useCode/useUIState.mjs +2 -2
- package/pipeline/loaderUtils/convertCommentsToOneIndexed.d.mts +0 -8
- package/pipeline/loaderUtils/convertCommentsToOneIndexed.mjs +0 -16
|
@@ -1025,7 +1025,15 @@ export function CodeHighlighterClient(props) {
|
|
|
1025
1025
|
}
|
|
1026
1026
|
let restored = residualMap ? scatterResidualFallbacks(base, residualMap) : base;
|
|
1027
1027
|
if (!props.fallbackCollapsed) {
|
|
1028
|
-
|
|
1028
|
+
// `preserveExisting`: never let the hoist overwrite a `fallback` already
|
|
1029
|
+
// on the variant. A fully-loaded `hastCompressed` source carries its own
|
|
1030
|
+
// source-paired (structured) `fallback`, which is the only valid DEFLATE
|
|
1031
|
+
// dictionary. The hoist can be an un-highlighted *raw-string* fallback
|
|
1032
|
+
// whose text keeps a trailing newline `buildRootFallback` drops, so
|
|
1033
|
+
// overwriting the structured one makes `decodeHastSource` throw a
|
|
1034
|
+
// dictionary mismatch. The hoist is the dictionary only when the variant's
|
|
1035
|
+
// own was stripped, so apply it solely where one isn't already present.
|
|
1036
|
+
restored = scatterResidualFallbacks(restored, hoistedFallbackHasts, true);
|
|
1029
1037
|
}
|
|
1030
1038
|
return restored;
|
|
1031
1039
|
}, [residualMap, hoistedFallbackHasts, props.fallbackCollapsed]);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SourceComments, SourceEnhancers } from "./types.mjs";
|
|
2
|
+
import { type FallbackNode } from "./fallbackFormat.mjs";
|
|
3
|
+
export interface StringFallbackResult {
|
|
4
|
+
/** Compact, windowed fallback frames (text only — `.line` spans stripped). */
|
|
5
|
+
fallback: FallbackNode[];
|
|
6
|
+
/** Total source lines. */
|
|
7
|
+
totalLines: number;
|
|
8
|
+
/** Lines visible in the collapsed window (the sum of visible frame sizes). */
|
|
9
|
+
focusedLines: number;
|
|
10
|
+
/** Whether the enhanced frame structure has hidden content to expand into. */
|
|
11
|
+
collapsible: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Derive a *windowed* fallback for a plain-string source by running the same
|
|
15
|
+
* `sourceEnhancers` the live render uses over a cheap line-guttered HAST
|
|
16
|
+
* (`parsePlainText` — gutters, no syntax highlighting). The inline-string
|
|
17
|
+
* fallback path otherwise wraps the whole source in one un-windowed focus frame,
|
|
18
|
+
* so an oversized / `@focus` / `@highlight` block paints its full text before
|
|
19
|
+
* hydration then snaps to the collapsed window. Running the enhancers here makes
|
|
20
|
+
* the loading frames match the live render, and the resulting `root.data` carries
|
|
21
|
+
* the `totalLines` / `focusedLines` the compact fallback can't preserve.
|
|
22
|
+
*
|
|
23
|
+
* Synchronous by design — it runs at server fallback-prep time inside the
|
|
24
|
+
* (sync) `prepareInitialSource`. An enhancer that returns a promise is skipped
|
|
25
|
+
* (returns `undefined`) so the caller falls back to the naive single-frame wrap
|
|
26
|
+
* rather than blocking; the built-in `enhanceCodeEmphasis` is synchronous, so
|
|
27
|
+
* the common case windows.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildStringFallback(source: string, comments: SourceComments | undefined, fileName: string, sourceEnhancers: SourceEnhancers): StringFallbackResult | undefined;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { buildRootFallback } from "./fallbackFormat.mjs";
|
|
2
|
+
import { parsePlainText } from "../pipeline/parseSource/index.mjs";
|
|
3
|
+
function isPromiseLike(value) {
|
|
4
|
+
return typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Derive a *windowed* fallback for a plain-string source by running the same
|
|
9
|
+
* `sourceEnhancers` the live render uses over a cheap line-guttered HAST
|
|
10
|
+
* (`parsePlainText` — gutters, no syntax highlighting). The inline-string
|
|
11
|
+
* fallback path otherwise wraps the whole source in one un-windowed focus frame,
|
|
12
|
+
* so an oversized / `@focus` / `@highlight` block paints its full text before
|
|
13
|
+
* hydration then snaps to the collapsed window. Running the enhancers here makes
|
|
14
|
+
* the loading frames match the live render, and the resulting `root.data` carries
|
|
15
|
+
* the `totalLines` / `focusedLines` the compact fallback can't preserve.
|
|
16
|
+
*
|
|
17
|
+
* Synchronous by design — it runs at server fallback-prep time inside the
|
|
18
|
+
* (sync) `prepareInitialSource`. An enhancer that returns a promise is skipped
|
|
19
|
+
* (returns `undefined`) so the caller falls back to the naive single-frame wrap
|
|
20
|
+
* rather than blocking; the built-in `enhanceCodeEmphasis` is synchronous, so
|
|
21
|
+
* the common case windows.
|
|
22
|
+
*/
|
|
23
|
+
export function buildStringFallback(source, comments, fileName, sourceEnhancers) {
|
|
24
|
+
let root = parsePlainText(source);
|
|
25
|
+
for (const enhancer of sourceEnhancers) {
|
|
26
|
+
const result = enhancer(root, comments, fileName);
|
|
27
|
+
if (isPromiseLike(result)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
root = result;
|
|
31
|
+
}
|
|
32
|
+
const data = root.data;
|
|
33
|
+
const totalLines = data?.totalLines ?? 0;
|
|
34
|
+
const focusedLines = data?.focusedLines ?? totalLines;
|
|
35
|
+
const collapsible = data?.collapsible === true;
|
|
36
|
+
return {
|
|
37
|
+
fallback: buildRootFallback(root),
|
|
38
|
+
totalLines,
|
|
39
|
+
focusedLines,
|
|
40
|
+
collapsible
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { BaseContentLoadingProps, Code, Fallbacks } from "./types.mjs";
|
|
2
|
-
|
|
2
|
+
import { type SourceLineCounts } from "../useCode/sourceLineCounts.mjs";
|
|
3
|
+
/** Per-variant → per-file line metadata threaded for the fallback. */
|
|
4
|
+
export type LineCountsByVariant = Record<string, Record<string, SourceLineCounts>>;
|
|
5
|
+
export declare function codeToFallbackProps(variant: string, code?: Code, _fileName?: string, _needsAllFiles?: boolean, needsAllVariants?: boolean, allFallbackHasts?: Record<string, Fallbacks>, allLineCounts?: LineCountsByVariant): BaseContentLoadingProps;
|
|
3
6
|
/**
|
|
4
7
|
* Read a variant's per-file fallbacks straight off its `VariantCode` `fallback`
|
|
5
8
|
* fields (main + extra files), returning a `Fallbacks` map keyed by file name.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { hastToFallback } from "./fallbackFormat.mjs";
|
|
2
2
|
import { getLanguageFromExtension } from "../pipeline/loaderUtils/getLanguageFromExtension.mjs";
|
|
3
|
+
import { getVariantFileLineCounts } from "../useCode/sourceLineCounts.mjs";
|
|
4
|
+
|
|
5
|
+
/** Per-variant → per-file line metadata threaded for the fallback. */
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Resolve a `language-{language}` hint for a file from its extension, used to
|
|
@@ -23,9 +26,10 @@ function getLanguageFromFileName(fileName) {
|
|
|
23
26
|
* one from the source for live/dev trees that never went through the loader.
|
|
24
27
|
*
|
|
25
28
|
* A plain-string source (an unparsed code block, e.g. `<CodeHighlighter>{code}`)
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* dictionary), so without a variant
|
|
29
|
+
* is wrapped in a single focus frame so the fallback always has the same frame
|
|
30
|
+
* structure as the highlighted render — never a bare text node. `hastCompressed`
|
|
31
|
+
* payloads can't be decoded here (no DEFLATE dictionary), so without a variant
|
|
32
|
+
* `fallback` they yield `undefined`.
|
|
29
33
|
*/
|
|
30
34
|
function sourceToFallback(source, fallback) {
|
|
31
35
|
if (fallback) {
|
|
@@ -35,8 +39,13 @@ function sourceToFallback(source, fallback) {
|
|
|
35
39
|
return undefined;
|
|
36
40
|
}
|
|
37
41
|
if (typeof source === 'string') {
|
|
38
|
-
//
|
|
39
|
-
|
|
42
|
+
// Wrap the raw code in a single focus frame so the fallback always carries a
|
|
43
|
+
// frame, matching the highlighted render (`buildRootFallback` likewise emits
|
|
44
|
+
// frames with text children) — never a bare text node. The whole source is
|
|
45
|
+
// the visible window; collapse-to-empty demotes it like any other focus frame.
|
|
46
|
+
return [['span', 'frame', {
|
|
47
|
+
dataFrameType: 'focus'
|
|
48
|
+
}, source]];
|
|
40
49
|
}
|
|
41
50
|
if ('type' in source && source.type === 'root') {
|
|
42
51
|
return hastToFallback(source);
|
|
@@ -57,9 +66,40 @@ function sourceToFallback(source, fallback) {
|
|
|
57
66
|
* `source` is present, mirroring how consumers gate the `language-{language}`
|
|
58
67
|
* class behind a rendered source.
|
|
59
68
|
*/
|
|
60
|
-
function deriveVariantSources(variantCode, variantHasts) {
|
|
69
|
+
function deriveVariantSources(variantCode, variantHasts, variantLineCounts) {
|
|
61
70
|
const fileNames = [variantCode.fileName, ...Object.keys(variantCode.extraFiles || {})].filter(name => Boolean(name));
|
|
62
71
|
const mainFile = variantCode.fileName || fileNames[0];
|
|
72
|
+
|
|
73
|
+
// Per-file line counts: prefer render-time windowing (`variantLineCounts`), else
|
|
74
|
+
// the counts the loader stored on the code (`VariantCode` / extra-file `totalLines`
|
|
75
|
+
// / `focusedLines`). So every file/variant carries its window, not just the main one.
|
|
76
|
+
const fileCounts = fileName => {
|
|
77
|
+
const threaded = variantLineCounts?.[fileName];
|
|
78
|
+
if (threaded) {
|
|
79
|
+
return threaded;
|
|
80
|
+
}
|
|
81
|
+
const file = variantCode.fileName === fileName ? variantCode : variantCode.extraFiles?.[fileName];
|
|
82
|
+
if (file && typeof file !== 'string' && file.totalLines !== undefined) {
|
|
83
|
+
return {
|
|
84
|
+
totalLines: file.totalLines,
|
|
85
|
+
focusedLines: file.focusedLines ?? file.totalLines,
|
|
86
|
+
collapsible: file.collapsible === true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Last resort: count lines off the source (a plain string with no enhancers ⇒
|
|
90
|
+
// `focusedLines === totalLines`, matching `<Pre>`'s `getSourceLineCounts`). Guarded
|
|
91
|
+
// because a `hastCompressed` source can't be decoded without its dictionary (which
|
|
92
|
+
// the server strips before this runs) — the server passes `variantLineCounts`
|
|
93
|
+
// instead, so this branch only really fires client-side where the dictionary is on
|
|
94
|
+
// the code.
|
|
95
|
+
try {
|
|
96
|
+
const counts = getVariantFileLineCounts(variantCode, fileName);
|
|
97
|
+
// `totalLines === 0` means a hast with no `root.data` counts (not a real count).
|
|
98
|
+
return counts && counts.totalLines > 0 ? counts : undefined;
|
|
99
|
+
} catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
63
103
|
let source;
|
|
64
104
|
let extraSource;
|
|
65
105
|
if (variantHasts) {
|
|
@@ -70,7 +110,10 @@ function deriveVariantSources(variantCode, variantHasts) {
|
|
|
70
110
|
const extra = {};
|
|
71
111
|
for (const [fName, nodes] of Object.entries(variantHasts)) {
|
|
72
112
|
if (fName !== mainFile) {
|
|
73
|
-
extra[fName] =
|
|
113
|
+
extra[fName] = {
|
|
114
|
+
source: nodes,
|
|
115
|
+
...fileCounts(fName)
|
|
116
|
+
};
|
|
74
117
|
}
|
|
75
118
|
}
|
|
76
119
|
if (Object.keys(extra).length > 0) {
|
|
@@ -85,7 +128,10 @@ function deriveVariantSources(variantCode, variantHasts) {
|
|
|
85
128
|
if (typeof fData === 'object' && fData.source) {
|
|
86
129
|
const fb = sourceToFallback(fData.source, fData.fallback);
|
|
87
130
|
if (fb) {
|
|
88
|
-
extra[fName] =
|
|
131
|
+
extra[fName] = {
|
|
132
|
+
source: fb,
|
|
133
|
+
...fileCounts(fName)
|
|
134
|
+
};
|
|
89
135
|
}
|
|
90
136
|
}
|
|
91
137
|
}
|
|
@@ -94,9 +140,13 @@ function deriveVariantSources(variantCode, variantHasts) {
|
|
|
94
140
|
}
|
|
95
141
|
}
|
|
96
142
|
const language = source ? variantCode.language ?? getLanguageFromFileName(mainFile) : undefined;
|
|
143
|
+
const mainCounts = source && mainFile ? fileCounts(mainFile) : undefined;
|
|
97
144
|
return {
|
|
98
145
|
fileNames,
|
|
99
146
|
source,
|
|
147
|
+
totalLines: mainCounts?.totalLines,
|
|
148
|
+
focusedLines: mainCounts?.focusedLines,
|
|
149
|
+
collapsible: mainCounts?.collapsible,
|
|
100
150
|
extraSource,
|
|
101
151
|
language
|
|
102
152
|
};
|
|
@@ -107,7 +157,7 @@ export function codeToFallbackProps(variant, code,
|
|
|
107
157
|
// upstream in `stripFallbackHastsFromCode` (which only hoists the allowed
|
|
108
158
|
// files into `allFallbackHasts`), so the derivation below reads them off the
|
|
109
159
|
// already-gated `allFallbackHasts` rather than re-applying the flags here.
|
|
110
|
-
_fileName, _needsAllFiles = false, needsAllVariants = false, allFallbackHasts) {
|
|
160
|
+
_fileName, _needsAllFiles = false, needsAllVariants = false, allFallbackHasts, allLineCounts) {
|
|
111
161
|
const variantCode = code?.[variant];
|
|
112
162
|
if (!variantCode || typeof variantCode === 'string') {
|
|
113
163
|
return {};
|
|
@@ -115,23 +165,38 @@ _fileName, _needsAllFiles = false, needsAllVariants = false, allFallbackHasts) {
|
|
|
115
165
|
const {
|
|
116
166
|
fileNames,
|
|
117
167
|
source,
|
|
168
|
+
totalLines,
|
|
169
|
+
focusedLines,
|
|
170
|
+
collapsible,
|
|
118
171
|
extraSource,
|
|
119
172
|
language
|
|
120
|
-
} = deriveVariantSources(variantCode, allFallbackHasts?.[variant]);
|
|
173
|
+
} = deriveVariantSources(variantCode, allFallbackHasts?.[variant], allLineCounts?.[variant]);
|
|
121
174
|
if (needsAllVariants) {
|
|
122
175
|
const extraVariants = Object.entries(code || {}).reduce((acc, [name, vCode]) => {
|
|
123
176
|
if (name !== variant && vCode && typeof vCode !== 'string') {
|
|
124
177
|
const {
|
|
125
178
|
fileNames: evFileNames,
|
|
126
179
|
source: evSource,
|
|
180
|
+
totalLines: evTotalLines,
|
|
181
|
+
focusedLines: evFocusedLines,
|
|
182
|
+
collapsible: evCollapsible,
|
|
127
183
|
extraSource: evExtraSource,
|
|
128
184
|
language: evLanguage
|
|
129
|
-
} = deriveVariantSources(vCode, allFallbackHasts?.[name]);
|
|
185
|
+
} = deriveVariantSources(vCode, allFallbackHasts?.[name], allLineCounts?.[name]);
|
|
130
186
|
acc[name] = {
|
|
131
187
|
fileNames: evFileNames,
|
|
132
188
|
...(evSource ? {
|
|
133
189
|
source: evSource
|
|
134
190
|
} : undefined),
|
|
191
|
+
...(evTotalLines !== undefined ? {
|
|
192
|
+
totalLines: evTotalLines
|
|
193
|
+
} : undefined),
|
|
194
|
+
...(evFocusedLines !== undefined ? {
|
|
195
|
+
focusedLines: evFocusedLines
|
|
196
|
+
} : undefined),
|
|
197
|
+
...(evCollapsible !== undefined ? {
|
|
198
|
+
collapsible: evCollapsible
|
|
199
|
+
} : undefined),
|
|
135
200
|
...(evLanguage ? {
|
|
136
201
|
language: evLanguage
|
|
137
202
|
} : undefined),
|
|
@@ -147,6 +212,15 @@ _fileName, _needsAllFiles = false, needsAllVariants = false, allFallbackHasts) {
|
|
|
147
212
|
...(source ? {
|
|
148
213
|
source
|
|
149
214
|
} : undefined),
|
|
215
|
+
...(totalLines !== undefined ? {
|
|
216
|
+
totalLines
|
|
217
|
+
} : undefined),
|
|
218
|
+
...(focusedLines !== undefined ? {
|
|
219
|
+
focusedLines
|
|
220
|
+
} : undefined),
|
|
221
|
+
...(collapsible !== undefined ? {
|
|
222
|
+
collapsible
|
|
223
|
+
} : undefined),
|
|
150
224
|
...(language ? {
|
|
151
225
|
language
|
|
152
226
|
} : undefined),
|
|
@@ -161,6 +235,15 @@ _fileName, _needsAllFiles = false, needsAllVariants = false, allFallbackHasts) {
|
|
|
161
235
|
...(source ? {
|
|
162
236
|
source
|
|
163
237
|
} : undefined),
|
|
238
|
+
...(totalLines !== undefined ? {
|
|
239
|
+
totalLines
|
|
240
|
+
} : undefined),
|
|
241
|
+
...(focusedLines !== undefined ? {
|
|
242
|
+
focusedLines
|
|
243
|
+
} : undefined),
|
|
244
|
+
...(collapsible !== undefined ? {
|
|
245
|
+
collapsible
|
|
246
|
+
} : undefined),
|
|
164
247
|
...(language ? {
|
|
165
248
|
language
|
|
166
249
|
} : undefined),
|
|
@@ -25,7 +25,16 @@ export function createClientProps(props) {
|
|
|
25
25
|
name: props.name,
|
|
26
26
|
slug: props.slug,
|
|
27
27
|
url,
|
|
28
|
-
variantType: props.variantType
|
|
28
|
+
variantType: props.variantType,
|
|
29
|
+
// Thread the render-time display flags into the content channel so both
|
|
30
|
+
// `useCode`/`<Pre>` and the loading fallback honor them. A top-level prop
|
|
31
|
+
// wins over one already present in `contentProps`.
|
|
32
|
+
...(props.collapseToEmpty !== undefined ? {
|
|
33
|
+
collapseToEmpty: props.collapseToEmpty
|
|
34
|
+
} : undefined),
|
|
35
|
+
...(props.initialExpanded !== undefined ? {
|
|
36
|
+
initialExpanded: props.initialExpanded
|
|
37
|
+
} : undefined)
|
|
29
38
|
};
|
|
30
39
|
return {
|
|
31
40
|
url,
|
|
@@ -61,16 +61,33 @@ export declare function extractResidualFallbacks(code: Code): {
|
|
|
61
61
|
* non-consolidated payload would have had, so downstream consumers are unaware
|
|
62
62
|
* the residual ever travelled compressed.
|
|
63
63
|
*
|
|
64
|
+
* `preserveExisting` keeps any `fallback` already on the variant/extra file
|
|
65
|
+
* instead of overwriting it. The hoisted-fallback scatter passes `true`: the
|
|
66
|
+
* hoist is an *initial-paint* dictionary that, for an un-highlighted load, is a
|
|
67
|
+
* raw-string fallback whose text keeps a trailing newline that `buildRootFallback`
|
|
68
|
+
* drops — so it's the WRONG DEFLATE dictionary for a fully-loaded `hastCompressed`
|
|
69
|
+
* source, which already carries its own source-paired (structured) `fallback`.
|
|
70
|
+
* Overwriting that with the hoist makes `decodeHastSource` throw a dictionary
|
|
71
|
+
* mismatch. The hoist is only the dictionary when the variant's own was stripped,
|
|
72
|
+
* so apply it only where one isn't already present. The residual-blob scatter
|
|
73
|
+
* keeps the default (`false`): it always writes onto server-stripped, fallback-less
|
|
74
|
+
* variants, so there is nothing to preserve.
|
|
75
|
+
*
|
|
64
76
|
* Pure: only the variants that regain a fallback are shallow-cloned.
|
|
65
77
|
*/
|
|
66
|
-
export declare function scatterResidualFallbacks(code: Code, residual: ResidualFallbacks): Code;
|
|
78
|
+
export declare function scatterResidualFallbacks(code: Code, residual: ResidualFallbacks, preserveExisting?: boolean): Code;
|
|
67
79
|
/**
|
|
68
80
|
* Reduce every fallback in a rendered-subset map to its collapsed window (see
|
|
69
81
|
* `collapsedVisibleFallback`). Used by `fallbackCollapsed` to hand
|
|
70
82
|
* `ContentLoading` only the on-screen lines while the full fallbacks ride along
|
|
71
83
|
* in the residual blob.
|
|
84
|
+
*
|
|
85
|
+
* `collapsesToEmpty(variantName, fileName)` reports the `oversizedFocus: 'hide'`
|
|
86
|
+
* collapse-to-nothing case (the source's `focusedLines === 0`): such files get
|
|
87
|
+
* an empty collapsed window so the loading UI matches the hydrated render
|
|
88
|
+
* instead of briefly painting the first frame.
|
|
72
89
|
*/
|
|
73
|
-
export declare function collapseRenderedFallbacks(rendered: ResidualFallbacks): ResidualFallbacks;
|
|
90
|
+
export declare function collapseRenderedFallbacks(rendered: ResidualFallbacks, collapsesToEmpty?: (variantName: string, fileName: string) => boolean): ResidualFallbacks;
|
|
74
91
|
/**
|
|
75
92
|
* Deep-merge two residual maps (`variant → fileName → fallback`), with `b`
|
|
76
93
|
* winning on conflicts. Used to fold the rendered files' full fallbacks into
|
|
@@ -146,9 +146,21 @@ export function extractResidualFallbacks(code) {
|
|
|
146
146
|
* non-consolidated payload would have had, so downstream consumers are unaware
|
|
147
147
|
* the residual ever travelled compressed.
|
|
148
148
|
*
|
|
149
|
+
* `preserveExisting` keeps any `fallback` already on the variant/extra file
|
|
150
|
+
* instead of overwriting it. The hoisted-fallback scatter passes `true`: the
|
|
151
|
+
* hoist is an *initial-paint* dictionary that, for an un-highlighted load, is a
|
|
152
|
+
* raw-string fallback whose text keeps a trailing newline that `buildRootFallback`
|
|
153
|
+
* drops — so it's the WRONG DEFLATE dictionary for a fully-loaded `hastCompressed`
|
|
154
|
+
* source, which already carries its own source-paired (structured) `fallback`.
|
|
155
|
+
* Overwriting that with the hoist makes `decodeHastSource` throw a dictionary
|
|
156
|
+
* mismatch. The hoist is only the dictionary when the variant's own was stripped,
|
|
157
|
+
* so apply it only where one isn't already present. The residual-blob scatter
|
|
158
|
+
* keeps the default (`false`): it always writes onto server-stripped, fallback-less
|
|
159
|
+
* variants, so there is nothing to preserve.
|
|
160
|
+
*
|
|
149
161
|
* Pure: only the variants that regain a fallback are shallow-cloned.
|
|
150
162
|
*/
|
|
151
|
-
export function scatterResidualFallbacks(code, residual) {
|
|
163
|
+
export function scatterResidualFallbacks(code, residual, preserveExisting = false) {
|
|
152
164
|
const restored = {};
|
|
153
165
|
for (const [variantName, variant] of Object.entries(code)) {
|
|
154
166
|
const files = residual[variantName];
|
|
@@ -160,6 +172,9 @@ export function scatterResidualFallbacks(code, residual) {
|
|
|
160
172
|
let nextExtraFiles;
|
|
161
173
|
for (const [fileName, fallback] of Object.entries(files)) {
|
|
162
174
|
if (fileName === variant.fileName) {
|
|
175
|
+
if (preserveExisting && nextVariant.fallback) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
163
178
|
nextVariant = {
|
|
164
179
|
...nextVariant,
|
|
165
180
|
fallback
|
|
@@ -167,6 +182,9 @@ export function scatterResidualFallbacks(code, residual) {
|
|
|
167
182
|
} else {
|
|
168
183
|
const fileData = variant.extraFiles?.[fileName];
|
|
169
184
|
if (fileData && typeof fileData === 'object') {
|
|
185
|
+
if (preserveExisting && fileData.fallback) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
170
188
|
if (!nextExtraFiles) {
|
|
171
189
|
nextExtraFiles = {
|
|
172
190
|
...variant.extraFiles
|
|
@@ -195,13 +213,18 @@ export function scatterResidualFallbacks(code, residual) {
|
|
|
195
213
|
* `collapsedVisibleFallback`). Used by `fallbackCollapsed` to hand
|
|
196
214
|
* `ContentLoading` only the on-screen lines while the full fallbacks ride along
|
|
197
215
|
* in the residual blob.
|
|
216
|
+
*
|
|
217
|
+
* `collapsesToEmpty(variantName, fileName)` reports the `oversizedFocus: 'hide'`
|
|
218
|
+
* collapse-to-nothing case (the source's `focusedLines === 0`): such files get
|
|
219
|
+
* an empty collapsed window so the loading UI matches the hydrated render
|
|
220
|
+
* instead of briefly painting the first frame.
|
|
198
221
|
*/
|
|
199
|
-
export function collapseRenderedFallbacks(rendered) {
|
|
222
|
+
export function collapseRenderedFallbacks(rendered, collapsesToEmpty) {
|
|
200
223
|
const collapsed = {};
|
|
201
224
|
for (const [variantName, files] of Object.entries(rendered)) {
|
|
202
225
|
const collapsedFiles = {};
|
|
203
226
|
for (const [fileName, fallback] of Object.entries(files)) {
|
|
204
|
-
collapsedFiles[fileName] = collapsedVisibleFallback(fallback);
|
|
227
|
+
collapsedFiles[fileName] = collapsedVisibleFallback(fallback, collapsesToEmpty?.(variantName, fileName) ?? false);
|
|
205
228
|
}
|
|
206
229
|
collapsed[variantName] = collapsedFiles;
|
|
207
230
|
}
|
|
@@ -86,8 +86,14 @@ export declare function redistributeRootFallback(root: HastRoot, fallback: Fallb
|
|
|
86
86
|
* (the whole source is the focused window) the first frame stands in. Returns
|
|
87
87
|
* the input unchanged when it has no frames at all.
|
|
88
88
|
*
|
|
89
|
+
* When `collapsesToEmpty` is `true` the source records `focusedLines === 0`
|
|
90
|
+
* (the `oversizedFocus: 'hide'` collapse-to-nothing case): the collapsed window
|
|
91
|
+
* is intentionally empty, so the first-frame fallback is skipped and an empty
|
|
92
|
+
* array is returned. Mirrors the runtime rule in `Pre.tsx` /
|
|
93
|
+
* `getInitialVisibleSourceLines`.
|
|
94
|
+
*
|
|
89
95
|
* Used by `fallbackCollapsed` to paint only the on-screen lines while the
|
|
90
96
|
* file's full fallback rides along compressed (see the prop-compression
|
|
91
97
|
* pattern's "Splitting the Fallback by Visibility").
|
|
92
98
|
*/
|
|
93
|
-
export declare function collapsedVisibleFallback(fallback: FallbackNode[]): FallbackNode[];
|
|
99
|
+
export declare function collapsedVisibleFallback(fallback: FallbackNode[], collapsesToEmpty?: boolean): FallbackNode[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { COLLAPSED_VISIBLE_FRAME_TYPES } from "../pipeline/parseSource/frameVisibility.mjs";
|
|
2
|
+
import { isFrameSpan } from "../pipeline/parseSource/isFrameSpan.mjs";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Compact serialization format for fallback HAST trees.
|
|
@@ -155,10 +156,6 @@ function nodeText(node) {
|
|
|
155
156
|
}
|
|
156
157
|
return children.map(nodeText).join('');
|
|
157
158
|
}
|
|
158
|
-
function isFrameSpan(element) {
|
|
159
|
-
const className = element.properties?.className;
|
|
160
|
-
return className === 'frame' || Array.isArray(className) && className.includes('frame');
|
|
161
|
-
}
|
|
162
159
|
|
|
163
160
|
/**
|
|
164
161
|
* Collects the plain text of a frame from its `.line` spans and the newline
|
|
@@ -291,11 +288,20 @@ function fallbackFrameType(frame) {
|
|
|
291
288
|
* (the whole source is the focused window) the first frame stands in. Returns
|
|
292
289
|
* the input unchanged when it has no frames at all.
|
|
293
290
|
*
|
|
291
|
+
* When `collapsesToEmpty` is `true` the source records `focusedLines === 0`
|
|
292
|
+
* (the `oversizedFocus: 'hide'` collapse-to-nothing case): the collapsed window
|
|
293
|
+
* is intentionally empty, so the first-frame fallback is skipped and an empty
|
|
294
|
+
* array is returned. Mirrors the runtime rule in `Pre.tsx` /
|
|
295
|
+
* `getInitialVisibleSourceLines`.
|
|
296
|
+
*
|
|
294
297
|
* Used by `fallbackCollapsed` to paint only the on-screen lines while the
|
|
295
298
|
* file's full fallback rides along compressed (see the prop-compression
|
|
296
299
|
* pattern's "Splitting the Fallback by Visibility").
|
|
297
300
|
*/
|
|
298
|
-
export function collapsedVisibleFallback(fallback) {
|
|
301
|
+
export function collapsedVisibleFallback(fallback, collapsesToEmpty = false) {
|
|
302
|
+
if (collapsesToEmpty) {
|
|
303
|
+
return [];
|
|
304
|
+
}
|
|
299
305
|
let firstFrame = -1;
|
|
300
306
|
let firstVisible = -1;
|
|
301
307
|
let lastVisible = -1;
|
|
@@ -76,5 +76,5 @@ function warnOnIndexingMismatch(input, mine) {
|
|
|
76
76
|
if (Object.keys(other).length === 0) {
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
console.warn('mergeComments: inputs appear to use different line-indexing conventions ' + '(one contains a `0` key, the other does not).
|
|
79
|
+
console.warn('mergeComments: inputs appear to use different line-indexing conventions ' + '(one contains a `0` key, the other does not). Comments are 1-indexed everywhere ' + '(a `0` key means something emitted 0-indexed comments); both inputs must be ' + '1-indexed or markers will land on the wrong lines.');
|
|
80
80
|
}
|