Skip to content

Order aggregated CSS by module dependency in styled-ppx.generate - #598

Closed
davesnx wants to merge 6 commits into
ppx-library-cookiefrom
generate-dependency-order
Closed

davesnx wants to merge 6 commits into
ppx-library-cookiefrom
generate-dependency-order

Conversation

@davesnx

@davesnx davesnx commented Sep 18, 2026

Copy link
Copy Markdown
Owner

styled-ppx.generate now emits a module's rules after the rules of every module it depends on, instead of in the order dune lists the files. Every atomic rule has the same specificity, so a consumer's equal-specificity declaration now beats its dependency's by construction, and renaming a file no longer moves rules. Modules with no dependency between them keep alphabetical path order, so output changes only where a dependency forces it. Dedup by first occurrence runs after the sort.

Mechanism: a new packages/generate/order.ml computes each file's referenced module names with compiler-libs Depend on the AST the generator already reads (the same walk ocamldep -modules uses), resolves them to input files by longest shared directory prefix, and runs a Kahn sort that always picks the smallest path among ready nodes. A cycle can only come from an ambiguous module name; the generator warns once, drops the edge whose source sorts last, and continues. --order source keeps the previous behaviour for one release; --log info prints the module order and --log debug the edges.

$ styled-ppx.generate a_consumer.ml z_base.ml   # a_consumer references Z_base
/* This file is generated by styled-ppx, do not edit manually */
.css-...-base{...}
.css-...-consumer{...}

Evidence at a594d02:

  • New cram tests order-dependency, order-unrelated, order-ambiguous-name, order-source-flag, order-cycle in packages/generate/test; log-flags.t gains the new order: info line.
  • On the Ahrefs monorepo's 5,824 post-PPX files: source and dependency order produce the identical set of 23,274 rules; dependency order took 3.7 s against 2.7 s for source order; two runs with different input order gave byte-identical dependency-order output.
  • make format-check, make build, make test: all exit 0.

Review from packages/generate/order.ml, then order_by_dependency in generate.ml. The resolution rule for same-named modules is deliberately conservative (no edge on a tie) and is replaced by library membership in the follow-up library-order PR.

Risk: two-way door. A revert restores input-order emission. Until then, consumers aggregating several modules see rules move relative to today; a winner-diff script in the next PR of this stack lists exactly which cascade ties flip.

@vercel

vercel Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
styled-ppx Ignored Ignored Preview Sep 23, 2026 6:40am UTC

Request Review

Order.sort bounded its emission loop by List.length nodes, but node_of_key
collapses nodes that share a key (Hashtbl.replace, later wins), so with the
same input file passed twice the loop could never emit enough nodes and
styled-ppx.generate hung. Bound the loop by the number of distinct keys
instead. Cram test duplicate-input.t passes one file twice and expects the
single-input output.
@davesnx

davesnx commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #616, which carries this branch's commits rebased onto main together with the rest of the CSS ordering work.

@davesnx davesnx closed this Sep 23, 2026
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