Less 5 alpha.1: Jess-powered compiler preview - #4487
Conversation
* Fix issue less#4339 by limiting the whitespace check for the deprecation notice to not produce false positives.
* Correct deprecation notice for issue less#4339.
* Fix for issue less#4397 container query with variable names like @container @foo () {}.
fix:(issue#4397): container query variable names
Fix for issue less#4339 false positive deprecation notice for mixins
…ess 5.x New deprecation infrastructure with automatic repetition limiting (max 5 per type): - deprecation.js: registry of deprecation IDs with descriptions - Parser warn() accepts deprecation IDs for categorized warnings - --quiet-deprecations: suppress only deprecation warnings (keeps other warnings) New deprecation warnings for features being removed in 5.x: - js-eval: inline JavaScript backtick expressions - at-plugin: @plugin directive Existing warnings now tagged with stable IDs: - mixin-call-no-parens, mixin-call-whitespace, dot-slash-operator - variable-in-unknown-value, property-in-unknown-value CLI deprecation notices for: --js, --line-numbers, --math=always
…t tracking
Results organized as:
results/latest/{system-id}.json - most recent per system
results/runs/{date}_{system-id}.json - historical archive (gitignored)
…nner
path.resolve('less') turns the package name into an absolute filesystem
path, preventing Node's package resolution from finding npm-installed
versions. Only resolve relative paths starting with '.'.
…iance_pct variance_pct was computing (max-min)/avg which is range-over-mean. Now uses stddev/avg (coefficient of variation) which is a proper variability statistic.
Prevents same-day runs from overwriting each other in the runs/ archive.
Deprecation warnings from flags like --js, --line-numbers, and --math=always were printed immediately during arg parsing, so --quiet-deprecations only worked if it appeared before the deprecated flag. Now all CLI deprecation messages are queued and flushed after parsing completes, respecting --silent, --quiet, and --quiet-deprecations regardless of flag order.
feat: deprecation system and benchmark suite for Less 5.x prep
Follows-up 53f84f0, which started the conditional with a check for `i + 1 < this.value.length`, which is the same as the parent block.
.substr() is deprecated so we replace it with .slice() which works similarily but isn't deprecated Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
* Handle optional dependencies * Handle optional dependency image-size
* remove phantom stuff * lint fix * use deep clone
* Fix issue less#4354 unknown at-rule expressions should not have commas in a keyword list. * Add some additional layer at-rule tests.
* Update README.md copyright year.
Co-authored-by: Timo Tijhof <krinkle@fastmail.com>
In Less.js 2.6.0, parsing of dimensions changed so that `5_large` is seen as one value, instead of as a list containing "5" and "_large". In updating the Less.php port, we forgot to consider this change because none of the Less.js 3.13 tests seem to cover this behavior. Follows-up less#2485. This adds the test case from less#2462, as inpired by downstream https://gerrit.wikimedia.org/r/1197310. Co-authored-by: Timo Tijhof <krinkle@fastmail.com>
…sing (less#4407) * fix(less#4331): exclude CSS at-rule keywords from declarationCall parsing * fix(less#4331): normalize spacing after CSS at-rule keywords in media queries When `and`, `or`, `not`, or `only` keywords appear without a space before `(` in media queries, ensure spacing is added in the output to produce valid CSS.
…less#4409) * fix: correct import and error handling in style() function - Fix incorrect import: `Anonymous` was imported from '../tree/variable' instead of '../tree/anonymous' (worked by accident since Variable was imported on the line above) - Simplify switch/case with single case 0 to a plain if statement - Add explanatory comment to the catch block documenting why it exists (CSS pass-through for @container style() queries) * refactor: remove dead boolean logic in evalRoot() - Remove `allAmpersands` variable that was initialized to false and never set to true, making it dead code - Replace string-based ampersand detection (genCSS + regex) with direct element value checks, avoiding unnecessary AST-to-string conversion - Simplify boolean conditions that referenced the dead variable * fix: add missing parserInput.forget() in colorOperand The colorOperand parser rule called parserInput.save() but only called restore() on failure, missing the forget() call on the success path. * refactor: QueryInParens eval() returns new node instead of mutating this QueryInParens.eval() was mutating `this` directly instead of returning a new node, violating the core Less.js tree pattern. It also used a brittle queue pattern where deep copies were pushed to an `mvalues` array during eval() and shifted off during genCSS(). Now eval() creates and returns a new QueryInParens with evaluated children, and genCSS() reads directly from the node's properties. The `copy-anything` import is removed from this file (still used elsewhere in the codebase). * refactor: extract mergeRules into shared utility to fix AtRule layering violation AtRule.eval() was directly calling ToCSSVisitor.prototype._mergeRules, which breaks the architectural boundary between tree nodes and visitors. Extract the merge logic into a standalone utility (merge-rules.js) that both AtRule.eval() and ToCSSVisitor can use without coupling. * fix: remove Container copy-paste duplication and fix evalNested splice index bug Container was overriding evalNested, permute, and bubbleSelectors with identical copies of the methods already provided by NestableAtRulePrototype. Remove the redundant overrides so Container properly inherits from the shared prototype. Also fix a bug in NestableAtRulePrototype.evalNested where context.mediaBlocks.splice(i, 1) used `i` (the index into `path`) to splice `mediaBlocks`. These are different arrays with different contents, so the index was wrong. Use indexOf(this) to find the correct position.
* fix(benchmark): fix division in benchmark files for v4 math defaults Wrap bare divisions inside percentage() calls in extra parens so benchmarks work with v4's default parens-division math mode. Add --math option passthrough to benchmark-runner.js and pass --math=always in run-historical.sh for consistent cross-version results. * perf: remove unnecessary closures in hot paths - Remove `extendVisitor` alias in findMatch, use `this` directly - Replace IIFE closure for functionRegistry lookup in Ruleset.eval with inline loop ~5% improvement on main benchmark (median 38.6ms → 37.1ms) * perf: replace forEach/map closures with for loops in hot paths - Selector.eval: replace map() closures with pre-allocated for loops - Ruleset transformDeclaration: replace forEach with for loop - extend-visitor visitRuleset: replace forEach with for loop, cache extend and pathCount to reduce repeated property access Combined with previous commit: ~8% improvement on 104KB benchmark (median 38.6ms → 36.4ms) * fix(benchmark): handle all v3.12+/v4.x build scenarios - Use pnpm for v4.3+ (workspace: protocol) - Fallback tsc installation when npm can't install locally - Install runtime deps separately when npm fails due to unpublished workspace packages (@less/test-import-module) - Use last patch version of each minor release - Skip v3.13.x (broken source: missing tree/util.js) * bench: update benchmark results after hot-path optimizations Median: 39.07ms → 34.32ms (~12% improvement) Throughput: 2,495 KB/s → 2,828 KB/s System: macbook-pro arm64 * bench: add historical benchmark results and track runs in git - Add historical benchmark data (v3.5–v4.2) to results/runs/ - Update latest/ with all versions including v4.5.0-dev optimized results - Format JSON with 2-space indentation - Update .gitignore to track runs/ (historical records belong in git) * bench: full historical benchmark run (v2.0–v4.5, 23 versions) Apple M4 Pro, arm64, Node v18/v20/v24 Key findings: - v2.4-v2.5 fastest era (~31ms median on 104KB file) - v3.10-v3.12 massive regression (3-5x slower, 126-185ms) - v4.0 recovered to ~40ms - v4.2 fastest v4.x (35.4ms) - v4.5.1 current master: 42.2ms * bench: prune version list to significant performance changes Reduced from 23 to 15 versions based on full benchmark data. Dropped versions with <5% difference from their predecessor: - v2.1 (broken), v2.5, v2.7 (plateau with v2.4/v2.6) - v3.6–v3.9 (all within 1ms, flat ~41ms) - v4.1 (identical to v4.0) The full set can still be run with --versions flag.
* feat: migrate to native ESM with no build step - Rename src/ to lib/ — source files are shipped directly, no compilation - Add "type": "module" to package.json for native ESM support (Node 18+) - Convert bin/lessc, test files, and build scripts from CJS to ESM - Rename Gruntfile.js and .eslintrc.js to .cjs (must remain CommonJS) - Add .js extensions to all relative import paths for ESM resolution - Use createRequire() for optional dependency resolution (npm packages, JSON) - Configure TypeScript for check-only mode (noEmit: true, allowJs: true) - Update Rollup config to read from lib/ directly - Update CI matrix to drop Node 16 (minimum Node 18+) - Browser build is smaller: 500KB (was 509KB), minified 153KB (was 158KB) - All 139 tests pass * chore: fix trailing semicolons from linter * chore: gitignore generated .css.map files in lib/ * fix(ci): restore lts/-3 to test matrix * chore: stop tracking dist/ build artifacts Generated browser bundles don't need to be in source control — they're built during publish and included in the npm package via the files field. Removes duplicate copies from both root dist/ and packages/less/dist/. * fix(ci): use pnpm exec for playwright install npx doesn't reliably find binaries with pnpm. Since playwright is already a devDependency, use pnpm exec to run the installed version. * fix(ci): use pnpm --filter for playwright, disable fail-fast pnpm exec at workspace root can't find playwright binary since it's a devDependency of the less package. Use --filter to run in that context. Also disable fail-fast so all matrix jobs complete independently. * fix(ci): move playwright to root devDependencies Makes pnpm exec playwright work from workspace root in CI. * fix: upgrade copy-anything to v3 for ESM compat, fix Windows test paths copy-anything v2 lacks "type": "module", causing named import failures on Node 18. v3 has proper ESM exports. Revert testFolder to absolute path (matching original behavior) so debug test path replacements match Less compiler output on Windows. * chore: add CodeRabbit config to raise file review limit * fix: add files field to package.json, remove postinstall from published package Restricts npm package to only bin/, lib/, dist/, index.js, and README.md. Previously shipped test files, Gruntfile, eslint config, etc. Removes postinstall script (Playwright browser install) which only applies in the monorepo dev environment and fails when installed from npm. Verified: npm pack --dry-run shows 120 files (was 229), lessc CLI and API both work from a clean tarball install.
* refactor: convert prototype-based tree nodes to ES6 classes
Convert all 30 tree node files from `Object.assign(new Node(), {...})`
prototype pattern to proper `class extends Node` syntax. This enables
TypeScript to understand the inheritance chain, reducing checkJs errors
from 2756 to 0.
- All tree nodes now use `class X extends Node` (or appropriate parent)
- Node.type converted from instance property to getter for clean override
- Factory functions in index.js updated to use `new` instead of
Object.create + apply (required for ES6 class compatibility)
- Benchmark script converted to ESM
- Added @types/node devDependency for checkJs support
- Enabled checkJs in tsconfig.json
- Added JSDoc types to node.js base class and several utility files
No behavioral changes - all 139 tests pass, benchmark performance
unchanged vs historical baselines (avg 36-39ms for 104KB).
* fix: @plugin deprecation says "replaced" not "removed"
* fix: use constructor params for AtRule selectors, path.resolve in benchmark
* fix: align @types/node with engines.node >=18 floor
* feat: add JSDoc type annotations with @ts-check to all tree node files Add proper JSDoc type annotations to all 44 files in lib/less/tree/, enabling per-file TypeScript checking via @ts-check. No {*} or {any} casts — all types are derived from reading the actual code. Key changes: - Shared types (EvalContext, CSSOutput, TreeVisitor, FileInfo, VisibilityInfo) defined in node.js - Node.value typed as union: Node | Node[] | string | number | undefined - Node.prototype.parse declared for parser-injected prototype property - Constructor properties explicitly declared with proper types - Inline casts used to narrow union types at usage sites - Widened base class params where subclasses pass different types Also adds typecheck to prepublishOnly and pre-commit hook to catch regressions as more files are annotated toward global checkJs: true. All 139 tests pass, zero TypeScript errors. * fix: remove duplicate JSDoc type annotation in ruleset.js
* Initial plan * Fix failing Request Copilot review CI job by handling 403 gracefully --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
) * feat: deprecate bare @variable in non-value at-rule positions Bare @var in at-rule preludes, names, and identifiers is deprecated in favour of @{foo} interpolation; the bare form still resolves, so this is a warning only (id: variable-in-at-rule-prelude, respects --quiet-deprecations and the repetition cap). Covered positions: - @media / @container feature preludes - @supports / @document / unknown & custom at-rule preludes - @Keyframes / @counter-style / @charset identifiers - @layer names and lists - @namespace prefix @{foo} interpolation is now accepted in these positions as the migration target (previously it errored in most of them). A bare @var in a nested declaration value -- e.g. @supports (display: @v) or @media (min-width: @v) -- is NOT deprecated: it is a declaration value and stays valid, detected via paren-depth awareness so parsing and output are unchanged. Value-position parsing is otherwise untouched: @var works, @{var} is not newly accepted in top-level declaration values. Migrates existing fixtures to @{var} and adds a dedicated fixture locking in backward-compatible resolution of the bare form. * fix: also deprecate @@variable-variable prefix in @namespace The @namespace prefix lookahead used `@[\w-]`, which misses an indirect `@@ref` (variable-variable) reference — entities.variable() accepts `@@name`, so `@namespace @@ref "..."` fell through to expression() and resolved without the deprecation warning. Widen the lookahead to `@@?[\w-]` so @@-prefixes hit the same warning path. Adds fixture coverage.
…ries (less#4461) * Fix less#4460: parse comparison/range syntax in container style() queries The mediaFeature lookahead regex only matched a bare identifier before a comparison operator (=, >, <, >=, <=), so it failed whenever the operand was a function call, e.g. var(--n) or calc(6/2). Widened the regex to also match a single level of balanced parens before the operator. Added regression tests covering: @container style(var(--n) = 3) @container style(calc(6 / 2) = var(--n)) @container style(var(--size) > 1lh) * Refactor parser.js for improved readability --------- Co-authored-by: dweep <existing1.001@gmail>
* refactor: extract shared ESLint config and add lint scripts Addresses discussion less#3787 by extracting shared ESLint rules to config/eslint/base.cjs and adding lint/lint:fix npm scripts. Changes: - Created config/eslint/base.cjs with common ESLint rules - Updated packages/less/.eslintrc.cjs to extend the shared config - Added lint and lint:fix scripts to root package.json The shared config maintains compatibility with both JS and TS files. TypeScript-specific recommended rules are scoped to .ts files only to avoid noise in legacy .js source files. Verified: pnpm run lint passes with zero errors, 139/139 unit tests pass (pre-commit hook failed only on unrelated port conflict). * style: apply eslint --fix formatting Auto-generated by 'pnpm run lint:fix' using the new shared config. Touches only quote style and indentation; no logic changes. - benchmark/benchmark-runner.js: indent - build/rollup.js: quotes (backtick -> single) - lib/less-node/environment.js: indent - lib/less/tree/nested-at-rule.js: indent * test(benchmark): add JSDoc for coverage Addresses docstring coverage warning in PR less#4459 by adding full JSDoc to all functions in the benchmark-runner script.
* Replace image-size with probe-image-size * validation * Use loaded contents for image size probing * Use loaded contents for image size probing
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…ude bare-@var scan, add warnings coverage (less#4469) * test: migrate remaining bare @variable at-rule fixtures to @{variable} Follow-up to less#4462, which deprecated bare @variable in non-value at-rule positions and migrated most fixtures to @{variable} but left two feature fixtures on the bare form: - tests-unit/layer/layer.less @layer @layer-name - tests-unit/import/import/import-reference.less @Keyframes @keyframeName Migrated to @{layer-name} / @{keyframeName} (byte-identical render). Also repairs pnpm-lock.yaml: master had a dangling `minimatch: 3.1.2` dependency edge with no package entry (bad-merge artifact), so `pnpm install --frozen-lockfile` failed for every PR. Repinned to the resolved 3.1.5 already present; no dependency version changes. * test: assert deprecation/warning emission + suppress warnings in test output less.js asserts errors via tests-error/*.txt but had no coverage that warnings actually fire, so deprecation notices were unguarded (nothing would catch a regression that silently stopped emitting one). - Suppress warnings from normal test output (they are noise across the corpus); set LESS_TEST_SHOW_WARNINGS=1 to see them. - Add testWarnings() (run from index.js) which installs a capturing logger listener and asserts each render-reachable warning fires: variable-in-at-rule- prelude (incl. bar[@v] top-level -> warns and (x:@v) decl-value -> no warn), js-eval, mixin-call-whitespace, mixin-call-no-parens, variable-in-unknown-value, dot-slash-operator, complex-selector, extend-no-match, compress, at-plugin. Documented gaps (not render-reachable): property-in-unknown-value (a $prop ref resolves via the entity path before the permissive text scan), math-always and dumpLineNumbers (registered in deprecation.js but never emitted via warn()). * refactor(parser): fold at-rule prelude bare-@var detection into $parseUntil (DRY) The at-rule-prelude deprecation detected a top-level bare @var two ways: the permissiveValue entity loop (structural), plus a standalone hasTopLevelBareVariable() that RE-SCANNED the same text $parseUntil had already walked, with its own hand-rolled paren counter (and no string/comment handling). Fold that second scan into $parseUntil's single pass: it already skips strings/comments/ escapes and tracks brackets, so add an opt-in `detectBareVar` that records the first bare @var (not @{interp}) seen at PAREN depth 0 — [...]/{...} don't shield a reference, only a declaration-value (...) does — exposed as `.bareVarIndex`. $parseUntil has a single caller (permissiveValue), so the extra arg/property is contained. Delete hasTopLevelBareVariable. Behaviour preserved (regression-guarded by testWarnings): @foo @bar -> 1, @A and @b -> 2, bar[@v] -> 1 (bracket is top-level), (x:@v) -> 0 (decl value), and the mixed (a:@x) y[@z] -> 1. Also drops the testWarnings 'variable-in-unknown-value' case: it only fires for the inconsistent bracket edge (--x: bar[@bar]) while --x: @bar / 1px @bar / foo(@bar) resolve silently, so asserting it would lock in an artifact (now documented).
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…sions (less#4472) * Initial plan * Fix boolean comparison of inline condition expressions --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
…charset (less#4475) * test: migrate remaining bare @variable at-rule fixtures to @{variable} Follow-up to less#4462, which deprecated bare @variable in non-value at-rule positions and migrated most fixtures to @{variable} but left two feature fixtures on the bare form: - tests-unit/layer/layer.less @layer @layer-name - tests-unit/import/import/import-reference.less @Keyframes @keyframeName Migrated to @{layer-name} / @{keyframeName} (byte-identical render). Also repairs pnpm-lock.yaml: master had a dangling `minimatch: 3.1.2` dependency edge with no package entry (bad-merge artifact), so `pnpm install --frozen-lockfile` failed for every PR. Repinned to the resolved 3.1.5 already present; no dependency version changes. * test: assert deprecation/warning emission + suppress warnings in test output less.js asserts errors via tests-error/*.txt but had no coverage that warnings actually fire, so deprecation notices were unguarded (nothing would catch a regression that silently stopped emitting one). - Suppress warnings from normal test output (they are noise across the corpus); set LESS_TEST_SHOW_WARNINGS=1 to see them. - Add testWarnings() (run from index.js) which installs a capturing logger listener and asserts each render-reachable warning fires: variable-in-at-rule- prelude (incl. bar[@v] top-level -> warns and (x:@v) decl-value -> no warn), js-eval, mixin-call-whitespace, mixin-call-no-parens, variable-in-unknown-value, dot-slash-operator, complex-selector, extend-no-match, compress, at-plugin. Documented gaps (not render-reachable): property-in-unknown-value (a $prop ref resolves via the entity path before the permissive text scan), math-always and dumpLineNumbers (registered in deprecation.js but never emitted via warn()). * refactor(parser): fold at-rule prelude bare-@var detection into $parseUntil (DRY) The at-rule-prelude deprecation detected a top-level bare @var two ways: the permissiveValue entity loop (structural), plus a standalone hasTopLevelBareVariable() that RE-SCANNED the same text $parseUntil had already walked, with its own hand-rolled paren counter (and no string/comment handling). Fold that second scan into $parseUntil's single pass: it already skips strings/comments/ escapes and tracks brackets, so add an opt-in `detectBareVar` that records the first bare @var (not @{interp}) seen at PAREN depth 0 — [...]/{...} don't shield a reference, only a declaration-value (...) does — exposed as `.bareVarIndex`. $parseUntil has a single caller (permissiveValue), so the extra arg/property is contained. Delete hasTopLevelBareVariable. Behaviour preserved (regression-guarded by testWarnings): @foo @bar -> 1, @A and @b -> 2, bar[@v] -> 1 (bracket is top-level), (x:@v) -> 0 (decl value), and the mixed (a:@x) y[@z] -> 1. Also drops the testWarnings 'variable-in-unknown-value' case: it only fires for the inconsistent bracket edge (--x: bar[@bar]) while --x: @bar / 1px @bar / foo(@bar) resolve silently, so asserting it would lock in an artifact (now documented). * deprecate dash-only variable names * deprecate dynamic charset interpolation * chore: release v4.8.0
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Matthew Dean <matthew-dean@users.noreply.github.com>
…less#4477) Signed-off-by: 林晨 (Leo Cheng) <leo-cheng@vip.qq.com>
…me CSS var() (less#4479) Signed-off-by: 林晨 (Leo Cheng) <leo-cheng@vip.qq.com>
* fix(release): sync release version from PR title * fix(release): harden title sync automation * fix(release): harden title sync workflow * fix(release): make changelog title sync idempotent * fix(release): insert missing changelog heading on title sync * fix(release): insert changelog heading without prior releases
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* fix(issue#4339): limit whitespace check * Fix issue less#4339 by limiting the whitespace check for the deprecation notice to not produce false positives. * fix(issue#4339): correct deprecation notice * Correct deprecation notice for issue less#4339. * Some updated tests for Less v5 * Update various acceptance tests for new parser * Lots of v5 syntax tweaks * Unit test updates for v5 - atrules * v5 updates in test expectations * Extend stylesheet changes * fix:(issue#4397): container query variable names * Fix for issue less#4397 container query with variable names like @container @foo () {}. * Update unit tests to Less v5 engine * Rebaseline test-data fixtures and move removed legacy JavaScript cases. This updates expected outputs for current behavior and relocates removed inline-JavaScript and IE filter fixtures into explicit legacy/REMOVED folders to preserve historical references. * Rebaseline import/media fixtures and skip alpha pre-commit test gate. This updates import/media fixture expectations and moves the previous media v5 snapshot into legacy while keeping current output at media.css, and bypasses pre-commit verification on the alpha branch during broad migration work. * test-data: update mixin fixture baselines with legacy snapshots Capture accepted fixture output updates for mixin guard/default ordering and mixin output parity while preserving prior snapshots under legacy paths for traceability. * Update parse-interpolation fixtures for selector capture semantics. This removes the invalid quoted ampersand merge-template case from unit output expectations and moves it into tests-error/eval with an explicit invalid merge-template error fixture. * Add nesting fixture variants (legacy, uncollapsed, styles.config) * fix(test-data): update import-reference expected CSS for jess compat Update expected output to match jess's collapseNesting behavior: - Unwrap single-item :is() wrappers (.visible instead of :is(.visible)) - Simplify extend selectors (.visible + .visible instead of :is() form) - Add .b { color: green; } from bare & { } (matches lessc output) * fix(test-data): update extract-and-length expected CSS for @arguments semantics BREAKING: @arguments no longer flattens Sequence arguments. Passing .mixin(a b c d) to (...) gives @arguments length 1 (one Sequence), not 4 (individual items). Previous expected output saved to legacy/. * feat(benchmark): add historical benchmark suite for Less v2.0-v4.4 Comprehensive benchmark harness that tests every major/minor Less release using git worktrees for isolation and fnm for Node version management. - benchmark.less: enhanced with extend, guards, property merging, detached rulesets, complex nesting, color functions, loops, and more - benchmark-v3.less: v3.6+ features (if, boolean, property lookups) - benchmark-v37.less: v3.7+ features (each with lists/maps) - benchmark-v39.less: v3.9+ features (range for columns/spacing) - benchmark-runner.js: portable runner with ESM interop for v4.x - run-historical.sh: orchestrator with per-system result tracking Results organized as: results/latest/{system-id}.json - most recent per system results/runs/{date}_{system-id}.json - historical archive * feat(deprecation): add deprecation warnings for features removed in Less 5.x New deprecation infrastructure with automatic repetition limiting (max 5 per type): - deprecation.js: registry of deprecation IDs with descriptions - Parser warn() accepts deprecation IDs for categorized warnings - --quiet-deprecations: suppress only deprecation warnings (keeps other warnings) New deprecation warnings for features being removed in 5.x: - js-eval: inline JavaScript backtick expressions - at-plugin: @plugin directive Existing warnings now tagged with stable IDs: - mixin-call-no-parens, mixin-call-whitespace, dot-slash-operator - variable-in-unknown-value, property-in-unknown-value CLI deprecation notices for: --js, --line-numbers, --math=always * feat(benchmark): add historical benchmark suite with per-system result tracking Results organized as: results/latest/{system-id}.json - most recent per system results/runs/{date}_{system-id}.json - historical archive (gitignored) * fix(benchmark): don't path.resolve bare package names in benchmark-runner path.resolve('less') turns the package name into an absolute filesystem path, preventing Node's package resolution from finding npm-installed versions. Only resolve relative paths starting with '.'. * fix(benchmark): use coefficient of variation instead of range for variance_pct variance_pct was computing (max-min)/avg which is range-over-mean. Now uses stddev/avg (coefficient of variation) which is a proper variability statistic. * fix(benchmark): use timestamp instead of date for run filenames Prevents same-day runs from overwriting each other in the runs/ archive. * fix(cli): queue deprecation warnings until after arg parsing Deprecation warnings from flags like --js, --line-numbers, and --math=always were printed immediately during arg parsing, so --quiet-deprecations only worked if it appeared before the deprecated flag. Now all CLI deprecation messages are queued and flushed after parsing completes, respecting --silent, --quiet, and --quiet-deprecations regardless of flag order. * Remove duplicate length check from expression.genCSS() (less#4327) Follows-up 53f84f0, which started the conditional with a check for `i + 1 < this.value.length`, which is the same as the parent block. * Remove unused `parsers.entities.propertyCurly()` (less#4271) Follows-up a38f8a1, which introduced this as part of implementing property accessors. The method was not used there, and hasn't been used elsewhere since then either. Ref less#3163. * Remove redundant return from `parsers.blockRuleset()` (less#4265) * chore: replace deprecated String.prototype.substr() (less#3702) .substr() is deprecated so we replace it with .slice() which works similarily but isn't deprecated Signed-off-by: Tobias Speicher <rootcommander@gmail.com> * Handle the lack of the optional dependencies (less#3791) * Handle optional dependencies * Handle optional dependency image-size * remove phantom stuff (less#3782) * remove phantom stuff * lint fix * use deep clone * fixed bug in import subpath module (less#4236) * fix(issue#4354): unknown at-rule expression commas (less#4389) * Fix issue less#4354 unknown at-rule expressions should not have commas in a keyword list. * Add some additional layer at-rule tests. * chore: update README.md copyright (less#4386) * Update README.md copyright year. * Fix no-prototype-builtins issues in Ruleset and ToCSSVisitor (less#4404) Co-authored-by: Timo Tijhof <krinkle@fastmail.com> * chore: add test for number with underscore parsing (less#4406) In Less.js 2.6.0, parsing of dimensions changed so that `5_large` is seen as one value, instead of as a list containing "5" and "_large". In updating the Less.php port, we forgot to consider this change because none of the Less.js 3.13 tests seem to cover this behavior. Follows-up less#2485. This adds the test case from less#2462, as inpired by downstream https://gerrit.wikimedia.org/r/1197310. Co-authored-by: Timo Tijhof <krinkle@fastmail.com> * fix(less#4331): exclude CSS at-rule keywords from declarationCall parsing (less#4407) * fix(less#4331): exclude CSS at-rule keywords from declarationCall parsing * fix(less#4331): normalize spacing after CSS at-rule keywords in media queries When `and`, `or`, `not`, or `only` keywords appear without a space before `(` in media queries, ensure spacing is added in the output to produce valid CSS. * fix(less#4358): resolve parent selectors in comma-separated pseudo-selector lists (less#4408) * refactor: code quality cleanup for container queries and related code (less#4409) * fix: correct import and error handling in style() function - Fix incorrect import: `Anonymous` was imported from '../tree/variable' instead of '../tree/anonymous' (worked by accident since Variable was imported on the line above) - Simplify switch/case with single case 0 to a plain if statement - Add explanatory comment to the catch block documenting why it exists (CSS pass-through for @container style() queries) * refactor: remove dead boolean logic in evalRoot() - Remove `allAmpersands` variable that was initialized to false and never set to true, making it dead code - Replace string-based ampersand detection (genCSS + regex) with direct element value checks, avoiding unnecessary AST-to-string conversion - Simplify boolean conditions that referenced the dead variable * fix: add missing parserInput.forget() in colorOperand The colorOperand parser rule called parserInput.save() but only called restore() on failure, missing the forget() call on the success path. * refactor: QueryInParens eval() returns new node instead of mutating this QueryInParens.eval() was mutating `this` directly instead of returning a new node, violating the core Less.js tree pattern. It also used a brittle queue pattern where deep copies were pushed to an `mvalues` array during eval() and shifted off during genCSS(). Now eval() creates and returns a new QueryInParens with evaluated children, and genCSS() reads directly from the node's properties. The `copy-anything` import is removed from this file (still used elsewhere in the codebase). * refactor: extract mergeRules into shared utility to fix AtRule layering violation AtRule.eval() was directly calling ToCSSVisitor.prototype._mergeRules, which breaks the architectural boundary between tree nodes and visitors. Extract the merge logic into a standalone utility (merge-rules.js) that both AtRule.eval() and ToCSSVisitor can use without coupling. * fix: remove Container copy-paste duplication and fix evalNested splice index bug Container was overriding evalNested, permute, and bubbleSelectors with identical copies of the methods already provided by NestableAtRulePrototype. Remove the redundant overrides so Container properly inherits from the shared prototype. Also fix a bug in NestableAtRulePrototype.evalNested where context.mediaBlocks.splice(i, 1) used `i` (the index into `path`) to splice `mediaBlocks`. These are different arrays with different contents, so the index was wrong. Use indexOf(this) to find the correct position. * fix(benchmark): update scripts and results for v4.x compatibility - Fix percentage() calls in benchmark .less files for parens-division - Add --math=always passthrough to benchmark-runner.js - Handle all v3.12+/v4.x build scenarios in run-historical.sh (pnpm for workspace protocol, fallback tsc, separate runtime deps) - Use last patch of each minor version in historical suite - Update latest benchmark results (v2.0–v4.5.1) * perf: optimize hot paths and fix benchmark infrastructure (less#4410) * fix(benchmark): fix division in benchmark files for v4 math defaults Wrap bare divisions inside percentage() calls in extra parens so benchmarks work with v4's default parens-division math mode. Add --math option passthrough to benchmark-runner.js and pass --math=always in run-historical.sh for consistent cross-version results. * perf: remove unnecessary closures in hot paths - Remove `extendVisitor` alias in findMatch, use `this` directly - Replace IIFE closure for functionRegistry lookup in Ruleset.eval with inline loop ~5% improvement on main benchmark (median 38.6ms → 37.1ms) * perf: replace forEach/map closures with for loops in hot paths - Selector.eval: replace map() closures with pre-allocated for loops - Ruleset transformDeclaration: replace forEach with for loop - extend-visitor visitRuleset: replace forEach with for loop, cache extend and pathCount to reduce repeated property access Combined with previous commit: ~8% improvement on 104KB benchmark (median 38.6ms → 36.4ms) * fix(benchmark): handle all v3.12+/v4.x build scenarios - Use pnpm for v4.3+ (workspace: protocol) - Fallback tsc installation when npm can't install locally - Install runtime deps separately when npm fails due to unpublished workspace packages (@less/test-import-module) - Use last patch version of each minor release - Skip v3.13.x (broken source: missing tree/util.js) * bench: update benchmark results after hot-path optimizations Median: 39.07ms → 34.32ms (~12% improvement) Throughput: 2,495 KB/s → 2,828 KB/s System: macbook-pro arm64 * bench: add historical benchmark results and track runs in git - Add historical benchmark data (v3.5–v4.2) to results/runs/ - Update latest/ with all versions including v4.5.0-dev optimized results - Format JSON with 2-space indentation - Update .gitignore to track runs/ (historical records belong in git) * bench: full historical benchmark run (v2.0–v4.5, 23 versions) Apple M4 Pro, arm64, Node v18/v20/v24 Key findings: - v2.4-v2.5 fastest era (~31ms median on 104KB file) - v3.10-v3.12 massive regression (3-5x slower, 126-185ms) - v4.0 recovered to ~40ms - v4.2 fastest v4.x (35.4ms) - v4.5.1 current master: 42.2ms * bench: prune version list to significant performance changes Reduced from 23 to 15 versions based on full benchmark data. Dropped versions with <5% difference from their predecessor: - v2.1 (broken), v2.5, v2.7 (plateau with v2.4/v2.6) - v3.6–v3.9 (all within 1ms, flat ~41ms) - v4.1 (identical to v4.0) The full set can still be run with --versions flag. * fix(benchmark): fix invalid CSS in benchmark.less and add Jess wrapper support - Fix invalid CSS patterns in benchmark.less: hex-color selectors (#808080), bare declarations in @media, :not(1), unquoted attr values, empty margin - Add benchmark-runner.cjs for CJS compatibility with ESM package - Add callback support to render() in lib/index.js alongside Promise return * feat: migrate to native ESM with no build step (less#4411) * feat: migrate to native ESM with no build step - Rename src/ to lib/ — source files are shipped directly, no compilation - Add "type": "module" to package.json for native ESM support (Node 18+) - Convert bin/lessc, test files, and build scripts from CJS to ESM - Rename Gruntfile.js and .eslintrc.js to .cjs (must remain CommonJS) - Add .js extensions to all relative import paths for ESM resolution - Use createRequire() for optional dependency resolution (npm packages, JSON) - Configure TypeScript for check-only mode (noEmit: true, allowJs: true) - Update Rollup config to read from lib/ directly - Update CI matrix to drop Node 16 (minimum Node 18+) - Browser build is smaller: 500KB (was 509KB), minified 153KB (was 158KB) - All 139 tests pass * chore: fix trailing semicolons from linter * chore: gitignore generated .css.map files in lib/ * fix(ci): restore lts/-3 to test matrix * chore: stop tracking dist/ build artifacts Generated browser bundles don't need to be in source control — they're built during publish and included in the npm package via the files field. Removes duplicate copies from both root dist/ and packages/less/dist/. * fix(ci): use pnpm exec for playwright install npx doesn't reliably find binaries with pnpm. Since playwright is already a devDependency, use pnpm exec to run the installed version. * fix(ci): use pnpm --filter for playwright, disable fail-fast pnpm exec at workspace root can't find playwright binary since it's a devDependency of the less package. Use --filter to run in that context. Also disable fail-fast so all matrix jobs complete independently. * fix(ci): move playwright to root devDependencies Makes pnpm exec playwright work from workspace root in CI. * fix: upgrade copy-anything to v3 for ESM compat, fix Windows test paths copy-anything v2 lacks "type": "module", causing named import failures on Node 18. v3 has proper ESM exports. Revert testFolder to absolute path (matching original behavior) so debug test path replacements match Less compiler output on Windows. * chore: add CodeRabbit config to raise file review limit * fix: add files field to package.json, remove postinstall from published package Restricts npm package to only bin/, lib/, dist/, index.js, and README.md. Previously shipped test files, Gruntfile, eslint config, etc. Removes postinstall script (Playwright browser install) which only applies in the monorepo dev environment and fails when installed from npm. Verified: npm pack --dry-run shows 120 files (was 229), lessc CLI and API both work from a clean tarball install. * refactor: convert prototype-based tree nodes to ES6 classes (less#4412) * refactor: convert prototype-based tree nodes to ES6 classes Convert all 30 tree node files from `Object.assign(new Node(), {...})` prototype pattern to proper `class extends Node` syntax. This enables TypeScript to understand the inheritance chain, reducing checkJs errors from 2756 to 0. - All tree nodes now use `class X extends Node` (or appropriate parent) - Node.type converted from instance property to getter for clean override - Factory functions in index.js updated to use `new` instead of Object.create + apply (required for ES6 class compatibility) - Benchmark script converted to ESM - Added @types/node devDependency for checkJs support - Enabled checkJs in tsconfig.json - Added JSDoc types to node.js base class and several utility files No behavioral changes - all 139 tests pass, benchmark performance unchanged vs historical baselines (avg 36-39ms for 104KB). * fix: @plugin deprecation says "replaced" not "removed" * fix: use constructor params for AtRule selectors, path.resolve in benchmark * fix: align @types/node with engines.node >=18 floor * feat: JSDoc type annotations for all tree node files (less#4413) * feat: add JSDoc type annotations with @ts-check to all tree node files Add proper JSDoc type annotations to all 44 files in lib/less/tree/, enabling per-file TypeScript checking via @ts-check. No {*} or {any} casts — all types are derived from reading the actual code. Key changes: - Shared types (EvalContext, CSSOutput, TreeVisitor, FileInfo, VisibilityInfo) defined in node.js - Node.value typed as union: Node | Node[] | string | number | undefined - Node.prototype.parse declared for parser-injected prototype property - Constructor properties explicitly declared with proper types - Inline casts used to narrow union types at usage sites - Widened base class params where subclasses pass different types Also adds typecheck to prepublishOnly and pre-commit hook to catch regressions as more files are annotated toward global checkJs: true. All 139 tests pass, zero TypeScript errors. * fix: remove duplicate JSDoc type annotation in ruleset.js * fix: pre-existing bug fixes in tree nodes (less#4414) * fix: preserve alpha 0 for fully transparent hex colors #0000 and #00000000 parsed alpha as 0 which was treated as falsy by the || operator, causing it to fall back to 1 (opaque). Use typeof check instead so alpha 0 is preserved. * fix: selector getElements callback `this` binding and forEach lint - Capture `this._fileInfo` and `this.parse.imports` into locals before the plain function callback in Selector.getElements(), where `this` is undefined in strict mode (ES modules) - Use explicit block in forEach to avoid implicit return of assignment * fix: preserve full error context when rethrowing mixin call errors The catch block in MixinCall.eval() only copied message and stack, dropping type, extract, callLine, and other LessError fields. This caused all mixin call errors to be reported as SyntaxError regardless of their actual type (e.g. NameError). Use spread to preserve all fields while still overriding index/filename to the call site. * fix: guard functionRegistry.inherit() and fix atrule parenting - Container and Media eval() now guard functionRegistry before calling .inherit(), matching mixin-definition.js defensive pattern - AtRule constructor: remove dead setParent(selectors) on orphaned local, parent this.declarations and this.rules with null checks * chore: release v4.6.0 (less#4415) * chore: prepare v4.6.0 release - Bump version to 4.6.0 in all package.json files - Add CHANGELOG entry for v4.6.0 - Update publish workflow: replace deprecated actions/create-release with gh release create, attach dist files (less.js, less.min.js) as release assets, bump contents permission to write - Remove .github/** from paths-ignore (was preventing workflow updates) - Update CONTRIBUTING.md with detailed release documentation version: 4.6.0 * fix: publish workflow and provenance errors - Add repository field to test-data package.json (fixes npm OIDC provenance verification failure) - Skip publish workflow on forks (only run on less/less.js) - Remove duplicate require('fs') in bump-and-publish.js - Add language specifier to markdown code block in CONTRIBUTING.md * fix: handle existing releases for idempotent workflow re-runs * fix: CJS compatibility, enriched npm README, ESM tests (less#4417) * docs: enrich npm README with usage examples and feature highlights version: 4.6.0 * fix: update README and tests to show ESM + promise/await usage The package is ESM-only ("type": "module"), so the README now correctly shows `import less from 'less'` with `await` instead of CJS `require()`. The ES6 test now verifies both promise/await and callback APIs. version: 4.6.0 * fix: add CJS compatibility wrapper so require('less') works Adds index.cjs as a one-line wrapper that re-exports the ESM default. The exports field now has both import and require conditions. Adds test-cjs.cjs to verify CJS consumption alongside the existing ESM test. version: 4.6.0 * fix: lazy Proxy CJS wrapper for Node 18+ compatibility Node 22+ uses native require(esm). Node 18-20 uses a lazy Proxy with dynamic import() — transparent because render()/parse() already return promises. Tested with render, callback, and version property access. * fix: include Node 20.19+ in native require(esm) path * fix: add alt text to README images for accessibility * fix: publish script skips stale version markers in squash merges (less#4418) * fix: skip stale version markers in squash merge commit messages When a squash merge includes commit messages with `version: X.Y.Z` from a previous release, the publish script would use that version instead of auto-incrementing. Now checks if the requested version already has a tag — if so, skips it and falls through to auto-increment. * fix: simplify publish version logic — compare package.json vs NPM Remove commit message version parsing entirely. The publish script now: 1. Checks EXPLICIT_VERSION env var (override) 2. If package.json > NPM version, uses package.json 3. Otherwise, bumps from latest NPM patch version Updated CONTRIBUTING.md to reflect simplified workflow. * chore: remove .claude directory and add to .gitignore (less#4419) * chore: remove .claude directory and add to .gitignore * ci: skip publish for .gitignore and .claude changes * feat(alpha): refresh Jess wrapper integration * fix: update extend.css expected output (.ff selector removal) * test: update expected test outputs * Prepare Less alpha publish gate and sync test-data * benchmark runner update * Fix scope value in v5 * Fix scope value in v5 * Don't collapse media queries in Less v5 * test-data: reconcile Jess alpha fixture baselines * test-data: rebaseline import-reference fixture * test-data: rebaseline scope fixture * test: preserve Jess serializer expectations * test: align extend nest generated is output * test: align reference import comment output * Consolidate Less alpha Jess wrapper * perf(less-compat): plugin-free fast-path (__jessSkipLessCompatWhenPluginFree) + harden stableStringify (functions/circular/plugin objects); benchmark tooling + color-stress fixture + fast-path test * test-data: graduate 13 pure-nesting Less goldens to v5 nested shape For each fixture, v5 flat render == current golden but nested differs (pure-nesting, semantically identical). Moved flat golden to legacy/, wrote v5 nested output as the new top-level golden, and set collapseNesting:false in the fixture's styles.config. Graduated (13): at-rules, css-escapes, layer, mixin-noparens, mixins-closure, mixins-guards, mixins-interpolated, mixins-nested, mixins/maps, rulesets, namespacing/namespacing-7, strict-imports, import/import-reference-issues. Verified: full all-less harness 106/106 green against graduated data (zero sibling regressions). * test(test-data): add top-level styles.config default (collapseNesting: true) Provide the flat-output default at the root of the fixture corpus so the Jess all-less harness can source it from the config cascade instead of hardcoding it. Fixture-directory styles.config files override this per directory; fixtures without an explicit collapseNesting inherit this default. * fix(test-data): @Viewport initial-scale stays 1.0 (v5 un-operated verbatim) * test(test-data): migrate bare @var at-rule preludes to @{var} interpolation (v5) Cherry-pick of 31bbe396 (fix/atrule-var-migrate-v5) onto content branch. Source .less bare-@var -> @{var}; goldens unchanged. Un-migrated source was the sole reason ast/ (correct v5 strict-prelude behavior) diverged. * fix(test-data): correct extend/extend-exact/merge goldens (v5) extend/extend-exact: alpha goldens carried the exact-extend-into-children bug; merge: alpha anchored at first occurrence, v5 uses last. Applies the owner-staged proposed-alpha-corrections; ast/ output is byte-identical to these (gated by extend-byte-identity + r4-byte-identity tests). * chore: prepare Less 5 alpha.1 release guards * test(test-data): restore property accessor v5 oracle * test(test-data): retain property accessor source order * test(test-data): preserve important mixin source order * test(test-data): preserve source order in collapsed fixtures * test(less): honor fixture nesting output modes * test(release): prove packed Less alpha consumer * docs: prepare Less 5 alpha.1 release notes * docs(release): link Less alpha corpus inventory * test(v5): classify removed charset interpolation * docs: clarify Less 5 alpha changelog * docs: remove Less alpha support cruft * docs: note Less 5 browser build follow-up * fix: preserve Linecraft diagnostics in Less alpha * docs: expand Less 5 alpha release notes * chore: remove Grunt from Less alpha * fix: skip browser assets for Less alpha releases * chore: harden Less 5 alpha release candidate * fix: normalize packed alpha tarball checks * fix: harden Less alpha public release surface * fix: address Less alpha review lint findings * test-data: v5 numeric-precision expectations, 4.x snapshotted to legacy/ jess replaces the 8-decimal-place output floor with a shortest-decimal-within- 1e-10 tolerance trim, and stops applying that floor to un-operated SOURCE literals. Both are intended v5 divergences from 4.x, so the top-level .css moves to the v5 value and legacy/{name}.css keeps the 4.x one. - variables/variable-advanced.css: add-px-2 393.35275591px -> 393.3527559px (1cm = 96/2.54 px, a genuine repeating decimal). legacy/ created. - property-name-interp/property-name-interp.css: 3.141592653589793 -> 3.1415926536 x2 — the interpolation splice no longer bypasses the number policy. legacy/ created. The unrelated `/* foo */` divergence is left intact. - css-3/css-3.css: rotate(0deg) -> rotate(-0.0000000001deg) — an un-operated source literal is now preserved verbatim instead of being denoised to 0. legacy/ already existed. - functions/functions.css: 6 trig/luma literals gain their earned digits. legacy/ already existed. The unrelated hsl-clamp and length-1 divergences are left intact. * test-data: label legacy/*.css as inert Less 4.x records legacy/{name}.css holds the Less 4.x output recorded when a fixture graduated to v5-expected. Nothing reads these files (fixture globs are tests-unit/*/*.less one level deep; no styles.config names an output under legacy/), but they share the shape and extension of an asserted fixture one directory away, so they have been misread as asserted. Header states the truth at the top of each file. Additive only. Skipped the three legacy/ dirs that also hold a .less (functions, ie-filters-REMOVED, javascript-REMOVED): those are fixture pairs read by the optional corpus-report tool, not pure records. * test(functions): graduate min/max to the coherent v5 output `min(6em, 5, 4ex, 3, 2pt, 1)` and `max(1px, 2, 3em, 4, 5m, 6)` recorded less.js 4.x's PARTIAL reduction (`min(1, 4ex, 2pt)` / `max(5m, 3em)`). That output is not a policy jess reproduces. less.js throws on mismatched units only when the FIRST argument carries a unit; an intervening unitless argument resets its `unitStatic` bookkeeping and reaches a per-unit-group reduction branch instead, so the same expression reduces or is preserved depending on argument ORDER. dart-sass reduces the same input all the way to `1`/`6`. Under jess's coherent model `min`/`max` reduce when the units reduce and fail otherwise, and a failed CSS-function call in bare position is preserved verbatim, so both lines now emit every original argument. Verified against lessc 4.8.0 and dart-sass 1.101.0: this deliberately matches neither on THESE two inputs, and matches both everywhere else. No `legacy/` record written: `tests-unit/functions/legacy/functions.css` already exists as a live fixture pair with its own `legacy/functions.less`, so it is not a graduation slot and must not be overwritten. * fix(release): sync release version from PR title (less#4484) * fix(release): sync release version from PR title (less#4483) * fix(release): sync release version from PR title * fix(release): harden title sync automation * fix(release): harden title sync workflow * fix(release): make changelog title sync idempotent * fix(release): insert missing changelog heading on title sync * fix(release): insert changelog heading without prior releases * fix(release): automate alpha release tests and pin title sync actions * test: sync upstream alpha fixtures * fix: use Jess alpha.10 compiler closure for Less alpha * test: assert structured Less alpha warnings * fix(release): abort on remote tag mismatch * test-data(container): fix v5 expected container output * test: run alpha fixture gate in less package * test: cover alpha error and warning fixtures * test: narrow alpha fixture diagnostic assertions * fix: format lessc errors with Jess diagnostics * fix(lessc): route Jess warnings to stderr * test: suppress intentional legacy units * test: require Linecraft diagnostics in packed lessc * test: require stable Linecraft diagnostics * test: harden alpha release gates * ci: align alpha node matrix with Jess runtime * fix: preserve Jess diagnostic messages * test: preserve Jess dynamic charset diagnostics * test: derive packed Jess alpha version * test: derive Jess alpha release assertions * test: consume Jess alpha 11 in Less alpha * test: surface alpha fixture timeouts * test: tighten Less alpha review hygiene * ci: harden release metadata defaults * test: support focused alpha fixture runs * docs: record upstream fixture sync check --------- Signed-off-by: Tobias Speicher <rootcommander@gmail.com> Co-authored-by: Daniel Puckowski <puckowski.d@gmail.com> Co-authored-by: Timo Tijhof <krinkle@fastmail.com> Co-authored-by: CommanderRoot <CommanderRoot@users.noreply.github.com> Co-authored-by: Memmie Lenglet <github.memmie@lenglet.name> Co-authored-by: Jimmy Wärting <jimmy@warting.se> Co-authored-by: Shahadat Hossain <71395891+HridoyHazard@users.noreply.github.com> Co-authored-by: Matthew Dean <matthewdean.me@users.noreply.github.com>
# Conflicts: # .github/workflows/ci.yml # .github/workflows/create-release-pr.yml # .github/workflows/publish.yml # scripts/release-metadata.js # scripts/test-release-automation.js
# Conflicts: # .coderabbit.yaml # .github/workflows/ci.yml # .github/workflows/create-release-pr.yml # .github/workflows/publish.yml # .husky/pre-commit # CHANGELOG.md # CONTRIBUTING.md # package.json # packages/less/.eslintrc.cjs # packages/less/Gruntfile.cjs # packages/less/README.md # packages/less/benchmark/benchmark-runner.js # packages/less/benchmark/benchmark-v37.less # packages/less/benchmark/benchmark-v39.less # packages/less/benchmark/run-historical.sh # packages/less/bin/lessc # packages/less/build/rollup.js # packages/less/index.cjs # packages/less/lib/less-browser/add-default-options.js # packages/less/lib/less-browser/bootstrap.js # packages/less/lib/less-browser/browser.js # packages/less/lib/less-browser/cache.js # packages/less/lib/less-browser/error-reporting.js # packages/less/lib/less-browser/file-manager.js # packages/less/lib/less-browser/image-size.js # packages/less/lib/less-browser/index.js # packages/less/lib/less-browser/log-listener.js # packages/less/lib/less-browser/plugin-loader.js # packages/less/lib/less-browser/utils.js # packages/less/lib/less-node/file-manager.js # packages/less/lib/less-node/image-size.js # packages/less/lib/less-node/lessc-helper.js # packages/less/lib/less-node/plugin-loader.js # packages/less/lib/less-node/url-file-manager.js # packages/less/lib/less/constants.js # packages/less/lib/less/contexts.js # packages/less/lib/less/data/colors.js # packages/less/lib/less/data/unit-conversions.js # packages/less/lib/less/default-options.js # packages/less/lib/less/environment/abstract-file-manager.js # packages/less/lib/less/environment/abstract-plugin-loader.js # packages/less/lib/less/environment/environment-api.ts # packages/less/lib/less/environment/environment.js # packages/less/lib/less/environment/file-manager-api.ts # packages/less/lib/less/functions/boolean.js # packages/less/lib/less/functions/color-blending.js # packages/less/lib/less/functions/color.js # packages/less/lib/less/functions/data-uri.js # packages/less/lib/less/functions/default.js # packages/less/lib/less/functions/function-caller.js # packages/less/lib/less/functions/function-registry.js # packages/less/lib/less/functions/index.js # packages/less/lib/less/functions/list.js # packages/less/lib/less/functions/number.js # packages/less/lib/less/functions/string.js # packages/less/lib/less/functions/svg.js # packages/less/lib/less/functions/types.js # packages/less/lib/less/import-manager.js # packages/less/lib/less/index.js # packages/less/lib/less/less-error.js # packages/less/lib/less/logger.js # packages/less/lib/less/parse-tree.js # packages/less/lib/less/parse.js # packages/less/lib/less/parser/parser-input.js # packages/less/lib/less/parser/parser.js # packages/less/lib/less/plugin-manager.js # packages/less/lib/less/render.js # packages/less/lib/less/source-map-builder.js # packages/less/lib/less/source-map-output.js # packages/less/lib/less/transform-tree.js # packages/less/lib/less/tree/atrule-syntax.js # packages/less/lib/less/tree/call.js # packages/less/lib/less/tree/color.js # packages/less/lib/less/tree/debug-info.js # packages/less/lib/less/tree/dimension.js # packages/less/lib/less/tree/js-eval-node.js # packages/less/lib/less/tree/property.js # packages/less/lib/less/tree/ruleset.js # packages/less/lib/less/tree/unit.js # packages/less/lib/less/tree/variable.js # packages/less/lib/less/utils.js # packages/less/lib/less/visitors/extend-visitor.js # packages/less/lib/less/visitors/import-sequencer.js # packages/less/lib/less/visitors/import-visitor.js # packages/less/lib/less/visitors/join-selector-visitor.js # packages/less/lib/less/visitors/set-tree-visibility-visitor.js # packages/less/lib/less/visitors/to-css-visitor.js # packages/less/lib/less/visitors/visitor.js # packages/less/package.json # packages/less/test/exports/import-patterns.cjs # packages/less/test/index.js # packages/less/test/less-test.js # packages/less/test/modify-vars.js # packages/less/test/test-cjs.cjs # packages/less/test/test-es6.js # packages/test-data/package.json # packages/test-data/tests-config/filemanagerPlugin/styles.config.cjs # packages/test-data/tests-unit/container/container.css # packages/test-data/tests-unit/container/container.less # packages/test-data/tests-unit/functions/functions.css # packages/test-data/tests-unit/functions/functions.less # packages/test-data/tests-unit/layer/layer.css # packages/test-data/tests-unit/math-css-vars/math-css-vars.css # packages/test-data/tests-unit/math-css-vars/math-css-vars.less # packages/test-data/tests-unit/mixins-guards/mixins-guards.less # packages/test-data/tests-unit/rulesets/rulesets.css # packages/test-data/tests-unit/selectors/selectors.css # packages/test-import-module/package.json # pnpm-lock.yaml # scripts/bump-and-publish.js # scripts/release-metadata.js # scripts/test-release-automation.js
|
Too many files changed for review. ( Bypass the limit by tagging |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
I will have some time this Friday (7/31) and this weekend so I will review soon. @matthew-dean |
|
@puckowski Thanks! Note this is of course the first v5 alpha. There's probably other holes / bugs and other rough spots I missed. Also, I of course do NOT expect a review of 423 file changes. It's a complete replacement of the AST, parsing, and eval engine entirely. I think the first test is just some real world |
|
@puckowski I'll try to leave Discord open if you have questions -- https://discord.gg/rb5EXYmpB |
|
Merging (alpha only) so this is actually easily testable (under an NPM alpha tag) |
Summary
This PR opens the Less 5 alpha.1 preview on top of the current upstream
alphabranch. Less 5 is the first Less release powered by the Jess stylesheet engine, a shared CSS-family compiler engine for modern stylesheet languages. The aim for this alpha is to let people try the new compiler path early while the remaining Less 4 compatibility surface is still being filled in.Less 5 alpha.1 feature checklist
New in the Less 5 direction
collapseNesting: trueandlessc --collapse-nestingare available when flattened selector output is desired. This mode does not try to join separate@media/@supportswrappers the way Less 4.x sometimes did: separate at-rules cannot be joined generally, and attempting that can recreate the historical combinatorial explosion that extend/nesting used to trigger. CSS at-rule nesting for@media/@supportshas been supported since 2013, while native CSS selector nesting has been supported since 2023..block { &__item { ... } }, dash suffixes like&-item, modifier forms like&--primary, and numeric suffixes like&1.:is()where that is the cleanest representation.:is()forms where appropriate. The updated extend fixture shows the input case, and the generated output shows the cleaner nested-extend result.Ready for alpha testing
lesspackage installs as a thin package that uses the Jess stylesheet engine directly.less.render(),less.renderFile(), andlesscare wired for the supported alpha surface.Not ready yet
@plugin, render-option function plugins, file-manager plugins, and pre/post-processors are not alpha.1 execution paths.urlArgsand remote/import URL behavior, is still pending.Verification
pnpm run test:releasepnpm run test:alphamatthew-dean:alphapassedThis is an alpha preview, not the final Less 5 compatibility line. The checklist above is intended to make the current contract easy to review and easy to extend in follow-up PRs.