@unocss/core 0.4.15 → 0.6.0

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/dist/index.d.ts CHANGED
@@ -7,14 +7,14 @@ declare class UnoGenerator {
7
7
  constructor(userConfig?: UserConfig, defaults?: UserConfigDefaults);
8
8
  setConfig(userConfig?: UserConfig, defaults?: UserConfigDefaults): void;
9
9
  applyExtractors(code: string, id?: string, set?: Set<string>): Promise<Set<string>>;
10
- generate(input: string | Set<string>, id?: string, scope?: string): Promise<GenerateResult>;
10
+ generate(input: string | Set<string>, { id, scope, preflights, layerComments, }?: GenerateOptions): Promise<GenerateResult>;
11
11
  matchVariants(raw: string): VariantMatchedResult;
12
12
  applyVariants(parsed: ParsedUtil, variantHandlers?: VariantHandler[], raw?: string): readonly [string, CSSEntries, string | undefined];
13
13
  constructCustomCSS(context: Readonly<RuleContext>, body: CSSObject | CSSEntries, overrideSelector?: string): string;
14
14
  parseUtil(input: string | VariantMatchedResult): Promise<ParsedUtil | RawUtil | undefined>;
15
15
  stringifyUtil(parsed?: ParsedUtil | RawUtil): StringifiedUtil | undefined;
16
- expandShortcut(processed: string, depth?: number): string[] | undefined;
17
- stringifyShortcuts(parent: VariantMatchedResult, expanded: string[]): Promise<StringifiedUtil[]>;
16
+ expandShortcut(processed: string, depth?: number): [string[], RuleMeta | undefined] | undefined;
17
+ stringifyShortcuts(parent: VariantMatchedResult, expanded: string[], meta?: RuleMeta): Promise<StringifiedUtil[] | undefined>;
18
18
  isExcluded(raw: string): boolean;
19
19
  }
20
20
  declare function createGenerator(config?: UserConfig, defaults?: UserConfigDefaults): UnoGenerator;
@@ -57,15 +57,33 @@ interface RuleContext<Theme extends {} = {}> {
57
57
  */
58
58
  constructCSS: (body: CSSEntries | CSSObject, overrideSelector?: string) => string;
59
59
  }
60
- declare type Extractor = (code: string, id?: string) => Awaitable<Set<string> | undefined>;
61
- declare type DynamicRule<Theme extends {} = {}> = [RegExp, ((match: string[], context: Readonly<RuleContext<Theme>>) => Awaitable<CSSObject | CSSEntries | string | undefined>)];
62
- declare type StaticRule = [string, CSSObject | CSSEntries];
60
+ interface ExtractorContext {
61
+ readonly original: string;
62
+ code: string;
63
+ id?: string;
64
+ }
65
+ interface Extractor {
66
+ name: string;
67
+ extract(ctx: ExtractorContext): Awaitable<Set<string> | undefined>;
68
+ order?: number;
69
+ }
70
+ interface RuleMeta {
71
+ layer?: string;
72
+ }
73
+ declare type DynamicMatcher<Theme extends {} = {}> = ((match: string[], context: Readonly<RuleContext<Theme>>) => Awaitable<CSSObject | CSSEntries | string | undefined>);
74
+ declare type DynamicRule<Theme extends {} = {}> = [RegExp, DynamicMatcher<Theme>] | [RegExp, DynamicMatcher<Theme>, RuleMeta];
75
+ declare type StaticRule = [string, CSSObject | CSSEntries] | [string, CSSObject | CSSEntries, RuleMeta];
63
76
  declare type Rule<Theme extends {} = {}> = DynamicRule<Theme> | StaticRule;
64
- declare type DynamicShortcut = [RegExp, ((match: string[]) => (string | string[] | undefined))];
65
- declare type StaticShortcut = [string, string | string[]];
77
+ declare type DynamicShortcutMatcher = ((match: string[]) => (string | string[] | undefined));
78
+ declare type DynamicShortcut = [RegExp, DynamicShortcutMatcher] | [RegExp, DynamicShortcutMatcher, RuleMeta];
79
+ declare type StaticShortcut = [string, string | string[]] | [string, string | string[], RuleMeta];
66
80
  declare type StaticShortcutMap = Record<string, string | string[]>;
67
81
  declare type UserShortcuts = StaticShortcutMap | (StaticShortcut | DynamicShortcut | StaticShortcutMap)[];
68
82
  declare type Shortcut = StaticShortcut | DynamicShortcut;
83
+ interface Preflight {
84
+ getCSS: () => string | undefined;
85
+ layer?: string;
86
+ }
69
87
  declare type ExcludeRule = string | RegExp;
70
88
  interface VariantHandler {
71
89
  /**
@@ -124,10 +142,22 @@ interface ConfigBase<Theme extends {} = {}> {
124
142
  * Can be language-aware.
125
143
  */
126
144
  extractors?: Extractor[];
145
+ /**
146
+ * Raw CSS injections.
147
+ */
148
+ preflights?: Preflight[];
127
149
  /**
128
150
  * Theme object for shared configuration between rules
129
151
  */
130
152
  theme?: Theme;
153
+ /**
154
+ * Layer orders. Default to 0.
155
+ */
156
+ layers?: Record<string, number>;
157
+ /**
158
+ * Custom function to sort layers.
159
+ */
160
+ sortLayers?: (layers: string[]) => string[];
131
161
  }
