| 1 | #!/usr/bin/env node |
| 2 | import { createHash } from "node:crypto"; |
| 3 | import { existsSync, readFileSync } from "node:fs"; |
| 4 | import { basename, dirname, join } from "node:path"; |
| 5 | import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping"; |
| 6 | |
| 7 | function fail(message) { |
| 8 | console.error(`symbolize: ${message}`); |
| 9 | process.exit(1); |
| 10 | } |
| 11 | |
| 12 | function args(argv) { |
| 13 | const out = {}; |
| 14 | for (let i = 0; i < argv.length; i += 2) out[argv[i]?.replace(/^--/, "")] = argv[i + 1]; |
| 15 | return out; |
| 16 | } |
| 17 | |
| 18 | const input = args(process.argv.slice(2)); |
| 19 | if (!input.manifest || !input.bundle || !input.line || !input.column) { |
| 20 | fail("usage: node packaging/symbolize.mjs --manifest <manifest.json> --bundle <bundle.js> --line <line> --column <column>"); |
| 21 | } |
| 22 | if (!existsSync(input.manifest) || !existsSync(input.bundle)) fail("manifest or bundle does not exist"); |
| 23 | const manifest = JSON.parse(readFileSync(input.manifest, "utf8")); |
| 24 | if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.entries)) fail("unsupported source map manifest"); |
| 25 | const bundleHash = `sha256:${createHash("sha256").update(readFileSync(input.bundle)).digest("hex")}`; |
| 26 | const candidates = manifest.entries.filter((entry) => basename(entry.bundle) === basename(input.bundle)); |
| 27 | const entry = candidates.find((candidate) => candidate.bundleHash === bundleHash); |
| 28 | if (!entry) fail(`bundle hash ${bundleHash} is absent from the manifest`); |
| 29 | const mapPath = join(dirname(input.manifest), entry.map); |
| 30 | if (!existsSync(mapPath)) fail(`map is missing: ${entry.map}`); |
| 31 | const mapHash = `sha256:${createHash("sha256").update(readFileSync(mapPath)).digest("hex")}`; |
| 32 | if (mapHash !== entry.mapHash) fail(`map hash mismatch for ${entry.map}`); |
| 33 | const position = originalPositionFor(new TraceMap(JSON.parse(readFileSync(mapPath, "utf8"))), { |
| 34 | line: Number(input.line), |
| 35 | column: Number(input.column), |
| 36 | }); |
| 37 | if (!position.source || position.line == null || position.column == null) fail("the generated position has no source mapping"); |
| 38 | console.log(`${position.source}:${position.line}:${position.column}${position.name ? ` (${position.name})` : ""}`); |
| 39 |