Skip to content

chore(alpha): pin Jess 2.0.0-alpha.29 - #4560

Merged
matthew-dean merged 15 commits into
less:alphafrom
matthew-dean:chore/pin-jess-2.0.0-alpha.29
Oct 20, 2026
Merged

matthew-dean merged 15 commits into
less:alphafrom
matthew-dean:chore/pin-jess-2.0.0-alpha.29

Conversation

@matthew-dean

Copy link
Copy Markdown
Member

Pins Less 5 to Jess 2.0.0-alpha.29 and brings the alpha tests and test data in line with it.

Pin: every @jesscss/* dependency in packages/less/package.json goes from 2.0.0-alpha.27 to 2.0.0-alpha.29; pnpm-lock.yaml updated.

Test data that travels with the pin (these encode Jess behaviour newer than alpha.27, so they land together):

  • updated goldens for the v5 output rules (calc parentheses, extend :is() grouping, import-once per output context, parent references and escaped selector text written in place, glued condition functions and page selectors, bootstrap4);
  • a single tests-unit styles.config with the math the goldens use;
  • parse-interpolation.less spells selector capture selector(…), which replaced *[…] in Jess.

Expectations updated for alpha.29:

  • 13 expected failures now pass and are removed: the eight goldens that were waiting on this pin, sourcemaps-url, and the four mixed-unit error fixtures that now reject.
  • functions.css: length() of a ~( … ) list bound to a variable is the list's length (3). ~( … ) passes a list as one argument; it is not part of the value. (~"1, 2, 3" is still one value.)
  • ampersand-merge-template-invalid.less now renders the escaped text as written instead of erroring; listed as an accepted divergence.
  • Source maps: empty output gets neither a map nor an annotation, and a source-mapped CSS file ends with its sourceMappingURL comment and no trailing newline.
  • collapseNesting: 'native' keeps child branches of different specificity apart (:is(.a, .b) .c, :is(.a, .b) #d); 'compact' folds them (:is(.a, .b) :is(.c, #d)).

pnpm test passes locally, including the packed-consumer proof against Jess 2.0.0-alpha.29.

matthew-dean and others added 14 commits October 14, 2026 14:19
… goldens use

tests-unit/ had two root configs that disagreed. styles.config.ts set
mathMode: 'always'; styles.config.cjs carried the options the Less 4.x
runner used for `_main/` (relativeUrls, silent, javascriptEnabled). The
Jess fixture runner reads the .ts first and test/alpha-fixtures.mjs reads
the .cjs first, so the settings covering tests-unit depended on the runner.

The goldens settle which settings are right:

- calc/calc.css keeps `50vh / 2`, and operations/operations-advanced.css
  keeps `10px / 2` and `10 / 5`: a division outside parens is not
  evaluated, which is the default `parens-division` math. Under 'always'
  both fixtures change. The Less 4.x outputs in legacy/ agree: Less 4.x
  matches operations-advanced and color-functions/modern-syntax only
  under parens-division.
- urls/urls.css keeps urls from imported files as written. With
  relativeUrls: true, Less 4.x rewrites eight of its lines.
- javascriptEnabled matters only to javascript-REMOVED/legacy, which has
  its own config, and silent does not change output.
- Flat output already comes from the corpus root styles.config.ts.

Keep styles.config.cjs, which both runners load in full, set it to
mathMode: 'parens-division', and delete styles.config.ts.
test(test-data): keep one tests-unit styles.config, with the math the goldens use
…is()

`:is()` takes the specificity of its most specific argument, so grouping
extenders of different specificity raises the weaker ones. An extend group
now holds only extenders of equal specificity; the others are written as
their own selectors:

- extend-chaining: `.g` (0,1,0) and `:is(.i, .k).j` (0,2,0) are no longer
  grouped: `.g.h, :is(.i, .k).j.h`.
- extend-nest: `.sidebar, .sidebar2` (0,1,0) split from
  `.type1 .sidebar3, .type2.sidebar4` (0,2,0).
- extend, extend-selector: `.ext1 .ext2` (0,2,0) leaves the
  `.foo, .ext3, .ext4` (0,1,0) group.

extend-nest also writes a leading `&` with its parent in place: `.amp-test-f&`
under a multi-compound parent joins `.amp-test-f` to the parent's last
compound instead of wrapping the parent in `:is()`. The `&` after `+` still
refers to the whole parent and keeps its `:is()`.
…n place

selectors: a compound written before `&` (`.qux&`, `.active&`, `.tst&:hover`)
joins the last compound of the parent selector. `.qux&` under
`.foo .bar, .foo .baz` means `.qux:is(.foo .bar, .foo .baz)`, so it is written
`.foo .bar.qux, .foo .baz.qux`; the old golden put `.qux` on the ancestor
(`.qux.foo .bar`), which matches different elements. Likewise
`.first-level .second-level.active` and
`.foo:not(.only-nested .level2.tst:hover)`.

parse-interpolation: escaped text (`~'.a, .b, .c'`) interpolated into a
selector prints exactly as written rather than being reparsed into an
`:is()` group, so `.q@{classes-quoted}&:hover` gives `.q.a, .b, .c&:hover` and
`@{c-quoted}@{d-quoted}` gives `.a, .b.c, .d`. The header's own comma before
`baz-quoted` is a real selector-list separator, so that branch goes on its own
line like every other nested selector list.
Five kinds of change, all in the expected output only:

- Extenders from another imported file: the `.input-group-sm/lg > ...`
  selectors that extend `.form-control-plaintext.form-control-lg` and
  `select.form-control-sm/lg:not([size]):not([multiple])` live in a different
  imported sheet. The golden was generated while those cross-import extenders
  were being dropped, so it lacked them.
- `color-yiq()` text colour: on the `.btn-warning`/`.btn-light` hover and
  open-dropdown states the darkened background is still light, so the
  contrasting text colour is `#212529`, not `#fff`. The golden was generated
  while the darkened colour was read as black.
- Nested rules under a multi-branch parent: a child selector without `&`
  combines with every branch of its parent. The golden kept only the first
  branch, dropping `.btn-group-toggle > .btn-group > .btn input[...]` and the
  `.input-group > .custom-select/.custom-file + ...` selectors. They are now
  written with the parent list in `:is()`.
- Native nesting output folds a run of sibling children with equal
  specificity under one ancestor into `:is()`: `.table th, .table td` becomes
  `.table :is(th, td)`. The specificity is unchanged because every member is
  equal.
- Extend groups keep specificity: `.btn-sm` (0,1,0) and
  `.btn-group-sm > .btn` (0,2,0), and each `.bs-tooltip-*`/`.bs-popover-*`
  pair (0,1,0 vs 0,2,0), are no longer joined in one `:is()`, which would
  raise the weaker member to the stronger specificity; each is written as its
  own selector. `&::before` under the two-compound parent `<owner> .arrow`
  writes the parent in place rather than in a one-item `:is()`.
…g slash loosest

calc: parentheses written inside `calc()` are kept exactly as authored, around
math and around a lone value alike: `calc(((10vh)) + calc((5vh)))` stays as
written, and `calc(100% - (3 * 1))` keeps its parentheses. `calc()` is
evaluated by the browser, and the parentheses are part of what the author
wrote; the old golden kept only the ones that changed precedence.

math/parens-division: under `math: parens-division` a `/` outside parentheses
does not divide; it is the separator in `<a> / <b>`, as in `border-radius` or
`font`. A separator binds more loosely than arithmetic, so each side is its own
expression: `4px * (1 + 1) / @var + 3px` (with `@var: 4`) is
`8px / (4 + 3px)`, and a unitless number takes the other operand's unit, so
the result is `8px / 7px`. The old golden applied `+ 3px` to the whole slash
and printed `8px / 4 + 3px`.
A sheet imported once is skipped on a later import only when that later copy
would land in the same output context, so it would print the same CSS twice.
An import inside `@media` or inside a rule block places the sheet's rules
somewhere else, so it is rendered there too.

- strict-imports: `imported.less` is imported at the root, inside
  `@media (max-width: 768px)` and inside `.container`; `.imported` now appears
  in all three places. The old golden applied import-once across the whole
  document and dropped the two nested copies.
- import: `import-test-f.less` imports `import-test-e` at the root after an
  `@import` inside `@media screen` already placed a copy; the root copy's
  `body { width: 100%; }` is now in the output.
…s authored

- media `and(max-width: 1280px)`, container `size(min-width: 60ch)`: with no
  space before `(`, `and(` and `size(` tokenize as a single function token
  (CSS Syntax 3, consume an ident-like token), so the condition is a
  `<general-enclosed>` function, not `and` or a container name followed by a
  parenthesized feature. Inserting a space changes what the query means
  (Media Queries 4, `<general-enclosed>`; CSS Conditional 5, `@container`), so
  the output keeps the authored bytes.
- media `@page Test:first`: a `<page-selector>` allows no whitespace between
  the page name and its pseudo-page (CSS Paged Media 3), so `Test :first`
  matches no page. The authored `Test:first` is written as is.
…erty

A custom property's value is an arbitrary token sequence (CSS Custom
Properties 1, `<declaration-value>`), and Less 5 writes a bare `@value` inside
it as literal text, just as CSS would. Only `@{...}` interpolation substitutes
a value there. The fixture means to emit each map entry as a custom property,
so the source now writes `--@{key}: @{value};`. The expected output is
unchanged.
…moves

The alpha fixture gate renders each fixture with the pinned Jess release,
2.0.0-alpha.27. The goldens for calc, container, extend, extend-chaining,
extend-nest, extend-selector, selectors and strict-imports now record output
that release predates, so they are listed as expected render failures, each
with what the golden now records. The gate reports any of them that starts
passing, so each entry is removed when the pin moves to a release that
emits it.
…n-edits

test(test-data): update goldens for v5 selector, calc, import-once and condition output
Moves every Jess dependency to 2.0.0-alpha.29 and brings the alpha test
expectations in line with it:

- 13 expected failures now pass and are removed (the eight goldens waiting on
  the pin, sourcemaps-url, and four mixed-unit error fixtures that now reject).
- functions.css: length() of a ~( … ) list bound to a variable is the list's
  length (3): ~( … ) passes a list as one argument and is not part of the value.
- ampersand-merge-template-invalid.less renders the escaped text as written and
  is listed as an accepted divergence.
- Empty output gets neither a source map nor an annotation, and a source-mapped
  file ends with its annotation and no trailing newline.
- collapseNesting 'native' keeps child branches of different specificity apart;
  'compact' folds them into one :is().
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: less/less.js/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3571dde2-5473-4795-82cc-0e09d528a335

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The update reaches Less 5 compilation and its test corpus.

The PR appears safe to merge, with one non-blocking source-map test improvement.

Summary

Pins Jess dependencies and the optional JavaScript peer to 2.0.0-alpha.29, updates the lockfile, and aligns fixture output and tests.

  • Less 5 now compiles with Jess alpha.29.
  • Less fixture goldens now match Jess alpha.29's CSS output.
  • Alpha checks now match Jess alpha.29's compiler results.

Intentional behavior confirmed from matthew-dean's description and commit explanations:

  • Glued conditions and escaped selector text print as authored.
  • Custom-property values use explicit interpolation.
  • Import-once applies separately to each output context.
  • Empty CSS has no map; mapped CSS ends at its annotation.

Reviews (1) · Last reviewed commit: "chore(alpha): pin Jess 2.0.0-alpha.29" · Reviewed by Greptile

Comment thread packages/less/test/alpha-sourcemaps.mjs Outdated
Comment on lines +148 to +150
const { css, map } = await less.render('', { sourceMap: true });
assert.equal(map, undefined, 'empty output gets no map');
assert.doesNotMatch(css, /sourceMappingURL/, 'empty output gets no annotation');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Empty-output annotations go unchecked

This test uses sourceMap: true without requesting an annotation. That option produces no annotation even for nonempty CSS, so this check cannot catch a regression in the new rule for empty output. Add empty-output cases with sourceMapURL and sourceMapFileInline: true, and assert that neither returns a map nor writes an annotation.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — sourceMap: true alone writes no annotation, so it couldn't catch a regression. Case 6 now also renders empty input with { sourceMapURL: 'empty.css.map' } and { sourceMapFileInline: true } and asserts neither returns a map nor writes an annotation (verified against Jess 2.0.0-alpha.29).

@matthew-dean
matthew-dean merged commit f7dd7c1 into less:alpha Oct 20, 2026
10 checks passed
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