scramjet/src/client/shared/sourcemaps.ts
2024-09-01 19:37:33 -04:00

67 lines
2.1 KiB
TypeScript

import { ScramjetClient } from "../client";
let sourcemaps: {
source: string;
map: [string, number, number][];
}[] = [];
export const enabled = self.$scramjet.config.flags.sourcemaps;
export default function (client: ScramjetClient, self: Self) {
// every script will push a sourcemap
Object.defineProperty(self, "$scramjet$pushsourcemap", {
value: (map, source) => {
sourcemaps.push({ map, source });
},
enumerable: false,
writable: false,
configurable: false,
});
// when we rewrite javascript it will make function.toString leak internals
// this can lead to double rewrites which is bad
client.Proxy("Function.prototype.toString", {
apply(ctx) {
const stringified = ctx.fn.call(ctx.this);
let newString = "";
// find the sourcemap, brute force, just check every file until the body of the function shows up
// it doesnt matter if there's multiple with the same content because it will be the same function
const sourcemap = sourcemaps.find(({ source }) =>
source.includes(stringified)
);
// i don't know what cases this would happen under, but it does
if (!sourcemap) return ctx.return(stringified);
const { source, map } = sourcemap;
// first we need to find the character # where the function starts relative to the *transformed* source
let starting = source.indexOf(stringified);
const beforeFunctionRewrites = map.filter(
([str, start, end]) => start < starting
);
// map the offsets of the original source to the transformed source
for (const [str, start, end] of beforeFunctionRewrites) {
starting -= end - start - str.length;
}
const relevantRewrites = map.filter(
([str, start, end]) =>
start >= starting && end <= starting + stringified.length
);
let i = 0;
let offset = 0;
for (const [str, start, end] of relevantRewrites) {
// ooh i should really document this before i forget how it works
newString += stringified.slice(i, start - starting + offset);
newString += str;
offset += end - start - str.length;
i = start - starting + offset + str.length;
}
return ctx.return(newString + stringified.slice(i));
},
});
}