Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 1x 1x 1x 1x 17x 17x 644x 17x 19x 19x 19x 10x 10x 4x 19x 1057x 337x 91x 91x 246x 246x 1220x 974x 1x 19x 19x 19x 21x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 246x 19x 19x 19x 19x 19x 19x 17x 17x | import type { Plugin } from "rollup";
import type { FilterPattern } from "@rollup/pluginutils";
import { createFilter } from "@rollup/pluginutils";
import { getSwc } from "../swc/loadSwc.js";
import { buildSourceLoc } from "../utils/source-loc";
import type * as swcTypes from "@swc/wasm-web";
export interface JsxSourceOptions {
/** @default ["**\/*.jsx", "**\/*.tsx"] */
include?: FilterPattern;
exclude?: FilterPattern;
/** @default process.cwd() */
rootDir?: string;
}
const DUMMY_SPAN: swcTypes.Span = { start: 0, end: 0, ctxt: 0 };
function buildLineTable(code: string): number[] {
const table: number[] = [0];
for (let i = 0; i < code.length; i++) {
if (code[i] === "\n") table.push(i + 1);
}
return table;
}
function offsetToLineCol(
table: number[],
offset: number,
): { line: number; col: number } {
let lo = 0;
let hi = table.length - 1;
while (lo < hi) {
const mid = (lo + hi + 1) >> 1;
if (table[mid] <= offset) lo = mid;
else hi = mid - 1;
}
return { line: lo + 1, col: offset - table[lo] + 1 };
}
function walkMutate(
node: swcTypes.Node,
visit: (node: swcTypes.Node) => void,
): void {
if (!node || typeof node !== "object") return;
if (Array.isArray(node)) {
for (const item of node) walkMutate(item as swcTypes.Node, visit);
return;
}
visit(node);
for (const key of Object.keys(node)) {
if (key === "span") continue;
walkMutate(
(node as unknown as Record<string, unknown>)[key] as swcTypes.Node,
visit,
);
}
}
/**
* Rollup plugin that injects `data-source-loc="file:line:col"` onto every JSX
* opening element at build time.
*
* Place this plugin **before** `swc()` in your plugin list so it sees raw
* TypeScript+JSX before compilation:
*
* ```ts
* plugins: [jsxSource({ rootDir: 'src' }), swc()]
* ```
*/
export function jsxSource(options: JsxSourceOptions = {}): Plugin {
const { rootDir = process.cwd(), include, exclude } = options;
const filter = createFilter(include ?? ["**/*.jsx", "**/*.tsx"], exclude);
return {
name: "jsx-source",
async transform(code, id) {
if (!filter(id)) return null;
const swc = await getSwc();
const isTs = /\.tsx?$/.test(id);
const hasJsx = /\.[jt]sx$/.test(id);
const program = await swc.parse(
code,
isTs
? { syntax: "typescript", tsx: hasJsx }
: { syntax: "ecmascript", jsx: hasJsx },
);
const lineTable = buildLineTable(code);
// Compute relative path for the data-source value
const sep = id.includes("/") ? "/" : "\\";
const rootWithSep = rootDir.endsWith(sep) ? rootDir : rootDir + sep;
const relPath = id.startsWith(rootWithSep)
? id.slice(rootWithSep.length)
: id;
const baseOffset: number = program.span?.start ?? 0;
walkMutate(program as swcTypes.Node, (node) => {
if (node.type !== "JSXOpeningElement") return;
const jsxNode = node as swcTypes.JSXOpeningElement;
const { line, col } = offsetToLineCol(
lineTable,
jsxNode.span.start - baseOffset,
);
const locValue = buildSourceLoc(relPath, line, col);
const sourceAttr: swcTypes.JSXAttribute = {
type: "JSXAttribute",
span: DUMMY_SPAN,
name: {
type: "Identifier",
span: DUMMY_SPAN,
value: "data-source-loc",
optional: false,
},
value: {
type: "StringLiteral",
span: DUMMY_SPAN,
value: locValue,
raw: JSON.stringify(locValue),
},
};
Iif (!Array.isArray(jsxNode.attributes)) jsxNode.attributes = [];
jsxNode.attributes.unshift(sourceAttr);
});
const result = await swc.print(program, {
filename: id,
sourceMaps: true,
});
return { code: result.code, map: result.map };
},
};
}
|