Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
3eb8302
fix(issue#4339): limit whitespace check
puckowski Aug 30, 2025
043c195
fix(issue#4339): correct deprecation notice
puckowski Aug 30, 2025
830682a
fix:(issue#4397): container query variable names
puckowski Jan 25, 2026
c625197
Merge pull request #4398 from puckowski/master_fix_issue4397
matthew-dean Mar 8, 2026
39eaa5b
Merge pull request #4365 from puckowski/master_fix_4339
matthew-dean Mar 8, 2026
3c03d00
feat(deprecation): add deprecation warnings for features removed in L…
matthew-dean Mar 9, 2026
65dadc9
feat(benchmark): add historical benchmark suite with per-system resul…
matthew-dean Mar 9, 2026
ab9a74b
fix(benchmark): don't path.resolve bare package names in benchmark-ru…
matthew-dean Mar 9, 2026
28bc523
fix(benchmark): use coefficient of variation instead of range for var…
matthew-dean Mar 9, 2026
d2d2cd5
fix(benchmark): use timestamp instead of date for run filenames
matthew-dean Mar 9, 2026
63c98ee
fix(cli): queue deprecation warnings until after arg parsing
matthew-dean Mar 9, 2026
d01c210
Merge pull request #4402 from matthew-dean/dev/4.x-prep
matthew-dean Mar 9, 2026
90b0cb5
Remove duplicate length check from expression.genCSS() (#4327)
Krinkle Mar 9, 2026
b8c23ae
Remove unused `parsers.entities.propertyCurly()` (#4271)
Krinkle Mar 9, 2026
c4c41af
Remove redundant return from `parsers.blockRuleset()` (#4265)
Krinkle Mar 9, 2026
87bec51
chore: replace deprecated String.prototype.substr() (#3702)
CommanderRoot Mar 9, 2026
3f89d17
Handle the lack of the optional dependencies (#3791)
mems Mar 9, 2026
489892b
remove phantom stuff (#3782)
jimmywarting Mar 9, 2026
b00cedd
fixed bug in import subpath module (#4236)
HridoyHazard Mar 9, 2026
f5b7ad8
fix(issue#4354): unknown at-rule expression commas (#4389)
puckowski Mar 9, 2026
774f188
chore: update README.md copyright (#4386)
puckowski Mar 9, 2026
9c91714
Fix no-prototype-builtins issues in Ruleset and ToCSSVisitor (#4404)
matthew-dean Mar 9, 2026
b9f67e7
chore: add test for number with underscore parsing (#4406)
matthew-dean Mar 9, 2026
3fda5b2
fix(#4331): exclude CSS at-rule keywords from declarationCall parsing…
matthew-dean Mar 9, 2026
daa3e00
fix(#4358): resolve parent selectors in comma-separated pseudo-select…
matthew-dean Mar 9, 2026
7e48ad9
refactor: code quality cleanup for container queries and related code…
matthew-dean Mar 9, 2026
6f82d19
perf: optimize hot paths and fix benchmark infrastructure (#4410)
matthew-dean Mar 9, 2026
c21e465
feat: migrate to native ESM with no build step (#4411)
matthew-dean Mar 9, 2026
76fc00c
refactor: convert prototype-based tree nodes to ES6 classes (#4412)
matthew-dean Mar 10, 2026
784dfa9
feat: JSDoc type annotations for all tree node files (#4413)
matthew-dean Mar 10, 2026
e6a8efb
fix: pre-existing bug fixes in tree nodes (#4414)
matthew-dean Mar 10, 2026
30c3a97
chore: release v4.6.0 (#4415)
matthew-dean Mar 10, 2026
290900a
fix: CJS compatibility, enriched npm README, ESM tests (#4417)
matthew-dean Mar 10, 2026
95f0bf9
fix: publish script skips stale version markers in squash merges (#4418)
matthew-dean Mar 10, 2026
5b65bfc
chore: remove .claude directory and add to .gitignore (#4419)
matthew-dean Mar 10, 2026
6cafcc0
fix: webpack browser build - use UMD dist/less.js, add CJS bundle (#4…
matthew-dean Mar 11, 2026
48a386f
test: Add coverage for :is()/:matches()/:where() containing nested :h…
Copilot Mar 13, 2026
efd8b82
fix: ensure npm publish is always backed by a GitHub tag, release, an…
Copilot Mar 17, 2026
e3805d0
fix(less): upgrade make-dir to v4 to fix security vulnerability (#4426)
jorenbroekema Mar 17, 2026
da51403
fix(issue#4356): parenthesis in media query (#4427)
puckowski Mar 18, 2026
2958e9d
feat: PR-based release flow for alpha; test suite proving all three r…
Copilot Mar 19, 2026
d250d62
Fix `not` keyword silently ignored when used without parentheses in c…
Copilot Mar 19, 2026
ea62d74
test: regression test for @container mixin parameter variable resolut…
Copilot Mar 19, 2026
060fd7f
fix: update packageManager to pnpm@9.15.9 to match lockfileVersion 9.…
Copilot Mar 24, 2026
c228e52
ci: fix Playwright chromium install hang on Node current/lts/* (#4445)
matthew-dean Jun 11, 2026
c573ab6
Fix browser export interop by routing bundlers to CJS-typed UMD artif…
Copilot Jun 11, 2026
f4a63f2
Add Copilot review request job to CI workflow
matthew-dean Jun 11, 2026
a5f115b
fix: add continue-on-error to copilot-review CI job (#4448)
matthew-dean Jun 13, 2026
a5b62c8
chore: automate CHANGELOG generation in release workflow (#4447)
matthew-dean Jun 13, 2026
b092bd2
chore: release v4.6.5 (#4436)
github-actions[bot] Jun 13, 2026
7a0dd6d
fix: correct push event typo in create-release-pr workflow (pus → pus…
matthew-dean Jun 13, 2026
d904698
fix: use printf for PR body to avoid YAML indentation error in run bl…
matthew-dean Jun 13, 2026
06428b3
docs: fix incorrect contributor attributions in v4.6.0 changelog (#4437)
barry3406 Jun 13, 2026
eeb335a
chore: prevent dependency lifecycle scripts (#4440)
puckowski Jun 14, 2026
83bc8d4
fix(issue#4316): color calc inside from expression (#4434)
puckowski Jun 14, 2026
d03e7a4
fix: avoid crash on nested @supports with dumpLineNumbers (#4446)
sarathfrancis90 Jun 14, 2026
888f687
Preserve spacing for container feature functions (#4441)
puneetdixit200 Jun 14, 2026
7787557
chore: release v4.6.6 (#4451)
github-actions[bot] Jun 14, 2026
91b2919
Fix failing "Request Copilot review" CI job (#4457)
Copilot Jun 20, 2026
8ae2cc3
chore: release v4.6.7 (#4458)
github-actions[bot] Jun 22, 2026
c58808f
feat: deprecate bare @variable in non-value at-rule positions (#4462)
matthew-dean Jul 12, 2026
d38b43a
Fix #4460: parse comparison/range syntax in container style() queries…
dweep-js Jul 12, 2026
aeb8027
refactor: extract shared ESLint config and add lint scripts (#4459)
JessenReinhart Jul 12, 2026
0c8db11
Replace image-size with probe-image-size (#4456)
priyamkarn Jul 12, 2026
4d6e67e
chore: release v4.6.8 (#4463)
github-actions[bot] Jul 12, 2026
8e1105f
Follow-up to #4462: migrate remaining @var fixtures, DRY the prelude …
matthew-dean Jul 18, 2026
937300c
chore: release v4.7.0 (#4471)
github-actions[bot] Jul 18, 2026
6161ecf
Fix boolean() parsing for comparisons between inline condition expres…
Copilot Jul 22, 2026
6b04d2d
fix(mixing): resolves issue #4234 (#4473)
puckowski Jul 22, 2026
191c8a1
chore: release v4.8.0 — deprecate legacy identifier forms and dynamic…
matthew-dean Jul 22, 2026
ebbce6d
chore: release v4.8.0 (#4474)
github-actions[bot] Jul 22, 2026
d6b20ee
fix: forwarding an unset variadic no longer overrides callee defaults…
Lfan-ke Jul 26, 2026
1ee86aa
fix: leave math functions for the browser when an argument is a runti…
Lfan-ke Jul 26, 2026
ed8ceb8
fix(release): sync release version from PR title (#4483)
matthew-dean Jul 26, 2026
89c33e0
chore: release v4.8.1 (#4482)
github-actions[bot] Jul 27, 2026
7d07c11
Less 5 alpha.1: Jess-powered compiler preview (#19)
matthew-dean Jul 29, 2026
583b5d3
Merge upstream alpha into Less 5 alpha
matthew-dean Jul 29, 2026
2f309b6
Merge upstream master into Less 5 alpha
matthew-dean Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
reviews:
max_files: 500
29 changes: 22 additions & 7 deletions .github/TESTING_PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Dry-run mode will:
# Check current version
node -p "require('./packages/less/package.json').version"

# Run dry-run to see what version would be created
# Run dry-run to see what version would be published
DRY_RUN=true pnpm run publish
```

Expand All @@ -55,9 +55,9 @@ git checkout alpha
DRY_RUN=true GITHUB_REF_NAME=alpha pnpm run publish

# This will show:
# - Version validation (must contain -alpha.)
# - Master sync check
# - Version comparison with master
# - The committed alpha version
# - Alpha branch validation
# - A dry-run publish plan without npm mutation
```

### 4. Test Version Override
Expand Down Expand Up @@ -127,12 +127,25 @@ git checkout alpha
DRY_RUN=true GITHUB_REF_NAME=alpha pnpm run publish

# Should show:
# - Alpha version increment (e.g., 5.0.0-alpha.1 → 5.0.0-alpha.2)
# - The committed alpha version (for example 5.0.0-alpha.1)
# - Publishing with 'alpha' tag
# - Pre-release creation
# - All alpha validations passing
# - A release plan only; it does not prove the prerequisite Jess alpha has
# been published or that the worktree is clean
```

### Test Less 5 Alpha Readiness

```bash
pnpm run test:alpha
```

This is the Less 5 alpha release gate. It checks the supported alpha contract
and prints the unsupported alpha.1 inventory. The broad legacy corpus remains
available through `pnpm --dir packages/less run test:legacy-node`, but it is
not expected to be green yet and is intentionally not the alpha.1 publish gate
until the unsupported buckets are drained.

### Test Version Validation

```bash
Expand All @@ -147,7 +160,9 @@ DRY_RUN=true GITHUB_REF_NAME=alpha pnpm run publish

Before actually publishing:

- [ ] Run dry-run mode to verify version calculation
- [ ] Prepare, commit, and push the exact alpha version before publishing
- [ ] Run `pnpm run test:alpha` to verify the supported Less 5 alpha contract
- [ ] Run dry-run mode to verify the release plan (not release readiness)
- [ ] Verify branch restrictions work (try from wrong branch)
- [ ] Test alpha validations (if testing alpha branch)
- [ ] Check that version override works (if needed)
Expand Down
36 changes: 18 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,33 @@ jobs:
node: 'lts/*'
- os: ubuntu-latest
node: 'lts/-1'
- os: ubuntu-latest
node: 'lts/-2'
- os: ubuntu-latest
node: 'lts/-3'

runs-on: ${{ matrix.os }}
- os: ubuntu-latest
node: 'lts/-2'

runs-on: ${{ matrix.os }}
# This has copy/paste steps and should be refactored using DRY
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Print put node & npm version
run: node --version && pnpm --version
- name: Run node tests (ESM + CJS)
node-version: ${{ matrix.node }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Print put node & npm version
run: node --version && pnpm --version
- name: Run Less v5 alpha support contract
run: pnpm run test:alpha
- name: Run Less v5 Node module smoke tests
run: pnpm run test:node
- name: Run release automation tests
if: matrix.os == 'ubuntu-latest' && matrix.node == 'lts/*'
run: pnpm run test:release

copilot-review:
name: Request Copilot review
name: Request Copilot review
runs-on: ubuntu-latest
continue-on-error: true
if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened')
Expand All @@ -61,7 +61,7 @@ jobs:
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/requested_reviewers \
-f "reviewers[]=copilot-pull-request-reviewer" \
|| echo "::warning::Could not request Copilot review (the token may lack pull-requests: write access, or Copilot PR reviews may not be enabled for this repository)"
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/requested_reviewers \
-f "reviewers[]=copilot-pull-request-reviewer" \
|| echo "::warning::Could not request Copilot review (the token may lack pull-requests: write access, or Copilot PR reviews may not be enabled for this repository)"
39 changes: 15 additions & 24 deletions .github/workflows/create-release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ jobs:
CURRENT=$(node -p "require('./packages/less/package.json').version")

if [ "$BRANCH" = "alpha" ]; then
NPM_VERSION=$(npm view less dist-tags.alpha)
NPM_VERSION=$(npm view less dist-tags.alpha 2>/dev/null || echo "")
else
NPM_VERSION=$(npm view less version)
NPM_VERSION=$(npm view less version 2>/dev/null || echo "")
fi

DEFAULT_NEXT=$(node scripts/release-metadata.js next-version "$BRANCH" "$CURRENT" "$NPM_VERSION")
Expand All @@ -83,6 +83,11 @@ jobs:
if [ -n "$EXISTING_TITLE" ]; then
NEXT=$(node scripts/release-metadata.js parse-title "$BRANCH" "$EXISTING_TITLE")
RELEASE_BRANCH="$EXISTING_BRANCH"
if ! node scripts/release-metadata.js validate "$BRANCH" "$NEXT" "$NPM_VERSION" 2>/dev/null; then
echo "Existing release PR title v${NEXT} is no longer publishable; falling back to v${DEFAULT_NEXT}"
NEXT="$DEFAULT_NEXT"
RELEASE_BRANCH=$(node scripts/release-metadata.js branch "$BRANCH" "$NEXT")
fi
else
NEXT="$DEFAULT_NEXT"
RELEASE_BRANCH=$(node scripts/release-metadata.js branch "$BRANCH" "$NEXT")
Expand Down Expand Up @@ -121,7 +126,7 @@ jobs:
git checkout -b "${RELEASE_BRANCH}"
fi

node scripts/release-metadata.js sync-package-versions "${NEXT_VERSION}"
node scripts/release-metadata.js sync-package-versions "${RELEASE_BASE}" "${NEXT_VERSION}"

git add package.json packages/*/package.json

Expand All @@ -136,7 +141,7 @@ jobs:
--json number,title,author \
--jq '.[] | "- [#\(.number)](https://github.com/${{ github.repository }}/pull/\(.number)) \(.title) (@\(.author.login))"' \
2>/dev/null || echo "")
if [ -n "$PR_LINES" ]; then
if [ -n "$PR_LINES" ] && ! grep -q "^### v${NEXT_VERSION}\\b" CHANGELOG.md; then
TODAY=$(date +%Y-%m-%d)
{
head -1 CHANGELOG.md
Expand All @@ -150,30 +155,16 @@ jobs:
tail -n +2 CHANGELOG.md
} > CHANGELOG.tmp && mv CHANGELOG.tmp CHANGELOG.md
git add CHANGELOG.md
elif grep -q "^### v${NEXT_VERSION}\\b" CHANGELOG.md; then
echo "CHANGELOG.md already has a v${NEXT_VERSION} section; keeping the curated release notes."
fi
fi

COMMITTED=false
if git diff --cached --quiet; then
echo "No version changes; branch is already at v${NEXT_VERSION}"
echo "No manifest changes; creating an explicit release commit for v${NEXT_VERSION}"
git commit --allow-empty -m "${TITLE}"
else
git commit -m "${TITLE}"
COMMITTED=true
fi

# If no new commit was created the release branch has no commits
# ahead of master, so pushing it and trying to open a PR would fail
# with "no commits between head and base". Instead, just report
# whether an existing release PR is open and exit cleanly.
if [ "$COMMITTED" = "false" ]; then
EXISTING=$(gh pr list --head "${RELEASE_BRANCH}" --base "${RELEASE_BASE}" \
--json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "${EXISTING}" ]; then
echo "✅ No new changes; release PR #${EXISTING} already exists"
else
echo "✅ No version bump needed and no existing release PR; nothing to do"
fi
exit 0
fi

# --force-with-lease refuses to overwrite if the remote has advanced
Expand Down Expand Up @@ -264,9 +255,9 @@ jobs:
PREVIOUS_VERSION=$(node -p "require('./packages/less/package.json').version")
VERSION=$(node .release-scripts/release-metadata.js parse-title "$RELEASE_BASE" "$RELEASE_TITLE")
if [ "$RELEASE_BASE" = "alpha" ]; then
NPM_VERSION=$(npm view less dist-tags.alpha)
NPM_VERSION=$(npm view less dist-tags.alpha 2>/dev/null || echo "")
else
NPM_VERSION=$(npm view less version)
NPM_VERSION=$(npm view less version 2>/dev/null || echo "")
fi
node .release-scripts/release-metadata.js validate-title-sync "$RELEASE_BASE" "$VERSION" "$PREVIOUS_VERSION" "$NPM_VERSION"
TITLE=$(node .release-scripts/release-metadata.js title "$RELEASE_BASE" "$VERSION")
Expand Down
34 changes: 27 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ jobs:
node scripts/release-metadata.js validate "$RELEASE_BASE" "$TITLE_VERSION" "$NPM_VERSION"
echo "✅ Release title, package.json, and npm version checks agree on v${TITLE_VERSION}"

- name: Run node tests (ESM + CJS)
run: pnpm run test:node
- name: Run Less v5 alpha support contract
run: pnpm run test:alpha

- name: Build
run: |
Expand Down Expand Up @@ -202,7 +202,15 @@ jobs:
PRERELEASE="--prerelease"
BODY="## Alpha Release

This is an alpha release from the alpha branch.
This Less 5 alpha is a Jess-powered compiler rewrite for early testing, not a drop-in Less 4.x replacement yet.

Highlights:
- CSS nesting is preserved by default.
- Less-style ampersand joining for BEM-style modifiers and similar selector patterns is still supported.
- Nested and extended selector output may use modern selectors such as :is(); selector compatibility remains part of the alpha feedback surface.
- Browser compilation is not included in this alpha; a new browser build mechanism will be introduced in a future alpha.

See CHANGELOG.md for the current supported surface and work-in-progress areas.

## Installation

Expand All @@ -229,14 +237,26 @@ jobs:
\`\`\`"
fi

ASSETS=()
if [ "$IS_ALPHA" != "true" ]; then
for asset in packages/less/dist/less.js packages/less/dist/less.min.js; do
if [ -f "$asset" ]; then
ASSETS+=("$asset")
fi
done
else
echo "Alpha release has no browser build assets; browser compilation will return through a future build mechanism."
fi

if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, uploading assets to existing release"
gh release upload "$TAG" packages/less/dist/less.js packages/less/dist/less.min.js --clobber
echo "Release $TAG already exists"
if [ "${#ASSETS[@]}" -gt 0 ]; then
gh release upload "$TAG" "${ASSETS[@]}" --clobber
fi
else
gh release create "$TAG" \
--title "$TITLE" \
$PRERELEASE \
--notes "$BODY" \
packages/less/dist/less.js \
packages/less/dist/less.min.js
"${ASSETS[@]}"
fi
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ npm-debug.log
.nyc_output
coverage
*.lcov

# Build output
dist

# Claude Code
.claude/
4 changes: 4 additions & 0 deletions .husky/post-merge
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@

# Post-merge hook to preserve alpha versions when merging master into alpha
node scripts/post-merge-version-fix.js




8 changes: 8 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
branch="$(git branch --show-current)"

if [ "$branch" = "alpha" ]; then
echo "Skipping pre-commit verification on alpha branch"
exit 0
fi

cd packages/less && npm run typecheck && cd ../..
pnpm test
85 changes: 85 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,90 @@
## Change Log

### v5.0.0-alpha.1 (unreleased)

This is the first public alpha for Less 5, a major rewrite of the Less compiler
on top of [Jess](https://github.com/jesscss/jess). Jess is a new CSS-family
compiler engine that Less now uses for parsing, evaluation, rendering, imports,
and the `lessc` command.

This alpha is meant for early testing of the new compiler path. It is not yet a
drop-in replacement for Less 4.x.

#### Highlights

- Introduces the Jess-powered Less compiler and CLI through the existing
`less` package.
- Keeps `lessc` owned by Less. The separate `jess` package has its own CLI and
no longer installs a competing `lessc` binary.
- Preserves authored CSS nesting by default. Use `collapseNesting: true` or
`lessc --collapse-nesting` to emit flattened selector output.
- Still supports Less-style ampersand joining for BEM-style modifiers and
similar selector patterns, such as `.block { &__item { ... } }`.
- Preserves cascade order when nested rules are collapsed, so declarations
after a nested child remain after that child in the generated CSS.
- May use modern selector output such as `:is()` in generated nested and
extended selectors. Selector compatibility remains part of the alpha feedback
surface.
- Adds an alpha readiness test path for the supported Less 5 surface, including
package loading, `lessc`, file imports, malformed-input diagnostics, and clean
npm consumer installation.

#### Current alpha support

Less 5 alpha.1 currently focuses on core compile behavior: `less.render()`,
`less.renderFile()`, `lessc`, variables, arithmetic, mixin calls, sibling file
imports, and nested-rule output.

Less 5 alpha.1 requires Node.js `^20.19.0 || >=22.12.0`, matching its Jess
runtime dependencies.

The broader Less 4.x compatibility surface is still in progress. Known
work-in-progress areas include legacy plugin execution, file-manager and
pre/post-processor hooks, source maps, URL rewriting options, compressed-output
parity, browser compilation, and the remaining long-tail Less 4 fixture corpus.
Less 5 alpha.1 does not include browser compilation support; a new browser
build mechanism will be introduced in a future alpha.
Unsupported syntax should fail with filename, line, column, and source context
rather than raw parser offsets.

### v4.6.0 (2026-03-09)

#### Bug Fixes

- [#4414](https://github.com/less/less.js/pull/4414) Fix pre-existing bugs in tree nodes: selector `this` binding, atrule parenting, mixin-call error propagation, container/media functionRegistry guard (@matthew-dean)
- [#4408](https://github.com/less/less.js/pull/4408) Fix [#4358](https://github.com/less/less.js/issues/4358) Resolve parent selectors in comma-separated pseudo-selector lists (@matthew-dean)
- [#4407](https://github.com/less/less.js/pull/4407) Fix [#4331](https://github.com/less/less.js/issues/4331) Exclude CSS at-rule keywords from declarationCall parsing (@matthew-dean)
- [#4389](https://github.com/less/less.js/pull/4389) Fix [#4354](https://github.com/less/less.js/issues/4354) Unknown at-rule expression commas (@puckowski)
- [#4404](https://github.com/less/less.js/pull/4404) Fix no-prototype-builtins issues in Ruleset and ToCSSVisitor (@matthew-dean)
- [#4236](https://github.com/less/less.js/pull/4236) Fix import subpath module bug (@nicolo-ribaudo)
- [#4327](https://github.com/less/less.js/pull/4327) Remove duplicate length check from expression.genCSS() (@nicolo-ribaudo)
- [#3791](https://github.com/less/less.js/pull/3791) Handle the lack of optional dependencies (@nicolo-ribaudo)

#### Features & Improvements

- [#4413](https://github.com/less/less.js/pull/4413) Add JSDoc type annotations for all tree node files (@matthew-dean)
- [#4412](https://github.com/less/less.js/pull/4412) Convert prototype-based tree nodes to ES6 classes (@matthew-dean)
- [#4411](https://github.com/less/less.js/pull/4411) Migrate to native ESM with no build step (@matthew-dean)
- [#4410](https://github.com/less/less.js/pull/4410) Optimize hot paths and fix benchmark infrastructure (@matthew-dean)
- [#4409](https://github.com/less/less.js/pull/4409) Code quality cleanup for container queries and related code (@matthew-dean)

#### Deprecation Warnings

- [#4402](https://github.com/less/less.js/pull/4402) Add deprecation warnings for features removed in Less 5.x, container query variable name fix, deprecation notice fix (@matthew-dean, @puckowski)

#### Chores

- [#4406](https://github.com/less/less.js/pull/4406) Add test for number with underscore parsing (@matthew-dean)
- [#4386](https://github.com/less/less.js/pull/4386) Update README.md copyright (@matthew-dean)
- [#3782](https://github.com/less/less.js/pull/3782) Remove phantom stuff (@nicolo-ribaudo)
- [#3702](https://github.com/less/less.js/pull/3702) Replace deprecated String.prototype.substr() (@nicolo-ribaudo)
- [#4265](https://github.com/less/less.js/pull/4265) Remove redundant return from parsers.blockRuleset() (@nicolo-ribaudo)
- [#4271](https://github.com/less/less.js/pull/4271) Remove unused parsers.entities.propertyCurly() (@nicolo-ribaudo)

### v4.5.1 (2025-12-28)

_Automated patch release — no user-facing changes._

### v4.4.2 (2025-08-27)

- [#4357](https://github.com/less/less.js/pull/4357) Migrate Less test data to use valid CSS (@matthew-dean)
Expand Down
Loading
Loading