Click to Source: Embedding Runtime Locations

Build-time transforms inject source locations into runtime code. Alt+Click any element to jump to its source file.

Try It

Hold Alt and hover over any button, then click to view its source. The mock console shows what file would open, and the viewer displays the source code.

React Component Locations

Transform $ prop shorthand into data-loc attributes:

if (enableReact$Loc && path.endsWith(".tsx")) {
  for (const match of code.matchAll(CLASSNAME_$_RE)) {
    const index = match.index!;
    const [_, pre, post] = match;
    const { line, column } = indexToLineAndColumn(code, index + pre.length);

    const link = `${path.slice(inCodebase.index + IN_CODEBASE_SLICE)}:${line}:${column}`;
    let dataAttrs = `data-loc=${JSON.stringify(link)}`;
    if (pre.startsWith("<")) {
      dataAttrs = `${dataAttrs} data-name=${JSON.stringify(pre.split(" ")[0].slice(1).trim())}`;
    }

    if (post === "=") {
      string.overwrite(index + pre.length, index + _.length - post.length, `${dataAttrs} className`);
    } else {
      string.overwrite(index + pre.length, index + _.length - post.length, dataAttrs);
    }
  }
}
  // Input:
  <div $="card">Content</div>

  // Output (at build):
  <div data-loc="components/Card.tsx:26:7" className="card">Content</div>

Runtime handler walks React fiber tree and opens the file:

const getPath = (fiber: Fiber, element: HTMLElement | undefined): string | undefined => {
  // First check for data-loc attribute if element is provided
  if (element?.dataset.loc) {
    return element.dataset.loc;
  }

  const source = fiber._debugSource ?? fiber._debugInfo ?? fiber._source;

  if (!source) return undefined;

  const { fileName, lineNumber = 1, columnNumber = 1 } = source;
  return `${fileName}:${lineNumber}:${columnNumber}`;
};

const getLayersForElement = (element: HTMLElement, root: string): ComponentLayer[] => {
  let instance = getReactInstanceForElement(element);
  const layers: ComponentLayer[] = [];

  while (instance) {
    // Try to find the DOM element for this fiber to check for data-loc
    const fiberElement = instance.stateNode instanceof HTMLElement ? instance.stateNode : undefined;
    const path = getPath(instance, fiberElement);
    if (path) {
      const name =
        typeof instance.type === "string"
          ? instance.type
          : (instance.type.displayName ?? instance.type.name ?? instance.type.render?.name ?? "undefined");
      layers.push({ name, path: path.replace(`${root}/`, "") });
    }
    instance = instance._debugOwner ?? undefined;
  }
  return layers;
};

DevString Locations

Tagged templates automatically capture call-site locations:

if (enableDevLoc) {
  for (const match of code.matchAll(DEV_RE)) {
    const index = match.index!;
    const { line, column } = indexToLineAndColumn(code, index);
    const [_, fn, message] = match;
    const link = `${path.slice(inCodebase.index + IN_CODEBASE_SLICE)}:${line}:${column}`;
    string.overwrite(index, index + _.length, `${fn}\`${message}\`.ctx({ loc: ${JSON.stringify(link)} })`);
  }
}
// Input:
handler(dev`User pressed X`);

// Output (at build):
handler(dev`User pressed X`.ctx({ loc: "Card.tsx:83:12" }));

Usage:

function deleteCard(reason: DevString) {
  console.log("Called from:", reason.toJSON().context.loc);
}

deleteCard(dev`User clicked delete`);
// Automatically knows source location

DevStrings compose with .because() to build audit trails:

const rootEvent = dev`keydown from root`;
handler(rootEvent.because(dev`Looking for action handler`));
// Creates chain: "keydown from root → Looking for action handler"

Editor Integration

Local HTTP endpoint opens files:

let lastOpenedFile: string | null = null;
/**
 * uses our launch-editor endpoint to open the file in the dev's editor
 * this has been set up as a vite plugin.
 */
export const openInDevEditor = (loc: string) => {
  if (lastOpenedFile === loc) return;
  lastOpenedFile = loc;
  setTimeout(() => (lastOpenedFile = null), 500);
  void fetch(`http://localhost:5090/__open-in-editor?file=${loc}`).catch((error) => {
    console.error("Failed to open in editor", error);
  });
};
openInDevEditor("Card.tsx:26:7");
fetch("http://localhost:5090/__open-in-editor?file=Card.tsx:26:7");
// Editor opens at that line

Setup

Build plugin:

import { bunDevStringAndReact$ClassNameLoc } from "./bun-dev-and-react-$-className-loc.mts";

await Bun.build({
  plugins: [
    bunDevStringAndReact$ClassNameLoc({
      enableReact$Loc: true,
      enableDevLoc: true,
    }),
  ],
});

Runtime:

import { initClickToSource } from "./click-to-source.client.ts";

if (import.meta.env.DEV) {
  initClickToSource();
}