()
| 516 | |
| 517 | |
| 518 | def main() -> int: |
| 519 | ap = argparse.ArgumentParser(description=__doc__) |
| 520 | ap.add_argument("output_dir", help="Translator output directory (the *-js/ folder)") |
| 521 | ap.add_argument( |
| 522 | "--min-occurrences", |
| 523 | type=int, |
| 524 | default=2, |
| 525 | help="Skip identifiers that appear fewer than this many times (net loss to mangle).", |
| 526 | ) |
| 527 | ap.add_argument( |
| 528 | "--map-output", |
| 529 | default=None, |
| 530 | help="Where to write the reverse mangle map (JSON). Defaults to output_dir/mangle-map.json; " |
| 531 | "callers that ship the output_dir to users typically redirect this somewhere outside " |
| 532 | "so the ~6 MiB map doesn't bloat the shipped bundle.", |
| 533 | ) |
| 534 | args = ap.parse_args() |
| 535 | |
| 536 | out_dir = Path(args.output_dir) |
| 537 | if not out_dir.is_dir(): |
| 538 | print(f"[mangle] output dir missing: {out_dir}", file=sys.stderr) |
| 539 | return 2 |
| 540 | |
| 541 | files = collect_files(out_dir) |
| 542 | if not files: |
| 543 | print("[mangle] no eligible .js files in output dir", file=sys.stderr) |
| 544 | return 0 |
| 545 | |
| 546 | counts, preserved = collect_counts(files, out_dir) |
| 547 | # An identifier that appears once at all can't be shrunk (the one |
| 548 | # definition site is its one use; mangling makes the file bigger by |
| 549 | # the length of the mapping entry unless we're willing to write a |
| 550 | # runtime lookup table — which we aren't). |
| 551 | if args.min_occurrences > 1: |
| 552 | counts = Counter({k: v for k, v in counts.items() if v >= args.min_occurrences}) |
| 553 | |
| 554 | mapping = build_mapping(counts) |
| 555 | saved = rewrite(files, mapping) |
| 556 | |
| 557 | total_bytes = sum(path.stat().st_size for path in files) |
| 558 | preserved_count = len(preserved) |
| 559 | print( |
| 560 | f"[mangle] {len(mapping):,} identifiers mangled across {len(files)} files; " |
| 561 | f"saved ~{saved / (1024 * 1024):.1f} MiB " |
| 562 | f"(total after: {total_bytes / (1024 * 1024):.1f} MiB; " |
| 563 | f"preserved {preserved_count} JSO-bridge / excluded names)" |
| 564 | ) |
| 565 | |
| 566 | # Persist the reverse mapping so stack traces / debugging can demangle |
| 567 | # symbols after the fact without rebuilding. |
| 568 | map_path = Path(args.map_output) if args.map_output else out_dir / "mangle-map.json" |
| 569 | map_path.parent.mkdir(parents=True, exist_ok=True) |
| 570 | reverse = {short: original for original, short in mapping.items()} |
| 571 | map_path.write_text(json.dumps(reverse, indent=0, sort_keys=True), encoding="utf-8") |
| 572 | return 0 |
| 573 | |
| 574 | |
| 575 | if __name__ == "__main__": |
no test coverage detected