Skip to content

📦🔧:survive minifiers that rename properties - #135

Merged
openinf-commit-queue[bot] merged 1 commit into
mainfrom
claude/project-thread-1csmoe
Oct 31, 2026
Merged

openinf-commit-queue[bot] merged 1 commit into
mainfrom
claude/project-thread-1csmoe

Conversation

@DerekNonGeneric

@DerekNonGeneric DerekNonGeneric commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Requested by DerekNonGeneric · project thread

The SDK was put through Google Closure Compiler v20261021 (SIMPLE and ADVANCED), both on the built ESM output and on tsickle's output from the TypeScript sources. It compiles in both modes, but ADVANCED, which renames every property it has no declaration for, broke four things at runtime. A differential run (each of the 221 exported is*/has*/can* guards on about 90 values, Closure output compared with plain Node.js, with and without --harmony-temporal) found 19 guards that answered differently before this change and none after.

  • UnicodeEscapes: it was a TypeScript enum, which compiles to quoted writes read back unquoted. Only the reads were renamed, so curlyQuote('x') returned undefinedxundefined, ellipsify did the same, and so did every error message that quotes a name with them. It is a plain as const object again, the shape it had in 1.x, plus a type of the same name. erasableSyntaxOnly in tsconfig.base.json now keeps enums, namespaces and parameter properties out. Three tests used parameter properties and now declare the field instead.
  • isRawJSON, isNativeError, isTemporal*: they probe JSON.isRawJSON, Error.isError, process.getBuiltinModule and Temporal, and Closure has no declaration for any of them. Once renamed, each probe found nothing. isRawJSON and all eight Temporal guards said false for every value, and isNativeError accepted an Object.create(Error.prototype) that carries a tag. These are now read by quoted key, like the globals['WeakRef'] reads the same file already does.
  • util-object declare let Object: this module-local declaration was never used. tsickle turned it into an extern var Object typed ?, which erased Closure's type for Object.

