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 | 1x 1x 1x 1x 1x 24x 24x 24x 24x 3x 33x 31x 27x 25x 47x 47x 2x 2x 25x 23x 22x 23x 23x 23x 28x 27x 26x 26x 23x 21x 27x 7x 1x 26x | import type {
Plugin,
PluginContext,
TransformPluginContext,
SourceDescription,
} from "rollup";
import type { FilterPattern } from "@rollup/pluginutils";
import { createFilter } from "@rollup/pluginutils";
import MagicString from "magic-string";
import astMatcher from "ast-matcher";
import escapeStringRegexp from "escape-string-regexp";
export interface DefineOptions {
include?: FilterPattern;
exclude?: FilterPattern;
replacements?: Record<string, string | ((key: string) => string)>;
}
type Edit = [number, number];
type AstNode = { start: number; end: number };
export default function define({
include = ["**/*.{svelte,tsx,ts,mjs,jsx,js,cjs}"],
exclude,
replacements = {},
}: DefineOptions = {}): Plugin {
const filter = createFilter(include, exclude);
const keys = Object.keys(replacements);
let matchers: ReturnType<typeof astMatcher>[];
// eslint-disable-next-line unicorn/no-fn-reference-in-iterator
const firstpass = new RegExp(
`(?:${keys.map(escapeStringRegexp).join("|")})`,
"g",
);
return {
name: "define",
transform,
renderChunk(code, chunk) {
return transform.call(this, code, chunk.fileName);
},
};
function transform(
this: {
parse: TransformPluginContext["parse"];
warn: TransformPluginContext["warn"];
},
code: string,
id: string,
): SourceDescription | null {
if (keys.length === 0) return null;
if (!filter(id)) return null;
if (code.search(firstpass) === -1) return null;
const parse = (
code: string,
source = code,
): ReturnType<PluginContext["parse"]> => {
try {
return this.parse(code, undefined); // eslint-disable-line unicorn/no-useless-undefined
} catch (error) {
(error as Error).message += ` in ${source}`;
throw error;
}
};
const ast = parse(code, id);
if (!matchers) {
matchers = keys.map((key) => astMatcher(parse(key)));
}
const magicString = new MagicString(code);
const edits: Edit[] = [];
matchers.forEach((matcher, index) => {
for (const { node } of (matcher(ast) || []) as { node: AstNode }[]) {
if (markEdited(node, edits)) {
const replacement = replacements[keys[index]];
magicString.overwrite(
node.start,
node.end,
typeof replacement === "function"
? replacement(keys[index])
: replacement,
);
}
}
});
if (edits.length === 0) return null;
return {
code: magicString.toString(),
map: magicString.generateMap({
source: code,
includeContent: true,
hires: true,
}),
};
}
}
function markEdited(node: AstNode, edits: Edit[]): number | false {
for (const [start, end] of edits) {
if (
(start <= node.start && node.start < end) ||
(start < node.end && node.end <= end)
) {
return false; // Already edited
}
}
// Not edited
return edits.push([node.start, node.end]);
}
|