@pre.style/core 3.1.0-alpha.2 → 3.1.0-alpha.4
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 +409 -3
- package/dist/cn.d.ts +2 -1
- package/dist/cn.js +4 -2
- package/dist/compiler/block.d.ts +3 -0
- package/dist/compiler/block.js +17 -4
- package/dist/compiler/compiler.d.ts +17 -0
- package/dist/compiler/compiler.js +90 -22
- package/dist/compiler/config.d.ts +1 -0
- package/dist/compiler/config.js +4 -0
- package/dist/compiler/custom-properties.d.ts +5 -0
- package/dist/compiler/custom-properties.js +150 -0
- package/dist/compiler/index.d.ts +3 -2
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/interpolate.d.ts +11 -0
- package/dist/compiler/interpolate.js +318 -0
- package/dist/compiler/libraries.d.ts +2 -0
- package/dist/compiler/libraries.js +9 -2
- package/dist/compiler/mapper.js +3 -1
- package/dist/compiler/stylesheet.js +9 -0
- package/dist/compiler/variables.d.ts +13 -0
- package/dist/compiler/variables.js +272 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/runtime-dev.d.ts +2 -0
- package/dist/runtime-dev.js +9 -0
- package/dist/runtime.d.ts +16 -0
- package/dist/runtime.js +105 -0
- package/package.json +11 -1
- package/package.schema.json +8 -0
|
@@ -3,9 +3,11 @@ import { dirname, relative, resolve as resolvePath, sep } from 'node:path';
|
|
|
3
3
|
import { transform } from 'lightningcss';
|
|
4
4
|
import SweatMap from 'sweatmap';
|
|
5
5
|
import { dropOverridden, parseBlock, PreStyleError, valueOf } from './block.js';
|
|
6
|
+
import { interpolate } from './interpolate.js';
|
|
6
7
|
import { print } from './print.js';
|
|
7
8
|
import { lvhfaRank, specificity, withClass } from './selectors.js';
|
|
8
9
|
import { KEYFRAMES_PREFIX } from './keyframes.js';
|
|
10
|
+
import { VARIABLE_PREFIX } from './variables.js';
|
|
9
11
|
import { MARKER_PREFIX, markerKey, parseStylesheet } from './stylesheet.js';
|
|
10
12
|
import { SHORTHANDS } from '../shorthands.js';
|
|
11
13
|
const RANGES = {
|
|
@@ -31,6 +33,16 @@ const QUIRKS_NAME_OPTIONS = {
|
|
|
31
33
|
cssSafe: true,
|
|
32
34
|
ranges: Object.fromEntries(Object.entries(RANGES).filter(([name]) => name !== 'Uppercase')),
|
|
33
35
|
};
|
|
36
|
+
// A block with `${…}`, from the text around its expressions and their source.
|
|
37
|
+
export function interpolatedBlock(block) {
|
|
38
|
+
const { strings, expressions, ...rest } = block;
|
|
39
|
+
try {
|
|
40
|
+
return { ...rest, ...interpolate(strings, expressions, block.lang) };
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw error instanceof PreStyleError ? locate(error, { ...rest, code: '' }) : error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
34
46
|
export async function createCompiler(options = {}) {
|
|
35
47
|
let sass = null;
|
|
36
48
|
try {
|
|
@@ -55,19 +67,22 @@ export class Compiler {
|
|
|
55
67
|
#root;
|
|
56
68
|
// One name list for every class, one for prefixes (the at-rules and
|
|
57
69
|
// selector variant a class is under), which only the `cn` table uses, and
|
|
58
|
-
// one for localized keyframes.
|
|
70
|
+
// one each for localized keyframes and runtime variables.
|
|
59
71
|
#names;
|
|
60
|
-
// How often each class, prefix,
|
|
61
|
-
// scanned blocks.
|
|
72
|
+
// How often each class, prefix, localized keyframes, and variable is used
|
|
73
|
+
// across scanned blocks.
|
|
62
74
|
#counts = {
|
|
63
75
|
atoms: new Map(),
|
|
64
76
|
prefixes: new Map(),
|
|
65
77
|
keyframes: new Map(),
|
|
78
|
+
variables: new Map(),
|
|
66
79
|
};
|
|
67
80
|
#atoms = new Map();
|
|
68
81
|
#globals = new Map();
|
|
69
82
|
// The current global for each overridable at-rule.
|
|
70
83
|
#overrides = new Map();
|
|
84
|
+
// Each runtime variable's custom property, or null for `--<its name>`.
|
|
85
|
+
#variables = new Map();
|
|
71
86
|
#order = 0;
|
|
72
87
|
constructor(options, sass) {
|
|
73
88
|
this.#options = options;
|
|
@@ -78,6 +93,8 @@ export class Compiler {
|
|
|
78
93
|
// four characters only after about 227,000 names, so they're never
|
|
79
94
|
// `none` or a CSS-wide keyword.
|
|
80
95
|
keyframes: new SweatMap(NAME_OPTIONS),
|
|
96
|
+
// Part of the classes generated at runtime, so these follow class names.
|
|
97
|
+
variables: new SweatMap(options.quirksMode ? QUIRKS_NAME_OPTIONS : NAME_OPTIONS),
|
|
81
98
|
};
|
|
82
99
|
this.#layer = layerRule(options.layer);
|
|
83
100
|
const root = options.root ?? process.cwd();
|
|
@@ -90,6 +107,7 @@ export class Compiler {
|
|
|
90
107
|
dir: dir + sep,
|
|
91
108
|
prepend: readPrepend(dir, library.prependFiles),
|
|
92
109
|
keyframeLocalization: library.keyframeLocalization ?? false,
|
|
110
|
+
customPropertyLocalization: library.customPropertyLocalization ?? false,
|
|
93
111
|
};
|
|
94
112
|
})
|
|
95
113
|
.toSorted((a, b) => b.dir.length - a.dir.length);
|
|
@@ -111,7 +129,9 @@ export class Compiler {
|
|
|
111
129
|
this.#register(block, parsed);
|
|
112
130
|
this.#count(this.#resolveBlock(block, parsed, []));
|
|
113
131
|
for (const global of parsed.globals)
|
|
114
|
-
this.#
|
|
132
|
+
this.#countNames(global.key);
|
|
133
|
+
for (const variable of parsed.variables ?? [])
|
|
134
|
+
count(this.#counts.variables, variable.key);
|
|
115
135
|
}
|
|
116
136
|
// Pass 1 of naming for a stylesheet file: counts what each class uses.
|
|
117
137
|
scanStylesheet(sheet) {
|
|
@@ -119,12 +139,12 @@ export class Compiler {
|
|
|
119
139
|
for (const name of parsed.classes.keys())
|
|
120
140
|
this.#count(this.#resolveClass(sheet.file, parsed, name, [], []));
|
|
121
141
|
for (const global of parsed.globals)
|
|
122
|
-
this.#
|
|
142
|
+
this.#countNames(global.key);
|
|
123
143
|
}
|
|
124
144
|
// Pass 2 of naming: gives the most-used entries the shortest names. Ties
|
|
125
145
|
// are broken by key, so names don't depend on the order files are read.
|
|
126
146
|
assignNames() {
|
|
127
|
-
for (const list of ['atoms', 'prefixes', 'keyframes']) {
|
|
147
|
+
for (const list of ['atoms', 'prefixes', 'keyframes', 'variables']) {
|
|
128
148
|
const sorted = [...this.#counts[list]].toSorted(([a, countA], [b, countB]) => countB - countA || (a < b ? -1 : a > b ? 1 : 0));
|
|
129
149
|
for (const [key] of sorted)
|
|
130
150
|
this.#names[list].set(key);
|
|
@@ -137,6 +157,20 @@ export class Compiler {
|
|
|
137
157
|
this.#register(block, parsed);
|
|
138
158
|
return this.#classString(this.#resolveBlock(block, parsed, []));
|
|
139
159
|
}
|
|
160
|
+
// The variables a block sets at runtime, in the order they're written.
|
|
161
|
+
variables(block) {
|
|
162
|
+
const parsed = this.#parse(block);
|
|
163
|
+
this.#register(block, parsed);
|
|
164
|
+
return (parsed.variables ?? []).map((variable) => {
|
|
165
|
+
const name = this.#names.variables.set(variable.key);
|
|
166
|
+
return {
|
|
167
|
+
name,
|
|
168
|
+
property: variable.property ? this.#propertyName(variable.property) : `--${name}`,
|
|
169
|
+
slot: block.slots[variable.slot],
|
|
170
|
+
selector: variable.selector,
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
140
174
|
// The files a block's class string depends on, through `composes … from`.
|
|
141
175
|
dependencies(block) {
|
|
142
176
|
const dependencies = [];
|
|
@@ -240,18 +274,29 @@ export class Compiler {
|
|
|
240
274
|
const key = prefixKey(record.atom);
|
|
241
275
|
const prefix = key === '' ? '' : this.#names.prefixes.set(key);
|
|
242
276
|
const properties = (table[prefix] ??= {});
|
|
243
|
-
const
|
|
277
|
+
const property = this.#propertyName(record.atom.property);
|
|
244
278
|
const name = this.#nameOf(record);
|
|
245
279
|
properties[property] = properties[property] ? `${properties[property]} ${name}` : name;
|
|
246
280
|
}
|
|
247
281
|
return table;
|
|
248
282
|
}
|
|
283
|
+
// The variables runtime values set, for `cn` to recognize generated classes.
|
|
284
|
+
cnVariables() {
|
|
285
|
+
const variables = {};
|
|
286
|
+
for (const [key, property] of this.#variables)
|
|
287
|
+
variables[this.#names.variables.set(key)] = property ? this.#propertyName(property) : '';
|
|
288
|
+
return variables;
|
|
289
|
+
}
|
|
249
290
|
// A library's blocks and files get its prepended files and keyframes
|
|
250
291
|
// setting instead of the app's.
|
|
251
292
|
#library(file) {
|
|
252
293
|
const library = file === undefined ? -1 : this.#libraries.findIndex(({ dir }) => file.startsWith(dir));
|
|
253
|
-
const { prepend, keyframeLocalization } = library === -1
|
|
254
|
-
? {
|
|
294
|
+
const { prepend, keyframeLocalization, customPropertyLocalization } = library === -1
|
|
295
|
+
? {
|
|
296
|
+
prepend: this.#prepend,
|
|
297
|
+
keyframeLocalization: this.#options.keyframeLocalization,
|
|
298
|
+
customPropertyLocalization: this.#options.customPropertyLocalization,
|
|
299
|
+
}
|
|
255
300
|
: this.#libraries[library];
|
|
256
301
|
return {
|
|
257
302
|
library,
|
|
@@ -259,6 +304,7 @@ export class Compiler {
|
|
|
259
304
|
...this.#parseOptions,
|
|
260
305
|
prepend,
|
|
261
306
|
keyframeLocalization: keyframeLocalization ?? false,
|
|
307
|
+
customPropertyLocalization: customPropertyLocalization ?? false,
|
|
262
308
|
},
|
|
263
309
|
};
|
|
264
310
|
}
|
|
@@ -377,7 +423,7 @@ export class Compiler {
|
|
|
377
423
|
const prefix = prefixKey(atom);
|
|
378
424
|
if (prefix !== '')
|
|
379
425
|
count(this.#counts.prefixes, prefix);
|
|
380
|
-
this.#
|
|
426
|
+
this.#countNames(atom.declaration.key);
|
|
381
427
|
}
|
|
382
428
|
for (const extra of resolved.extras)
|
|
383
429
|
if (extra.startsWith(MARKER_PREFIX))
|
|
@@ -389,12 +435,18 @@ export class Compiler {
|
|
|
389
435
|
names.push(extra.startsWith(MARKER_PREFIX) ? this.#names.atoms.set(extra) : extra);
|
|
390
436
|
return [...new Set(names)].join(' ');
|
|
391
437
|
}
|
|
392
|
-
// Counts the localized keyframes a JSON key names.
|
|
393
|
-
#
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
438
|
+
// Counts the localized keyframes and custom properties a JSON key names.
|
|
439
|
+
#countNames(json) {
|
|
440
|
+
if (json.includes(KEYFRAMES_KEY))
|
|
441
|
+
for (const [, hash] of json.matchAll(KEYFRAMES_KEY_RE))
|
|
442
|
+
count(this.#counts.keyframes, KEYFRAMES_PREFIX + hash);
|
|
443
|
+
if (json.includes(VARIABLE_KEY))
|
|
444
|
+
for (const [, hash] of json.matchAll(VARIABLE_KEY_RE))
|
|
445
|
+
count(this.#counts.variables, hash);
|
|
446
|
+
}
|
|
447
|
+
// A property's name in the output, for a custom property with a generated one.
|
|
448
|
+
#propertyName(property) {
|
|
449
|
+
return property.startsWith(VARIABLE_PREFIX) ? this.#named(property) : property;
|
|
398
450
|
}
|
|
399
451
|
// Replaces markers in selectors, and localized keyframes names, with their
|
|
400
452
|
// names.
|
|
@@ -402,6 +454,8 @@ export class Compiler {
|
|
|
402
454
|
const visit = (node) => {
|
|
403
455
|
if (Array.isArray(node))
|
|
404
456
|
return node.map(visit);
|
|
457
|
+
if (typeof node === 'string' && node.startsWith(VARIABLE_PREFIX))
|
|
458
|
+
return `--${this.#names.variables.set(node.slice(VARIABLE_PREFIX.length))}`;
|
|
405
459
|
if (node === null || typeof node !== 'object')
|
|
406
460
|
return node;
|
|
407
461
|
const record = node;
|
|
@@ -418,6 +472,13 @@ export class Compiler {
|
|
|
418
472
|
return visit(value);
|
|
419
473
|
}
|
|
420
474
|
#register(block, parsed) {
|
|
475
|
+
for (const variable of parsed.variables ?? []) {
|
|
476
|
+
if (this.#variables.has(variable.key))
|
|
477
|
+
continue;
|
|
478
|
+
this.#variables.set(variable.key, variable.property);
|
|
479
|
+
// A custom property set directly adds no atom or rule, but `cn` needs it.
|
|
480
|
+
this.#order++;
|
|
481
|
+
}
|
|
421
482
|
for (const atom of parsed.atoms) {
|
|
422
483
|
const key = atomKey(atom);
|
|
423
484
|
const source = sourceOf(block, atom.position);
|
|
@@ -483,7 +544,7 @@ export class Compiler {
|
|
|
483
544
|
}
|
|
484
545
|
#atomRule(record, loc) {
|
|
485
546
|
const { atom } = record;
|
|
486
|
-
const value = atom.declaration.key
|
|
547
|
+
const value = hasGenerated(atom.declaration.key)
|
|
487
548
|
? this.#named(atom.declaration.value)
|
|
488
549
|
: atom.declaration.value;
|
|
489
550
|
const declarations = atom.declaration.important
|
|
@@ -502,9 +563,11 @@ export class Compiler {
|
|
|
502
563
|
return this.#atomRule(record, loc(source));
|
|
503
564
|
});
|
|
504
565
|
for (const child of sortGroups([...group.children.values()])) {
|
|
566
|
+
const segment = child.segment;
|
|
567
|
+
const named = hasGenerated(segment.key) ? this.#named(segment.rule) : segment.rule;
|
|
505
568
|
rules.push({
|
|
506
|
-
type:
|
|
507
|
-
value: { ...valueOf(
|
|
569
|
+
type: segment.rule.type,
|
|
570
|
+
value: { ...valueOf(named), rules: emit(child) },
|
|
508
571
|
});
|
|
509
572
|
}
|
|
510
573
|
return rules;
|
|
@@ -524,7 +587,8 @@ export class Compiler {
|
|
|
524
587
|
className: record.className,
|
|
525
588
|
selector: code.slice(0, open).replaceAll('\n', ' '),
|
|
526
589
|
atRules: atom.atRules.map((segment) => {
|
|
527
|
-
const
|
|
590
|
+
const named = hasGenerated(segment.key) ? this.#named(segment.rule) : segment.rule;
|
|
591
|
+
const wrapped = { ...valueOf(named), rules: [rule] };
|
|
528
592
|
const printed = text({ type: segment.rule.type, value: wrapped });
|
|
529
593
|
return printed.slice(0, printed.indexOf(' {\n'));
|
|
530
594
|
}),
|
|
@@ -532,7 +596,7 @@ export class Compiler {
|
|
|
532
596
|
.slice(open + 3, code.lastIndexOf('\n}'))
|
|
533
597
|
.trim()
|
|
534
598
|
.replace(/;$/, ''),
|
|
535
|
-
property: atom.property,
|
|
599
|
+
property: this.#propertyName(atom.property),
|
|
536
600
|
covers: Object.hasOwn(SHORTHANDS, atom.property) ? SHORTHANDS[atom.property] : [],
|
|
537
601
|
important: atom.declaration.important,
|
|
538
602
|
specificity: specificity(this.#atomSelectors(record)),
|
|
@@ -545,10 +609,14 @@ export class Compiler {
|
|
|
545
609
|
// escape the `\0`.
|
|
546
610
|
const MARKER_KEY = JSON.stringify(MARKER_PREFIX).slice(1, -1);
|
|
547
611
|
const KEYFRAMES_KEY = JSON.stringify(KEYFRAMES_PREFIX).slice(1, -1);
|
|
612
|
+
const VARIABLE_KEY = JSON.stringify(VARIABLE_PREFIX).slice(1, -1);
|
|
613
|
+
const VARIABLE_KEY_RE = new RegExp(`${RegExp.escape(VARIABLE_KEY)}([\\w-]+)`, 'g');
|
|
548
614
|
const KEYFRAMES_KEY_RE = new RegExp(`${RegExp.escape(KEYFRAMES_KEY)}([\\w-]+)`, 'g');
|
|
549
615
|
// Rules that can't be inside a layer.
|
|
550
616
|
const outside = (rule) => rule.type === 'import' || rule.type === 'namespace';
|
|
551
|
-
|
|
617
|
+
// Whether a declaration has generated names.
|
|
618
|
+
const hasGenerated = (json) => json.includes(KEYFRAMES_KEY) || json.includes(VARIABLE_KEY);
|
|
619
|
+
const hasPlaceholder = (json) => json.includes(MARKER_KEY) || hasGenerated(json);
|
|
552
620
|
// Parses the `layer` option into an `@layer` block rule.
|
|
553
621
|
function layerRule(layer) {
|
|
554
622
|
if (layer === undefined || layer === false)
|
|
@@ -7,6 +7,7 @@ export interface PackageConfig {
|
|
|
7
7
|
libraries?: Record<string, LibraryConfig | boolean>;
|
|
8
8
|
quirksMode?: boolean;
|
|
9
9
|
keyframeLocalization?: boolean;
|
|
10
|
+
customPropertyLocalization?: boolean;
|
|
10
11
|
}
|
|
11
12
|
export declare function checkField(field: Record<string, unknown>, where: string, keys: Set<string>): string[];
|
|
12
13
|
export declare function readConfig(root: string): {
|
package/dist/compiler/config.js
CHANGED
|
@@ -7,6 +7,7 @@ const KEYS = new Set([
|
|
|
7
7
|
'libraries',
|
|
8
8
|
'quirksMode',
|
|
9
9
|
'keyframeLocalization',
|
|
10
|
+
'customPropertyLocalization',
|
|
10
11
|
]);
|
|
11
12
|
const isStrings = (value) => typeof value === 'string' ||
|
|
12
13
|
(Array.isArray(value) && value.every((item) => typeof item === 'string'));
|
|
@@ -31,6 +32,9 @@ export function checkField(field, where, keys) {
|
|
|
31
32
|
fail('quirksMode', '`true` or `false`');
|
|
32
33
|
if (field.keyframeLocalization !== undefined && typeof field.keyframeLocalization !== 'boolean')
|
|
33
34
|
fail('keyframeLocalization', '`true` or `false`');
|
|
35
|
+
if (field.customPropertyLocalization !== undefined &&
|
|
36
|
+
typeof field.customPropertyLocalization !== 'boolean')
|
|
37
|
+
fail('customPropertyLocalization', '`true` or `false`');
|
|
34
38
|
if (field.libraries !== undefined &&
|
|
35
39
|
(field.libraries === null ||
|
|
36
40
|
typeof field.libraries !== 'object' ||
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Atom, GlobalRule, Lang, ParseOptions } from './block.ts';
|
|
2
|
+
export declare function localizeCustomProperties(atoms: Atom[], globals: GlobalRule[], scope: string, lang: Lang, options: ParseOptions): {
|
|
3
|
+
globals: GlobalRule[];
|
|
4
|
+
atoms: (atoms: Atom[]) => Atom[];
|
|
5
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { key, overrideKey, parseRules, valueOf, walk } from './block.js';
|
|
3
|
+
import { VARIABLE_PREFIX } from './variables.js';
|
|
4
|
+
// With `customPropertyLocalization`, a custom property that a block or
|
|
5
|
+
// stylesheet file declares gets a generated name, used only in that block or
|
|
6
|
+
// file. In parsed rules it's a variable name, which the compiler names when
|
|
7
|
+
// printing, in the same list as the variables runtime values set. Names it only
|
|
8
|
+
// reads, and names from prepended files, which are how values are shared, stay
|
|
9
|
+
// global.
|
|
10
|
+
// Properties whose values name custom properties. Other dashed names, such as
|
|
11
|
+
// `anchor-name: --x` or `anchor(--x top)`, aren't custom properties.
|
|
12
|
+
const NAMING = new Set(['will-change', 'transition', 'transition-property']);
|
|
13
|
+
// Calls `declared` with each custom property a value declares.
|
|
14
|
+
function eachDeclared(value, declared) {
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
for (const item of value)
|
|
17
|
+
eachDeclared(item, declared);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (value === null || typeof value !== 'object')
|
|
21
|
+
return;
|
|
22
|
+
const node = value;
|
|
23
|
+
if (node.property === 'custom' && node.value?.name?.startsWith('--'))
|
|
24
|
+
declared(node.value.name);
|
|
25
|
+
if (node.type === 'property' && typeof node.value?.name === 'string')
|
|
26
|
+
declared(node.value.name);
|
|
27
|
+
for (const child of Object.values(node))
|
|
28
|
+
eachDeclared(child, declared);
|
|
29
|
+
}
|
|
30
|
+
// Replaces custom property names in a value. Returns `value` itself when nothing
|
|
31
|
+
// changed.
|
|
32
|
+
function rename(value, local, naming = false) {
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
const mapped = value.map((item) => rename(item, local, naming));
|
|
35
|
+
return (mapped.some((item, i) => item !== value[i]) ? mapped : value);
|
|
36
|
+
}
|
|
37
|
+
if (value === null || typeof value !== 'object')
|
|
38
|
+
return value;
|
|
39
|
+
const node = value;
|
|
40
|
+
// `var(--x)`.
|
|
41
|
+
const ident = node.ident;
|
|
42
|
+
// `transition-property`'s `{ property: '--x' }`.
|
|
43
|
+
const property = node.property;
|
|
44
|
+
// `will-change: --x`.
|
|
45
|
+
const dashed = naming && node.type === 'dashed-ident' ? node.value : undefined;
|
|
46
|
+
const isNaming = naming ||
|
|
47
|
+
(node.property === 'custom' && NAMING.has(node.value?.name)) ||
|
|
48
|
+
(node.property === 'unparsed' && NAMING.has(node.value?.propertyId?.property));
|
|
49
|
+
let result = node;
|
|
50
|
+
const set = (field, next) => {
|
|
51
|
+
if (result === node)
|
|
52
|
+
result = { ...node };
|
|
53
|
+
result[field] = next;
|
|
54
|
+
};
|
|
55
|
+
for (const [field, child] of Object.entries(node)) {
|
|
56
|
+
const next = rename(child, local, isNaming);
|
|
57
|
+
if (next !== child)
|
|
58
|
+
set(field, next);
|
|
59
|
+
}
|
|
60
|
+
if (typeof ident === 'string' && local.has(ident))
|
|
61
|
+
set('ident', local.get(ident));
|
|
62
|
+
if (typeof property === 'string' && local.has(property))
|
|
63
|
+
set('property', local.get(property));
|
|
64
|
+
if (typeof dashed === 'string' && local.has(dashed))
|
|
65
|
+
set('value', local.get(dashed));
|
|
66
|
+
// `--x: 1` and `@property --x`.
|
|
67
|
+
if ((node.property === 'custom' || node.type === 'property') && local.has(result.value?.name))
|
|
68
|
+
set('value', { ...result.value, name: local.get(result.value.name) });
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const prepended = new Map();
|
|
72
|
+
// The rules and custom properties prepended files output. They're parsed once
|
|
73
|
+
// more on their own, and only with this setting on, since within a block they
|
|
74
|
+
// can't be told apart from its own rules.
|
|
75
|
+
function prependedOf(lang, options) {
|
|
76
|
+
const code = options.prepend[lang];
|
|
77
|
+
const cacheKey = `${lang}\n${code}`;
|
|
78
|
+
let result = prepended.get(cacheKey);
|
|
79
|
+
if (result !== undefined)
|
|
80
|
+
return result;
|
|
81
|
+
result = { keys: new Set(), names: new Set() };
|
|
82
|
+
if (code.trim() !== '') {
|
|
83
|
+
const css = lang === 'scss' && options.sass !== null ? options.sass.compileString(code).css : code;
|
|
84
|
+
const parsed = { atoms: [], globals: [], composes: [] };
|
|
85
|
+
parseRules(css, options.lightningcss, () => null, (rules) => walk(rules, [], null, parsed, () => null));
|
|
86
|
+
for (const global of parsed.globals) {
|
|
87
|
+
result.keys.add(global.key);
|
|
88
|
+
eachDeclared(global.rule, (name) => result.names.add(name));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
prepended.set(cacheKey, result);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
// Localizes the custom properties a scope (a block, or a whole stylesheet file)
|
|
95
|
+
// declares. `scope` identifies it, and has to be stable between builds.
|
|
96
|
+
export function localizeCustomProperties(atoms, globals, scope, lang, options) {
|
|
97
|
+
const shared = prependedOf(lang, options);
|
|
98
|
+
const local = new Map();
|
|
99
|
+
const declared = (name) => {
|
|
100
|
+
if (shared.names.has(name) || local.has(name))
|
|
101
|
+
return;
|
|
102
|
+
const hash = createHash('sha256').update(`${scope}\n${name}`).digest('base64url').slice(0, 16);
|
|
103
|
+
local.set(name, VARIABLE_PREFIX + hash);
|
|
104
|
+
};
|
|
105
|
+
for (const atom of atoms)
|
|
106
|
+
eachDeclared(atom.declaration.value, declared);
|
|
107
|
+
const own = (global) => !shared.keys.has(global.key);
|
|
108
|
+
for (const global of globals)
|
|
109
|
+
if (own(global))
|
|
110
|
+
eachDeclared(global.rule, declared);
|
|
111
|
+
if (local.size === 0)
|
|
112
|
+
return { globals, atoms: (list) => list };
|
|
113
|
+
return {
|
|
114
|
+
globals: globals.map((global) => {
|
|
115
|
+
if (!own(global))
|
|
116
|
+
return global;
|
|
117
|
+
const rule = rename(global.rule, local);
|
|
118
|
+
if (rule === global.rule)
|
|
119
|
+
return global;
|
|
120
|
+
return {
|
|
121
|
+
...global,
|
|
122
|
+
key: key(rule),
|
|
123
|
+
rule,
|
|
124
|
+
overrideKey: global.overrideKey === null ? null : overrideKey(rule),
|
|
125
|
+
};
|
|
126
|
+
}),
|
|
127
|
+
atoms: (list) => list.map((atom) => {
|
|
128
|
+
const value = rename(atom.declaration.value, local);
|
|
129
|
+
const atRules = atom.atRules.map((segment) => {
|
|
130
|
+
const rule = rename(segment.rule, local);
|
|
131
|
+
return rule === segment.rule ? segment : { key: key(valueKey(rule)), rule };
|
|
132
|
+
});
|
|
133
|
+
const unchanged = value === atom.declaration.value && atRules.every((s, i) => s === atom.atRules[i]);
|
|
134
|
+
if (unchanged)
|
|
135
|
+
return atom;
|
|
136
|
+
return {
|
|
137
|
+
...atom,
|
|
138
|
+
atRules,
|
|
139
|
+
property: local.get(atom.property) ?? atom.property,
|
|
140
|
+
declaration: {
|
|
141
|
+
...atom.declaration,
|
|
142
|
+
key: key(value) + (atom.declaration.important ? '!' : ''),
|
|
143
|
+
value,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// The same key `walk` gives an at-rule.
|
|
150
|
+
const valueKey = (rule) => ({ type: rule.type, value: valueOf(rule) });
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { Compiler, createCompiler } from './compiler.ts';
|
|
2
|
-
export type { Block, ClassUsage, CompiledStylesheet, CompilerOptions, Library, PrependFiles, Source, Stylesheet, } from './compiler.ts';
|
|
1
|
+
export { Compiler, createCompiler, interpolatedBlock } from './compiler.ts';
|
|
2
|
+
export type { Block, BlockVariable, ClassUsage, CompiledStylesheet, CompilerOptions, Library, PrependFiles, Source, Stylesheet, } from './compiler.ts';
|
|
3
3
|
export { PreStyleError } from './block.ts';
|
|
4
4
|
export type { Lang, LightningOptions, Position } from './block.ts';
|
|
5
|
+
export type { Slot } from './interpolate.ts';
|
|
5
6
|
export type { Printed } from './print.ts';
|
|
6
7
|
export { checkField, readConfig } from './config.ts';
|
|
7
8
|
export type { PackageConfig } from './config.ts';
|
package/dist/compiler/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Compiler, createCompiler } from './compiler.js';
|
|
1
|
+
export { Compiler, createCompiler, interpolatedBlock } from './compiler.js';
|
|
2
2
|
export { PreStyleError } from './block.js';
|
|
3
3
|
export { checkField, readConfig } from './config.js';
|
|
4
4
|
export { findLibraries } from './libraries.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Lang } from './block.ts';
|
|
2
|
+
export interface Slot {
|
|
3
|
+
strings: string[];
|
|
4
|
+
expressions: number[];
|
|
5
|
+
}
|
|
6
|
+
export interface Interpolated {
|
|
7
|
+
code: string;
|
|
8
|
+
slots: Slot[];
|
|
9
|
+
}
|
|
10
|
+
export declare const SLOT_PREFIX = "--__pre-style-slot-";
|
|
11
|
+
export declare function interpolate(strings: string[], expressions: string[], lang: Lang): Interpolated;
|