Home

Awesome

chrome-debugging-client

Build Status npm install size

An async/await friendly Chrome debugging client with TypeScript support, designed with automation in mind.

Table of Contents

Features

Examples

Print URL as PDF

#!/usr/bin/env node
const { writeFileSync } = require("fs");
const { spawnChrome } = require("chrome-debugging-client");

/**
 * Print a url to a PDF file.
 * @param url {string}
 * @param file {string}
 */
async function printToPDF(url, file) {
  const chrome = spawnChrome({ headless: true });
  try {
    const browser = chrome.connection;

    // we create with a target of about:blank so that we can
    // setup Page events before navigating to url
    const { targetId } = await browser.send("Target.createTarget", {
      url: "about:blank",
    });

    const page = await browser.attachToTarget(targetId);
    // enable events for Page domain
    await page.send("Page.enable");

    // concurrently wait until load and navigate
    await Promise.all([
      page.until("Page.loadEventFired"),
      page.send("Page.navigate", { url }),
    ]);

    const { data } = await page.send("Page.printToPDF");

    writeFileSync(file, data, "base64");

    // attempt graceful close
    await chrome.close();
  } finally {
    // kill process if hasn't exited
    await chrome.dispose();
  }

  console.log(`${url} written to ${file}`);
}

if (process.argv.length < 4) {
  console.log(`usage: printToPDF.js url file`);
  console.log(
    `example: printToPDF.js https://en.wikipedia.org/wiki/Binomial_coefficient Binomial_coefficient.pdf`,
  );
  process.exit(1);
}

printToPDF(process.argv[2], process.argv[3]).catch((err) => {
  console.log("print failed %o", err);
});

Node Debugging

#!/usr/bin/env node
const { spawnWithWebSocket } = require("chrome-debugging-client");

async function main() {
  const script = `const obj = {
    hello: "world",
  };
  console.log("end");
  `;

  // start node requesting it break on start at debug port that
  // is available
  const node = await spawnWithWebSocket(process.execPath, [
    // node will pick an available port and wait for debugger
    "--inspect-brk=0",
    "-e",
    script,
  ]);

  async function doDebugging() {
    const { connection } = node;

    // Setup console api handler
    connection.on("Runtime.consoleAPICalled", ({ type, args }) => {
      console.log(`console.${type}: ${JSON.stringify(args)}`);
    });

    // We requested Node to break on start, so we runIfWaitingForDebugger
    // and wait for it to break at the start of our script.
    // These commands must be sent concurrently with
    // the pause event setup.
    const [
      {
        callFrames: [
          {
            location: { scriptId },
          },
        ],
        reason,
      },
    ] = await Promise.all([
      connection.until("Debugger.paused"),
      connection.send("Debugger.enable"),
      connection.send("Runtime.enable"),
      connection.send("Runtime.runIfWaitingForDebugger"),
    ]);
    // Right now we are paused at the start of the script
    console.log(`paused reason: ${reason}`); //= paused: Break on start
    console.log(`set breakpoint on line 3`);
    await connection.send("Debugger.setBreakpoint", {
      location: {
        lineNumber: 3,
        scriptId,
      },
    });

    console.log("resume and wait for next paused event");
    const [breakpoint] = await Promise.all([
      connection.until("Debugger.paused"),
      connection.send("Debugger.resume"),
    ]);
    const {
      callFrames: [{ location, callFrameId }],
    } = breakpoint;
    console.log(`paused at line ${location.lineNumber}`);

    console.log("evaluate `obj`");
    const { result } = await connection.send("Debugger.evaluateOnCallFrame", {
      callFrameId,
      expression: "obj",
      returnByValue: true,
    });
    console.log(JSON.stringify(result.value)); //= {"hello":"world"}

    console.log("resume and wait for execution context to be destroyed");
    await Promise.all([
      connection.until("Runtime.executionContextDestroyed"),
      connection.send("Debugger.resume"),
    ]);
  }

  try {
    await doDebugging();

    // Node is still alive here and waiting for the debugger to disconnect
    console.log("close websocket");
    node.close();

    // Node should exit on its own after the websocket closes
    console.log("wait for exit");
    await node.waitForExit();

    console.log("node exited");
  } finally {
    // kill process if still alive
    await node.dispose();
  }
}

main().catch((err) => {
  console.log("print failed %o", err);
});