{"version":3,"file":"cadence-babel.cjs","sourceRoot":"","sources":["../src/cadence-babel.cts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Babel plugin that cuts a Playwright spec, so each case records which of\n * its statements first reached each region of the application.\n *\n * Playwright compiles every spec through Babel, and `babelPlugins` under\n * `'@playwright/test'` in the configuration is the one place it lets a project\n * add to that, which is where `withTestSelection` puts this. The cut is the one\n * every runner's seam makes ([`cadence`](https://github.com/Variance-Authority/variance-authority/blob/main/packages/sense/src/instrument/cadence.ts)),\n * awaited: the page is outside the test's realm, so each statement of an async\n * body waits while the `context` fixture drains what the statement before it\n * reached ([`documents.ts`](./documents.ts)).\n *\n * The cut has to happen before Babel parses, because it is a walk of the text\n * and not of Babel's tree, so this overrides the parser rather than visiting:\n * it parses the cut text and moves every position back to the text the project\n * wrote. Lines never move; a column inside an inserted call names the place it\n * was inserted at. That keeps Babel's map, and so every stack frame Playwright\n * reads a location from, naming the file as written — an inline aria snapshot\n * is written back by that location.\n *\n * CommonJS, because Playwright `require`s each plugin by its path.\n */\n\nimport instrument = require('@variance-authority/sense/instrument');\nimport path = require('node:path');\n\n/** Babel's parser, as `parserOverride` is handed it. */\ntype Parse = (code: string, options: ParserOptions) => object;\n\ninterface ParserOptions {\n  readonly sourceFileName?: string;\n}\n\ninterface Position {\n  line: number;\n  column: number;\n  index?: number;\n}\n\n// FIXME: Playwright keys its compile cache on the plugin's path and not on its\n// version, so a spec compiled under an older cut is read from the cache after\n// an upgrade until the spec or Playwright changes; `--clear-cache` drops it.\nexport = function cadenceBabel(): { readonly name: string; readonly parserOverride: (code: string, options: ParserOptions, parse: Parse) => object | undefined } {\n  return {\n    name: 'variance-authority-cadence',\n    parserOverride(code, options, parse) {\n      const file = options.sourceFileName;\n      // The fixture tells a cut of the running spec from one of a file it\n      // imported by this name, against `testInfo.file`, which is absolute.\n      if (file === undefined || !path.isAbsolute(file)) return undefined;\n      const cut = instrument.cadence(code, file, { awaited: true });\n      if (cut === undefined) return undefined;\n      return written(parse(cut.code, options), code, cut.code, cut.shifts);\n    },\n  };\n};\n\n/** Babel's tree of `cut`, with every position moved back to `code`. */\nfunction written(tree: object, code: string, cut: string, shifts: readonly number[]): object {\n  const back = instrument.columnBehindCut(shifts);\n  const writtenStarts = lineStarts(code);\n  const cutStarts = lineStarts(cut);\n  // Babel shares a position between a node and the first of its children, and\n  // a comment between the file's list and the node it is attached to.\n  const moved = new WeakSet<object>();\n\n  const index = (at: number): number => {\n    let low = 0;\n    let high = cutStarts.length - 1;\n    while (low < high) {\n      const middle = (low + high + 1) >>> 1;\n      if (cutStarts[middle]! <= at) low = middle;\n      else high = middle - 1;\n    }\n    return writtenStarts[low]! + back(low, at - cutStarts[low]!);\n  };\n  const position = (at: Position): void => {\n    if (moved.has(at)) return;\n    moved.add(at);\n    at.column = back(at.line - 1, at.column);\n    if (typeof at.index === 'number') at.index = writtenStarts[at.line - 1]! + at.column;\n  };\n  const visit = (value: unknown): void => {\n    if (typeof value !== 'object' || value === null || moved.has(value)) return;\n    moved.add(value);\n    if (Array.isArray(value)) {\n      for (const item of value) visit(item);\n      return;\n    }\n    const node = value as Record<string, unknown>;\n    for (const [key, field] of Object.entries(node)) {\n      if ((key === 'start' || key === 'end') && typeof field === 'number') node[key] = index(field);\n      else if (key === 'loc' && typeof field === 'object' && field !== null) {\n        const loc = field as { start?: Position; end?: Position };\n        if (loc.start !== undefined) position(loc.start);\n        if (loc.end !== undefined) position(loc.end);\n      } else if (key === 'range' && Array.isArray(field)) node[key] = field.map((at: number) => index(at));\n      else if (key === 'extra' && typeof field === 'object' && field !== null) {\n        const extra = field as { parenStart?: number };\n        if (typeof extra.parenStart === 'number') extra.parenStart = index(extra.parenStart);\n      } else visit(field);\n    }\n  };\n  visit(tree);\n  return tree;\n}\n\n/** Where each line of `text` starts, by Babel's line terminators. */\nfunction lineStarts(text: string): number[] {\n  const starts = [0];\n  for (const found of text.matchAll(/\\r\\n?|[\\n\\u2028\\u2029]/gu)) starts.push(found.index + found[0].length);\n  return starts;\n}\n"]}