@pre.style/core 3.1.0-alpha.4 → 3.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/compiler/block.d.ts +8 -5
- package/dist/compiler/block.js +89 -9
- package/dist/compiler/compiler.js +24 -8
- package/dist/compiler/custom-properties.js +5 -9
- package/dist/compiler/keyframes.js +4 -11
- package/dist/compiler/stylesheet.js +2 -2
- package/dist/compiler/variables.js +9 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -922,13 +922,40 @@ Expanding means some shorthands produce many classes. For example, `font` also r
|
|
|
922
922
|
|
|
923
923
|
### Order
|
|
924
924
|
|
|
925
|
-
Within one block, a later declaration overrides an earlier one, as in plain CSS. Across blocks, the CSS file is ordered so the usual rules hold:
|
|
925
|
+
Within one block, a later declaration overrides an earlier one, as in plain CSS (see [Fallbacks](#fallbacks) for the same property twice in a row). Across blocks, the CSS file is ordered so the usual rules hold:
|
|
926
926
|
|
|
927
927
|
1. Hoisted at-rules (`@layer` statements, `@import`, `@namespace`), then other copied at-rules. With the `layer` option, everything but `@import` and `@namespace` is inside the layer.
|
|
928
928
|
2. Classes on the element.
|
|
929
929
|
3. Selector variants, with pseudo-classes in LVHFA order (`:link`, `:visited`, `:hover`, `:focus`, `:active`).
|
|
930
930
|
4. At-rule groups. `@media` queries with only a `min-width` come last, smallest first (mobile-first).
|
|
931
931
|
|
|
932
|
+
### Fallbacks
|
|
933
|
+
|
|
934
|
+
Declaring the same property more than once in a rule keeps the earlier values as fallbacks, for browsers that can't parse the later ones. They stay together in one class:
|
|
935
|
+
|
|
936
|
+
```ts
|
|
937
|
+
css`
|
|
938
|
+
height: 100%;
|
|
939
|
+
height: 100svh;
|
|
940
|
+
`;
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
```css
|
|
944
|
+
.A {
|
|
945
|
+
height: 100%;
|
|
946
|
+
height: 100svh;
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
Lightning CSS drops the fallbacks your [`targets`](#configuration) don't need, so set `targets` to keep them. Without `targets`, it assumes current browsers and writes only `height: 100svh` (which then shares a class with any other `height: 100svh`). Values it can't parse, such as `calc-size()`, always keep their fallbacks.
|
|
951
|
+
|
|
952
|
+
A few things aren't fallbacks:
|
|
953
|
+
|
|
954
|
+
- **A declaration in another rule**, such as `height: 100%; & { height: 100svh; }`, overrides the earlier one, since separate rules become separate classes.
|
|
955
|
+
- **A value with `var()`, `env()`, or `${…}`** replaces the declarations before it. Browsers only check these values when they're used, so they never fall back: an invalid value, or a `${…}` that is `null`, makes the property `unset`. To fall back from `${…}`, use a default (`height: var(${h}, 100%)`). A `${…}` followed by a static value is a real fallback (`height: ${h}; height: 100svh`).
|
|
956
|
+
- **Custom properties**, which accept any value.
|
|
957
|
+
- **Shorthands** are expanded into longhands, so `background: red; background: linear-gradient(…)` loses the `red`: the gradient's `background-color` (transparent) is a valid value in every browser.
|
|
958
|
+
|
|
932
959
|
## Compared to utility-first frameworks
|
|
933
960
|
|
|
934
961
|
Utility-first frameworks such as [Tailwind CSS](https://tailwindcss.com/) also produce atomic CSS, so the same styles cost about the same in either. The differences are elsewhere:
|
|
@@ -1006,6 +1033,7 @@ preStyle({
|
|
|
1006
1033
|
| --------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1007
1034
|
| `lightningcss` | | [Lightning CSS options](https://lightningcss.dev/transforms.html), such as `minify`, `targets`, `drafts`, and `visitor`. A `visitor` runs before pre.style processes each block. `filename`, `code`, `sourceMap`, `projectRoot`, `inputSourceMap`, and `cssModules` are ignored. |
|
|
1008
1035
|
| `sourceMap` | `true` in dev, `false` in builds | Generate a source map for the CSS, mapping each class to the first block it appears in (a source map can only point to one place; the [DevTools extension](#tracing-classes-with-the-devtools-extension) lists them all). In dev, it's shown in the browser (the plugin turns on Vite's `css.devSourcemap`, unless you set it). In builds, it's written next to `output.file`. When you import `virtual:pre.style.css`, builds have no CSS source map, because Vite doesn't write them for bundled CSS. |
|
|
1036
|
+
| `sourceAttributes` | `true` in dev, except in test runs | In dev, mark each `className`/`class` attribute in JSX and Astro components with where it's written (as `data-pre-style-at` on elements and `data-pre-style-from` on components), so the [DevTools extension](#tracing-classes-with-the-devtools-extension) can link an element to the line that set its classes. Needs `sourceMap`. Off by default in Vite's `test` mode (as in Vitest), so rendered markup and snapshots don't change; set it to `true` to have them there too. Builds never have the attributes. |
|
|
1009
1037
|
| `layer` | `false` | Wrap the CSS in a [cascade layer](https://developer.mozilla.org/docs/Web/CSS/@layer): `false` for none, `""` for an unnamed layer, or the layer's name, such as `"pre.style"` or `"vendor.pre-style"`. `@import` and `@namespace` rules stay before the layer, since they can't be inside one. `@layer` rules from blocks become layers nested in it. Browsers without cascade layers (Chrome before 99, Safari before 15.4, Firefox before 97) ignore everything inside it. |
|
|
1010
1038
|
| `quotes` | `'double-quote'` | Quotes around the class string that replaces each block in JS: `'single-quote'`, `'double-quote'`, or `'backtick'`. |
|
|
1011
1039
|
| `output.file` | `'pre.style.css'` | Path and name of the CSS file, relative to Vite's `build.outDir`. |
|
|
@@ -1020,7 +1048,15 @@ Before 3.1, `include`, `exclude`, `prependFiles`, `libraries`, and `quirksMode`
|
|
|
1020
1048
|
|
|
1021
1049
|
Classes are shared, so one class can come from many blocks, and a source map can only point to one of them. The [pre.style DevTools extension](packages/devtools) adds a **pre.style** tab to the Elements panel in Chrome and Firefox. For the selected element, it lists each pre.style rule like the Styles pane does, with a link to every block that uses it.
|
|
1022
1050
|
|
|
1023
|
-
It works with the Vite dev server while `sourceMap` is on, which is the default in dev. Setting `sourceMap: false` turns it off.
|
|
1051
|
+
It works with the Vite dev server while `sourceMap` is on, which is the default in dev. Setting `sourceMap: false` turns it off.
|
|
1052
|
+
|
|
1053
|
+
It also links to where the element got its classes: the `className` or `class` attribute on the element, and the one on the component it was passed to, when the component passes its props on. For this, the plugin adds `data-pre-style-at` and `data-pre-style-from` attributes to your JSX and Astro components in dev (not in builds or test runs), which you'll see in the Elements panel. If you'd rather not see them, turn them off with the `sourceAttributes` option.
|
|
1054
|
+
|
|
1055
|
+
```ts
|
|
1056
|
+
preStyle({ sourceAttributes: false });
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
It only runs on `localhost`, `*.localhost`, loopback addresses, and hostnames you add in its settings, and sends other sites no requests.
|
|
1024
1060
|
|
|
1025
1061
|
Its links open your editor through Vite, which guesses which one you use. When the dev server runs in VS Code's terminal, the plugin sets the `LAUNCH_EDITOR` environment variable to `code` if it isn't set, which also applies to Vite's error overlay. To choose an editor yourself, set `LAUNCH_EDITOR`.
|
|
1026
1062
|
|
package/dist/compiler/block.d.ts
CHANGED
|
@@ -16,15 +16,17 @@ export interface Atom {
|
|
|
16
16
|
key: string;
|
|
17
17
|
selectors: SelectorList;
|
|
18
18
|
} | null;
|
|
19
|
-
declaration:
|
|
20
|
-
key: string;
|
|
21
|
-
value: Declaration;
|
|
22
|
-
important: boolean;
|
|
23
|
-
};
|
|
19
|
+
declaration: AtomDeclaration;
|
|
24
20
|
property: string;
|
|
25
21
|
shorthand: boolean;
|
|
26
22
|
position: Position | null;
|
|
27
23
|
}
|
|
24
|
+
export interface AtomDeclaration {
|
|
25
|
+
key: string;
|
|
26
|
+
values: Declaration[];
|
|
27
|
+
important: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare const atomDeclaration: (values: Declaration[], important: boolean) => AtomDeclaration;
|
|
28
30
|
export interface GlobalRule {
|
|
29
31
|
key: string;
|
|
30
32
|
rule: Rule;
|
|
@@ -77,6 +79,7 @@ export interface ParseOptions {
|
|
|
77
79
|
export declare const key: (value: unknown) => string;
|
|
78
80
|
export declare function parseBlock(code: string, lang: Lang, options: ParseOptions): ParsedBlock;
|
|
79
81
|
export declare function parseRules(css: string, lightningcss: LightningOptions, position: ToPosition, collect: (rules: Rule[]) => void): void;
|
|
82
|
+
export declare function pruneFallbacks(atoms: Atom[], lightningcss: LightningOptions): Atom[];
|
|
80
83
|
export declare function dropOverridden(atoms: Atom[]): Atom[];
|
|
81
84
|
export type ToPosition = (loc: {
|
|
82
85
|
line: number;
|
package/dist/compiler/block.js
CHANGED
|
@@ -5,9 +5,16 @@ import { localizeCustomProperties } from './custom-properties.js';
|
|
|
5
5
|
import { SLOT_PREFIX } from './interpolate.js';
|
|
6
6
|
import { localizeKeyframes } from './keyframes.js';
|
|
7
7
|
import { compose, fromPlaceholder, isElement, PLACEHOLDER, repair } from './selectors.js';
|
|
8
|
+
import { print } from './print.js';
|
|
8
9
|
import { splitBlock } from './split.js';
|
|
9
10
|
import { extractVariables } from './variables.js';
|
|
10
11
|
import { SHORTHANDS } from '../shorthands.js';
|
|
12
|
+
// An atom's declaration. A single declaration's key is its own key.
|
|
13
|
+
export const atomDeclaration = (values, important) => ({
|
|
14
|
+
key: key(values.length === 1 ? values[0] : values) + (important ? '!' : ''),
|
|
15
|
+
values,
|
|
16
|
+
important,
|
|
17
|
+
});
|
|
11
18
|
export class PreStyleError extends Error {
|
|
12
19
|
position;
|
|
13
20
|
constructor(message, position, options) {
|
|
@@ -86,7 +93,7 @@ export function parseBlock(code, lang, options) {
|
|
|
86
93
|
// Sass output positions don't match the block, so scss atoms have none.
|
|
87
94
|
const position = (loc) => lang === 'css' && loc ? toBlock(loc.line, loc.column) : null;
|
|
88
95
|
parseRules(css, options.lightningcss, position, (rules) => walk(rules, [], null, parsed, position));
|
|
89
|
-
let atoms = dropOverridden(parsed.atoms);
|
|
96
|
+
let atoms = dropOverridden(pruneFallbacks(parsed.atoms, options.lightningcss));
|
|
90
97
|
let { globals } = parsed;
|
|
91
98
|
if (options.customPropertyLocalization) {
|
|
92
99
|
const local = localizeCustomProperties(atoms, globals, `block\n${code}`, lang, options);
|
|
@@ -127,6 +134,60 @@ export function parseRules(css, lightningcss, position, collect) {
|
|
|
127
134
|
throw new PreStyleError(`Invalid CSS: ${error.message}`, loc ? position({ line: loc.line - 1, column: loc.column }) : null, { cause: error });
|
|
128
135
|
}
|
|
129
136
|
}
|
|
137
|
+
// Drops the fallbacks in each stack that Lightning CSS wouldn't print for the
|
|
138
|
+
// user's targets, so a stack that collapses to one declaration shares its
|
|
139
|
+
// class. Values Lightning CSS can't parse (such as `calc-size()`) are kept.
|
|
140
|
+
// Printing and parsing back changes some values' trees, so the round trip
|
|
141
|
+
// only decides which of the original declarations are kept.
|
|
142
|
+
export function pruneFallbacks(atoms, lightningcss) {
|
|
143
|
+
if (atoms.every((atom) => atom.declaration.values.length === 1))
|
|
144
|
+
return atoms;
|
|
145
|
+
const { visitor: _visitor, minify: _minify, ...options } = lightningcss;
|
|
146
|
+
// The stack as Lightning CSS prints it, parsed back, as keys. Declarations
|
|
147
|
+
// it adds for other properties (vendor prefixes) are left out.
|
|
148
|
+
const printed = (stack, important, property) => {
|
|
149
|
+
const block = important ? { importantDeclarations: stack } : { declarations: stack };
|
|
150
|
+
const rule = {
|
|
151
|
+
type: 'style',
|
|
152
|
+
value: {
|
|
153
|
+
selectors: [[{ type: 'class', name: 'x' }]],
|
|
154
|
+
declarations: block,
|
|
155
|
+
rules: [],
|
|
156
|
+
loc: { source_index: 0, line: 0, column: 1 },
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
let values = [];
|
|
160
|
+
parseRules(print([rule], [], options, false).code, options, () => null, (rules) => {
|
|
161
|
+
const parsed = valueOf(rules[0]).declarations ?? {};
|
|
162
|
+
values = (important ? parsed.importantDeclarations : parsed.declarations) ?? [];
|
|
163
|
+
});
|
|
164
|
+
return values.filter((value) => propertyOf(value).property === property).map(key);
|
|
165
|
+
};
|
|
166
|
+
return atoms.map((atom) => {
|
|
167
|
+
const { values: stack, important } = atom.declaration;
|
|
168
|
+
if (stack.length === 1)
|
|
169
|
+
return atom;
|
|
170
|
+
let kept;
|
|
171
|
+
try {
|
|
172
|
+
const remaining = printed(stack, important, atom.property);
|
|
173
|
+
if (remaining.length === stack.length)
|
|
174
|
+
return atom;
|
|
175
|
+
kept = stack.filter((value) => {
|
|
176
|
+
const own = printed([value], important, atom.property).at(-1);
|
|
177
|
+
if (own !== remaining[0])
|
|
178
|
+
return false;
|
|
179
|
+
remaining.shift();
|
|
180
|
+
return true;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return atom;
|
|
185
|
+
}
|
|
186
|
+
if (kept.length === 0)
|
|
187
|
+
return atom;
|
|
188
|
+
return { ...atom, declaration: atomDeclaration(kept, important) };
|
|
189
|
+
});
|
|
190
|
+
}
|
|
130
191
|
// Within a block, a later declaration overrides an earlier one that sets the
|
|
131
192
|
// same property under the same at-rules and selector variant, as in CSS.
|
|
132
193
|
// `!important` declarations are collected after normal ones in each rule,
|
|
@@ -225,24 +286,43 @@ export function addDeclarations(block, atRules, selectors, atoms, composes, posi
|
|
|
225
286
|
composes.push(parseComposes(d, position));
|
|
226
287
|
normal = normal.slice(own);
|
|
227
288
|
}
|
|
289
|
+
// Declarations of the same property in this list are stacked, in order, as
|
|
290
|
+
// fallbacks, at the place of the last one. A declaration that always parses
|
|
291
|
+
// replaces the stack, since nothing before it can ever apply.
|
|
228
292
|
const add = (declarations = [], important) => {
|
|
293
|
+
const stacks = new Map();
|
|
294
|
+
const list = [];
|
|
229
295
|
for (const declaration of declarations) {
|
|
230
296
|
for (const value of expand(declaration)) {
|
|
231
297
|
const { property, shorthand } = propertyOf(value);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
position,
|
|
239
|
-
});
|
|
298
|
+
const stack = stacks.get(property);
|
|
299
|
+
if (stack)
|
|
300
|
+
list.splice(list.findIndex((item) => item.values === stack), 1);
|
|
301
|
+
const values = stack && !alwaysParses(value, property) ? [...stack, value] : [value];
|
|
302
|
+
stacks.set(property, values);
|
|
303
|
+
list.push({ property, shorthand, values });
|
|
240
304
|
}
|
|
241
305
|
}
|
|
306
|
+
for (const { property, shorthand, values } of list)
|
|
307
|
+
atoms.push({
|
|
308
|
+
atRules,
|
|
309
|
+
variant,
|
|
310
|
+
declaration: atomDeclaration(values, important),
|
|
311
|
+
property,
|
|
312
|
+
shorthand,
|
|
313
|
+
position,
|
|
314
|
+
});
|
|
242
315
|
};
|
|
243
316
|
add(normal, false);
|
|
244
317
|
add(block?.importantDeclarations, true);
|
|
245
318
|
}
|
|
319
|
+
// Whether a browser always parses a declaration, so it can't fall back to an
|
|
320
|
+
// earlier one: custom properties take any value, and `var()` and `env()`
|
|
321
|
+
// (including `${…}`, which becomes a `var()`) are only checked once they're
|
|
322
|
+
// substituted, when an invalid value makes the property `unset`.
|
|
323
|
+
function alwaysParses(value, property) {
|
|
324
|
+
return property.startsWith('--') || /"type":"(?:var|env)"/.test(key(value));
|
|
325
|
+
}
|
|
246
326
|
const ident = (t) => t?.type === 'token' && t.value.type === 'ident' ? t.value.value : null;
|
|
247
327
|
// Reads `composes: a b`, `composes: a from './file.css'`, or
|
|
248
328
|
// `composes: a from global`.
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname, relative, resolve as resolvePath, sep } from 'node:path';
|
|
3
3
|
import { transform } from 'lightningcss';
|
|
4
4
|
import SweatMap from 'sweatmap';
|
|
5
|
-
import { dropOverridden, parseBlock, PreStyleError, valueOf } from './block.js';
|
|
5
|
+
import { atomDeclaration, dropOverridden, parseBlock, PreStyleError, valueOf } from './block.js';
|
|
6
6
|
import { interpolate } from './interpolate.js';
|
|
7
7
|
import { print } from './print.js';
|
|
8
8
|
import { lvhfaRank, specificity, withClass } from './selectors.js';
|
|
@@ -544,12 +544,12 @@ export class Compiler {
|
|
|
544
544
|
}
|
|
545
545
|
#atomRule(record, loc) {
|
|
546
546
|
const { atom } = record;
|
|
547
|
-
const
|
|
548
|
-
? this.#named(atom.declaration.
|
|
549
|
-
: atom.declaration.
|
|
547
|
+
const values = hasGenerated(atom.declaration.key)
|
|
548
|
+
? this.#named(atom.declaration.values)
|
|
549
|
+
: atom.declaration.values;
|
|
550
550
|
const declarations = atom.declaration.important
|
|
551
|
-
? { importantDeclarations:
|
|
552
|
-
: { declarations:
|
|
551
|
+
? { importantDeclarations: values }
|
|
552
|
+
: { declarations: values };
|
|
553
553
|
return {
|
|
554
554
|
type: 'style',
|
|
555
555
|
value: { selectors: this.#atomSelectors(record), declarations, rules: [], loc },
|
|
@@ -574,6 +574,20 @@ export class Compiler {
|
|
|
574
574
|
};
|
|
575
575
|
return emit(this.#atomGroups());
|
|
576
576
|
}
|
|
577
|
+
// An atom's rule as text. Without the user's targets, Lightning CSS would
|
|
578
|
+
// drop a stack's fallbacks, so each declaration is printed on its own.
|
|
579
|
+
#printedStack(record, rule, text) {
|
|
580
|
+
const code = text(rule);
|
|
581
|
+
const { values, important } = record.atom.declaration;
|
|
582
|
+
if (values.length === 1)
|
|
583
|
+
return code;
|
|
584
|
+
const lines = values.map((value) => {
|
|
585
|
+
const atom = { ...record.atom, declaration: atomDeclaration([value], important) };
|
|
586
|
+
const own = text(this.#atomRule({ ...record, atom }, valueOf(rule).loc));
|
|
587
|
+
return own.slice(own.indexOf(' {\n') + 3, own.lastIndexOf('\n}'));
|
|
588
|
+
});
|
|
589
|
+
return `${code.slice(0, code.indexOf(' {\n'))} {\n${lines.join('\n')}\n}`;
|
|
590
|
+
}
|
|
577
591
|
#usage(record, order) {
|
|
578
592
|
const { atom } = record;
|
|
579
593
|
const loc = { source_index: 0, line: 0, column: 1 };
|
|
@@ -581,7 +595,7 @@ export class Compiler {
|
|
|
581
595
|
// Printed for reading, so without the user's `minify` and `targets`.
|
|
582
596
|
const { drafts, nonStandard, errorRecovery } = this.#parseOptions.lightningcss;
|
|
583
597
|
const text = (rules) => print([rules], [], { drafts, nonStandard, errorRecovery }, false).code;
|
|
584
|
-
const code = (record.printed ??=
|
|
598
|
+
const code = (record.printed ??= this.#printedStack(record, rule, text));
|
|
585
599
|
const open = code.indexOf(' {\n');
|
|
586
600
|
return {
|
|
587
601
|
className: record.className,
|
|
@@ -595,7 +609,9 @@ export class Compiler {
|
|
|
595
609
|
declaration: code
|
|
596
610
|
.slice(open + 3, code.lastIndexOf('\n}'))
|
|
597
611
|
.trim()
|
|
598
|
-
.replace(/;$/, '')
|
|
612
|
+
.replace(/;$/, '')
|
|
613
|
+
// Fallbacks, on one line.
|
|
614
|
+
.replaceAll(/;\n\s*/g, '; '),
|
|
599
615
|
property: this.#propertyName(atom.property),
|
|
600
616
|
covers: Object.hasOwn(SHORTHANDS, atom.property) ? SHORTHANDS[atom.property] : [],
|
|
601
617
|
important: atom.declaration.important,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { key, overrideKey, parseRules, valueOf, walk } from './block.js';
|
|
2
|
+
import { atomDeclaration, key, overrideKey, parseRules, valueOf, walk } from './block.js';
|
|
3
3
|
import { VARIABLE_PREFIX } from './variables.js';
|
|
4
4
|
// With `customPropertyLocalization`, a custom property that a block or
|
|
5
5
|
// stylesheet file declares gets a generated name, used only in that block or
|
|
@@ -103,7 +103,7 @@ export function localizeCustomProperties(atoms, globals, scope, lang, options) {
|
|
|
103
103
|
local.set(name, VARIABLE_PREFIX + hash);
|
|
104
104
|
};
|
|
105
105
|
for (const atom of atoms)
|
|
106
|
-
eachDeclared(atom.declaration.
|
|
106
|
+
eachDeclared(atom.declaration.values, declared);
|
|
107
107
|
const own = (global) => !shared.keys.has(global.key);
|
|
108
108
|
for (const global of globals)
|
|
109
109
|
if (own(global))
|
|
@@ -125,23 +125,19 @@ export function localizeCustomProperties(atoms, globals, scope, lang, options) {
|
|
|
125
125
|
};
|
|
126
126
|
}),
|
|
127
127
|
atoms: (list) => list.map((atom) => {
|
|
128
|
-
const
|
|
128
|
+
const values = rename(atom.declaration.values, local);
|
|
129
129
|
const atRules = atom.atRules.map((segment) => {
|
|
130
130
|
const rule = rename(segment.rule, local);
|
|
131
131
|
return rule === segment.rule ? segment : { key: key(valueKey(rule)), rule };
|
|
132
132
|
});
|
|
133
|
-
const unchanged =
|
|
133
|
+
const unchanged = values === atom.declaration.values && atRules.every((s, i) => s === atom.atRules[i]);
|
|
134
134
|
if (unchanged)
|
|
135
135
|
return atom;
|
|
136
136
|
return {
|
|
137
137
|
...atom,
|
|
138
138
|
atRules,
|
|
139
139
|
property: local.get(atom.property) ?? atom.property,
|
|
140
|
-
declaration:
|
|
141
|
-
...atom.declaration,
|
|
142
|
-
key: key(value) + (atom.declaration.important ? '!' : ''),
|
|
143
|
-
value,
|
|
144
|
-
},
|
|
140
|
+
declaration: atomDeclaration(values, atom.declaration.important),
|
|
145
141
|
};
|
|
146
142
|
}),
|
|
147
143
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { key, overrideKey, valueOf } from './block.js';
|
|
2
|
+
import { atomDeclaration, key, overrideKey, valueOf } from './block.js';
|
|
3
3
|
// With `keyframeLocalization`, each `@keyframes` name is replaced with a
|
|
4
4
|
// generated one, like styled-components' `keyframes`. In parsed rules, a
|
|
5
5
|
// local name is an ident with this prefix and a hash of every definition of
|
|
@@ -55,17 +55,10 @@ export function localizeKeyframes(globals) {
|
|
|
55
55
|
atoms: (atoms) => atoms.map((atom) => {
|
|
56
56
|
if (!ANIMATION_PROPERTIES.has(atom.property.replace(/^-\w+-/, '')))
|
|
57
57
|
return atom;
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
58
|
+
const values = renameIdents(atom.declaration.values, local);
|
|
59
|
+
if (values === atom.declaration.values)
|
|
60
60
|
return atom;
|
|
61
|
-
return {
|
|
62
|
-
...atom,
|
|
63
|
-
declaration: {
|
|
64
|
-
...atom.declaration,
|
|
65
|
-
key: key(value) + (atom.declaration.important ? '!' : ''),
|
|
66
|
-
value,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
61
|
+
return { ...atom, declaration: atomDeclaration(values, atom.declaration.important) };
|
|
69
62
|
}),
|
|
70
63
|
};
|
|
71
64
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
2
2
|
import { atRuleInfo } from './at-rules.js';
|
|
3
|
-
import { addDeclarations, addGlobal, dropOverridden, key, parseRules, PreStyleError, valueOf, walk, } from './block.js';
|
|
3
|
+
import { addDeclarations, addGlobal, dropOverridden, pruneFallbacks, key, parseRules, PreStyleError, valueOf, walk, } from './block.js';
|
|
4
4
|
import { localizeCustomProperties } from './custom-properties.js';
|
|
5
5
|
import { localizeKeyframes } from './keyframes.js';
|
|
6
6
|
import { compose, isElement } from './selectors.js';
|
|
@@ -145,7 +145,7 @@ export function parseStylesheet(code, lang, file, id, options) {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
for (const target of classes.values())
|
|
148
|
-
target.atoms = dropOverridden(target.atoms);
|
|
148
|
+
target.atoms = dropOverridden(pruneFallbacks(target.atoms, options.lightningcss));
|
|
149
149
|
// Custom properties are local to the whole file, like keyframes.
|
|
150
150
|
if (options.customPropertyLocalization) {
|
|
151
151
|
const all = [...classes.values()].flatMap((target) => target.atoms);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { transform } from 'lightningcss';
|
|
3
|
-
import { key, overrideKey, PreStyleError, valueOf } from './block.js';
|
|
3
|
+
import { atomDeclaration, key, overrideKey, PreStyleError, valueOf } from './block.js';
|
|
4
4
|
import { atRuleInfo } from './at-rules.js';
|
|
5
5
|
import { SLOT_PREFIX } from './interpolate.js';
|
|
6
6
|
import { print } from './print.js';
|
|
@@ -43,7 +43,9 @@ const IGNORED = (token) => token.type === 'token' && token.value?.type === 'whit
|
|
|
43
43
|
function directSlot(atom) {
|
|
44
44
|
if (atom.atRules.length > 0 || atom.variant !== null || atom.declaration.important)
|
|
45
45
|
return null;
|
|
46
|
-
|
|
46
|
+
if (atom.declaration.values.length !== 1)
|
|
47
|
+
return null;
|
|
48
|
+
const declaration = atom.declaration.values[0];
|
|
47
49
|
// Lightning CSS also uses `custom` for properties it doesn't parse, such as
|
|
48
50
|
// `content` with a `var()`.
|
|
49
51
|
if (declaration.property !== 'custom' || !atom.property.startsWith('--'))
|
|
@@ -251,22 +253,17 @@ export function extractVariables(atoms, globals) {
|
|
|
251
253
|
}
|
|
252
254
|
continue;
|
|
253
255
|
}
|
|
254
|
-
const
|
|
256
|
+
const values = structuredClone(atom.declaration.values);
|
|
255
257
|
const selector = selectorOf(atom);
|
|
256
258
|
let index = 0;
|
|
257
|
-
|
|
259
|
+
// Counted across the whole stack, so `height: ${a}; height: ${b}` is two
|
|
260
|
+
// variables.
|
|
261
|
+
eachSlot(values, (node) => {
|
|
258
262
|
const { slot, syntax } = parseSlot(node);
|
|
259
263
|
const identity = atomIdentity(atom, index++);
|
|
260
264
|
useVariable(node, identity, slot, syntax, variables, added, atom.position, selector);
|
|
261
265
|
});
|
|
262
|
-
keptAtoms.push({
|
|
263
|
-
...atom,
|
|
264
|
-
declaration: {
|
|
265
|
-
...atom.declaration,
|
|
266
|
-
key: key(value) + (atom.declaration.important ? '!' : ''),
|
|
267
|
-
value,
|
|
268
|
-
},
|
|
269
|
-
});
|
|
266
|
+
keptAtoms.push({ ...atom, declaration: atomDeclaration(values, atom.declaration.important) });
|
|
270
267
|
}
|
|
271
268
|
return { atoms: keptAtoms, globals: [...kept, ...added.values()], variables };
|
|
272
269
|
}
|