Left alone, because they are limits of the tools rather than problems in the SDK:

  • Closure cannot parse #private class fields, so gh-file-importer only compiles when downleveled to ES2021.
  • Closure reads TSDoc's @category and @param a.b tags as bad JSDoc. These are warnings and can be silenced with --extra_annotation_name.
  • Closure treats a leading _ as private, which flags the _tagTester-style helpers the packages deliberately share.
  • Closure doesn't follow TypeScript's guard-based narrowing, and its externs are out of date (they don't know Object.hasOwn(obj, number), for example).
  • tsickle's README has declared it unmaintained since May 2024, and it supports TypeScript only up to 5.x. It ran only with TS 5.2, a lib shim for Temporal/isWellFormed, esModuleInterop off and importHelpers on. All of its ~290 warnings are types it can't translate (intersections, mapped and conditional types).
  • Under ADVANCED, minified class and function names change the names that the Assert decorator and guard expectations put in their messages. Any minifier does this.
  • GhFileImporter checks hasOwn(options, 'destDir') and then reads options.destDir. Under ADVANCED that always throws MissingOptionError, and tsickle doesn't help. It is left alone because the own-property check keeps a polluted Object.prototype.destDir from choosing where files are written, and a Node-only class that writes files isn't a realistic ADVANCED target.

Checked locally on Node 24: build, pnpm test (1331 tests, 0 failures), lint, lint:types, knip, format, md, spelling, examples, packages, docs:check and lint:commits.

Summary by CodeRabbit

  • New Features
    • Text utilities now provide Unicode escape constants as a read-only object, along with a type representing the available escape values.
    • Utility packages improve compatibility with property-renaming minification in supported build configurations.

Compiled with Closure Compiler's ADVANCED mode, which renames every
property it has no declaration for, the packages broke in two ways.

UnicodeEscapes was a TypeScript enum, which compiles to quoted writes
read back unquoted. Only the reads were renamed, so curlyQuote and
ellipsify, and every error message that quotes a name with them,
printed "undefined". It is a plain object again, as in 1.x, with a
type of the same name, and erasableSyntaxOnly now keeps enums,
namespaces and parameter properties out; three tests used the last.

isRawJSON, isNativeError and the isTemporal* guards probe the host for
JSON.isRawJSON, Error.isError, process and Temporal, none of which
Closure declares. Renamed, each probe found nothing: the guards said
false for every value, and isNativeError fell back to a check that a
forged prototype fools. They are now read by name, like the globals
the brand checks already read that way.

util-object also declared an unused module-local Object, which
tsickle turned into an extern that erased Closure's type for Object.

Signed-off-by: Derek Lewis <DerekNonGeneric@inf.is>
Assisted-by: Claude-Code:claude-opus-5
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 5088ce8c-18c6-402c-9203-b554839fd4de
Commits

Reviewing files that changed from the base of the PR and between 6687bb2 and abc5d40.

Files selected for processing: 9
  • .changeset/closure-advanced-safe.md
  • packages/util-array/test/helpers/copy-array.ts
  • packages/util-core/src/_internal/_brand-checks.ts
  • packages/util-core/test/types.ts
  • packages/util-object/src/types.ts
  • packages/util-object/test/helpers/create-class-wrapper.ts
  • packages/util-text/src/types.ts
  • packages/util-types/src/guards/is-raw-json.ts
  • tsconfig.base.json
Files with no reviewable changes: 1
  • packages/util-object/src/types.ts

Included review availability: This review used your included allowance. 4 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Walkthrough

The pull request updates utility packages to use quoted host-property reads and changes UnicodeEscapes from an enum to a const object with a matching union type. It also enables erasableSyntaxOnly and adjusts declarations and test fixtures to use explicit property assignments.

Changes: Utility compilation compatibility

Layer / File(s) Summary
Named host-property reads
packages/util-core/src/_internal/_brand-checks.ts, packages/util-types/src/guards/is-raw-json.ts, .changeset/closure-advanced-safe.md
The native error, Temporal, and raw JSON checks use quoted keys for host-property reads. The changeset records patch releases and describes these minification-related changes.
UnicodeEscapes object and type
packages/util-text/src/types.ts, .changeset/closure-advanced-safe.md
UnicodeEscapes is a const object with the same seven names and values as the prior enum. An exported type derives the union of those values.
Erasable syntax and explicit properties
tsconfig.base.json, packages/util-core/test/types.ts, packages/util-array/test/helpers/copy-array.ts, packages/util-object/test/helpers/create-class-wrapper.ts, packages/util-object/src/types.ts
TypeScript enables erasableSyntaxOnly. Test fixtures replace parameter properties with explicit assignments, and the module-level Object declaration is removed.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: Minimal · up to abc5d

No actionable issue is established for these compatibility changes; the PR is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 6 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "📦🔧:survive minifiers that rename properties" describes the main change. The PR makes the packages survive Closure Compiler ADVANCED mode, which renames properties. The changes replace the U…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "📦🔧:survive minifiers that rename properties" describes the main change. The PR makes the packages survive Closure Compiler ADVANCED mode, which renames properties. The changes replace the UnicodeEscapes enum with a const object, read host properties by quoted keys, and declare fields explicitly in tests. The title includes emoji and a full-width colon, which add noise, but the subject is specific and matches the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 6 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@DerekNonGeneric DerekNonGeneric self-assigned this Oct 25, 2026
@DerekNonGeneric DerekNonGeneric added the 🚀 Status: Commit Queue Land this pull request when its checks pass label Oct 31, 2026
@openinf-commit-queue
openinf-commit-queue Bot merged commit 22ca26c into main Oct 31, 2026
12 checks passed
@openinf-commit-queue openinf-commit-queue Bot removed the 🚀 Status: Commit Queue Land this pull request when its checks pass label Oct 31, 2026
@openinf-commit-queue
openinf-commit-queue Bot deleted the claude/project-thread-1csmoe branch October 31, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant