Repository navigation
Minifier and import-detection fixes, faster transforms, pnpm 12 - #429
Conversation
🦋 Changeset detectedLatest commit: 33c6157 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the minifier's string and paren tracking is rewritten to match the styled-components runtime, plus a pnpm 12 migration.
- String-aware comment stripping —
stripLineCommentis now a single pass that tracks the open quote (honoring backslash escapes) and counts parens only outside strings. A line comment aftercontent: "it's"orcontent: "("is now stripped instead of swallowing the rest of the joined template. Paren depth still resets per line, so an unmatched)from a previous line is ignored, as before. - String-aware symbol compression —
compressSymbolstracks quote state across fragments via the newquoteOpenAfterhelper instead of counting quote characters on the accumulated output, so whitespace inside a later string (' a ; b ') is preserved. This also drops the O(n²) rescan. - Tests — adds regression coverage for other-quote-type strings, escaped quotes, parens inside strings, multi-line selector and at-rule preludes, and
compressSymbolsquote tracking. - Tooling — pnpm 12.14.0 with
virtualStoreType: global,pnpm/action-setup@v6.1.0, and Jest scoped totest/.
I verified the full suite passes (76 tests) and pnpm build compiles. I also fuzzed the old and new implementations of both functions across quote/paren/backslash/symbol alphabets: every divergence involves escaped quotes or backslashes outside a string, where the new behavior is the more CSS-correct one, and none is a regression on realistic input.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the seven commits since the prior pullfrog review are performance refactors to import lookup and the css-prop transform, plus regression fixtures, sitting alongside the already-reviewed minify fix.
- Faster import lookup —
importLocalNamereadsProgram.bodydirectly for top-level imports and only falls back to a fullProgram.traversewhen a TS/Flow module declaration (TSModuleDeclaration/DeclareModule, includingexport-wrapped) is present. - One injected import per file —
transpileCssPropcaches the injected styled-components default import onstate.file, so a file whose css props need an injection now gets one import instead of one alias per prop. This is the only emitted-code shape change and is runtime-equivalent. - Single
isStyledcheck per call —displayNameAndIdcurriesisStyledonce per plugin and extractscalleeNeedsConfig, replacing four repeatedisStyledcalls and the inline OR chain with an equivalent predicate. - Matchers compiled once —
useTopLevelImportPathMatchersmemoizes compiled picomatch matchers pertopLevelImportPathsvalue in aWeakMap. - Regression fixtures — pin import lookup for ambient module declarations and exported ambient namespaces, the single injected import, and multiple css props under a namespace import.
I checked the refactors for behavior drift by compiling a 25-snippet corpus (tagged/call/attrs/withConfig shapes, namespace and rebind forms, require/interop/sequence shapes, TS ambient modules, and css props) across five configs with the plugin built at ffa23b0 versus c032536. The only output divergences are the intended one-import-per-file cleanup; every other result is byte-identical. The full suite (80 tests) and pnpm build both pass.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No critical issues — one minor documentation consistency note.
Reviewed changes — docs-only delta on top of the previously reviewed minify fix and perf refactors.
- Added
.changeset/faster-transforms.mddescribing the transform speedup. - Added an AGENTS.md testing rule noting the css-prop fixture family needs one fixture built without
@babel/preset-env(the CommonJS transform folds repeated imports into onerequire, hiding import-shape bugs).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the six commits since the prior pullfrog review fix two import-detection bugs and record them.
- Ambient imports ignored —
importLocalNamenow scans only the top level ofProgram.body, so astyled-componentsimport nested in a TypeScript or Flowdeclare module/declare namespacebody no longer masquerades as a real binding. Components in files with such ambient imports get theirdisplayName/componentIdback. - Namespace imports tracked separately — the new
importNamespaceNamesrecordsimport * as X from 'styled-components'bindings, soX.default.yis recognized even when a default import is also present or injected by the css-prop transform. - Regression fixtures — pin ambient imports in TypeScript
declare module, Flowdeclare module, exported ambient namespaces, and import-free files; namespace-alongside-default and aliased-namespace-with-css-prop; three existing fixture outputs shift counter values where previously-unrecognized components now receive config, which is the intended effect. - Docs — two
patchchangesets for the fixes, shortened changeset wording, and an AGENTS.md clarification that a changeset may state its own release's measured speedup.
I ran the full suite (84 passed) and pnpm build (17 files compiled). The ambient fix reads only top-level declarations and the namespace arm is gated behind the existing X.default. member shape, so neither widens recognition beyond genuine styled-components bindings.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the two commits since the prior pullfrog review add a benchmark/profiler toolchain and document it.
- A/B benchmark —
scripts/bench/index.jstransforms generated inputs (small/medium/large/large-cjs) with the plugin built at a git ref (--base, defaultorigin/main) and the working tree, interleaves runs, and reports each variant's median time minus the no-plugin baseline. It exits non-zero when outputs differ, and hints at--normalize-aliaseswhen only the injected_styledNaliases differ. - Input generator and build helpers —
gen.jssynthesizes realistic styled, css-prop, and CommonJS inputs;build-ref.jsextractssrcat a ref viagit archiveand compiles it in a temp dir linked against the repo'snode_modules, never writing the trackedlib/. - Profiler —
scripts/bench/profile.jsrecords a.cpuprofileover one generated input and ranks self time, inclusive time, and the nearest plugin callers of a target frame. - Docs — AGENTS.md gains the
pnpm benchandpnpm bench:profilecommands; the performance rule now points atpnpm bench.
I smoke-tested both. pnpm bench --rounds 1 flags the intended one-import-per-file output change on large-cjs.js with the alias hint, and --normalize-aliases clears it while showing current far below base; pnpm bench:profile --input small --runs 3 prints the ranked frames. The full suite and pnpm build still pass.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
6502e13 to
4a48ca7
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the four commits since the prior pullfrog review make the fixture snapshots machine-path-free and run CI on Windows.
- Machine-path-free error snapshots —
patches/babel-test@0.2.4.patchmakesbabel-testsnapshot the error message (which carries Babel's code frame) instead of the stack, replaces the working directory with<cwd>in either separator style and any drive-letter case, and rewrites the rest of each such path with forward slashes. I confirmed the patch applies cleanly to the pinnedbabel-test@0.2.4, that its sha256 matches thepatchedDependencieshash inpnpm-lock.yaml, and that the normalization collapses bothC:\...and/home/...cwds to<cwd>/.... - Absolute-path leak test —
test/index.test.jsscans everyoutput.js/error.jsfor POSIX, drive-letter, and UNC absolute paths and fails the suite on any leak. - Cross-platform CI — the test workflow gains a
windows-latest/Node 24 job beside Ubuntu 22/24, setsPNPM_CONFIG_VIRTUAL_STORE_TYPE=project(pnpm's documented env override forvirtualStoreType, which otherwise fails on Windows with the global layout), and a new.gitattributes(* text=auto eol=lf) keeps checkouts LF so snapshots match. The same env is set on the release workflow. - Docs — AGENTS.md records the no-machine-paths snapshot rule and the CI virtual-store override.
I ran the suite with the patch applied (86 passed) and dry-run verified the patch against the installed babel-test. The Windows job on this head was still pending at review time, so the Windows snapshot path is verified by the normalization behavior rather than a green run.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the six commits added since the prior pullfrog review (the branch was rebased; the net delta is four import-detection fixes plus formatting and docs) each close a real detection gap and ship a fixture.
- Type-only imports ignored —
importLocalNameandimportNamespaceNamesskipimportKind: 'type' | 'typeof'at both declaration and specifier level, soimport type { styled }no longer counts as the real binding (and thecssprop no longer emits a call to an erased identifier). - Real local names — a named
styledimport now resolves tospecifier.local.name, soimport { styled as sc }is recognized;importLocalName('default')starts from therequirevariable name, and aglobalStyledAliaskeeps a bare globalstyledresolving beside arequire. styled.default(Component)call forms —isNamespaceDefaultrecognizesns.defaulton the default local, the global alias, or a namespace local across member, call, and(0, ns.default)(...)sequence shapes, so these no longer emitwithConfig.withConfig(...).- Script-mode css prop — the injected default import is tracked on
state.fileand arequire()binding is treated as "no callable default", so a CommonJS file with therequireafter the prop no longer crashes and reuses one injected import. - Formatting and docs —
src/reformatted with Prettier (no semantic change) and AGENTS.md refreshed.
I rebuilt the base plugin (origin/main) and the working tree through scripts/bench/build-ref and diffed them across the new shapes: the only divergences are the intended fixes (named aliases now decorated, call forms no longer doubled, type-only imports left alone, script-mode css prop injecting a real default). I also checked the require + global styled + css-prop interaction and it is byte-identical to main, so the injected-name override does not regress it. Full suite passes (98 passed) and pnpm build compiles.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Fixes
Follow-up to #427.
Minification dropping rules. A line comment after a string containing an apostrophe, escaped quote, or parenthesis survived minification. styled-components then treated the rest of the joined template as a comment and dropped it:
The same miscount could strip spaces inside a later string (
' a ; b 'became' a;b '). The minifier now follows strings and parentheses the way the styled-components runtime does.Finding the styled-components import. Several shapes were missed or mishandled. Each is now covered by a fixture:
importinside a TypeScript or Flowdeclare moduleblock was treated as real and could hide the actual import.import * as styledloststyled.default.xdecoration once the file also used thecssprop or a default import.styled.default(Component), through a namespace import orrequire, emitted broken code (withConfig.withConfig(...)).import type/import typeofcounted as the real import.cssprop crashed in CommonJS script files without a styled-components import, or with therequireafter the prop.import { styled as sc }andconst sc = require('styled-components')weren't recognized under their real names. A bare globalstyledbeside arequireis still recognized, so existing componentIds don't move.Faster transforms
The plugin's own time per file drops about 2–4× on typical files, and much more on CommonJS files with many
cssprops (one large generated file went from about 1.8 s to about 65 ms). Emitted code is unchanged except that a file whosecssprops need styled-components injected now gets one import instead of one alias per prop.cssprops share one injected default import per file.topLevelImportPathspatterns compile once per config.pnpm benchcompares any git ref against the working tree over generated inputs and fails if the outputs differ.pnpm bench:profileprints a CPU profile summary.Tooling
.gitattributeskeeps checkouts LF.<cwd>on every OS. This comes from a small patch tobabel-test, and a test fails if any fixture contains an absolute path.test/.AGENTS.mdis refreshed for contributors' tools.