@pikacss/core 0.0.28 → 0.0.30

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.cjs CHANGED
@@ -78,12 +78,12 @@ function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
78
78
  const lineEnd = isFormatted ? "\n" : "";
79
79
  const lines = [];
80
80
  blocks.forEach(({ properties, children }, selector) => {
81
- if (properties.length === 0 && children == null)
81
+ if (properties.length === 0 && (children == null || children.size === 0))
82
82
  return;
83
83
  lines.push(...[
84
84
  `${blockIndent}${selector}${selectorEnd}{`,
85
85
  ...properties.map(({ property, value }) => `${blockBodyIndent}${property}:${propertySpace}${value};`),
86
- ...children != null ? [renderCSSStyleBlocks(children, isFormatted, depth + 1)] : [],
86
+ ...children != null && children.size > 0 ? [renderCSSStyleBlocks(children, isFormatted, depth + 1)] : [],
87
87
  `${blockIndent}}`
88
88
  ]);
89
89
  });
@@ -240,6 +240,14 @@ function defineEnginePlugin(plugin) {
240
240
  return plugin;
241
241
  }
242
242
 
243
+ function modifyPropertyValue(value) {
244
+ if (value == null)
245
+ return null;
246
+ if (Array.isArray(value)) {
247
+ return [`${value[0]} !important`, value[1].map((i) => `${i} !important`)];
248
+ }
249
+ return `${value} !important`;
250
+ }
243
251
  function important() {
244
252
  let defaultValue;
245
253
  return defineEnginePlugin({
@@ -260,10 +268,10 @@ function important() {
260
268
  return rest;
261
269
  return Object.fromEntries(
262
270
  Object.entries(rest).map(([k, v]) => {
263
- if (isPropertyValue(v) === false || v == null)
264
- return [k, v];
265
- const modified = [v].flat(2).map((v2) => `${v2} !important`);
266
- return [k, modified];
271
+ if (isPropertyValue(v)) {
272
+ return [k, modifyPropertyValue(v)];
273
+ }
274
+ return [k, v];
267
275
  })
268
276
  );
269
277
  });
@@ -277,7 +285,7 @@ function keyframes() {
277
285
  return defineEnginePlugin({
278
286
  name: "core:keyframes",
279
287
  rawConfigConfigured(config) {
280
- resolveKeyframesConfig = createResolveConfigFn$1({
288
+ resolveKeyframesConfig = createResolveConfigFn({
281
289
  pruneUnused: config.keyframes?.pruneUnused
282
290
  });
283
291
  configList = config.keyframes?.keyframes ?? [];
@@ -300,7 +308,7 @@ function keyframes() {
300
308
  }
301
309
  };
302
310
  engine.keyframes.add(...configList);
303
- engine.addPreflight((engine2, isFormatted) => {
311
+ engine.addPreflight((engine2) => {
304
312
  const maybeUsedName = /* @__PURE__ */ new Set();
305
313
  engine2.store.atomicStyles.forEach(({ content: { property, value } }) => {
306
314
  if (property === "animationName") {
@@ -316,30 +324,22 @@ function keyframes() {
316
324
  });
317
325
  }
318
326
  });
319
- return renderCSSStyleBlocks(
320
- new Map(Array.from(engine2.keyframes.store.entries()).filter(([name, { pruneUnused }]) => pruneUnused === false || maybeUsedName.has(name)).map(([name, { frames }]) => [
321
- `@keyframes ${name}`,
322
- {
323
- properties: [],
324
- children: new Map(Object.entries(frames).map(([frame, properties]) => [
325
- frame,
326
- {
327
- properties: Object.entries(properties).filter(([_, value]) => isNotNullish(value)).flatMap(([property, value]) => {
328
- if (Array.isArray(value))
329
- return value.map((v) => ({ property, value: String(v) }));
330
- return { property, value: String(value) };
331
- })
332
- }
333
- ]))
334
- }
335
- ])),
336
- isFormatted
337
- );
327
+ const maybeUsedKeyframes = Array.from(engine2.keyframes.store.values()).filter(({ name, frames, pruneUnused }) => (pruneUnused === false || maybeUsedName.has(name)) && frames != null);
328
+ const preflightDefinition = {};
329
+ maybeUsedKeyframes.forEach(({ name, frames }) => {
330
+ preflightDefinition[`@keyframes ${name}`] = Object.fromEntries(
331
+ Object.entries(frames).map(([frame, properties]) => [
332
+ frame,
333
+ properties
334
+ ])
335
+ );
336
+ });
337
+ return preflightDefinition;
338
338
  });
339
339
  }
340
340
  });
341
341
  }
342
- function createResolveConfigFn$1({
342
+ function createResolveConfigFn({
343
343
  pruneUnused: defaultPruneUnused = true
344
344
  } = {}) {
345
345
  return function resolveKeyframesConfig(config) {
@@ -686,67 +686,92 @@ function resolveShortcutConfig(config) {
686
686
  }
687
687
 
688
688
  function variables() {
689
- let resolveVariableConfig;
690
- let configList;
689
+ let resolveVariables;
690
+ let rawVariables;
691
+ let safeSet;
691
692
  return defineEnginePlugin({
692
693
  name: "core:variables",
693
694
  rawConfigConfigured(config) {
694
- resolveVariableConfig = createResolveConfigFn({
695
+ resolveVariables = createResolveVariablesFn({
695
696
  pruneUnused: config.variables?.pruneUnused
696
697
  });
697
- configList = config.variables?.variables ?? [];
698
+ rawVariables = config.variables?.variables ?? {};
699
+ safeSet = new Set(config.variables?.safeList ?? []);
698
700
  },
699
701
  configureEngine(engine) {
700
702
  engine.variables = {
701
703
  store: /* @__PURE__ */ new Map(),
702
- add: (...list) => {
703
- list.forEach((config) => {
704
- const resolved = resolveVariableConfig(config);
705
- const { name: _name, value, autocomplete: { asValueOf, asProperty } } = resolved;
706
- const name = normalizeVariableName(_name);
704
+ add: (variables2) => {
705
+ const list = resolveVariables(variables2);
706
+ list.forEach((resolved) => {
707
+ const { name, value, autocomplete: { asValueOf, asProperty } } = resolved;
707
708
  asValueOf.forEach((p) => {
708
709
  if (p !== "-")
709
710
  engine.appendAutocompleteCssPropertyValues(p, `var(${name})`);
710
711
  });
711
712
  if (asProperty)
712
713
  engine.appendAutocompleteExtraCssProperties(name);
713
- if (value != null)
714
- engine.variables.store.set(name, resolved);
714
+ if (value != null) {
715
+ const list2 = engine.variables.store.get(name) ?? [];
716
+ list2.push(resolved);
717
+ engine.variables.store.set(name, list2);
718
+ }
715
719
  });
716
720
  engine.notifyPreflightUpdated();
717
721
  }
718
722
  };
719
- engine.variables.add(...configList);
720
- engine.addPreflight((engine2, isFormatted) => {
723
+ engine.variables.add(rawVariables);
724
+ engine.addPreflight(async (engine2) => {
721
725
  const used = /* @__PURE__ */ new Set();
722
726
  engine2.store.atomicStyles.forEach(({ content: { value } }) => {
723
727
  value.flatMap(extractUsedVarNames).forEach((name) => used.add(normalizeVariableName(name)));
724
728
  });
725
- return renderCSSStyleBlocks(
726
- /* @__PURE__ */ new Map([[
727
- ":root",
728
- {
729
- properties: Array.from(engine2.variables.store.entries()).filter(([name, { pruneUnused, value }]) => (pruneUnused === false || used.has(name)) && value != null).map(([name, { value }]) => ({ property: name, value }))
730
- }
731
- ]]),
732
- isFormatted
733
- );
729
+ const usedVariables = Array.from(engine2.variables.store.values()).flat().filter(({ name, pruneUnused, value }) => (safeSet.has(name) || pruneUnused === false || used.has(name)) && value != null);
730
+ const preflightDefinition = {};
731
+ for (const { name, value, selector: _selector } of usedVariables) {
732
+ const selector = await engine2.pluginHooks.transformSelectors(engine2.config.plugins, _selector);
733
+ let current = preflightDefinition;
734
+ selector.forEach((s) => {
735
+ current[s] ||= {};
736
+ current = current[s];
737
+ });
738
+ Object.assign(current, { [name]: value });
739
+ }
740
+ return preflightDefinition;
734
741
  });
735
742
  }
736
743
  });
737
744
  }
738
- function createResolveConfigFn({
745
+ function createResolveVariablesFn({
739
746
  pruneUnused: defaultPruneUnused = true
740
747
  } = {}) {
741
- return function resolveVariableConfig(config) {
742
- if (typeof config === "string")
743
- return { name: config, value: null, autocomplete: { asValueOf: ["*"], asProperty: true }, pruneUnused: defaultPruneUnused };
744
- if (Array.isArray(config)) {
745
- const [name2, value2, { asValueOf: asValueOf2 = "*", asProperty: asProperty2 = true } = {}, pruneUnused2 = defaultPruneUnused] = config;
746
- return { name: name2, value: value2, autocomplete: { asValueOf: [asValueOf2].flat(), asProperty: asProperty2 }, pruneUnused: pruneUnused2 };
748
+ function _resolveVariables(variables2, levels, result) {
749
+ for (const [key, value] of Object.entries(variables2)) {
750
+ if (key.startsWith("--")) {
751
+ const isObject = typeof value === "object" && value !== null && !Array.isArray(value);
752
+ const {
753
+ value: varValue,
754
+ autocomplete = {},
755
+ pruneUnused = defaultPruneUnused
756
+ } = isObject ? value : { value };
757
+ result.push({
758
+ name: key,
759
+ value: varValue,
760
+ selector: levels.length > 0 ? levels : [":root"],
761
+ autocomplete: {
762
+ asValueOf: autocomplete.asValueOf ? [autocomplete.asValueOf].flat() : ["*"],
763
+ asProperty: autocomplete.asProperty ?? true
764
+ },
765
+ pruneUnused
766
+ });
767
+ } else {
768
+ _resolveVariables(value, [...levels, key], result);
769
+ }
747
770
  }
748
- const { name, value, autocomplete: { asValueOf = "*", asProperty = true } = {}, pruneUnused = defaultPruneUnused } = config;
749
- return { name, value, autocomplete: { asValueOf: [asValueOf].flat(), asProperty }, pruneUnused };
771
+ return result;
772
+ }
773
+ return function resolveVariables(variables2) {
774
+ return _resolveVariables(variables2, [], []);
750
775
  };
751
776
  }
752
777
  const VAR_NAME_RE = /var\((--[\w-]+)/g;
@@ -800,6 +825,7 @@ async function createEngine(config = {}) {
800
825
  }
801
826
  class Engine {
802
827
  config;
828
+ pluginHooks = hooks;
803
829
  extract;
804
830
  store = {
805
831
  atomicStyleIds: /* @__PURE__ */ new Map(),
@@ -878,13 +904,13 @@ class Engine {
878
904
  }
879
905
  async renderPreflights(isFormatted) {
880
906
  const lineEnd = isFormatted ? "\n" : "";
881
- return (await Promise.all(this.config.preflights.map((p) => {
882
- const result = p(this, isFormatted);
907
+ return (await Promise.all(this.config.preflights.map(async (p) => {
908
+ const result = await p(this, isFormatted);
883
909
  if (typeof result === "string")
884
910
  return result;
885
- return renderPreflight({
911
+ return renderPreflightDefinition({
886
912
  engine: this,
887
- preflight: result,
913
+ preflightDefinition: result,
888
914
  isFormatted
889
915
  });
890
916
  }))).join(lineEnd);
@@ -1008,7 +1034,7 @@ function renderAtomicStyles(payload) {
1008
1034
  });
1009
1035
  return renderCSSStyleBlocks(blocks, isFormatted);
1010
1036
  }
1011
- async function _renderPreflight({
1037
+ async function _renderPreflightDefinition({
1012
1038
  engine,
1013
1039
  preflightDefinition,
1014
1040
  blocks = /* @__PURE__ */ new Map()
@@ -1041,7 +1067,7 @@ async function _renderPreflight({
1041
1067
  } else {
1042
1068
  currentBlockBody.children ||= /* @__PURE__ */ new Map();
1043
1069
  currentBlockBody.children.set(k, currentBlockBody.children.get(k) || { properties: [] });
1044
- _renderPreflight({
1070
+ await _renderPreflightDefinition({
1045
1071
  engine,
1046
1072
  preflightDefinition: { [k]: v },
1047
1073
  blocks: currentBlockBody.children
@@ -1051,11 +1077,11 @@ async function _renderPreflight({
1051
1077
  }
1052
1078
  return blocks;
1053
1079
  }
1054
- async function renderPreflight(payload) {
1055
- const { engine, preflight, isFormatted } = payload;
1056
- const blocks = await _renderPreflight({
1080
+ async function renderPreflightDefinition(payload) {
1081
+ const { engine, preflightDefinition, isFormatted } = payload;
1082
+ const blocks = await _renderPreflightDefinition({
1057
1083
  engine,
1058
- preflightDefinition: preflight
1084
+ preflightDefinition
1059
1085
  });
1060
1086
  return renderCSSStyleBlocks(blocks, isFormatted);
1061
1087
  }
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _pikacss_core from '@pikacss/core';
1
2
  import * as CSS from 'csstype';
2
3
 
3
4
  interface ImportantConfig {
@@ -188,6 +189,8 @@ declare class ShortcutResolver extends AbstractResolver<StyleItem$1[]> {
188
189
  resolve(shortcut: string): Promise<StyleItem$1[]>;
189
190
  }
190
191
 
192
+ type ResolvedCSSProperty = keyof ResolvedCSSProperties;
193
+ type ResolvedCSSVarProperty = ResolvedCSSProperty extends infer T ? T extends `--${string}` ? T : never : never;
191
194
  interface VariableAutocomplete {
192
195
  /**
193
196
  * Specify the properties that the variable can be used as a value of.
@@ -202,39 +205,59 @@ interface VariableAutocomplete {
202
205
  */
203
206
  asProperty?: boolean;
204
207
  }
205
- type Variable = string | [name: string, value?: string, autocomplete?: VariableAutocomplete, pruneUnused?: boolean] | {
206
- name: string;
207
- value?: string;
208
+ interface VariableObject {
209
+ value?: ResolvedCSSProperties[`--${string}`];
208
210
  autocomplete?: VariableAutocomplete;
209
211
  pruneUnused?: boolean;
212
+ }
213
+ type Variable = ResolvedCSSProperties[`--${string}`] | VariableObject;
214
+ type VariablesDefinition = {
215
+ [key in UnionString | ResolvedSelector | (`--${string}` & {}) | ResolvedCSSVarProperty]?: Variable | VariablesDefinition;
210
216
  };
211
217
  interface VariablesConfig {
212
218
  /**
213
219
  * Define CSS variables with support for static values and autocomplete configuration.
214
220
  *
215
- * @default []
221
+ * @default {}
216
222
  * @example
217
223
  * ```ts
218
224
  * {
219
- * variables: [
220
- * // Basic usage
221
- * ['primary', '#ff0000'],
222
- * // With autocomplete configuration
223
- * ['accent', '#00ff00', {
224
- * asValueOf: ['color', 'background-color'],
225
- * asProperty: true
226
- * }]
227
- * ]
225
+ * variables: {
226
+ * // The variable with value "null" will not be included in the final CSS, but can be used in autocompletion.
227
+ * '--external-var': null,
228
+ *
229
+ * '--color-bg': '#fff',
230
+ * '--color-text': '#000',
231
+ *
232
+ * '[data-theme="dark"]': {
233
+ * '--color-bg': '#000',
234
+ * '--color-text': '#fff',
235
+ * },
236
+ * },
228
237
  * }
229
238
  * ```
230
239
  */
231
- variables: Variable[];
240
+ variables: VariablesDefinition;
232
241
  /**
233
242
  * Whether to prune unused variables from the final CSS.
234
243
  *
235
244
  * @default true
236
245
  */
237
246
  pruneUnused?: boolean;
247
+ /**
248
+ * A list of CSS variables that should always be included in the final CSS, you can use this to prevent certain variables from being pruned.
249
+ *
250
+ * @default []
251
+ * @example
252
+ * ```ts
253
+ * {
254
+ * variables: {
255
+ * safeList: ['--external-var', '--color-bg', '--color-text'],
256
+ * },
257
+ * }
258
+ * ```
259
+ */
260
+ safeList?: ((`--${string}` & {}) | ResolvedCSSVarProperty)[];
238
261
  }
239
262
  declare module '@pikacss/core' {
240
263
  interface EngineConfig {
@@ -242,14 +265,15 @@ declare module '@pikacss/core' {
242
265
  }
243
266
  interface Engine {
244
267
  variables: {
245
- store: Map<string, ResolvedVariableConfig>;
246
- add: (...list: Variable[]) => void;
268
+ store: Map<string, ResolvedVariable[]>;
269
+ add: (variables: VariablesDefinition) => void;
247
270
  };
248
271
  }
249
272
  }
250
- interface ResolvedVariableConfig {
273
+ interface ResolvedVariable {
251
274
  name: string;
252
- value: string | Nullish;
275
+ value: PropertyValue;
276
+ selector: string[];
253
277
  pruneUnused: boolean;
254
278
  autocomplete: {
255
279
  asValueOf: string[];
@@ -273,42 +297,6 @@ type FromKebab<T extends string> = T extends `--${string}` ? T : T extends `-${i
273
297
  type GetValue<Obj extends Record<string, any>, K extends string> = (IsEqual<Obj, object> | IsEqual<Obj, {}> | IsEqual<Obj[K], unknown>) extends false ? Obj[K] : never;
274
298
  type ResolveFrom<T, Key extends string, I, Fallback extends I> = Key extends keyof T ? T[Key] extends I ? T[Key] : Fallback : Fallback;
275
299
 
276
- interface PikaAugment {
277
- }
278
- type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
279
- type Properties$1 = Record<string, PropertyValue>;
280
- interface StyleDefinition$1 {
281
- [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
282
- }
283
- type StyleItem$1 = string | StyleDefinition$1;
284
- interface ExtractedStyleContent {
285
- selector: string[];
286
- property: string;
287
- value: string[] | Nullish;
288
- }
289
- interface StyleContent {
290
- selector: string[];
291
- property: string;
292
- value: string[];
293
- }
294
- interface AtomicStyle {
295
- id: string;
296
- content: StyleContent;
297
- }
298
- interface CSSStyleBlockBody {
299
- properties: {
300
- property: string;
301
- value: string;
302
- }[];
303
- children?: CSSStyleBlocks;
304
- }
305
- type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
306
- type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
307
- type ResolvedCSSProperty = ResolveFrom<PikaAugment, 'CSSProperty', string, string>;
308
- type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
309
- type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
310
- type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
311
-
312
300
  interface ResolvedAutocompleteConfig {
313
301
  selectors: Set<string>;
314
302
  styleItemStrings: Set<string>;
@@ -334,7 +322,6 @@ type EmptyAutocomplete = DefineAutocomplete<{
334
322
  PropertiesValue: never;
335
323
  CssPropertiesValue: never;
336
324
  }>;
337
- type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
338
325
 
339
326
  type DefineHooks<Hooks extends Record<string, [type: 'sync' | 'async', payload: any, returnValue?: any]>> = Hooks;
340
327
  type EngineHooksDefinition = DefineHooks<{
@@ -358,10 +345,48 @@ interface EnginePlugin extends EnginePluginHooksOptions {
358
345
  }
359
346
  declare function defineEnginePlugin(plugin: EnginePlugin): EnginePlugin;
360
347
 
348
+ interface PikaAugment {
349
+ }
350
+ type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
351
+ type Properties$1 = Record<string, PropertyValue>;
352
+ interface StyleDefinition$1 {
353
+ [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
354
+ }
355
+ type StyleItem$1 = string | StyleDefinition$1;
356
+ interface ExtractedStyleContent {
357
+ selector: string[];
358
+ property: string;
359
+ value: string[] | Nullish;
360
+ }
361
+ interface StyleContent {
362
+ selector: string[];
363
+ property: string;
364
+ value: string[];
365
+ }
366
+ interface AtomicStyle {
367
+ id: string;
368
+ content: StyleContent;
369
+ }
370
+ interface CSSStyleBlockBody {
371
+ properties: {
372
+ property: string;
373
+ value: string;
374
+ }[];
375
+ children?: CSSStyleBlocks;
376
+ }
377
+ type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
378
+
379
+ type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
380
+ type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
381
+ type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
382
+ type ResolvedCSSProperties = Omit<ResolvedProperties, ResolvedAutocomplete['ExtraProperty']>;
383
+ type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
384
+ type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
385
+
361
386
  type PreflightDefinition = {
362
- [selector in UnionString | ResolvedSelector]?: ResolvedProperties | PreflightDefinition;
387
+ [selector in UnionString | ResolvedSelector]?: ResolvedCSSProperties | PreflightDefinition;
363
388
  };
364
- type PreflightFn = (engine: Engine, isFormatted: boolean) => string | PreflightDefinition;
389
+ type PreflightFn = (engine: Engine, isFormatted: boolean) => Awaitable<string | PreflightDefinition>;
365
390
  /**
366
391
  * PreflightConfig can be a string or a function that returns a string.
367
392
  *
@@ -445,6 +470,18 @@ declare function defineEngineConfig(config: EngineConfig): EngineConfig;
445
470
  declare function createEngine(config?: EngineConfig): Promise<Engine>;
446
471
  declare class Engine {
447
472
  config: ResolvedEngineConfig;
473
+ pluginHooks: {
474
+ configureRawConfig: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => Promise<EngineConfig>;
475
+ rawConfigConfigured: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => void;
476
+ configureResolvedConfig: (plugins: _pikacss_core.EnginePlugin[], payload: ResolvedEngineConfig) => Promise<ResolvedEngineConfig>;
477
+ configureEngine: (plugins: _pikacss_core.EnginePlugin[], payload: Engine) => Promise<Engine>;
478
+ transformSelectors: (plugins: _pikacss_core.EnginePlugin[], payload: string[]) => Promise<string[]>;
479
+ transformStyleItems: (plugins: _pikacss_core.EnginePlugin[], payload: StyleItem$1[]) => Promise<StyleItem$1[]>;
480
+ transformStyleDefinitions: (plugins: _pikacss_core.EnginePlugin[], payload: StyleDefinition$1[]) => Promise<StyleDefinition$1[]>;
481
+ preflightUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
482
+ atomicStyleAdded: (plugins: _pikacss_core.EnginePlugin[], payload: AtomicStyle) => AtomicStyle;
483
+ autocompleteConfigUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
484
+ };
448
485
  extract: ExtractFn;
449
486
  store: {
450
487
  atomicStyleIds: Map<string, string>;
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _pikacss_core from '@pikacss/core';
1
2
  import * as CSS from 'csstype';
2
3
 
3
4
  interface ImportantConfig {
@@ -188,6 +189,8 @@ declare class ShortcutResolver extends AbstractResolver<StyleItem$1[]> {
188
189
  resolve(shortcut: string): Promise<StyleItem$1[]>;
189
190
  }
190
191
 
192
+ type ResolvedCSSProperty = keyof ResolvedCSSProperties;
193
+ type ResolvedCSSVarProperty = ResolvedCSSProperty extends infer T ? T extends `--${string}` ? T : never : never;
191
194
  interface VariableAutocomplete {
192
195
  /**
193
196
  * Specify the properties that the variable can be used as a value of.
@@ -202,39 +205,59 @@ interface VariableAutocomplete {
202
205
  */
203
206
  asProperty?: boolean;
204
207
  }
205
- type Variable = string | [name: string, value?: string, autocomplete?: VariableAutocomplete, pruneUnused?: boolean] | {
206
- name: string;
207
- value?: string;
208
+ interface VariableObject {
209
+ value?: ResolvedCSSProperties[`--${string}`];
208
210
  autocomplete?: VariableAutocomplete;
209
211
  pruneUnused?: boolean;
212
+ }
213
+ type Variable = ResolvedCSSProperties[`--${string}`] | VariableObject;
214
+ type VariablesDefinition = {
215
+ [key in UnionString | ResolvedSelector | (`--${string}` & {}) | ResolvedCSSVarProperty]?: Variable | VariablesDefinition;
210
216
  };
211
217
  interface VariablesConfig {
212
218
  /**
213
219
  * Define CSS variables with support for static values and autocomplete configuration.
214
220
  *
215
- * @default []
221
+ * @default {}
216
222
  * @example
217
223
  * ```ts
218
224
  * {
219
- * variables: [
220
- * // Basic usage
221
- * ['primary', '#ff0000'],
222
- * // With autocomplete configuration
223
- * ['accent', '#00ff00', {
224
- * asValueOf: ['color', 'background-color'],
225
- * asProperty: true
226
- * }]
227
- * ]
225
+ * variables: {
226
+ * // The variable with value "null" will not be included in the final CSS, but can be used in autocompletion.
227
+ * '--external-var': null,
228
+ *
229
+ * '--color-bg': '#fff',
230
+ * '--color-text': '#000',
231
+ *
232
+ * '[data-theme="dark"]': {
233
+ * '--color-bg': '#000',
234
+ * '--color-text': '#fff',
235
+ * },
236
+ * },
228
237
  * }
229
238
  * ```
230
239
  */
231
- variables: Variable[];
240
+ variables: VariablesDefinition;
232
241
  /**
233
242
  * Whether to prune unused variables from the final CSS.
234
243
  *
235
244
  * @default true
236
245
  */
237
246
  pruneUnused?: boolean;
247
+ /**
248
+ * A list of CSS variables that should always be included in the final CSS, you can use this to prevent certain variables from being pruned.
249
+ *
250
+ * @default []
251
+ * @example
252
+ * ```ts
253
+ * {
254
+ * variables: {
255
+ * safeList: ['--external-var', '--color-bg', '--color-text'],
256
+ * },
257
+ * }
258
+ * ```
259
+ */
260
+ safeList?: ((`--${string}` & {}) | ResolvedCSSVarProperty)[];
238
261
  }
239
262
  declare module '@pikacss/core' {
240
263
  interface EngineConfig {
@@ -242,14 +265,15 @@ declare module '@pikacss/core' {
242
265
  }
243
266
  interface Engine {
244
267
  variables: {
245
- store: Map<string, ResolvedVariableConfig>;
246
- add: (...list: Variable[]) => void;
268
+ store: Map<string, ResolvedVariable[]>;
269
+ add: (variables: VariablesDefinition) => void;
247
270
  };
248
271
  }
249
272
  }
250
- interface ResolvedVariableConfig {
273
+ interface ResolvedVariable {
251
274
  name: string;
252
- value: string | Nullish;
275
+ value: PropertyValue;
276
+ selector: string[];
253
277
  pruneUnused: boolean;
254
278
  autocomplete: {
255
279
  asValueOf: string[];
@@ -273,42 +297,6 @@ type FromKebab<T extends string> = T extends `--${string}` ? T : T extends `-${i
273
297
  type GetValue<Obj extends Record<string, any>, K extends string> = (IsEqual<Obj, object> | IsEqual<Obj, {}> | IsEqual<Obj[K], unknown>) extends false ? Obj[K] : never;
274
298
  type ResolveFrom<T, Key extends string, I, Fallback extends I> = Key extends keyof T ? T[Key] extends I ? T[Key] : Fallback : Fallback;
275
299
 
276
- interface PikaAugment {
277
- }
278
- type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
279
- type Properties$1 = Record<string, PropertyValue>;
280
- interface StyleDefinition$1 {
281
- [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
282
- }
283
- type StyleItem$1 = string | StyleDefinition$1;
284
- interface ExtractedStyleContent {
285
- selector: string[];
286
- property: string;
287
- value: string[] | Nullish;
288
- }
289
- interface StyleContent {
290
- selector: string[];
291
- property: string;
292
- value: string[];
293
- }
294
- interface AtomicStyle {
295
- id: string;
296
- content: StyleContent;
297
- }
298
- interface CSSStyleBlockBody {
299
- properties: {
300
- property: string;
301
- value: string;
302
- }[];
303
- children?: CSSStyleBlocks;
304
- }
305
- type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
306
- type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
307
- type ResolvedCSSProperty = ResolveFrom<PikaAugment, 'CSSProperty', string, string>;
308
- type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
309
- type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
310
- type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
311
-
312
300
  interface ResolvedAutocompleteConfig {
313
301
  selectors: Set<string>;
314
302
  styleItemStrings: Set<string>;
@@ -334,7 +322,6 @@ type EmptyAutocomplete = DefineAutocomplete<{
334
322
  PropertiesValue: never;
335
323
  CssPropertiesValue: never;
336
324
  }>;
337
- type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
338
325
 
339
326
  type DefineHooks<Hooks extends Record<string, [type: 'sync' | 'async', payload: any, returnValue?: any]>> = Hooks;
340
327
  type EngineHooksDefinition = DefineHooks<{
@@ -358,10 +345,48 @@ interface EnginePlugin extends EnginePluginHooksOptions {
358
345
  }
359
346
  declare function defineEnginePlugin(plugin: EnginePlugin): EnginePlugin;
360
347
 
348
+ interface PikaAugment {
349
+ }
350
+ type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
351
+ type Properties$1 = Record<string, PropertyValue>;
352
+ interface StyleDefinition$1 {
353
+ [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
354
+ }
355
+ type StyleItem$1 = string | StyleDefinition$1;
356
+ interface ExtractedStyleContent {
357
+ selector: string[];
358
+ property: string;
359
+ value: string[] | Nullish;
360
+ }
361
+ interface StyleContent {
362
+ selector: string[];
363
+ property: string;
364
+ value: string[];
365
+ }
366
+ interface AtomicStyle {
367
+ id: string;
368
+ content: StyleContent;
369
+ }
370
+ interface CSSStyleBlockBody {
371
+ properties: {
372
+ property: string;
373
+ value: string;
374
+ }[];
375
+ children?: CSSStyleBlocks;
376
+ }
377
+ type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
378
+
379
+ type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
380
+ type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
381
+ type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
382
+ type ResolvedCSSProperties = Omit<ResolvedProperties, ResolvedAutocomplete['ExtraProperty']>;
383
+ type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
384
+ type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
385
+
361
386
  type PreflightDefinition = {
362
- [selector in UnionString | ResolvedSelector]?: ResolvedProperties | PreflightDefinition;
387
+ [selector in UnionString | ResolvedSelector]?: ResolvedCSSProperties | PreflightDefinition;
363
388
  };
364
- type PreflightFn = (engine: Engine, isFormatted: boolean) => string | PreflightDefinition;
389
+ type PreflightFn = (engine: Engine, isFormatted: boolean) => Awaitable<string | PreflightDefinition>;
365
390
  /**
366
391
  * PreflightConfig can be a string or a function that returns a string.
367
392
  *
@@ -445,6 +470,18 @@ declare function defineEngineConfig(config: EngineConfig): EngineConfig;
445
470
  declare function createEngine(config?: EngineConfig): Promise<Engine>;
446
471
  declare class Engine {
447
472
  config: ResolvedEngineConfig;
473
+ pluginHooks: {
474
+ configureRawConfig: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => Promise<EngineConfig>;
475
+ rawConfigConfigured: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => void;
476
+ configureResolvedConfig: (plugins: _pikacss_core.EnginePlugin[], payload: ResolvedEngineConfig) => Promise<ResolvedEngineConfig>;
477
+ configureEngine: (plugins: _pikacss_core.EnginePlugin[], payload: Engine) => Promise<Engine>;
478
+ transformSelectors: (plugins: _pikacss_core.EnginePlugin[], payload: string[]) => Promise<string[]>;
479
+ transformStyleItems: (plugins: _pikacss_core.EnginePlugin[], payload: StyleItem$1[]) => Promise<StyleItem$1[]>;
480
+ transformStyleDefinitions: (plugins: _pikacss_core.EnginePlugin[], payload: StyleDefinition$1[]) => Promise<StyleDefinition$1[]>;
481
+ preflightUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
482
+ atomicStyleAdded: (plugins: _pikacss_core.EnginePlugin[], payload: AtomicStyle) => AtomicStyle;
483
+ autocompleteConfigUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
484
+ };
448
485
  extract: ExtractFn;
449
486
  store: {
450
487
  atomicStyleIds: Map<string, string>;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _pikacss_core from '@pikacss/core';
1
2
  import * as CSS from 'csstype';
2
3
 
3
4
  interface ImportantConfig {
@@ -188,6 +189,8 @@ declare class ShortcutResolver extends AbstractResolver<StyleItem$1[]> {
188
189
  resolve(shortcut: string): Promise<StyleItem$1[]>;
189
190
  }
190
191
 
192
+ type ResolvedCSSProperty = keyof ResolvedCSSProperties;
193
+ type ResolvedCSSVarProperty = ResolvedCSSProperty extends infer T ? T extends `--${string}` ? T : never : never;
191
194
  interface VariableAutocomplete {
192
195
  /**
193
196
  * Specify the properties that the variable can be used as a value of.
@@ -202,39 +205,59 @@ interface VariableAutocomplete {
202
205
  */
203
206
  asProperty?: boolean;
204
207
  }
205
- type Variable = string | [name: string, value?: string, autocomplete?: VariableAutocomplete, pruneUnused?: boolean] | {
206
- name: string;
207
- value?: string;
208
+ interface VariableObject {
209
+ value?: ResolvedCSSProperties[`--${string}`];
208
210
  autocomplete?: VariableAutocomplete;
209
211
  pruneUnused?: boolean;
212
+ }
213
+ type Variable = ResolvedCSSProperties[`--${string}`] | VariableObject;
214
+ type VariablesDefinition = {
215
+ [key in UnionString | ResolvedSelector | (`--${string}` & {}) | ResolvedCSSVarProperty]?: Variable | VariablesDefinition;
210
216
  };
211
217
  interface VariablesConfig {
212
218
  /**
213
219
  * Define CSS variables with support for static values and autocomplete configuration.
214
220
  *
215
- * @default []
221
+ * @default {}
216
222
  * @example
217
223
  * ```ts
218
224
  * {
219
- * variables: [
220
- * // Basic usage
221
- * ['primary', '#ff0000'],
222
- * // With autocomplete configuration
223
- * ['accent', '#00ff00', {
224
- * asValueOf: ['color', 'background-color'],
225
- * asProperty: true
226
- * }]
227
- * ]
225
+ * variables: {
226
+ * // The variable with value "null" will not be included in the final CSS, but can be used in autocompletion.
227
+ * '--external-var': null,
228
+ *
229
+ * '--color-bg': '#fff',
230
+ * '--color-text': '#000',
231
+ *
232
+ * '[data-theme="dark"]': {
233
+ * '--color-bg': '#000',
234
+ * '--color-text': '#fff',
235
+ * },
236
+ * },
228
237
  * }
229
238
  * ```
230
239
  */
231
- variables: Variable[];
240
+ variables: VariablesDefinition;
232
241
  /**
233
242
  * Whether to prune unused variables from the final CSS.
234
243
  *
235
244
  * @default true
236
245
  */
237
246
  pruneUnused?: boolean;
247
+ /**
248
+ * A list of CSS variables that should always be included in the final CSS, you can use this to prevent certain variables from being pruned.
249
+ *
250
+ * @default []
251
+ * @example
252
+ * ```ts
253
+ * {
254
+ * variables: {
255
+ * safeList: ['--external-var', '--color-bg', '--color-text'],
256
+ * },
257
+ * }
258
+ * ```
259
+ */
260
+ safeList?: ((`--${string}` & {}) | ResolvedCSSVarProperty)[];
238
261
  }
239
262
  declare module '@pikacss/core' {
240
263
  interface EngineConfig {
@@ -242,14 +265,15 @@ declare module '@pikacss/core' {
242
265
  }
243
266
  interface Engine {
244
267
  variables: {
245
- store: Map<string, ResolvedVariableConfig>;
246
- add: (...list: Variable[]) => void;
268
+ store: Map<string, ResolvedVariable[]>;
269
+ add: (variables: VariablesDefinition) => void;
247
270
  };
248
271
  }
249
272
  }
250
- interface ResolvedVariableConfig {
273
+ interface ResolvedVariable {
251
274
  name: string;
252
- value: string | Nullish;
275
+ value: PropertyValue;
276
+ selector: string[];
253
277
  pruneUnused: boolean;
254
278
  autocomplete: {
255
279
  asValueOf: string[];
@@ -273,42 +297,6 @@ type FromKebab<T extends string> = T extends `--${string}` ? T : T extends `-${i
273
297
  type GetValue<Obj extends Record<string, any>, K extends string> = (IsEqual<Obj, object> | IsEqual<Obj, {}> | IsEqual<Obj[K], unknown>) extends false ? Obj[K] : never;
274
298
  type ResolveFrom<T, Key extends string, I, Fallback extends I> = Key extends keyof T ? T[Key] extends I ? T[Key] : Fallback : Fallback;
275
299
 
276
- interface PikaAugment {
277
- }
278
- type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
279
- type Properties$1 = Record<string, PropertyValue>;
280
- interface StyleDefinition$1 {
281
- [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
282
- }
283
- type StyleItem$1 = string | StyleDefinition$1;
284
- interface ExtractedStyleContent {
285
- selector: string[];
286
- property: string;
287
- value: string[] | Nullish;
288
- }
289
- interface StyleContent {
290
- selector: string[];
291
- property: string;
292
- value: string[];
293
- }
294
- interface AtomicStyle {
295
- id: string;
296
- content: StyleContent;
297
- }
298
- interface CSSStyleBlockBody {
299
- properties: {
300
- property: string;
301
- value: string;
302
- }[];
303
- children?: CSSStyleBlocks;
304
- }
305
- type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
306
- type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
307
- type ResolvedCSSProperty = ResolveFrom<PikaAugment, 'CSSProperty', string, string>;
308
- type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
309
- type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
310
- type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
311
-
312
300
  interface ResolvedAutocompleteConfig {
313
301
  selectors: Set<string>;
314
302
  styleItemStrings: Set<string>;
@@ -334,7 +322,6 @@ type EmptyAutocomplete = DefineAutocomplete<{
334
322
  PropertiesValue: never;
335
323
  CssPropertiesValue: never;
336
324
  }>;
337
- type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
338
325
 
339
326
  type DefineHooks<Hooks extends Record<string, [type: 'sync' | 'async', payload: any, returnValue?: any]>> = Hooks;
340
327
  type EngineHooksDefinition = DefineHooks<{
@@ -358,10 +345,48 @@ interface EnginePlugin extends EnginePluginHooksOptions {
358
345
  }
359
346
  declare function defineEnginePlugin(plugin: EnginePlugin): EnginePlugin;
360
347
 
348
+ interface PikaAugment {
349
+ }
350
+ type PropertyValue = string | number | [value: string | number, fallback: (string | number)[]] | Nullish;
351
+ type Properties$1 = Record<string, PropertyValue>;
352
+ interface StyleDefinition$1 {
353
+ [K: string]: PropertyValue | StyleDefinition$1 | StyleItem$1[];
354
+ }
355
+ type StyleItem$1 = string | StyleDefinition$1;
356
+ interface ExtractedStyleContent {
357
+ selector: string[];
358
+ property: string;
359
+ value: string[] | Nullish;
360
+ }
361
+ interface StyleContent {
362
+ selector: string[];
363
+ property: string;
364
+ value: string[];
365
+ }
366
+ interface AtomicStyle {
367
+ id: string;
368
+ content: StyleContent;
369
+ }
370
+ interface CSSStyleBlockBody {
371
+ properties: {
372
+ property: string;
373
+ value: string;
374
+ }[];
375
+ children?: CSSStyleBlocks;
376
+ }
377
+ type CSSStyleBlocks = Map<string, CSSStyleBlockBody>;
378
+
379
+ type ResolvedAutocomplete = ResolveFrom<PikaAugment, 'Autocomplete', _Autocomplete, EmptyAutocomplete>;
380
+ type ResolvedSelector = ResolveFrom<PikaAugment, 'Selector', string, string>;
381
+ type ResolvedProperties = ResolveFrom<PikaAugment, 'Properties', any, Properties$1>;
382
+ type ResolvedCSSProperties = Omit<ResolvedProperties, ResolvedAutocomplete['ExtraProperty']>;
383
+ type ResolvedStyleDefinition = ResolveFrom<PikaAugment, 'StyleDefinition', any, StyleDefinition$1>;
384
+ type ResolvedStyleItem = ResolveFrom<PikaAugment, 'StyleItem', any, StyleItem$1>;
385
+
361
386
  type PreflightDefinition = {
362
- [selector in UnionString | ResolvedSelector]?: ResolvedProperties | PreflightDefinition;
387
+ [selector in UnionString | ResolvedSelector]?: ResolvedCSSProperties | PreflightDefinition;
363
388
  };
364
- type PreflightFn = (engine: Engine, isFormatted: boolean) => string | PreflightDefinition;
389
+ type PreflightFn = (engine: Engine, isFormatted: boolean) => Awaitable<string | PreflightDefinition>;
365
390
  /**
366
391
  * PreflightConfig can be a string or a function that returns a string.
367
392
  *
@@ -445,6 +470,18 @@ declare function defineEngineConfig(config: EngineConfig): EngineConfig;
445
470
  declare function createEngine(config?: EngineConfig): Promise<Engine>;
446
471
  declare class Engine {
447
472
  config: ResolvedEngineConfig;
473
+ pluginHooks: {
474
+ configureRawConfig: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => Promise<EngineConfig>;
475
+ rawConfigConfigured: (plugins: _pikacss_core.EnginePlugin[], payload: EngineConfig) => void;
476
+ configureResolvedConfig: (plugins: _pikacss_core.EnginePlugin[], payload: ResolvedEngineConfig) => Promise<ResolvedEngineConfig>;
477
+ configureEngine: (plugins: _pikacss_core.EnginePlugin[], payload: Engine) => Promise<Engine>;
478
+ transformSelectors: (plugins: _pikacss_core.EnginePlugin[], payload: string[]) => Promise<string[]>;
479
+ transformStyleItems: (plugins: _pikacss_core.EnginePlugin[], payload: StyleItem$1[]) => Promise<StyleItem$1[]>;
480
+ transformStyleDefinitions: (plugins: _pikacss_core.EnginePlugin[], payload: StyleDefinition$1[]) => Promise<StyleDefinition$1[]>;
481
+ preflightUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
482
+ atomicStyleAdded: (plugins: _pikacss_core.EnginePlugin[], payload: AtomicStyle) => AtomicStyle;
483
+ autocompleteConfigUpdated: (plugins: _pikacss_core.EnginePlugin[]) => void;
484
+ };
448
485
  extract: ExtractFn;
449
486
  store: {
450
487
  atomicStyleIds: Map<string, string>;
package/dist/index.mjs CHANGED
@@ -76,12 +76,12 @@ function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
76
76
  const lineEnd = isFormatted ? "\n" : "";
77
77
  const lines = [];
78
78
  blocks.forEach(({ properties, children }, selector) => {
79
- if (properties.length === 0 && children == null)
79
+ if (properties.length === 0 && (children == null || children.size === 0))
80
80
  return;
81
81
  lines.push(...[
82
82
  `${blockIndent}${selector}${selectorEnd}{`,
83
83
  ...properties.map(({ property, value }) => `${blockBodyIndent}${property}:${propertySpace}${value};`),
84
- ...children != null ? [renderCSSStyleBlocks(children, isFormatted, depth + 1)] : [],
84
+ ...children != null && children.size > 0 ? [renderCSSStyleBlocks(children, isFormatted, depth + 1)] : [],
85
85
  `${blockIndent}}`
86
86
  ]);
87
87
  });
@@ -238,6 +238,14 @@ function defineEnginePlugin(plugin) {
238
238
  return plugin;
239
239
  }
240
240
 
241
+ function modifyPropertyValue(value) {
242
+ if (value == null)
243
+ return null;
244
+ if (Array.isArray(value)) {
245
+ return [`${value[0]} !important`, value[1].map((i) => `${i} !important`)];
246
+ }
247
+ return `${value} !important`;
248
+ }
241
249
  function important() {
242
250
  let defaultValue;
243
251
  return defineEnginePlugin({
@@ -258,10 +266,10 @@ function important() {
258
266
  return rest;
259
267
  return Object.fromEntries(
260
268
  Object.entries(rest).map(([k, v]) => {
261
- if (isPropertyValue(v) === false || v == null)
262
- return [k, v];
263
- const modified = [v].flat(2).map((v2) => `${v2} !important`);
264
- return [k, modified];
269
+ if (isPropertyValue(v)) {
270
+ return [k, modifyPropertyValue(v)];
271
+ }
272
+ return [k, v];
265
273
  })
266
274
  );
267
275
  });
@@ -275,7 +283,7 @@ function keyframes() {
275
283
  return defineEnginePlugin({
276
284
  name: "core:keyframes",
277
285
  rawConfigConfigured(config) {
278
- resolveKeyframesConfig = createResolveConfigFn$1({
286
+ resolveKeyframesConfig = createResolveConfigFn({
279
287
  pruneUnused: config.keyframes?.pruneUnused
280
288
  });
281
289
  configList = config.keyframes?.keyframes ?? [];
@@ -298,7 +306,7 @@ function keyframes() {
298
306
  }
299
307
  };
300
308
  engine.keyframes.add(...configList);
301
- engine.addPreflight((engine2, isFormatted) => {
309
+ engine.addPreflight((engine2) => {
302
310
  const maybeUsedName = /* @__PURE__ */ new Set();
303
311
  engine2.store.atomicStyles.forEach(({ content: { property, value } }) => {
304
312
  if (property === "animationName") {
@@ -314,30 +322,22 @@ function keyframes() {
314
322
  });
315
323
  }
316
324
  });
317
- return renderCSSStyleBlocks(
318
- new Map(Array.from(engine2.keyframes.store.entries()).filter(([name, { pruneUnused }]) => pruneUnused === false || maybeUsedName.has(name)).map(([name, { frames }]) => [
319
- `@keyframes ${name}`,
320
- {
321
- properties: [],
322
- children: new Map(Object.entries(frames).map(([frame, properties]) => [
323
- frame,
324
- {
325
- properties: Object.entries(properties).filter(([_, value]) => isNotNullish(value)).flatMap(([property, value]) => {
326
- if (Array.isArray(value))
327
- return value.map((v) => ({ property, value: String(v) }));
328
- return { property, value: String(value) };
329
- })
330
- }
331
- ]))
332
- }
333
- ])),
334
- isFormatted
335
- );
325
+ const maybeUsedKeyframes = Array.from(engine2.keyframes.store.values()).filter(({ name, frames, pruneUnused }) => (pruneUnused === false || maybeUsedName.has(name)) && frames != null);
326
+ const preflightDefinition = {};
327
+ maybeUsedKeyframes.forEach(({ name, frames }) => {
328
+ preflightDefinition[`@keyframes ${name}`] = Object.fromEntries(
329
+ Object.entries(frames).map(([frame, properties]) => [
330
+ frame,
331
+ properties
332
+ ])
333
+ );
334
+ });
335
+ return preflightDefinition;
336
336
  });
337
337
  }
338
338
  });
339
339
  }
340
- function createResolveConfigFn$1({
340
+ function createResolveConfigFn({
341
341
  pruneUnused: defaultPruneUnused = true
342
342
  } = {}) {
343
343
  return function resolveKeyframesConfig(config) {
@@ -684,67 +684,92 @@ function resolveShortcutConfig(config) {
684
684
  }
685
685
 
686
686
  function variables() {
687
- let resolveVariableConfig;
688
- let configList;
687
+ let resolveVariables;
688
+ let rawVariables;
689
+ let safeSet;
689
690
  return defineEnginePlugin({
690
691
  name: "core:variables",
691
692
  rawConfigConfigured(config) {
692
- resolveVariableConfig = createResolveConfigFn({
693
+ resolveVariables = createResolveVariablesFn({
693
694
  pruneUnused: config.variables?.pruneUnused
694
695
  });
695
- configList = config.variables?.variables ?? [];
696
+ rawVariables = config.variables?.variables ?? {};
697
+ safeSet = new Set(config.variables?.safeList ?? []);
696
698
  },
697
699
  configureEngine(engine) {
698
700
  engine.variables = {
699
701
  store: /* @__PURE__ */ new Map(),
700
- add: (...list) => {
701
- list.forEach((config) => {
702
- const resolved = resolveVariableConfig(config);
703
- const { name: _name, value, autocomplete: { asValueOf, asProperty } } = resolved;
704
- const name = normalizeVariableName(_name);
702
+ add: (variables2) => {
703
+ const list = resolveVariables(variables2);
704
+ list.forEach((resolved) => {
705
+ const { name, value, autocomplete: { asValueOf, asProperty } } = resolved;
705
706
  asValueOf.forEach((p) => {
706
707
  if (p !== "-")
707
708
  engine.appendAutocompleteCssPropertyValues(p, `var(${name})`);
708
709
  });
709
710
  if (asProperty)
710
711
  engine.appendAutocompleteExtraCssProperties(name);
711
- if (value != null)
712
- engine.variables.store.set(name, resolved);
712
+ if (value != null) {
713
+ const list2 = engine.variables.store.get(name) ?? [];
714
+ list2.push(resolved);
715
+ engine.variables.store.set(name, list2);
716
+ }
713
717
  });
714
718
  engine.notifyPreflightUpdated();
715
719
  }
716
720
  };
717
- engine.variables.add(...configList);
718
- engine.addPreflight((engine2, isFormatted) => {
721
+ engine.variables.add(rawVariables);
722
+ engine.addPreflight(async (engine2) => {
719
723
  const used = /* @__PURE__ */ new Set();
720
724
  engine2.store.atomicStyles.forEach(({ content: { value } }) => {
721
725
  value.flatMap(extractUsedVarNames).forEach((name) => used.add(normalizeVariableName(name)));
722
726
  });
723
- return renderCSSStyleBlocks(
724
- /* @__PURE__ */ new Map([[
725
- ":root",
726
- {
727
- properties: Array.from(engine2.variables.store.entries()).filter(([name, { pruneUnused, value }]) => (pruneUnused === false || used.has(name)) && value != null).map(([name, { value }]) => ({ property: name, value }))
728
- }
729
- ]]),
730
- isFormatted
731
- );
727
+ const usedVariables = Array.from(engine2.variables.store.values()).flat().filter(({ name, pruneUnused, value }) => (safeSet.has(name) || pruneUnused === false || used.has(name)) && value != null);
728
+ const preflightDefinition = {};
729
+ for (const { name, value, selector: _selector } of usedVariables) {
730
+ const selector = await engine2.pluginHooks.transformSelectors(engine2.config.plugins, _selector);
731
+ let current = preflightDefinition;
732
+ selector.forEach((s) => {
733
+ current[s] ||= {};
734
+ current = current[s];
735
+ });
736
+ Object.assign(current, { [name]: value });
737
+ }
738
+ return preflightDefinition;
732
739
  });
733
740
  }
734
741
  });
735
742
  }
736
- function createResolveConfigFn({
743
+ function createResolveVariablesFn({
737
744
  pruneUnused: defaultPruneUnused = true
738
745
  } = {}) {
739
- return function resolveVariableConfig(config) {
740
- if (typeof config === "string")
741
- return { name: config, value: null, autocomplete: { asValueOf: ["*"], asProperty: true }, pruneUnused: defaultPruneUnused };
742
- if (Array.isArray(config)) {
743
- const [name2, value2, { asValueOf: asValueOf2 = "*", asProperty: asProperty2 = true } = {}, pruneUnused2 = defaultPruneUnused] = config;
744
- return { name: name2, value: value2, autocomplete: { asValueOf: [asValueOf2].flat(), asProperty: asProperty2 }, pruneUnused: pruneUnused2 };
746
+ function _resolveVariables(variables2, levels, result) {
747
+ for (const [key, value] of Object.entries(variables2)) {
748
+ if (key.startsWith("--")) {
749
+ const isObject = typeof value === "object" && value !== null && !Array.isArray(value);
750
+ const {
751
+ value: varValue,
752
+ autocomplete = {},
753
+ pruneUnused = defaultPruneUnused
754
+ } = isObject ? value : { value };
755
+ result.push({
756
+ name: key,
757
+ value: varValue,
758
+ selector: levels.length > 0 ? levels : [":root"],
759
+ autocomplete: {
760
+ asValueOf: autocomplete.asValueOf ? [autocomplete.asValueOf].flat() : ["*"],
761
+ asProperty: autocomplete.asProperty ?? true
762
+ },
763
+ pruneUnused
764
+ });
765
+ } else {
766
+ _resolveVariables(value, [...levels, key], result);
767
+ }
745
768
  }
746
- const { name, value, autocomplete: { asValueOf = "*", asProperty = true } = {}, pruneUnused = defaultPruneUnused } = config;
747
- return { name, value, autocomplete: { asValueOf: [asValueOf].flat(), asProperty }, pruneUnused };
769
+ return result;
770
+ }
771
+ return function resolveVariables(variables2) {
772
+ return _resolveVariables(variables2, [], []);
748
773
  };
749
774
  }
750
775
  const VAR_NAME_RE = /var\((--[\w-]+)/g;
@@ -798,6 +823,7 @@ async function createEngine(config = {}) {
798
823
  }
799
824
  class Engine {
800
825
  config;
826
+ pluginHooks = hooks;
801
827
  extract;
802
828
  store = {
803
829
  atomicStyleIds: /* @__PURE__ */ new Map(),
@@ -876,13 +902,13 @@ class Engine {
876
902
  }
877
903
  async renderPreflights(isFormatted) {
878
904
  const lineEnd = isFormatted ? "\n" : "";
879
- return (await Promise.all(this.config.preflights.map((p) => {
880
- const result = p(this, isFormatted);
905
+ return (await Promise.all(this.config.preflights.map(async (p) => {
906
+ const result = await p(this, isFormatted);
881
907
  if (typeof result === "string")
882
908
  return result;
883
- return renderPreflight({
909
+ return renderPreflightDefinition({
884
910
  engine: this,
885
- preflight: result,
911
+ preflightDefinition: result,
886
912
  isFormatted
887
913
  });
888
914
  }))).join(lineEnd);
@@ -1006,7 +1032,7 @@ function renderAtomicStyles(payload) {
1006
1032
  });
1007
1033
  return renderCSSStyleBlocks(blocks, isFormatted);
1008
1034
  }
1009
- async function _renderPreflight({
1035
+ async function _renderPreflightDefinition({
1010
1036
  engine,
1011
1037
  preflightDefinition,
1012
1038
  blocks = /* @__PURE__ */ new Map()
@@ -1039,7 +1065,7 @@ async function _renderPreflight({
1039
1065
  } else {
1040
1066
  currentBlockBody.children ||= /* @__PURE__ */ new Map();
1041
1067
  currentBlockBody.children.set(k, currentBlockBody.children.get(k) || { properties: [] });
1042
- _renderPreflight({
1068
+ await _renderPreflightDefinition({
1043
1069
  engine,
1044
1070
  preflightDefinition: { [k]: v },
1045
1071
  blocks: currentBlockBody.children
@@ -1049,11 +1075,11 @@ async function _renderPreflight({
1049
1075
  }
1050
1076
  return blocks;
1051
1077
  }
1052
- async function renderPreflight(payload) {
1053
- const { engine, preflight, isFormatted } = payload;
1054
- const blocks = await _renderPreflight({
1078
+ async function renderPreflightDefinition(payload) {
1079
+ const { engine, preflightDefinition, isFormatted } = payload;
1080
+ const blocks = await _renderPreflightDefinition({
1055
1081
  engine,
1056
- preflightDefinition: preflight
1082
+ preflightDefinition
1057
1083
  });
1058
1084
  return renderCSSStyleBlocks(blocks, isFormatted);
1059
1085
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.0.28",
7
+ "version": "0.0.30",
8
8
  "author": "DevilTea <ch19980814@gmail.com>",
9
9
  "license": "MIT",
10
10
  "repository": {