ast-v8-to-istanbul
TypeScript icon, indicating that this package has built-in type declarations

0.3.1 • Public • Published

ast-v8-to-istanbul

Version

  • Speed of V8 coverage 🏎
  • Accuracy of Istanbul coverage 🔍

Ignoring code | Source maps | Istanbul Compatibility | Limitations


AST-aware v8-to-istanbul.

Unopinionated - bring-your-own AST parser and source maps.

Passes all 195 tests* of istanbul-lib-instrument. ✅

Test cases run against:

  • vite/parseAst
  • acorn
  • oxc-parser
  • @babel/parser
import { convert } from "ast-v8-to-istanbul";
import { parseAstAsync } from "vite";
import type { CoverageMapData } from "istanbul-lib-coverage";

const data: CoverageMapData = await convert({
  // Bring-your-own AST parser
  ast: parseAstAsync(<code>),

  // Code of the executed file (not the source file)
  code: "function sum(a, b) {\n  return a + b ...",

  // Execution wrapper offset
  wrapperLength: 0,

  // Script coverage of the executed file
  coverage: {
    scriptId: "123",
    url: "file:///absolute/path/to/dist/index.js",
    functions: [
      {
        functionName: "sum",
        ranges: [{ startOffset: 223, endOffset: 261, count: 0 }],
        isBlockCoverage: false,
      },
      // ... etc
    ],
  },

  // Source map of the executed file
  sourceMap: {
    version: 3,
    sources: ["../sources.ts"],
    sourcesContent: ["export function sum(a: number, b: number) {\n..."],
    mappings: ";AAAO,SAAS,...",
    names: [],
  },
});

Ignoring code

Ignoring source code

The typical ignore hints from nyc are supported: https://github.com/istanbuljs/nyc?tab=readme-ov-file#parsing-hints-ignoring-lines:

  • /* istanbul ignore if */: ignore the next if statement.
  • /* istanbul ignore else */: ignore the else portion of an if statement.
  • /* istanbul ignore next */: ignore the next thing in the source-code (functions, if statements, classes, you name it).
  • /* istanbul ignore file */: ignore an entire source-file (this should be placed at the top of the file).

In addition to istanbul keyword, you can use v8, c8 and node:coverage:

  • /* istanbul ignore if */
  • /* v8 ignore else */
  • /* c8 ignore file */
  • /* node:coverage ignore next */

The ignore-class-method from nyc is also supported: https://github.com/istanbuljs/nyc?tab=readme-ov-file#ignoring-methods

You can ignore every instance of a method simply by adding its name to the ignore-class-method array in your nyc config.

import { convert } from "ast-v8-to-istanbul";

await convert({
  ignoreClassMethods: ['render']
});

Ignoring generated code

This API is mostly for developers integrating ast-v8-to-istanbul with other tooling.

If your code transform pipeline is adding generated code that's included in the source maps, it will be included in coverage too. You can exclude these known patterns by defining ignoreNode for filtering such nodes.

function ignoreNode(node: Node, type: "branch" | "function" | "statement"): boolean | void;
import { convert } from "ast-v8-to-istanbul";

await convert({
  ignoreNode: (node, type) => {
    // Ignore all `await __vite_ssr_import__( ... )` calls that Vite SSR transform adds
    return (
      type === "statement" &&
      node.type === "AwaitExpression" &&
      node.argument.type === "CallExpression" &&
      node.argument.callee.type === "Identifier" &&
      node.argument.callee.name === "__vite_ssr_import__"
    );
  },
});

Source maps

Source maps are optional and supported by various ways:

  • Pass directly to convert as argument:
    import { convert } from "ast-v8-to-istanbul";
    
    await convert({
      sourceMap: {
        version: 3,
        sources: ["../sources.ts"],
        sourcesContent: ["export function sum(a: number, b: number) {\n..."],
        mappings: ";AAAO,SAAS,...",
        names: [],
      }
    });
  • Include base64 encoded inline maps in code:
    await convert({
      code: `\
      function hello() {}
      //# sourceMappingURL=data:application/json;base64,eyJzb3VyY2VzIjpbIi9zb21lL...
      `
    });
  • Include inline maps with filename in code:
    await convert({
      code: `\
      function hello() {}
      //# sourceMappingURL=some-file-on-file-system.js.map
      `
    });
  • Don't use source maps at all, and pass original source code in code:
    await convert({
      code: `function hello() {}`,
      sourceMap: undefined,
    });

Istanbul Compatibility

This project tests itself against test cases of istanbul-lib-instrument and verifies coverage maps are 100% identical. Some cases, like deprecated with() statement and edge cases of strict mode are skipped, as all tests are run in strict mode.

100% istanbul compatibility guarantees that coverage reports between V8 and Istanbul can be merged together.

Limitations

The way how V8 reports runtime coverage has some limitations when compared to pre-instrumented coverage:

  • Unable to detect uncovered AssignmentPattern's if line is otherwise covered

  • Unable to detect uncovered parts when block execution stops due to function throwing:

    • function first() {
        throws()
      
        // unreachable, but incorrectly covered
        return "first";
      }
      
      const throws = ()  => { throw new Error() }
      
      try { first(1) } catch {}
    • [
        {
          "ranges": [{ "startOffset": 0, "endOffset": 165, "count": 1 }],
          "isBlockCoverage": true
        },
        {
          "functionName": "first",
          "ranges": [{ "startOffset": 0, "endOffset": 92, "count": 1 }],
          "isBlockCoverage": true
        },
        {
          "functionName": "throws",
          "ranges": [{ "startOffset": 109, "endOffset": 137, "count": 1 }],
          "isBlockCoverage": true
        }
      ]

Readme

Keywords

none

Package Sidebar

Install

npm i ast-v8-to-istanbul

Weekly Downloads

2,234

Version

0.3.1

License

MIT

Unpacked Size

30.4 kB

Total Files

4

Last publish

Collaborators

  • ariperkkio