Repository: microsoft/playwright
File: packages/trace-viewer/src/ui/sourceTab.tsx (TypeScript)

Code before:
    const actionLocation = stack?.[selectedFrame];
    const location = actionLocation?.file ? actionLocation : fallbackLocation;
    if (!location)
      return { source: { file: '', errors: [], content: undefined }, targetLine: 0, highlight: [] };

    const file = location.file;
    let source = sources.get(file);
    // Fallback location can fall outside the sources model.
    if (!source) {
      source = { errors: fallbackLocation?.source?.errors || [], content: fallbackLocation?.source?.content };
      sources.set(file, source);
    }

    const targetLine = location?.line || source.errors[0]?.line || 0;
    const fileName = rootDir && file.startsWith(rootDir) ? file.substring(rootDir.length + 1) : file;
    const highlight: SourceHighlight[] = source.errors.map(e => ({ type: 'error', line: e.line, message: e.message }));
    highlight.push({ line: targetLine, type: 'running' });

    // After the source update, but before the test run, don't trust the cache.
    if (fallbackLocation?.source?.content !== undefined) {
      source.content = fallbackLocation.source.content;
    } else if (source.content === undefined || (location === fallbackLocation)) {
      const sha1 = await calculateSha1(file);
      try {
        let response = model ? await fetch(model.createRelativeUrl(`sha1/src@${sha1}.txt`)) : undefined;
        if (!response || response.status === 404)
          response = await fetch(`file?path=${encodeURIComponent(file)}`);
        if (response.status >= 400)
          source.content = `<Unable to read "${file}">`;
        else
          source.content = await response.text();
      } catch {
        source.content = `<Unable to read "${file}">`;
      }
    }
    return { model, source, highlight, targetLine, fileName, location };
  }, [stack, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] });
}

export const SourceTab: React.FunctionComponent<{
  stack?: StackFrame[],
  stackFrameLocation: 'bottom' | 'right',
  sources: Map<string, SourceModel>,
  rootDir?: string,
  fallbackLocation?: SourceLocation,
  onOpenExternally?: (location: SourceLocation) => void,
}> = ({ stack, sources, rootDir, fallbackLocation, stackFrameLocation, onOpenExternally }) => {
  const [lastStack, setLastStack] = React.useState<StackFrame[] | undefined>();
  const [selectedFrame, setSelectedFrame] = React.useState<number>(0);

  React.useEffect(() => {

Review comment (bug):
Sidenote, related to #38364

This response currently always fails once per test run, since `TraceModel` is initiated with empty `traceUri`, due to the `loadSingleTraceFile` call failing inside the polling of `TraceView` (uiModeTraceView.tsx:75).

We could do a check if `model?.traceUri` is truthy instead of just the `model`

Code after:
    const actionLocation = stack?.[selectedFrame];
    const location = actionLocation?.file ? actionLocation : fallbackLocation;
    if (!location)
      return { source: { file: '', errors: [], content: undefined }, targetLine: 0, highlight: [] };

    const file = location.file;
    let source = sources.get(file);
    // Fallback location can fall outside the sources model.
    if (!source) {
      source = { errors: fallbackLocation?.source?.errors || [], content: fallbackLocation?.source?.content };
      sources.set(file, source);
    }

    const targetLine = location?.line || source.errors[0]?.line || 0;
    const fileName = rootDir && file.startsWith(rootDir) ? file.substring(rootDir.length + 1) : file;
    const highlight: SourceHighlight[] = source.errors.map(e => ({ type: 'error', line: e.line, message: e.message }));
    highlight.push({ line: targetLine, type: 'running' });

    // After the source update, but before the test run, don't trust the cache.
    if (fallbackLocation?.source?.content !== undefined) {
      source.content = fallbackLocation.source.content;
    } else if (source.content === undefined || (location === fallbackLocation)) {
      const sha1 = await calculateSha1(file);
      try {
        let response = model ? await fetch(model.createRelativeUrl(`sha1/src@${sha1}.txt`)) : undefined;
        if (!response || response.status === 404)
          response = await fetch(`file?path=${encodeURIComponent(file)}`);
        if (response.status >= 400)
          source.content = ``;
        else
          source.content = await response.text();
      } catch {
        source.content = `<Unable to read "${file}">`;
      }
    }
    return { model, source, highlight, targetLine, fileName, location };
  }, [stack, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] });
}

export const SourceTab: React.FunctionComponent<{
  stack?: StackFrame[],
  stackFrameLocation: 'bottom' | 'right',
  sources: Map<string, SourceModel>,
  rootDir?: string,
  fallbackLocation?: SourceLocation,
  onOpenExternally?: (location: SourceLocation) => void,
}> = ({ stack, sources, rootDir, fallbackLocation, stackFrameLocation, onOpenExternally }) => {
  const [lastStack, setLastStack] = React.useState<StackFrame[] | undefined>();
  const [selectedFrame, setSelectedFrame] = React.useState<number>(0);

  React.useEffect(() => {