Skip to content

Commit e12c061

Browse files
Copilotyifancong
andauthored
perf: Lazy-load loader input/output to reduce data volume by 90%+ (#1525)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: yifancong <18437716+yifancong@users.noreply.github.com> Co-authored-by: yifancong <congzhe@bytedance.com> Co-authored-by: yifancong <easy_cong@126.com>
1 parent 846bb1a commit e12c061

File tree

5 files changed

+280
-118
lines changed

5 files changed

+280
-118
lines changed

packages/components/src/components/Loader/executions.tsx

Lines changed: 140 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { CodeViewer, DiffViewer } from '../base';
2424
import { Card } from '../Card';
2525
import { CodeOpener } from '../Opener';
2626
import { Title } from '../Title';
27+
import { ServerAPIProvider } from '../Manifest';
2728
import styles from './Analysis/style.module.scss';
2829

2930
interface LoaderExecutionsProps {
@@ -37,7 +38,7 @@ const LoaderPropsItem = ({
3738
resource,
3839
cwd,
3940
}: {
40-
loader: SDK.LoaderTransformData & {
41+
loader: Omit<SDK.LoaderTransformData, 'input' | 'result'> & {
4142
costs: number;
4243
};
4344
resource: SDK.ResourceData;
@@ -108,6 +109,123 @@ const LoaderPropsItem = ({
108109
);
109110
};
110111

112+
// Component to render Loader Details content with lazy-loaded code
113+
const LoaderDetailsContent = ({
114+
loader,
115+
resource,
116+
isLight,
117+
codeData,
118+
}: {
119+
loader: Omit<SDK.LoaderTransformData, 'input' | 'result'> & { costs: number };
120+
resource: SDK.ResourceData;
121+
isLight: boolean;
122+
codeData: SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetLoaderFileInputAndOutput>;
123+
}): JSX.Element => {
124+
const hasError = loader.errors && loader.errors.length;
125+
const before = codeData?.input || '';
126+
const loaderResult = codeData?.output || '';
127+
128+
return (
129+
<div style={{ height: '100%' }}>
130+
{hasError ? (
131+
<Col span={24} style={{ height: '53%', minHeight: 400 }}>
132+
<div
133+
style={{
134+
padding: Size.BasePadding,
135+
borderTop: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
136+
borderBottom: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
137+
}}
138+
>
139+
<Title
140+
text={`the error stack of [${loader.loader}] ${loader.isPitch ? 'pitch' : ''}`}
141+
/>
142+
</div>
143+
<div style={{ height: '90%' }}>
144+
<CodeViewer code={loader.errors[0].message} lang="javascript" />
145+
</div>
146+
</Col>
147+
) : (
148+
<Col span={24} style={{ height: '53%', minHeight: 400 }}>
149+
<div
150+
style={{
151+
display: 'flex',
152+
alignItems: 'center',
153+
padding: Size.BasePadding,
154+
borderBottom: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
155+
}}
156+
>
157+
<Title
158+
text={
159+
<>
160+
{`the result of [${loader.loader}] ${loader.isPitch ? 'pitch' : ''}`}
161+
{!loader.isPitch && (
162+
<span style={{ fontWeight: 400 }}>
163+
(
164+
<InputIcon
165+
style={{
166+
verticalAlign: 'middle',
167+
margin: '0 2px',
168+
}}
169+
/>
170+
Input ⟷
171+
<OutputIcon
172+
style={{
173+
verticalAlign: 'middle',
174+
position: 'relative',
175+
top: -2,
176+
}}
177+
/>
178+
Output)
179+
</span>
180+
)}
181+
</>
182+
}
183+
/>
184+
<div style={{ flex: 1 }} />
185+
</div>
186+
{loader.isPitch ? (
187+
loaderResult ? (
188+
<div style={{ height: '90%' }}>
189+
<CodeViewer
190+
isEmbed
191+
code={loaderResult}
192+
filePath={resource.path}
193+
/>
194+
</div>
195+
) : (
196+
<Empty
197+
description={
198+
'No loader result. If you use the Brief Mode, there will not have loader results.'
199+
}
200+
/>
201+
)
202+
) : (
203+
<div style={{ minHeight: '700px' }}>
204+
<div style={{ height: '40rem', overflow: 'hidden' }}>
205+
{!loaderResult && !before ? (
206+
<Empty
207+
description={
208+
'No loader result. If you use the Brief Mode, there will not have loader results.'
209+
}
210+
/>
211+
) : (
212+
<DiffViewer
213+
isEmbed
214+
original={before}
215+
modified={loaderResult || ''}
216+
originalFilePath={resource.path}
217+
modifiedFilePath={resource.path}
218+
/>
219+
)}
220+
</div>
221+
</div>
222+
)}
223+
</Col>
224+
)}
225+
</div>
226+
);
227+
};
228+
111229
export const LoaderExecutions = ({
112230
data,
113231
cwd,
@@ -118,9 +236,7 @@ export const LoaderExecutions = ({
118236
const { theme } = useTheme();
119237
const isLight = theme === 'light';
120238
const loader = loaders[currentIndex];
121-
const before = loader.input || '';
122239
const leftSpan = 5;
123-
const hasError = loader.errors && loader.errors.length;
124240
const [activeKey, setActiveKey] = useState('loaderDetails');
125241
const onChange = useCallback((key: string) => {
126242
setActiveKey(key);
@@ -217,108 +333,27 @@ export const LoaderExecutions = ({
217333
{
218334
label: 'Loader Details',
219335
key: 'loaderDetails',
220-
children: (
221-
<div style={{ height: '100%' }}>
222-
{hasError ? (
223-
<Col span={24} style={{ height: '53%', minHeight: 400 }}>
224-
<div
225-
style={{
226-
padding: Size.BasePadding,
227-
borderTop: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
228-
borderBottom: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
229-
}}
230-
>
231-
<Title
232-
text={`the error stack of [${loader.loader}] ${loader.isPitch ? 'pitch' : ''}`}
233-
/>
234-
</div>
235-
<div style={{ height: '90%' }}>
236-
<CodeViewer
237-
code={loader.errors[0].message}
238-
lang="javascript"
239-
/>
240-
</div>
241-
</Col>
242-
) : (
243-
<Col span={24} style={{ height: '53%', minHeight: 400 }}>
244-
<div
245-
style={{
246-
display: 'flex',
247-
alignItems: 'center',
248-
padding: Size.BasePadding,
249-
borderBottom: `1px solid ${isLight ? '#f0f0f0' : 'rgba(253, 253, 253, 0.12)'}`,
250-
}}
251-
>
252-
<Title
253-
text={
254-
<>
255-
{`the result of [${loader.loader}] ${loader.isPitch ? 'pitch' : ''}`}
256-
{!loader.isPitch && (
257-
<span style={{ fontWeight: 400 }}>
258-
(
259-
<InputIcon
260-
style={{
261-
verticalAlign: 'middle',
262-
margin: '0 2px',
263-
}}
264-
/>
265-
Input ⟷
266-
<OutputIcon
267-
style={{
268-
verticalAlign: 'middle',
269-
position: 'relative',
270-
top: -2,
271-
}}
272-
/>
273-
Output)
274-
</span>
275-
)}
276-
</>
277-
}
278-
/>
279-
<div style={{ flex: 1 }} />
280-
</div>
281-
{loader.isPitch ? (
282-
loader.result ? (
283-
<div style={{ height: '90%' }}>
284-
<CodeViewer
285-
isEmbed
286-
code={loader.result}
287-
filePath={resource.path}
288-
/>
289-
</div>
290-
) : (
291-
<Empty
292-
description={
293-
'No loader result. If you use the Brief Mode, there will not have loader results.'
294-
}
295-
/>
296-
)
297-
) : (
298-
<div style={{ minHeight: '700px' }}>
299-
<div style={{ height: '40rem', overflow: 'hidden' }}>
300-
{!loader.result && !before ? (
301-
<Empty
302-
description={
303-
'No loader result. If you use the Brief Mode, there will not have loader results.'
304-
}
305-
/>
306-
) : (
307-
<DiffViewer
308-
isEmbed
309-
original={before}
310-
modified={loader.result || ''}
311-
originalFilePath={resource.path}
312-
modifiedFilePath={resource.path}
313-
/>
314-
)}
315-
</div>
316-
</div>
317-
)}
318-
</Col>
319-
)}
320-
</div>
321-
),
336+
children:
337+
activeKey === 'loaderDetails' ? (
338+
<ServerAPIProvider
339+
api={SDK.ServerAPI.API.GetLoaderFileInputAndOutput}
340+
body={{
341+
file: resource.path,
342+
loader: loader.loader,
343+
loaderIndex: loader.loaderIndex,
344+
}}
345+
showSkeleton={false}
346+
>
347+
{(codeData) => (
348+
<LoaderDetailsContent
349+
loader={loader}
350+
resource={resource}
351+
isLight={isLight}
352+
codeData={codeData}
353+
/>
354+
)}
355+
</ServerAPIProvider>
356+
) : null,
322357
},
323358
]}
324359
onChange={onChange}

packages/types/src/sdk/server/apis/loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface LoaderAPIResponse {
2929
>;
3030
[API.GetLoaderFileDetails]: {
3131
resource: ResourceData;
32-
loaders: (LoaderTransformData & {
32+
loaders: (Omit<LoaderTransformData, 'input' | 'result'> & {
3333
costs: number;
3434
})[];
3535
};

packages/utils/src/common/data/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,12 @@ export class APIDataLoader {
127127

128128
case SDK.ServerAPI.API.GetLoaderFileInputAndOutput:
129129
return this.loader.loadData('loader').then((res) => {
130-
return Loader.getLoaderFileFirstInput(
131-
(
132-
body as SDK.ServerAPI.InferRequestBodyType<SDK.ServerAPI.API.GetLoaderFileFirstInput>
133-
).file,
130+
const params =
131+
body as SDK.ServerAPI.InferRequestBodyType<SDK.ServerAPI.API.GetLoaderFileInputAndOutput>;
132+
return Loader.getLoaderFileInputAndOutput(
133+
params.file,
134+
params.loader,
135+
params.loaderIndex,
134136
res || [],
135137
) as R;
136138
});

packages/utils/src/common/loader.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,11 @@ export function getLoaderFileDetails(
210210
return {
211211
...data,
212212
loaders: data.loaders.map((el) => {
213+
// Strip large input/result fields to reduce data volume
214+
// These can be fetched on-demand via GetLoaderFileInputAndOutput API
215+
const { input, result, ...loaderWithoutCode } = el;
213216
return {
214-
...el,
217+
...loaderWithoutCode,
215218
loader: getLoadrName(el.loader),
216219
costs: getLoaderCosts(el, list),
217220
};
@@ -341,20 +344,17 @@ export function getLoaderFileInputAndOutput(
341344
const item = loaders[i];
342345

343346
if (item.resource.path === file) {
344-
// biome-ignore lint/correctness/noUnreachable: may not need change
345347
for (let j = 0; j < item.loaders.length; j++) {
346348
const l = item.loaders[j];
347-
if (l.loader === loader && l.loaderIndex === loaderIndex) {
349+
if (
350+
getLoadrName(l.loader) === loader &&
351+
l.loaderIndex === loaderIndex
352+
) {
348353
return {
349354
input: l.input || '',
350355
output: l.result || '',
351356
};
352357
}
353-
354-
return {
355-
input: '',
356-
output: '',
357-
};
358358
}
359359
}
360360
}

0 commit comments

Comments
 (0)