@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.
@@ -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, and localized keyframes is used across
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.#countKeyframes(global.key);
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.#countKeyframes(global.key);
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 { property } = record.atom;
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
- ? { prepend: this.#prepend, keyframeLocalization: this.#options.keyframeLocalization }
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.#countKeyframes(atom.declaration.key);
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
- #countKeyframes(json) {
394
- if (!json.includes(KEYFRAMES_KEY))
395
- return;
396
- for (const [, hash] of json.matchAll(KEYFRAMES_KEY_RE))
397
- count(this.#counts.keyframes, KEYFRAMES_PREFIX + hash);
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.includes(KEYFRAMES_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: child.segment.rule.type,
507
- value: { ...valueOf(child.segment.rule), rules: emit(child) },
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 wrapped = { ...valueOf(segment.rule), rules: [rule] };
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
- const hasPlaceholder = (json) => json.includes(MARKER_KEY) || json.includes(KEYFRAMES_KEY);
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): {
@@ -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) });
@@ -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';
@@ -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;