132
162
  interface Preset extends ConfigBase {
133
163
  enforce?: 'pre' | 'post';
@@ -146,46 +176,92 @@ interface GeneratorOptions {
146
176
  */
147
177
  warnExcluded?: boolean;
148
178
  }
149
- interface UserConfig<Theme extends {} = {}> extends ConfigBase<Theme>, GeneratorOptions {
179
+ interface UserOnlyOptions<Theme extends {} = {}> {
180
+ /**
181
+ * The theme object, will be merged with the theme provides by presets
182
+ */
150
183
  theme?: Theme;
184
+ /**
185
+ * Layout name of shortcuts
186
+ *
187
+ * @default 'shortcuts'
188
+ */
189
+ shortcutsLayer?: string;
190
+ /**
191
+ * Presets
192
+ */
151
193
  presets?: Preset[];
194
+ /**
195
+ * Environment mode
196
+ *
197
+ * @default 'build'
198
+ */
199
+ envMode?: 'dev' | 'build';
152
200
  }
153
- interface UserConfigDefaults<Theme extends {} = {}> extends ConfigBase<Theme> {
154
- theme?: Theme;
155
- presets?: Preset[];
201
+ interface UserConfig<Theme extends {} = {}> extends ConfigBase<Theme>, UserOnlyOptions<Theme>, GeneratorOptions {
202
+ }
203
+ interface UserConfigDefaults<Theme extends {} = {}> extends ConfigBase<Theme>, UserOnlyOptions<Theme> {
156
204
  }
157
205
  interface ResolvedConfig extends Omit<Required<UserConfig>, 'presets' | 'rules' | 'shortcuts'> {
158
206
  shortcuts: Shortcut[];
159
207
  variants: VariantObject[];
160
208
  rulesSize: number;
161
209
  rulesDynamic: (DynamicRule | undefined)[];
162
- rulesStaticMap: Record<string, [number, CSSObject | CSSEntries] | undefined>;
210
+ rulesStaticMap: Record<string, [number, CSSObject | CSSEntries, RuleMeta | undefined] | undefined>;
163
211
  }
164
212
  interface GenerateResult {
165
213
  css: string;
214
+ layers: string[];
215
+ getLayer(name?: string): string | undefined;
216
+ getLayers(excludes?: string[]): string;
166
217
  matched: Set<string>;
167
218
  }
168
219
  declare type VariantMatchedResult = readonly [
169
- string,
170
- string,
171
- VariantHandler[]
220
+ raw: string,
221
+ current: string,
222
+ variants: VariantHandler[]
172
223
  ];
173
224
  declare type ParsedUtil = readonly [
174
- number,
175
- string,
176
- CSSEntries,
177
- VariantHandler[]
225
+ index: number,
226
+ raw: string,
227
+ entries: CSSEntries,
228
+ meta: RuleMeta | undefined,
229
+ variants: VariantHandler[]
178
230
  ];
179
231
  declare type RawUtil = readonly [
180
- number,
181
- string
232
+ index: number,
233
+ rawCSS: string,
234
+ meta: RuleMeta | undefined
182
235
  ];
183
236
  declare type StringifiedUtil = readonly [
184
- number,
185
- string | undefined,
186
- string,
187
- string | undefined
237
+ index: number,
238
+ selector: string | undefined,
239
+ body: string,
240
+ mediaQuery: string | undefined,
241
+ meta: RuleMeta | undefined
188
242
  ];
243
+ interface GenerateOptions {
244
+ /**
245
+ * Filepath of the file being processed.
246
+ */
247
+ id?: string;
248
+ /**
249
+ * Generate preflights (if defined)
250
+ *
251
+ * @default true
252
+ */
253
+ preflights?: boolean;
254
+ /**
255
+ * @expiremental
256
+ */
257
+ scope?: string;
258
+ /**
259
+ * Show layer seperator in comments
260
+ *
261
+ * @default true
262
+ */
263
+ layerComments?: boolean;
264
+ }
189
265
 
190
266
  declare function escapeRegExp(string: string): string;
191
267
  /**
@@ -227,6 +303,8 @@ declare class BetterMap<K, V> extends Map<K, V> {
227
303
  map<R>(mapFn: (value: V, key: K) => R): R[];
228
304
  }
229
305
 
306
+ declare function withLayer<T>(layer: string, rules: Rule<T>[]): Rule<T>[];
307
+
230
308
  declare const extractorSplit: Extractor;
231
309
 
232
- export { ArgumentType, Awaitable, BetterMap, CSSEntries, CSSObject, ConfigBase, DeepPartial, DynamicRule, DynamicShortcut, ExcludeRule, Extractor, GenerateResult, GeneratorOptions, ParsedUtil, Preset, RawUtil, ResolvedConfig, RestArgs, Rule, RuleContext, Shift, Shortcut, StaticRule, StaticShortcut, StaticShortcutMap, StringifiedUtil, TwoKeyMap, UnoGenerator, UserConfig, UserConfigDefaults, UserShortcuts, Variant, VariantFunction, VariantHandler, VariantMatchedResult, VariantObject, attributifyRE, createGenerator, e, entriesToCss, escapeRegExp, escapeSelector, extractorSplit, hasScopePlaceholder, hex2rgba, isAttributifySelector, isObject, isRawUtil, isStaticRule, isStaticShortcut, isValidSelector, mergeDeep, mergeSet, normalizeVariant, toArray, uniq, validateFilterRE };
310
+ export { ArgumentType, Awaitable, BetterMap, CSSEntries, CSSObject, ConfigBase, DeepPartial, DynamicMatcher, DynamicRule, DynamicShortcut, DynamicShortcutMatcher, ExcludeRule, Extractor, ExtractorContext, GenerateOptions, GenerateResult, GeneratorOptions, ParsedUtil, Preflight, Preset, RawUtil, ResolvedConfig, RestArgs, Rule, RuleContext, RuleMeta, Shift, Shortcut, StaticRule, StaticShortcut, StaticShortcutMap, StringifiedUtil, TwoKeyMap, UnoGenerator, UserConfig, UserConfigDefaults, UserOnlyOptions, UserShortcuts, Variant, VariantFunction, VariantHandler, VariantMatchedResult, VariantObject, attributifyRE, createGenerator, e, entriesToCss, escapeRegExp, escapeSelector, extractorSplit, hasScopePlaceholder, hex2rgba, isAttributifySelector, isObject, isRawUtil, isStaticRule, isStaticShortcut, isValidSelector, mergeDeep, mergeSet, normalizeVariant, toArray, uniq, validateFilterRE, withLayer };
package/dist/index.js CHANGED
@@ -31,7 +31,8 @@ __export(exports, {
31
31
  normalizeVariant: () => normalizeVariant,
32
32
  toArray: () => toArray,
33
33
  uniq: () => uniq,
34
- validateFilterRE: () => validateFilterRE
34
+ validateFilterRE: () => validateFilterRE,
35
+ withLayer: () => withLayer
35
36
  });
36
37
 
37
38
  // src/utils/escape.ts
@@ -147,7 +148,7 @@ function hex2rgba(hex = "") {
147
148
 
148
149
  // src/utils/helpers.ts
149
150
  var attributifyRE = /^\[(.+?)~?="(.*)"\]$/;
150
- var validateFilterRE = /[a-z]/;
151
+ var validateFilterRE = /[a-z?]/;
151
152
  function isAttributifySelector(selector) {
152
153
  return selector.match(attributifyRE);
153
154
  }
@@ -158,7 +159,7 @@ function normalizeVariant(variant) {
158
159
  return typeof variant === "function" ? { match: variant } : variant;
159
160
  }
160
161
  function isRawUtil(util) {
161
- return util.length === 2;
162
+ return util.length === 3;
162
163
  }
163
164
 
164
165
  // src/utils/map.ts
@@ -217,8 +218,25 @@ var BetterMap = class extends Map {
217
218
  }
218
219
  };
219
220
 
221
+ // src/utils/layer.ts
222
+ function withLayer(layer, rules) {
223
+ rules.forEach((r) => {
224
+ if (!r[2])
225
+ r[2] = { layer };
226
+ else
227
+ r[2].layer = layer;
228
+ });
229
+ return rules;
230
+ }
231
+
220
232
  // src/extractors/split.ts
221
- var extractorSplit = (code) => new Set(code.split(/[\s'"`;>=]+/g).filter(isValidSelector));
233
+ var extractorSplit = {
234
+ name: "split",
235
+ order: 0,
236
+ extract({ code }) {
237
+ return new Set(code.split(/[\s'"`;>=]+/g).filter(isValidSelector));
238
+ }
239
+ };
222
240
 
223
241
  // src/config.ts
224
242
  function resolveShortcuts(shortcuts) {
@@ -228,6 +246,10 @@ function resolveShortcuts(shortcuts) {
228
246
  return Object.entries(s);
229
247
  });
230
248
  }
249
+ var defaultLayers = {
250
+ shortcuts: -1,
251
+ default: 0
252
+ };
231
253
  function resolveConfig(userConfig = {}, defaults = {}) {
232
254
  const config = Object.assign({}, defaults, userConfig);
233
255
  const rawPresets = config.presets || [];
@@ -236,6 +258,7 @@ function resolveConfig(userConfig = {}, defaults = {}) {
236
258
  ...rawPresets.filter((p) => !p.enforce),
237
259
  ...rawPresets.filter((p) => p.enforce === "post")
238
260
  ];
261
+ const layers = Object.assign(defaultLayers, ...rawPresets.map((i) => i.layers), userConfig.layers);
239
262
  function mergePresets(key) {
240
263
  return uniq([
241
264
  ...sortedPresets.flatMap((p) => toArray(p[key] || [])),
@@ -245,12 +268,13 @@ function resolveConfig(userConfig = {}, defaults = {}) {
245
268
  const extractors = mergePresets("extractors");
246
269
  if (!extractors.length)
247
270
  extractors.push(extractorSplit);
271
+ extractors.sort((a, b) => (a.order || 0) - (b.order || 0));
248
272
  const rules = mergePresets("rules");
249
273
  const rulesStaticMap = {};
250
274
  const rulesSize = rules.length;
251
275
  rules.forEach((rule, i) => {
252
276
  if (isStaticRule(rule)) {
253
- rulesStaticMap[rule[0]] = [i, rule[1]];
277
+ rulesStaticMap[rule[0]] = [i, rule[1], rule[2]];
254
278
  delete rules[i];
255
279
  }
256
280
  });
@@ -262,11 +286,16 @@ function resolveConfig(userConfig = {}, defaults = {}) {
262
286
  mergeSelectors: true,
263
287
  warnExcluded: true,
264
288
  excluded: [],
289
+ sortLayers: (layers2) => layers2,
265
290
  ...config,
291
+ envMode: config.envMode || "build",
292
+ shortcutsLayer: config.shortcutsLayer || "shortcuts",
293
+ layers,
266
294
  theme,
267
295
  rulesSize,
268
296
  rulesDynamic: rules,
269
297
  rulesStaticMap,
298
+ preflights: mergePresets("preflights"),
270
299
  variants: mergePresets("variants").map(normalizeVariant),
271
300
  shortcuts: resolveShortcuts(mergePresets("shortcuts")),
272
301
  extractors
@@ -293,17 +322,31 @@ var UnoGenerator = class {
293
322
  this._cache = new Map();
294
323
  }
295
324
  async applyExtractors(code, id, set = new Set()) {
296
- await Promise.all(this.config.extractors.map(async (i) => {
297
- const result = await i(code, id);
325
+ const context = {
326
+ get original() {
327
+ return code;
328
+ },
329
+ code,
330
+ id
331
+ };
332
+ for (const extractor of this.config.extractors) {
333
+ const result = await extractor.extract(context);
298
334
  result == null ? void 0 : result.forEach((t) => set.add(t));
299
- }));
335
+ }
300
336
  return set;
301
337
  }
302
- async generate(input, id, scope) {
338
+ async generate(input, {
339
+ id,
340
+ scope,
341
+ preflights = true,
342
+ layerComments = true
343
+ } = {}) {
303
344
  const tokens = typeof input === "string" ? await this.applyExtractors(input, id) : input;
345
+ const layerSet = new Set(["default"]);
304
346
  const matched = new Set();
305
347
  const sheet = new Map();
306
348
  const hit = (raw, payload) => {
349
+ var _a;
307
350
  this._cache.set(raw, payload);
308
351
  matched.add(raw);
309
352
  for (const item of payload) {
@@ -311,6 +354,8 @@ var UnoGenerator = class {
311
354
  if (!sheet.has(query))
312
355
  sheet.set(query, []);
313
356
  sheet.get(query).push(item);
357
+ if ((_a = item[4]) == null ? void 0 : _a.layer)
358
+ layerSet.add(item[4].layer);
314
359
  }
315
360
  };
316
361
  await Promise.all(Array.from(tokens).map(async (raw) => {
@@ -334,9 +379,9 @@ var UnoGenerator = class {
334
379
  return;
335
380
  }
336
381
  const expanded = this.expandShortcut(applied[1]);
337
- if (expanded == null ? void 0 : expanded.length) {
338
- const utils = await this.stringifyShortcuts(applied, expanded);
339
- if (utils.length) {
382
+ if (expanded) {
383
+ const utils = await this.stringifyShortcuts(applied, expanded[0], expanded[1]);
384
+ if (utils == null ? void 0 : utils.length) {
340
385
  hit(raw, utils);
341
386
  return;
342
387
  }
@@ -349,30 +394,66 @@ var UnoGenerator = class {
349
394
  }
350
395
  this._cache.set(raw, null);
351
396
  }));
352
- const css = Array.from(sheet).map(([query, items]) => {
353
- const size = items.length;
354
- const sorted = items.sort((a, b) => {
355
- var _a;
356
- return a[0] - b[0] || ((_a = a[1]) == null ? void 0 : _a.localeCompare(b[1] || "")) || 0;
357
- }).map((a) => [a[1] ? applyScope(a[1], scope) : a[1], a[2]]);
358
- const rules = sorted.map(([selector, body], idx) => {
359
- if (selector && this.config.mergeSelectors) {
360
- for (let i = size - 1; i > idx; i--) {
361
- const current = sorted[i];
362
- if (current[0] && current[1] === body) {
363
- current[0] = `${selector},${current[0]}`;
364
- return null;
397
+ if (preflights) {
398
+ this.config.preflights.forEach((i) => {
399
+ if (i.layer)
400
+ layerSet.add(i.layer);
401
+ });
402
+ }
403
+ const layerCache = {};
404
+ const layers = this.config.sortLayers(Array.from(layerSet).sort((a, b) => {
405
+ var _a, _b;
406
+ return ((_a = this.config.layers[a]) != null ? _a : 0) - ((_b = this.config.layers[b]) != null ? _b : 0) || a.localeCompare(b);
407
+ }));
408
+ const getLayer = (layer) => {
409
+ if (layerCache[layer])
410
+ return layerCache[layer];
411
+ let css = Array.from(sheet).map(([query, items]) => {
412
+ const size = items.length;
413
+ const sorted = items.filter((i) => {
414
+ var _a;
415
+ return (((_a = i[4]) == null ? void 0 : _a.layer) || "default") === layer;
416
+ }).sort((a, b) => {
417
+ var _a;
418
+ return a[0] - b[0] || ((_a = a[1]) == null ? void 0 : _a.localeCompare(b[1] || "")) || 0;
419
+ }).map((a) => [a[1] ? applyScope(a[1], scope) : a[1], a[2]]);
420
+ if (!sorted.length)
421
+ return void 0;
422
+ const rules = sorted.map(([selector, body], idx) => {
423
+ if (selector && this.config.mergeSelectors) {
424
+ for (let i = size - 1; i > idx; i--) {
425
+ const current = sorted[i];
426
+ if (current && current[0] && current[1] === body) {
427
+ current[0] = `${selector},${current[0]}`;
428
+ return null;
429
+ }
365
430
  }
366
431
  }
367
- }
368
- return selector ? `${selector}{${body}}` : body;
369
- }).filter(Boolean).join("\n");
370
- return query ? `${query}{
432
+ return selector ? `${selector}{${body}}` : body;
433
+ }).filter(Boolean).join("\n");
434
+ return query ? `${query}{
371
435
  ${rules}
372
436
  }` : rules;
373
- }).join("\n");
437
+ }).filter(Boolean).join("\n");
438
+ if (preflights) {
439
+ css = [
440
+ ...this.config.preflights.filter((i) => (i.layer || "default") === layer).map((i) => i.getCSS()).filter(Boolean),
441
+ css
442
+ ].join("\n");
443
+ }
444
+ return layerCache[layer] = layerComments && css ? `/* layer: ${layer} */
445
+ ${css}` : css;
446
+ };
447
+ const getLayers = (excludes) => {
448
+ return layers.filter((i) => !(excludes == null ? void 0 : excludes.includes(i))).map((i) => getLayer(i) || "").filter(Boolean).join("\n");
449
+ };
374
450
  return {
375
- css,
451
+ get css() {
452
+ return getLayers();
453
+ },
454
+ layers,
455
+ getLayers,
456
+ getLayer,
376
457
  matched
377
458
  };
378
459
  }
@@ -406,7 +487,7 @@ ${rules}
406
487
  }
407
488
  return [raw, processed, handlers];
408
489
  }
409
- applyVariants(parsed, variantHandlers = parsed[3], raw = parsed[1]) {
490
+ applyVariants(parsed, variantHandlers = parsed[4], raw = parsed[1]) {
410
491
  const selector = variantHandlers.reduce((p, v) => {
411
492
  var _a;
412
493
  return ((_a = v.selector) == null ? void 0 : _a.call(v, p)) || p;
@@ -424,7 +505,7 @@ ${rules}
424
505
  }
425
506
  constructCustomCSS(context, body, overrideSelector) {
426
507
  body = normalizeEntries(body);
427
- const [selector, entries, mediaQuery] = this.applyVariants([0, overrideSelector || context.rawSelector, body, context.variantHandlers]);
508
+ const [selector, entries, mediaQuery] = this.applyVariants([0, overrideSelector || context.rawSelector, body, void 0, context.variantHandlers]);
428
509
  const cssBody = `${selector}{${entriesToCss(entries)}}`;
429
510
  if (mediaQuery)
430
511
  return `${mediaQuery}{${cssBody}}`;
@@ -435,7 +516,7 @@ ${rules}
435
516
  const [raw, processed, variantHandlers] = typeof input === "string" ? this.matchVariants(input) : input;
436
517
  const staticMatch = rulesStaticMap[processed];
437
518
  if (staticMatch == null ? void 0 : staticMatch[1])
438
- return [staticMatch[0], raw, normalizeEntries(staticMatch[1]), variantHandlers];
519
+ return [staticMatch[0], raw, normalizeEntries(staticMatch[1]), staticMatch[2], variantHandlers];
439
520
  const context = {
440
521
  rawSelector: raw,
441
522
  currentSelector: processed,
@@ -448,35 +529,37 @@ ${rules}
448
529
  const rule = rulesDynamic[i];
449
530
  if (!rule)
450
531
  continue;
451
- const [matcher, handler] = rule;
532
+ const [matcher, handler, meta] = rule;
452
533
  const match = processed.match(matcher);
453
534
  if (!match)
454
535
  continue;
455
536
  const result = await handler(match, context);
456
537
  if (typeof result === "string")
457
- return [i, result];
538
+ return [i, result, meta];
458
539
  if (result)
459
- return [i, raw, normalizeEntries(result), variantHandlers];
540
+ return [i, raw, normalizeEntries(result), meta, variantHandlers];
460
541
  }
461
542
  }
462
543
  stringifyUtil(parsed) {
463
544
  if (!parsed)
464
545
  return;
465
546
  if (isRawUtil(parsed))
466
- return [parsed[0], void 0, parsed[1], void 0];
547
+ return [parsed[0], void 0, parsed[1], void 0, parsed[2]];
467
548
  const [selector, entries, mediaQuery] = this.applyVariants(parsed);
468
549
  const body = entriesToCss(entries);
469
550
  if (!body)
470
551
  return;
471
- return [parsed[0], selector, body, mediaQuery];
552
+ return [parsed[0], selector, body, mediaQuery, parsed[3]];
472
553
  }
473
554
  expandShortcut(processed, depth = 3) {
474
555
  if (depth === 0)
475
556
  return;
557
+ let meta;
476
558
  let result;
477
559
  for (const s of this.config.shortcuts) {
478
560
  if (isStaticShortcut(s)) {
479
561
  if (s[0] === processed) {
562
+ meta = meta || s[2];
480
563
  result = s[1];
481
564
  break;
482
565
  }
@@ -484,22 +567,32 @@ ${rules}
484
567
  const match = processed.match(s[0]);
485
568
  if (match)
486
569
  result = s[1](match);
487
- if (result)
570
+ if (result) {
571
+ meta = meta || s[2];
488
572
  break;
573
+ }
489
574
  }
490
575
  }
491
576
  if (!result)
492
577
  return;
493
578
  if (typeof result === "string")
494
579
  result = result.split(/ /g);
495
- return result.flatMap((r) => this.expandShortcut(r, depth - 1) || [r]);
580
+ return [
581
+ result.flatMap((r) => {
582
+ var _a;
583
+ return ((_a = this.expandShortcut(r, depth - 1)) == null ? void 0 : _a[0]) || [r];
584
+ }),
585
+ meta
586
+ ];
496
587
  }
497
- async stringifyShortcuts(parent, expanded) {
588
+ async stringifyShortcuts(parent, expanded, meta = { layer: this.config.shortcutsLayer }) {
498
589
  const selectorMap = new TwoKeyMap();
499
590
  const parsed = (await Promise.all(uniq(expanded).map((i) => this.parseUtil(i)))).filter(Boolean).sort((a, b) => a[0] - b[0]);
500
591
  const [raw, , parentVariants] = parent;
501
592
  for (const item of parsed) {
502
- const [selector, entries, mediaQuery] = this.applyVariants(item, [...item[3], ...parentVariants], raw);
593
+ if (isRawUtil(item))
594
+ continue;
595
+ const [selector, entries, mediaQuery] = this.applyVariants(item, [...item[4], ...parentVariants], raw);
503
596
  const mapItem = selectorMap.getFallback(selector, mediaQuery, [[], item[0]]);
504
597
  mapItem[0].push(...entries);
505
598
  if (item[0] > mapItem[1])
@@ -507,9 +600,9 @@ ${rules}
507
600
  }
508
601
  return selectorMap.map(([entries, index], selector, mediaQuery) => {
509
602
  const body = entriesToCss(entries);
510
- if (!body)
511
- return void 0;
512
- return [index, selector, body, mediaQuery];
603
+ if (body)
604
+ return [index, selector, body, mediaQuery, meta];
605
+ return void 0;
513
606
  }).filter(Boolean);
514
607
  }
515
608
  isExcluded(raw) {
@@ -561,5 +654,6 @@ function normalizeEntries(obj) {
561
654
  normalizeVariant,
562
655
  toArray,
563
656
  uniq,
564
- validateFilterRE
657
+ validateFilterRE,
658
+ withLayer
565
659
  });
package/dist/index.mjs CHANGED
@@ -111,7 +111,7 @@ function hex2rgba(hex = "") {
111
111
 
112
112
  // src/utils/helpers.ts
113
113
  var attributifyRE = /^\[(.+?)~?="(.*)"\]$/;
114
- var validateFilterRE = /[a-z]/;
114
+ var validateFilterRE = /[a-z?]/;
115
115
  function isAttributifySelector(selector) {
116
116
  return selector.match(attributifyRE);
117
117
  }
@@ -122,7 +122,7 @@ function normalizeVariant(variant) {
122
122
  return typeof variant === "function" ? { match: variant } : variant;
123
123
  }
124
124
  function isRawUtil(util) {
125
- return util.length === 2;
125
+ return util.length === 3;
126
126
  }
127
127
 
128
128
  // src/utils/map.ts
@@ -181,8 +181,25 @@ var BetterMap = class extends Map {
181
181
  }
182
182
  };
183
183
 
184
+ // src/utils/layer.ts
185
+ function withLayer(layer, rules) {
186
+ rules.forEach((r) => {
187
+ if (!r[2])
188
+ r[2] = { layer };
189
+ else
190
+ r[2].layer = layer;
191
+ });
192
+ return rules;
193
+ }
194
+
184
195
  // src/extractors/split.ts
185
- var extractorSplit = (code) => new Set(code.split(/[\s'"`;>=]+/g).filter(isValidSelector));
196
+ var extractorSplit = {
197
+ name: "split",
198
+ order: 0,
199
+ extract({ code }) {
200
+ return new Set(code.split(/[\s'"`;>=]+/g).filter(isValidSelector));
201
+ }
202
+ };
186
203
 
187
204
  // src/config.ts
188
205
  function resolveShortcuts(shortcuts) {
@@ -192,6 +209,10 @@ function resolveShortcuts(shortcuts) {
192
209
  return Object.entries(s);
193
210
  });
194
211
  }
212
+ var defaultLayers = {
213
+ shortcuts: -1,
214
+ default: 0
215
+ };
195
216
  function resolveConfig(userConfig = {}, defaults = {}) {
196
217
  const config = Object.assign({}, defaults, userConfig);
197
218
  const rawPresets = config.presets || [];
@@ -200,6 +221,7 @@ function resolveConfig(userConfig = {}, defaults = {}) {
200
221
  ...rawPresets.filter((p) => !p.enforce),
201
222
  ...rawPresets.filter((p) => p.enforce === "post")
202
223
  ];
224
+ const layers = Object.assign(defaultLayers, ...rawPresets.map((i) => i.layers), userConfig.layers);
203
225
  function mergePresets(key) {
204
226
  return uniq([
205
227
  ...sortedPresets.flatMap((p) => toArray(p[key] || [])),
@@ -209,12 +231,13 @@ function resolveConfig(userConfig = {}, defaults = {}) {
209
231
  const extractors = mergePresets("extractors");
210
232
  if (!extractors.length)
211
233
  extractors.push(extractorSplit);
234
+ extractors.sort((a, b) => (a.order || 0) - (b.order || 0));
212
235
  const rules = mergePresets("rules");
213
236
  const rulesStaticMap = {};
214
237
  const rulesSize = rules.length;
215
238
  rules.forEach((rule, i) => {
216
239
  if (isStaticRule(rule)) {
217
- rulesStaticMap[rule[0]] = [i, rule[1]];
240
+ rulesStaticMap[rule[0]] = [i, rule[1], rule[2]];
218
241
  delete rules[i];
219
242
  }
220
243
  });
@@ -226,11 +249,16 @@ function resolveConfig(userConfig = {}, defaults = {}) {
226
249
  mergeSelectors: true,
227
250
  warnExcluded: true,
228
251
  excluded: [],
252
+ sortLayers: (layers2) => layers2,
229
253
  ...config,
254
+ envMode: config.envMode || "build",
255
+ shortcutsLayer: config.shortcutsLayer || "shortcuts",
256
+ layers,
230
257
  theme,
231
258
  rulesSize,
232
259
  rulesDynamic: rules,
233
260
  rulesStaticMap,
261
+ preflights: mergePresets("preflights"),
234
262
  variants: mergePresets("variants").map(normalizeVariant),
235
263
  shortcuts: resolveShortcuts(mergePresets("shortcuts")),
236
264
  extractors
@@ -257,17 +285,31 @@ var UnoGenerator = class {
257
285
  this._cache = new Map();
258
286
  }
259
287
  async applyExtractors(code, id, set = new Set()) {
260
- await Promise.all(this.config.extractors.map(async (i) => {
261
- const result = await i(code, id);
288
+ const context = {
289
+ get original() {
290
+ return code;
291
+ },
292
+ code,
293
+ id
294
+ };
295
+ for (const extractor of this.config.extractors) {
296
+ const result = await extractor.extract(context);
262
297
  result == null ? void 0 : result.forEach((t) => set.add(t));
263
- }));
298
+ }
264
299
  return set;
265
300
  }
266
- async generate(input, id, scope) {
301
+ async generate(input, {
302
+ id,
303
+ scope,
304
+ preflights = true,
305
+ layerComments = true
306
+ } = {}) {
267
307
  const tokens = typeof input === "string" ? await this.applyExtractors(input, id) : input;
308
+ const layerSet = new Set(["default"]);
268
309
  const matched = new Set();
269
310
  const sheet = new Map();
270
311
  const hit = (raw, payload) => {
312
+ var _a;
271
313
  this._cache.set(raw, payload);
272
314
  matched.add(raw);
273
315
  for (const item of payload) {
@@ -275,6 +317,8 @@ var UnoGenerator = class {
275
317
  if (!sheet.has(query))
276
318
  sheet.set(query, []);
277
319
  sheet.get(query).push(item);
320
+ if ((_a = item[4]) == null ? void 0 : _a.layer)
321
+ layerSet.add(item[4].layer);
278
322
  }
279
323
  };
280
324
  await Promise.all(Array.from(tokens).map(async (raw) => {
@@ -298,9 +342,9 @@ var UnoGenerator = class {
298
342
  return;
299
343
  }
300
344
  const expanded = this.expandShortcut(applied[1]);
301
- if (expanded == null ? void 0 : expanded.length) {
302
- const utils = await this.stringifyShortcuts(applied, expanded);
303
- if (utils.length) {
345
+ if (expanded) {
346
+ const utils = await this.stringifyShortcuts(applied, expanded[0], expanded[1]);
347
+ if (utils == null ? void 0 : utils.length) {
304
348
  hit(raw, utils);
305
349
  return;
306
350
  }
@@ -313,30 +357,66 @@ var UnoGenerator = class {
313
357
  }
314
358
  this._cache.set(raw, null);
315
359
  }));
316
- const css = Array.from(sheet).map(([query, items]) => {
317
- const size = items.length;
318
- const sorted = items.sort((a, b) => {
319
- var _a;
320
- return a[0] - b[0] || ((_a = a[1]) == null ? void 0 : _a.localeCompare(b[1] || "")) || 0;
321
- }).map((a) => [a[1] ? applyScope(a[1], scope) : a[1], a[2]]);
322
- const rules = sorted.map(([selector, body], idx) => {
323
- if (selector && this.config.mergeSelectors) {
324
- for (let i = size - 1; i > idx; i--) {
325
- const current = sorted[i];
326
- if (current[0] && current[1] === body) {
327
- current[0] = `${selector},${current[0]}`;
328
- return null;
360
+ if (preflights) {
361
+ this.config.preflights.forEach((i) => {
362
+ if (i.layer)
363
+ layerSet.add(i.layer);
364
+ });
365
+ }
366
+ const layerCache = {};
367
+ const layers = this.config.sortLayers(Array.from(layerSet).sort((a, b) => {
368
+ var _a, _b;
369
+ return ((_a = this.config.layers[a]) != null ? _a : 0) - ((_b = this.config.layers[b]) != null ? _b : 0) || a.localeCompare(b);
370
+ }));
371
+ const getLayer = (layer) => {
372
+ if (layerCache[layer])
373
+ return layerCache[layer];
374
+ let css = Array.from(sheet).map(([query, items]) => {
375
+ const size = items.length;
376
+ const sorted = items.filter((i) => {
377
+ var _a;
378
+ return (((_a = i[4]) == null ? void 0 : _a.layer) || "default") === layer;
379
+ }).sort((a, b) => {
380
+ var _a;
381
+ return a[0] - b[0] || ((_a = a[1]) == null ? void 0 : _a.localeCompare(b[1] || "")) || 0;
382
+ }).map((a) => [a[1] ? applyScope(a[1], scope) : a[1], a[2]]);
383
+ if (!sorted.length)
384
+ return void 0;
385
+ const rules = sorted.map(([selector, body], idx) => {
386
+ if (selector && this.config.mergeSelectors) {
387
+ for (let i = size - 1; i > idx; i--) {
388
+ const current = sorted[i];
389
+ if (current && current[0] && current[1] === body) {
390
+ current[0] = `${selector},${current[0]}`;
391
+ return null;
392
+ }
329
393
  }
330
394
  }
331
- }
332
- return selector ? `${selector}{${body}}` : body;
333
- }).filter(Boolean).join("\n");
334
- return query ? `${query}{
395
+ return selector ? `${selector}{${body}}` : body;
396
+ }).filter(Boolean).join("\n");
397
+ return query ? `${query}{
335
398
  ${rules}
336
399
  }` : rules;
337
- }).join("\n");
400
+ }).filter(Boolean).join("\n");
401
+ if (preflights) {
402
+ css = [
403
+ ...this.config.preflights.filter((i) => (i.layer || "default") === layer).map((i) => i.getCSS()).filter(Boolean),
404
+ css
405
+ ].join("\n");
406
+ }
407
+ return layerCache[layer] = layerComments && css ? `/* layer: ${layer} */
408
+ ${css}` : css;
409
+ };
410
+ const getLayers = (excludes) => {
411
+ return layers.filter((i) => !(excludes == null ? void 0 : excludes.includes(i))).map((i) => getLayer(i) || "").filter(Boolean).join("\n");
412
+ };
338
413
  return {
339
- css,
414
+ get css() {
415
+ return getLayers();
416
+ },
417
+ layers,
418
+ getLayers,
419
+ getLayer,
340
420
  matched
341
421
  };
342
422
  }
@@ -370,7 +450,7 @@ ${rules}
370
450
  }
371
451
  return [raw, processed, handlers];
372
452
  }
373
- applyVariants(parsed, variantHandlers = parsed[3], raw = parsed[1]) {
453
+ applyVariants(parsed, variantHandlers = parsed[4], raw = parsed[1]) {
374
454
  const selector = variantHandlers.reduce((p, v) => {
375
455
  var _a;
376
456
  return ((_a = v.selector) == null ? void 0 : _a.call(v, p)) || p;
@@ -388,7 +468,7 @@ ${rules}
388
468
  }
389
469
  constructCustomCSS(context, body, overrideSelector) {
390
470
  body = normalizeEntries(body);
391
- const [selector, entries, mediaQuery] = this.applyVariants([0, overrideSelector || context.rawSelector, body, context.variantHandlers]);
471
+ const [selector, entries, mediaQuery] = this.applyVariants([0, overrideSelector || context.rawSelector, body, void 0, context.variantHandlers]);
392
472
  const cssBody = `${selector}{${entriesToCss(entries)}}`;
393
473
  if (mediaQuery)
394
474
  return `${mediaQuery}{${cssBody}}`;
@@ -399,7 +479,7 @@ ${rules}
399
479
  const [raw, processed, variantHandlers] = typeof input === "string" ? this.matchVariants(input) : input;
400
480
  const staticMatch = rulesStaticMap[processed];
401
481
  if (staticMatch == null ? void 0 : staticMatch[1])
402
- return [staticMatch[0], raw, normalizeEntries(staticMatch[1]), variantHandlers];
482
+ return [staticMatch[0], raw, normalizeEntries(staticMatch[1]), staticMatch[2], variantHandlers];
403
483
  const context = {
404
484
  rawSelector: raw,
405
485
  currentSelector: processed,
@@ -412,35 +492,37 @@ ${rules}
412
492
  const rule = rulesDynamic[i];
413
493
  if (!rule)
414
494
  continue;
415
- const [matcher, handler] = rule;
495
+ const [matcher, handler, meta] = rule;
416
496
  const match = processed.match(matcher);
417
497
  if (!match)
418
498
  continue;
419
499
  const result = await handler(match, context);
420
500
  if (typeof result === "string")
421
- return [i, result];
501
+ return [i, result, meta];
422
502
  if (result)
423
- return [i, raw, normalizeEntries(result), variantHandlers];
503
+ return [i, raw, normalizeEntries(result), meta, variantHandlers];
424
504
  }
425
505
  }
426
506
  stringifyUtil(parsed) {
427
507
  if (!parsed)
428
508
  return;
429
509
  if (isRawUtil(parsed))
430
- return [parsed[0], void 0, parsed[1], void 0];
510
+ return [parsed[0], void 0, parsed[1], void 0, parsed[2]];
431
511
  const [selector, entries, mediaQuery] = this.applyVariants(parsed);
432
512
  const body = entriesToCss(entries);
433
513
  if (!body)
434
514
  return;
435
- return [parsed[0], selector, body, mediaQuery];
515
+ return [parsed[0], selector, body, mediaQuery, parsed[3]];
436
516
  }
437
517
  expandShortcut(processed, depth = 3) {
438
518
  if (depth === 0)
439
519
  return;
520
+ let meta;
440
521
  let result;
441
522
  for (const s of this.config.shortcuts) {
442
523
  if (isStaticShortcut(s)) {
443
524
  if (s[0] === processed) {
525
+ meta = meta || s[2];
444
526
  result = s[1];
445
527
  break;
446
528
  }
@@ -448,22 +530,32 @@ ${rules}
448
530
  const match = processed.match(s[0]);
449
531
  if (match)
450
532
  result = s[1](match);
451
- if (result)
533
+ if (result) {
534
+ meta = meta || s[2];
452
535
  break;
536
+ }
453
537
  }
454
538
  }
455
539
  if (!result)
456
540
  return;
457
541
  if (typeof result === "string")
458
542
  result = result.split(/ /g);
459
- return result.flatMap((r) => this.expandShortcut(r, depth - 1) || [r]);
543
+ return [
544
+ result.flatMap((r) => {
545
+ var _a;
546
+ return ((_a = this.expandShortcut(r, depth - 1)) == null ? void 0 : _a[0]) || [r];
547
+ }),
548
+ meta
549
+ ];
460
550
  }
461
- async stringifyShortcuts(parent, expanded) {
551
+ async stringifyShortcuts(parent, expanded, meta = { layer: this.config.shortcutsLayer }) {
462
552
  const selectorMap = new TwoKeyMap();
463
553
  const parsed = (await Promise.all(uniq(expanded).map((i) => this.parseUtil(i)))).filter(Boolean).sort((a, b) => a[0] - b[0]);
464
554
  const [raw, , parentVariants] = parent;
465
555
  for (const item of parsed) {
466
- const [selector, entries, mediaQuery] = this.applyVariants(item, [...item[3], ...parentVariants], raw);
556
+ if (isRawUtil(item))
557
+ continue;
558
+ const [selector, entries, mediaQuery] = this.applyVariants(item, [...item[4], ...parentVariants], raw);
467
559
  const mapItem = selectorMap.getFallback(selector, mediaQuery, [[], item[0]]);
468
560
  mapItem[0].push(...entries);
469
561
  if (item[0] > mapItem[1])
@@ -471,9 +563,9 @@ ${rules}
471
563
  }
472
564
  return selectorMap.map(([entries, index], selector, mediaQuery) => {
473
565
  const body = entriesToCss(entries);
474
- if (!body)
475
- return void 0;
476
- return [index, selector, body, mediaQuery];
566
+ if (body)
567
+ return [index, selector, body, mediaQuery, meta];
568
+ return void 0;
477
569
  }).filter(Boolean);
478
570
  }
479
571
  isExcluded(raw) {
@@ -524,5 +616,6 @@ export {
524
616
  normalizeVariant,
525
617
  toArray,
526
618
  uniq,
527
- validateFilterRE
619
+ validateFilterRE,
620
+ withLayer
528
621
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unocss/core",
3
- "version": "0.4.15",
3
+ "version": "0.6.0",
4
4
  "description": "The instant on-demand Atomic CSS engine.",
5
5
  "keywords": [
6
6
  "unocss",