Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,32 @@
"rules": {
"array-callback-return": "error",
"complexity": "error",
"eqeqeq": ["error", "always", { "null": "ignore" }],
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"guard-for-in": "error",
"import/no-cycle": "error",
"import/no-duplicates": "error",
"import/no-self-import": "error",
"max-depth": "error",
"max-lines": ["error", { "max": 400 }],
"max-lines": [
"error",
{
"max": 400
}
],
"no-bitwise": "error",
"no-case-declarations": "error",
"no-duplicate-imports": "error",
"no-empty": "error",
"no-fallthrough": "error",
"no-labels": "error",
"no-nested-ternary": "error",
"no-param-reassign": "error",
"no-promise-executor-return": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
Expand All @@ -28,15 +41,32 @@
"no-shadow": "error",
"no-template-curly-in-string": "error",
"no-throw-literal": "error",
"no-underscore-dangle": ["error", { "allow": ["_default"] }],
"no-underscore-dangle": [
"error",
{
"allow": ["_default"]
}
],
"no-unmodified-loop-condition": "error",
"no-unnecessary-await": "error",
"no-unreachable-loop": "error",
"no-useless-assignment": "error",
"no-var": "error",
"no-void": "error",
"object-shorthand": "error",
"only-throw-error": "error",
"oxc/no-accumulating-spread": "error",
"prefer-const": ["error", { "destructuring": "all" }],
"prefer-arrow-callback": "error",
"prefer-const": [
"error",
{
"destructuring": "all"
}
],
"prefer-numeric-literals": "error",
"prefer-object-has-own": "error",
"prefer-object-spread": "error",
"prefer-rest-params": "error",
"preserve-caught-error": "error",
"promise/always-return": "error",
"promise/no-nesting": "error",
Expand All @@ -45,22 +75,34 @@
"unicorn/consistent-function-scoping": "error",
"unicorn/no-array-for-each": "error",
"unicorn/no-array-reduce": "error",
"unicorn/no-array-reverse": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-unnecessary-slice-end": "error",
"unicorn/no-useless-fallback-in-spread": "error",
"unicorn/no-useless-spread": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-date-now": "error",
"unicorn/prefer-default-parameters": "error",
"unicorn/prefer-global-this": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-math-min-max": "error",
"unicorn/prefer-math-trunc": "error",
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-node-protocol": "error",
"unicorn/prefer-number-properties": "error",
"unicorn/prefer-object-from-entries": "error",
"unicorn/prefer-optional-catch-binding": "error",
"unicorn/prefer-regexp-test": "error",
"unicorn/prefer-set-has": "error",
"unicorn/prefer-set-size": "error",
"unicorn/prefer-string-replace-all": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-string-trim-start-end": "error",
"unicorn/prefer-type-error": "error"
},
"env": {
Expand Down
50 changes: 17 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
[PostCSS Calc] lets you reduce `calc()` references whenever it's possible.
When an expression mixes units that cannot be combined exactly (such as `px`
and `em`), or contains values only known at runtime (such as `var()`), the
unresolved part is left for the browser's [W3C calc() implementation].
unresolved part is left for the browser's [CSS Values 4][css-values-4]
implementation.

## Installation

Expand Down Expand Up @@ -69,9 +70,8 @@ reduceCalc('min(50px, calc(2 * 40px))');
// => 'calc(50px)'
```

It accepts `precision`, `unwrapSingleValue`, the deprecated
`unwrapSingleNegativeNumber` alias,
`warnWhenCannotResolve`, `onParseError`, and `onWarn`:
It accepts `precision`, `unwrapSingleValue`, `warnWhenCannotResolve`,
`onParseError`, and `onWarn`:

```js
const result = reduceCalc('calc(100% + var(--gap))', {
Expand Down Expand Up @@ -191,20 +191,20 @@ Example:
With `mediaQueries: true`, this becomes:

```css
@media (min-width: 200px) {
@media (min-width: calc(200px)) {
div {
width: 100px;
}
}
```

Add `unwrapSingleValue: true` to get the bare value, `(min-width: 200px)`.

#### `selectors` (default: `false`)

Reduces `calc()` functions found in selectors. Selectors do not accept
`calc()` functions, so the plugin replaces them with their reduced values.
Finite negative and fractional unitless results are serialized as bare values
because a selector cannot contain a `calc()` function; the plugin enables the
`unwrapSingleValue` automatically for selectors.
`calc()` functions, so the plugin replaces them with their reduced values, as
if `unwrapSingleValue` were enabled.

```js
var out = postcss()
Expand All @@ -224,8 +224,8 @@ With `selectors: true`, this becomes `div:nth-child(3)`.

#### `onParseError`

Callback invoked when a `calc()` body fails to parse or simplify. Matches
[`@csstools/css-calc`][csstools-css-calc]'s shape:
Callback invoked with the error and the input value when a `calc()` body fails
to parse or simplify:

```js
postcss().use(
Expand All @@ -250,13 +250,13 @@ canonical-form decisions:
- **Strict whitespace (§10.1).** `calc(2px+3px)` is invalid CSS (binary
`+` / `-` require surrounding whitespace) and is preserved with a
warning instead of reduced.
- **Canonical operand order.** Commutative operands serialize
numeric-first, matching [`@csstools/css-calc`][csstools-css-calc]:
- **Canonical operand order.** Sums serialize numbers first, then
dimensions in first-seen order, then unresolved terms:
`calc(var(--foo) + 10px)` → `calc(10px + var(--foo))`.
- **Zero buckets are kept.** `calc(100px - (100px - 100%))` →
`calc(0px + 100%)`, not `100%` — [WPT calc-serialization-002][wpt-calc-serialization]
requires the zero term because it carries the length-percentage type.
- **Constant folding.** `calc(43 + pi)` now folds to `46.14159` (§10.7.1).
- **Constant folding.** `calc(43 + pi)` now folds to `calc(46.14159)` (§10.7.1).
Previously `pi` / `e` stayed symbolic.
- **Distributive multiplication.** `calc(0.5 * (100vw - 10px))` becomes
`calc(50vw - 5px)`.
Expand All @@ -270,7 +270,6 @@ canonical-form decisions:
if you want validation behavior.

[css-values-4]: https://www.w3.org/TR/css-values-4/
[csstools-css-calc]: https://www.npmjs.com/package/@csstools/css-calc
[wpt-calc-serialization]: https://github.com/web-platform-tests/wpt/blob/master/css/css-values/calc-serialization-002.html
[jison]: https://github.com/zaach/jison

Expand All @@ -288,6 +287,7 @@ for the full guidelines. The project uses [pnpm](https://pnpm.io/).

```bash
git clone git@github.com:postcss/postcss-calc.git
cd postcss-calc
git checkout -b patch-1
pnpm install
pnpm test
Expand All @@ -302,23 +302,8 @@ when changing parsing/simplification behavior:
pnpm test:corpus:full
```

Performance changes to the parser, analyzer, simplifier, or serializer should be
checked with the benchmarks. `pnpm benchmark:arithmetic-chains` and
`pnpm benchmark:nested-fallbacks` compare the working tree against `HEAD` using
20 fresh-process paired blocks by default and write git-ignored schema-v2
reports under `reports/benchmarks/`. Re-check a saved report with
`pnpm benchmark:reanalyze <report>`. `pnpm benchmark:corpus` compares the whole
pipeline with `@csstools/css-calc` on real-world expressions and is
report-only.

These benchmarks time only the parser (or, for the corpus, the whole reducer)
on one machine, and a `pass` means "no regression detected at the declared
margin", not "no change". Read [BENCHMARKS.md](BENCHMARKS.md) before
interpreting results.

The PostCSS benchmark awaits `postcss().process(...)`, and that await already
triggers result stringification. It therefore does not add a redundant
`result.css` access.
Check performance changes with the benchmarks described in
[BENCHMARKS.md](BENCHMARKS.md), and read it before interpreting results.

## [Changelog](CHANGELOG.md)

Expand All @@ -332,4 +317,3 @@ triggers result stringification. It therefore does not add a redundant
[PostCSS Calc]: https://github.com/postcss/postcss-calc
[PostCSS Custom Properties]: https://github.com/postcss/postcss-custom-properties
[tests]: test/
[W3C calc() implementation]: https://www.w3.org/TR/css3-values/#calc-notation
16 changes: 15 additions & 1 deletion scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,23 @@ and software architecture, see [BENCHMARKS.md](../BENCHMARKS.md).
from an observed effect during a run.
- **`benchmark/compare-parser-benchmarks.js`** — reanalyzes one schema-v2 parser
artifact and applies the uncertainty-aware runtime, slope, and growth gates.
The analysis is split across `benchmark/parser-analysis.js` (entry point and
sensitivity analysis), `parser-analysis-endpoints.js` (per-endpoint
intervals, process-order summaries), and `parser-analysis-verdicts.js`
(runtime, slope, and growth verdicts).
- **`benchmark/corpus-analysis.js`** — recomputes corpus decisions from raw
observations and checks stored summaries against them. Its helpers are
`corpus-group-results.js` (per-group paired ratios and bootstrap intervals)
and `corpus-decisions.js` (statistical and practical verdicts).
- **`benchmark/validate-corpus.js`** — validates corpus artifact structure. Its
helpers are `validate-corpus-metadata.js` (correctness and group metadata)
and `validate-corpus-measurements.js` (per-batch and per-measurement checks).
- **`benchmark/benchmark-serialization.js`** — measures buffered serializer scaling for wide sums/products, nested calls, and nested opaque fallbacks.
- **`harvest-github.js`** — scrapes real-world `calc()` expressions from
public GitHub into `test/corpus/github/expressions.txt`.
public GitHub into `test/corpus/github/expressions.txt`. Its helpers live in
`harvest/`: `queries.js` (search phrases and languages), `github.js` (paced
`gh` search and fetch with rate-limit backoff), and `extract.js` (comment- and
string-aware `calc()` extraction).
- **`split-corpus.js`** — splits that file into `github-pure.txt` (feeds
`benchmark/benchmark-corpus.js`/`show-divergences.js` below), `preprocessor.txt`, and
`invalid.txt` (the latter two are used by real CI resilience tests).
Expand Down
Loading
Loading