@pikacss/core 0.0.47 → 0.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/index.d.mts +2990 -1324
- package/dist/index.mjs +1228 -71
- package/package.json +10 -5
package/dist/index.mjs
CHANGED
|
@@ -810,7 +810,6 @@ const PROPERTY_EFFECTS = {
|
|
|
810
810
|
"z-index": ["z-index"],
|
|
811
811
|
"zoom": ["zoom"]
|
|
812
812
|
};
|
|
813
|
-
|
|
814
813
|
//#endregion
|
|
815
814
|
//#region src/internal/property-effects.ts
|
|
816
815
|
const UNIVERSAL_EFFECT = "*";
|
|
@@ -821,11 +820,45 @@ function isCustomProperty(property) {
|
|
|
821
820
|
function isVendorPrefixedProperty(property) {
|
|
822
821
|
return property.startsWith("-") && property.startsWith("--") === false;
|
|
823
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Returns the list of CSS properties that a given property can affect, accounting for shorthand expansion.
|
|
825
|
+
* @internal
|
|
826
|
+
*
|
|
827
|
+
* @param property - A CSS property name in kebab-case.
|
|
828
|
+
* @returns An array of affected property names. For the `all` shorthand, returns `['*']`. For custom or vendor-prefixed properties, returns the property itself. For standard shorthands, returns the set of longhand properties they expand to.
|
|
829
|
+
*
|
|
830
|
+
* @remarks Used by the order-sensitivity detection logic to determine whether two properties in the same scope share overlapping effects (e.g. `margin` and `margin-top`). The lookup map is generated at build time from the CSS shorthand specification.
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```ts
|
|
834
|
+
* getPropertyEffects('margin') // ['margin-top', 'margin-right', 'margin-bottom', 'margin-left']
|
|
835
|
+
* getPropertyEffects('color') // ['color']
|
|
836
|
+
* getPropertyEffects('--my-var') // ['--my-var']
|
|
837
|
+
* getPropertyEffects('all') // ['*']
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
824
840
|
function getPropertyEffects(property) {
|
|
825
841
|
if (property === "all") return [UNIVERSAL_EFFECT];
|
|
826
842
|
if (isCustomProperty(property) || isVendorPrefixedProperty(property)) return [property];
|
|
827
843
|
return propertyEffectsLookup[property] || [property];
|
|
828
844
|
}
|
|
845
|
+
/**
|
|
846
|
+
* Determines whether two CSS properties have overlapping effects, meaning they can interfere with each other when both are present in the same selector scope.
|
|
847
|
+
* @internal
|
|
848
|
+
*
|
|
849
|
+
* @param left - First CSS property name in kebab-case.
|
|
850
|
+
* @param right - Second CSS property name in kebab-case.
|
|
851
|
+
* @returns `true` if the two properties share at least one common affected property or if either is the universal `all` shorthand.
|
|
852
|
+
*
|
|
853
|
+
* @remarks Custom properties never overlap with other properties. Identical properties always overlap. This check drives the order-sensitivity detection in `optimizeAtomicStyleContents`, ensuring that shorthand/longhand conflicts like `margin` + `margin-top` are correctly handled.
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* hasPropertyEffectOverlap('margin', 'margin-top') // true
|
|
858
|
+
* hasPropertyEffectOverlap('color', 'font-size') // false
|
|
859
|
+
* hasPropertyEffectOverlap('all', 'color') // true
|
|
860
|
+
* ```
|
|
861
|
+
*/
|
|
829
862
|
function hasPropertyEffectOverlap(left, right) {
|
|
830
863
|
if (left === right) return true;
|
|
831
864
|
if (isCustomProperty(left) || isCustomProperty(right)) return false;
|
|
@@ -835,9 +868,24 @@ function hasPropertyEffectOverlap(left, right) {
|
|
|
835
868
|
const rightEffectSet = new Set(rightEffects);
|
|
836
869
|
return leftEffects.some((effect) => rightEffectSet.has(effect));
|
|
837
870
|
}
|
|
838
|
-
|
|
839
871
|
//#endregion
|
|
840
872
|
//#region src/internal/utils.ts
|
|
873
|
+
/**
|
|
874
|
+
* Creates a scoped logger with configurable log-level functions and a toggleable debug mode.
|
|
875
|
+
*
|
|
876
|
+
* @param prefix - Label prepended to every log message (e.g. `'[PikaCSS]'`).
|
|
877
|
+
* @returns A logger object with `debug`, `info`, `warn`, `error` methods and configuration setters.
|
|
878
|
+
*
|
|
879
|
+
* @remarks Debug messages are suppressed by default. Call `log.toggleDebug()` to enable them. Each log level can be replaced with a custom implementation via the `set*Fn` methods, which is useful for redirecting output in non-browser environments.
|
|
880
|
+
*
|
|
881
|
+
* @example
|
|
882
|
+
* ```ts
|
|
883
|
+
* const log = createLogger('[MyPlugin]')
|
|
884
|
+
* log.info('initialized') // '[MyPlugin][INFO] initialized'
|
|
885
|
+
* log.toggleDebug()
|
|
886
|
+
* log.debug('verbose info') // '[MyPlugin][DEBUG] verbose info'
|
|
887
|
+
* ```
|
|
888
|
+
*/
|
|
841
889
|
function createLogger(prefix) {
|
|
842
890
|
let currentPrefix = prefix;
|
|
843
891
|
let enabledDebug = false;
|
|
@@ -879,9 +927,36 @@ function createLogger(prefix) {
|
|
|
879
927
|
}
|
|
880
928
|
};
|
|
881
929
|
}
|
|
930
|
+
/**
|
|
931
|
+
* Default logger instance used throughout the PikaCSS core engine, prefixed with `[PikaCSS]`.
|
|
932
|
+
*
|
|
933
|
+
* @remarks Shared across all internal modules. Plugins and integration code can call `log.toggleDebug()` to enable verbose output during development.
|
|
934
|
+
*
|
|
935
|
+
* @example
|
|
936
|
+
* ```ts
|
|
937
|
+
* log.info('Engine created')
|
|
938
|
+
* log.warn('Unknown layer detected')
|
|
939
|
+
* ```
|
|
940
|
+
*/
|
|
882
941
|
const log = createLogger("[PikaCSS]");
|
|
883
942
|
const chars = [..."abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
|
|
884
943
|
const numOfChars = chars.length;
|
|
944
|
+
/**
|
|
945
|
+
* Converts a non-negative integer to a compact alphabetic string using a bijective base-52 encoding (a-z, A-Z).
|
|
946
|
+
* @internal
|
|
947
|
+
*
|
|
948
|
+
* @param num - The non-negative integer to encode.
|
|
949
|
+
* @returns A short alphabetic string unique to the given integer.
|
|
950
|
+
*
|
|
951
|
+
* @remarks Used to generate compact, human-readable atomic style class IDs. The encoding is deterministic: the same number always produces the same string.
|
|
952
|
+
*
|
|
953
|
+
* @example
|
|
954
|
+
* ```ts
|
|
955
|
+
* numberToChars(0) // 'a'
|
|
956
|
+
* numberToChars(51) // 'Z'
|
|
957
|
+
* numberToChars(52) // 'ba'
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
885
960
|
function numberToChars(num) {
|
|
886
961
|
if (num < numOfChars) return chars[num];
|
|
887
962
|
let result = "";
|
|
@@ -893,34 +968,164 @@ function numberToChars(num) {
|
|
|
893
968
|
return result;
|
|
894
969
|
}
|
|
895
970
|
const UPPER_CASE = /[A-Z]/g;
|
|
971
|
+
/**
|
|
972
|
+
* Converts a camelCase string to kebab-case at runtime. CSS custom properties (`--*`) are returned unchanged.
|
|
973
|
+
* @internal
|
|
974
|
+
*
|
|
975
|
+
* @param str - The camelCase string to convert.
|
|
976
|
+
* @returns The kebab-case equivalent of the input string.
|
|
977
|
+
*
|
|
978
|
+
* @remarks Runtime counterpart of the `ToKebab` type utility. Used during style extraction to normalize JavaScript-style property names to CSS property names.
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```ts
|
|
982
|
+
* toKebab('backgroundColor') // 'background-color'
|
|
983
|
+
* toKebab('--my-var') // '--my-var'
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
896
986
|
function toKebab(str) {
|
|
897
987
|
if (str.startsWith("--")) return str;
|
|
898
988
|
return str.replace(UPPER_CASE, (c) => `-${c.toLowerCase()}`);
|
|
899
989
|
}
|
|
990
|
+
/**
|
|
991
|
+
* Type-narrowing guard that returns `true` when the value is neither `null` nor `undefined`.
|
|
992
|
+
* @internal
|
|
993
|
+
*
|
|
994
|
+
* @typeParam T - The type of the input value.
|
|
995
|
+
* @param value - The value to test.
|
|
996
|
+
* @returns `true` if the value is non-nullish, narrowing the type to `NonNullable<T>`.
|
|
997
|
+
*
|
|
998
|
+
* @remarks Commonly used as a `.filter()` predicate to strip nullish entries from arrays while preserving the narrowed type.
|
|
999
|
+
*
|
|
1000
|
+
* @example
|
|
1001
|
+
* ```ts
|
|
1002
|
+
* [1, null, 2, undefined].filter(isNotNullish) // [1, 2] typed as number[]
|
|
1003
|
+
* ```
|
|
1004
|
+
*/
|
|
900
1005
|
function isNotNullish(value) {
|
|
901
1006
|
return value != null;
|
|
902
1007
|
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Type-narrowing guard that returns `true` when the value is not a string, narrowing the type to `Exclude<V, string>`.
|
|
1010
|
+
* @internal
|
|
1011
|
+
*
|
|
1012
|
+
* @typeParam V - The union type of the input value.
|
|
1013
|
+
* @param value - The value to test.
|
|
1014
|
+
* @returns `true` if the value is not a `string`.
|
|
1015
|
+
*
|
|
1016
|
+
* @remarks Useful for filtering processed style items to separate resolved definition objects from unresolved string references.
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```ts
|
|
1020
|
+
* const items: (string | object)[] = ['btn', { color: 'red' }]
|
|
1021
|
+
* const objects = items.filter(isNotString) // [{ color: 'red' }]
|
|
1022
|
+
* ```
|
|
1023
|
+
*/
|
|
903
1024
|
function isNotString(value) {
|
|
904
1025
|
return typeof value !== "string";
|
|
905
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Tests whether a value conforms to the `InternalPropertyValue` shape: a string, a `[value, fallback[]]` tuple, or nullish.
|
|
1029
|
+
* @internal
|
|
1030
|
+
*
|
|
1031
|
+
* @param v - The value to inspect.
|
|
1032
|
+
* @returns `true` if the value is a valid property value.
|
|
1033
|
+
*
|
|
1034
|
+
* @remarks During extraction, the engine uses this guard to distinguish CSS property values from nested selector objects or style item arrays.
|
|
1035
|
+
*
|
|
1036
|
+
* @example
|
|
1037
|
+
* ```ts
|
|
1038
|
+
* isPropertyValue('red') // true
|
|
1039
|
+
* isPropertyValue(['red', ['blue']]) // true
|
|
1040
|
+
* isPropertyValue(null) // true
|
|
1041
|
+
* isPropertyValue({ color: 'red' }) // false
|
|
1042
|
+
* ```
|
|
1043
|
+
*/
|
|
906
1044
|
function isPropertyValue(v) {
|
|
907
1045
|
if (Array.isArray(v)) return v.length === 2 && typeof v[0] === "string" && Array.isArray(v[1]) && v[1].every((i) => typeof i === "string");
|
|
908
1046
|
if (v == null) return true;
|
|
909
1047
|
if (typeof v === "string") return true;
|
|
910
1048
|
return false;
|
|
911
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Serializes a value to a JSON string for use as a deterministic cache key.
|
|
1052
|
+
* @internal
|
|
1053
|
+
*
|
|
1054
|
+
* @param value - The value to serialize.
|
|
1055
|
+
* @returns The JSON string representation.
|
|
1056
|
+
*
|
|
1057
|
+
* @remarks Used to produce stable keys for selector chains and property content when building deduplication maps in the optimization pipeline.
|
|
1058
|
+
*
|
|
1059
|
+
* @example
|
|
1060
|
+
* ```ts
|
|
1061
|
+
* serialize(['.pk-%', 'color']) // '[[".pk-%"],"color"]'
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
912
1064
|
function serialize(value) {
|
|
913
1065
|
return JSON.stringify(value);
|
|
914
1066
|
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Adds one or more values to a `Set` and returns whether the set's size increased.
|
|
1069
|
+
* @internal
|
|
1070
|
+
*
|
|
1071
|
+
* @typeParam T - The element type of the set.
|
|
1072
|
+
* @param set - The target set to append to.
|
|
1073
|
+
* @param values - Values to add.
|
|
1074
|
+
* @returns `true` if at least one new element was added (the set grew).
|
|
1075
|
+
*
|
|
1076
|
+
* @remarks The boolean return is used by `appendAutocomplete` to determine whether the autocomplete config actually changed, avoiding unnecessary notification callbacks.
|
|
1077
|
+
*
|
|
1078
|
+
* @example
|
|
1079
|
+
* ```ts
|
|
1080
|
+
* const s = new Set(['a'])
|
|
1081
|
+
* addToSet(s, 'a', 'b') // true (added 'b')
|
|
1082
|
+
* addToSet(s, 'a') // false (no change)
|
|
1083
|
+
* ```
|
|
1084
|
+
*/
|
|
915
1085
|
function addToSet(set, ...values) {
|
|
916
1086
|
const before = set.size;
|
|
917
1087
|
values.forEach((value) => set.add(value));
|
|
918
1088
|
return set.size !== before;
|
|
919
1089
|
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Flattens an `Arrayable<string>` value and adds all entries to a `Set`, returning whether the set grew.
|
|
1092
|
+
* @internal
|
|
1093
|
+
*
|
|
1094
|
+
* @param set - The target set to append to.
|
|
1095
|
+
* @param values - A single string or array of strings to add, or `undefined`/`null` to skip.
|
|
1096
|
+
* @returns `true` if at least one new entry was added; `false` if the input was nullish or all entries already existed.
|
|
1097
|
+
*
|
|
1098
|
+
* @remarks Short-circuits on nullish input for convenience, since many autocomplete contribution fields are optional.
|
|
1099
|
+
*
|
|
1100
|
+
* @example
|
|
1101
|
+
* ```ts
|
|
1102
|
+
* const s = new Set<string>()
|
|
1103
|
+
* appendAutocompleteEntries(s, 'hover') // true
|
|
1104
|
+
* appendAutocompleteEntries(s, ['hover']) // false (already present)
|
|
1105
|
+
* appendAutocompleteEntries(s, undefined) // false
|
|
1106
|
+
* ```
|
|
1107
|
+
*/
|
|
920
1108
|
function appendAutocompleteEntries(set, values) {
|
|
921
1109
|
if (values == null) return false;
|
|
922
1110
|
return addToSet(set, ...[values].flat());
|
|
923
1111
|
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Merges a record of `Arrayable<string>` values into a `Map<string, string[]>`, returning whether any entry was added.
|
|
1114
|
+
* @internal
|
|
1115
|
+
*
|
|
1116
|
+
* @param map - The target map to append entries to.
|
|
1117
|
+
* @param entries - A record mapping keys to single or arrayed string values, or `undefined` to skip.
|
|
1118
|
+
* @returns `true` if at least one entry was added or extended; `false` if the input was nullish or empty.
|
|
1119
|
+
*
|
|
1120
|
+
* @remarks Existing map entries are extended (not replaced) with the new values, maintaining all previously registered suggestions for a given key. This accumulative behavior allows multiple plugins to contribute value suggestions for the same property.
|
|
1121
|
+
*
|
|
1122
|
+
* @example
|
|
1123
|
+
* ```ts
|
|
1124
|
+
* const map = new Map<string, string[]>()
|
|
1125
|
+
* appendAutocompleteRecordEntries(map, { color: ['red', 'blue'] }) // true
|
|
1126
|
+
* appendAutocompleteRecordEntries(map, { color: 'green' }) // true (now ['red','blue','green'])
|
|
1127
|
+
* ```
|
|
1128
|
+
*/
|
|
924
1129
|
function appendAutocompleteRecordEntries(map, entries) {
|
|
925
1130
|
if (entries == null) return false;
|
|
926
1131
|
let changed = false;
|
|
@@ -937,21 +1142,56 @@ function normalizeAutocompleteRecordEntries(entries) {
|
|
|
937
1142
|
if (entries == null) return void 0;
|
|
938
1143
|
return Array.isArray(entries) ? Object.fromEntries(entries) : entries;
|
|
939
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Merges an `AutocompleteContribution` or `AutocompleteConfig` into the resolved autocomplete state, returning whether any entry changed.
|
|
1147
|
+
*
|
|
1148
|
+
* @param config - The resolved engine config (or a subset with the `autocomplete` field) to mutate.
|
|
1149
|
+
* @param contribution - The autocomplete entries to merge in.
|
|
1150
|
+
* @returns `true` if any selector, shortcut, property, CSS property, or pattern entry was added or extended.
|
|
1151
|
+
*
|
|
1152
|
+
* @remarks Called by `engine.appendAutocomplete()` and during initial config resolution. Each sub-field (selectors, shortcuts, etc.) is independently merged and the function returns `true` if any of them changed, which triggers an `autocompleteConfigUpdated` notification.
|
|
1153
|
+
*
|
|
1154
|
+
* @example
|
|
1155
|
+
* ```ts
|
|
1156
|
+
* const changed = appendAutocomplete(resolvedConfig, {
|
|
1157
|
+
* selectors: 'dark',
|
|
1158
|
+
* cssProperties: { color: 'primary' },
|
|
1159
|
+
* })
|
|
1160
|
+
* ```
|
|
1161
|
+
*/
|
|
940
1162
|
function appendAutocomplete(config, contribution) {
|
|
941
1163
|
const { patterns, properties, cssProperties, ...literals } = contribution;
|
|
942
1164
|
return [
|
|
943
1165
|
appendAutocompleteEntries(config.autocomplete.selectors, literals.selectors),
|
|
944
|
-
appendAutocompleteEntries(config.autocomplete.
|
|
1166
|
+
appendAutocompleteEntries(config.autocomplete.shortcuts, literals.shortcuts),
|
|
945
1167
|
appendAutocompleteEntries(config.autocomplete.extraProperties, literals.extraProperties),
|
|
946
1168
|
appendAutocompleteEntries(config.autocomplete.extraCssProperties, literals.extraCssProperties),
|
|
947
1169
|
appendAutocompleteRecordEntries(config.autocomplete.properties, normalizeAutocompleteRecordEntries(properties)),
|
|
948
1170
|
appendAutocompleteRecordEntries(config.autocomplete.cssProperties, normalizeAutocompleteRecordEntries(cssProperties)),
|
|
949
1171
|
appendAutocompleteEntries(config.autocomplete.patterns.selectors, patterns?.selectors),
|
|
950
|
-
appendAutocompleteEntries(config.autocomplete.patterns.
|
|
1172
|
+
appendAutocompleteEntries(config.autocomplete.patterns.shortcuts, patterns?.shortcuts),
|
|
951
1173
|
appendAutocompleteRecordEntries(config.autocomplete.patterns.properties, patterns?.properties),
|
|
952
1174
|
appendAutocompleteRecordEntries(config.autocomplete.patterns.cssProperties, patterns?.cssProperties)
|
|
953
1175
|
].some(Boolean);
|
|
954
1176
|
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Serializes a `CSSStyleBlocks` tree into a CSS string, optionally formatted with indentation and newlines.
|
|
1179
|
+
*
|
|
1180
|
+
* @param blocks - The CSS block tree to render.
|
|
1181
|
+
* @param isFormatted - When `true`, output includes indentation and newlines for readability; when `false`, output is minified.
|
|
1182
|
+
* @param depth - Current nesting depth for indentation (defaults to `0`).
|
|
1183
|
+
* @returns The rendered CSS string.
|
|
1184
|
+
*
|
|
1185
|
+
* @remarks Recursively renders nested blocks (e.g. media queries wrapping selectors). Empty blocks (no properties and no children) are omitted from the output.
|
|
1186
|
+
*
|
|
1187
|
+
* @example
|
|
1188
|
+
* ```ts
|
|
1189
|
+
* const blocks: CSSStyleBlocks = new Map()
|
|
1190
|
+
* blocks.set('.pk-a', { properties: [{ property: 'color', value: 'red' }] })
|
|
1191
|
+
* renderCSSStyleBlocks(blocks, true)
|
|
1192
|
+
* // '.pk-a {\n color: red;\n}'
|
|
1193
|
+
* ```
|
|
1194
|
+
*/
|
|
955
1195
|
function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
|
|
956
1196
|
const blockIndent = isFormatted ? " ".repeat(depth) : "";
|
|
957
1197
|
const blockBodyIndent = isFormatted ? " ".repeat(depth + 1) : "";
|
|
@@ -970,9 +1210,22 @@ function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
|
|
|
970
1210
|
});
|
|
971
1211
|
return lines.join(lineEnd);
|
|
972
1212
|
}
|
|
973
|
-
|
|
974
1213
|
//#endregion
|
|
975
1214
|
//#region src/internal/atomic-style.ts
|
|
1215
|
+
/**
|
|
1216
|
+
* Creates a fresh, empty `EngineStore` with all maps initialized.
|
|
1217
|
+
* @internal
|
|
1218
|
+
*
|
|
1219
|
+
* @returns A new `EngineStore` instance with empty maps.
|
|
1220
|
+
*
|
|
1221
|
+
* @remarks Called once during engine construction. Each engine instance owns a single store.
|
|
1222
|
+
*
|
|
1223
|
+
* @example
|
|
1224
|
+
* ```ts
|
|
1225
|
+
* const store = createEngineStore()
|
|
1226
|
+
* store.atomicStyles.size // 0
|
|
1227
|
+
* ```
|
|
1228
|
+
*/
|
|
976
1229
|
function createEngineStore() {
|
|
977
1230
|
return {
|
|
978
1231
|
atomicStyleIds: /* @__PURE__ */ new Map(),
|
|
@@ -981,6 +1234,24 @@ function createEngineStore() {
|
|
|
981
1234
|
atomicStyleOrder: /* @__PURE__ */ new Map()
|
|
982
1235
|
};
|
|
983
1236
|
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Assigns or retrieves a compact atomic style ID for the given resolved style content.
|
|
1239
|
+
* @internal
|
|
1240
|
+
*
|
|
1241
|
+
* @param options - Object containing the style `content`, the engine `prefix`, and the `stored` ID map.
|
|
1242
|
+
* @param options.content - The resolved style content to hash and identify.
|
|
1243
|
+
* @param options.prefix - The class-name prefix used when constructing a new atomic style ID.
|
|
1244
|
+
* @param options.stored - The map that caches assigned IDs by serialized key.
|
|
1245
|
+
* @returns The short alphabetic ID string (e.g. `'pk-a'`, `'pk-bA'`).
|
|
1246
|
+
*
|
|
1247
|
+
* @remarks For non-order-sensitive content, returns a cached ID if one already exists for the same base key. For order-sensitive content (where `orderSensitiveTo` is set), always generates a new ID to prevent incorrect reuse across different call-site orderings.
|
|
1248
|
+
*
|
|
1249
|
+
* @example
|
|
1250
|
+
* ```ts
|
|
1251
|
+
* const id = getAtomicStyleId({ content, prefix: 'pk-', stored: store.atomicStyleIds })
|
|
1252
|
+
* // 'pk-a'
|
|
1253
|
+
* ```
|
|
1254
|
+
*/
|
|
984
1255
|
function getAtomicStyleId({ content, prefix, stored }) {
|
|
985
1256
|
const baseKey = getAtomicStyleBaseKey(content);
|
|
986
1257
|
if (isOrderSensitiveContent(content) === false) {
|
|
@@ -1001,6 +1272,26 @@ function getAtomicStyleId({ content, prefix, stored }) {
|
|
|
1001
1272
|
log.debug(`Generated new atomic style ID: ${id}`);
|
|
1002
1273
|
return id;
|
|
1003
1274
|
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Resolves a `StyleContent` into an atomic style: either reusing an existing ID or creating a new `AtomicStyle` entry in the store.
|
|
1277
|
+
* @internal
|
|
1278
|
+
*
|
|
1279
|
+
* @param options - Object containing the style `content`, `prefix`, `store`, and the per-use-call `resolvedIdsByBaseKey` map for order-sensitive reuse tracking.
|
|
1280
|
+
* @param options.content - The style content to resolve into a cached or newly registered atomic style.
|
|
1281
|
+
* @param options.prefix - The atomic style ID prefix for any newly created IDs.
|
|
1282
|
+
* @param options.store - The engine store holding existing atomic styles and lookup maps.
|
|
1283
|
+
* @param options.resolvedIdsByBaseKey - Per-call memoization map for reusing order-sensitive IDs within one `engine.use()` execution.
|
|
1284
|
+
* @returns An `AtomicStyleResolution` with the assigned `id` and optionally the newly created `atomicStyle` (absent when the ID was already registered).
|
|
1285
|
+
*
|
|
1286
|
+
* @remarks First checks for reusable order-sensitive IDs within the current `engine.use()` call, then falls back to `getAtomicStyleId` for general ID assignment. When a new atomic style is created, it is registered in all store indices.
|
|
1287
|
+
*
|
|
1288
|
+
* @example
|
|
1289
|
+
* ```ts
|
|
1290
|
+
* const { id, atomicStyle } = resolveAtomicStyle({
|
|
1291
|
+
* content, prefix: 'pk-', store, resolvedIdsByBaseKey,
|
|
1292
|
+
* })
|
|
1293
|
+
* ```
|
|
1294
|
+
*/
|
|
1004
1295
|
function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey }) {
|
|
1005
1296
|
const reusableId = findReusableOrderSensitiveAtomicStyleId({
|
|
1006
1297
|
content,
|
|
@@ -1027,6 +1318,19 @@ function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey }) {
|
|
|
1027
1318
|
atomicStyle
|
|
1028
1319
|
};
|
|
1029
1320
|
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Deduplicates and optimizes a list of extracted style contents by merging duplicate selector-property pairs and detecting order-sensitive shorthand overlaps.
|
|
1323
|
+
* @internal
|
|
1324
|
+
* @param list - The raw extracted style contents to optimize.
|
|
1325
|
+
* @returns An optimized array of `StyleContent` entries with nullish-value removals applied and `orderSensitiveTo` metadata attached where needed.
|
|
1326
|
+
*
|
|
1327
|
+
* @remarks Later definitions of the same selector-property pair cancel earlier ones. When two properties in the same scope share overlapping CSS effects (e.g. `margin` and `margin-top`), the later one is marked as order-sensitive to prevent incorrect ID reuse.
|
|
1328
|
+
*
|
|
1329
|
+
* @example
|
|
1330
|
+
* ```ts
|
|
1331
|
+
* const optimized = optimizeAtomicStyleContents(extractedList)
|
|
1332
|
+
* ```
|
|
1333
|
+
*/
|
|
1030
1334
|
function optimizeAtomicStyleContents(list) {
|
|
1031
1335
|
const map = /* @__PURE__ */ new Map();
|
|
1032
1336
|
const scopedEntries = /* @__PURE__ */ new Map();
|
|
@@ -1051,6 +1355,20 @@ function optimizeAtomicStyleContents(list) {
|
|
|
1051
1355
|
});
|
|
1052
1356
|
return [...map.values()];
|
|
1053
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Computes the base cache key for an atomic style from its selector, property, and value.
|
|
1360
|
+
* @internal
|
|
1361
|
+
*
|
|
1362
|
+
* @param content - An object with `selector`, `property`, and `value` fields.
|
|
1363
|
+
* @returns A deterministic serialized string key.
|
|
1364
|
+
*
|
|
1365
|
+
* @remarks Used for deduplication: two atomic styles with the same base key are considered equivalent (unless order-sensitive). The key is derived by serializing the triple `[selector, property, value]`.
|
|
1366
|
+
*
|
|
1367
|
+
* @example
|
|
1368
|
+
* ```ts
|
|
1369
|
+
* const key = getAtomicStyleBaseKey({ selector: ['.pk-__ID__'], property: 'color', value: ['red'] })
|
|
1370
|
+
* ```
|
|
1371
|
+
*/
|
|
1054
1372
|
function getAtomicStyleBaseKey(content) {
|
|
1055
1373
|
return serialize([
|
|
1056
1374
|
content.selector,
|
|
@@ -1091,7 +1409,7 @@ function findReusableOrderSensitiveAtomicStyleId({ content, store, resolvedIdsBy
|
|
|
1091
1409
|
if (isOrderSensitiveContent(content) === false) return void 0;
|
|
1092
1410
|
const baseKey = getAtomicStyleBaseKey(content);
|
|
1093
1411
|
const requiredOrder = getRequiredAtomicStyleOrder({
|
|
1094
|
-
dependencyKeys: content.orderSensitiveTo
|
|
1412
|
+
dependencyKeys: content.orderSensitiveTo,
|
|
1095
1413
|
store,
|
|
1096
1414
|
resolvedIdsByBaseKey
|
|
1097
1415
|
});
|
|
@@ -1105,14 +1423,22 @@ function getOrderSensitiveDependencyKeys(scoped, property) {
|
|
|
1105
1423
|
for (const existing of scoped.values()) if (hasPropertyEffectOverlap(existing.property, property)) dependencyKeys.push(getAtomicStyleBaseKey(existing));
|
|
1106
1424
|
return dependencyKeys;
|
|
1107
1425
|
}
|
|
1108
|
-
|
|
1109
1426
|
//#endregion
|
|
1110
1427
|
//#region src/internal/constants.ts
|
|
1428
|
+
/**
|
|
1429
|
+
* CSS `@layer` at-rule prefix used when constructing layer-scoped selectors
|
|
1430
|
+
* in generated stylesheet output.
|
|
1431
|
+
*
|
|
1432
|
+
* @internal
|
|
1433
|
+
*/
|
|
1111
1434
|
const LAYER_SELECTOR_PREFIX = "@layer ";
|
|
1112
|
-
|
|
1113
|
-
|
|
1435
|
+
/**
|
|
1436
|
+
* Global regex matching all occurrences of {@link ATOMIC_STYLE_ID_PLACEHOLDER}
|
|
1437
|
+
* for batch replacement in selector templates.
|
|
1438
|
+
*
|
|
1439
|
+
* @internal
|
|
1440
|
+
*/
|
|
1114
1441
|
const ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL = /%/g;
|
|
1115
|
-
|
|
1116
1442
|
//#endregion
|
|
1117
1443
|
//#region src/internal/extractor.ts
|
|
1118
1444
|
function replaceBySplitAndJoin(str, split, mapFn, join) {
|
|
@@ -1124,9 +1450,42 @@ const RE_SPLIT = /\s*,\s*/g;
|
|
|
1124
1450
|
const DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL = /\$/g;
|
|
1125
1451
|
const ATTRIBUTE_SUFFIX_MATCH = "$=";
|
|
1126
1452
|
const ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL = /\$=/g;
|
|
1453
|
+
/**
|
|
1454
|
+
* Normalizes selector strings by replacing placeholders (`$` → `defaultSelector`, `%` → atomic style ID placeholder) and splitting comma-separated selectors.
|
|
1455
|
+
* @internal
|
|
1456
|
+
*
|
|
1457
|
+
* @param options - Object containing the raw `selectors` array and the `defaultSelector` template.
|
|
1458
|
+
* @param options.selectors - The raw selector strings to normalize.
|
|
1459
|
+
* @param options.defaultSelector - The selector template that replaces `$` placeholders.
|
|
1460
|
+
* @returns An array of normalized selector strings with all placeholders resolved.
|
|
1461
|
+
*
|
|
1462
|
+
* @remarks The `$` character in a selector is replaced with the engine's `defaultSelector`. The `%` character is the atomic style ID placeholder, preserved for later substitution. Attribute suffix matches (`$=`) are protected from the `$` replacement.
|
|
1463
|
+
*
|
|
1464
|
+
* @example
|
|
1465
|
+
* ```ts
|
|
1466
|
+
* normalizeSelectors({ selectors: ['$hover $'], defaultSelector: '.%' })
|
|
1467
|
+
* // ['.%:hover .%'] (conceptually)
|
|
1468
|
+
* ```
|
|
1469
|
+
*/
|
|
1127
1470
|
function normalizeSelectors({ selectors, defaultSelector }) {
|
|
1128
|
-
return selectors.map((s) => replaceBySplitAndJoin(s.replace(RE_SPLIT, ","), ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, (a) => replaceBySplitAndJoin(a, ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL, (b) => replaceBySplitAndJoin(b, DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL, null, defaultSelector), ATTRIBUTE_SUFFIX_MATCH),
|
|
1471
|
+
return selectors.map((s) => replaceBySplitAndJoin(s.replace(RE_SPLIT, ","), ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, (a) => replaceBySplitAndJoin(a, ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL, (b) => replaceBySplitAndJoin(b, DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL, null, defaultSelector), ATTRIBUTE_SUFFIX_MATCH), "%"));
|
|
1129
1472
|
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Normalizes a raw `InternalPropertyValue` into the extraction output format: an array of trimmed, deduplicated CSS value strings with fallbacks ordered before the primary value, or `null`/`undefined` to signal removal.
|
|
1475
|
+
* @internal
|
|
1476
|
+
*
|
|
1477
|
+
* @param value - The raw property value to normalize.
|
|
1478
|
+
* @returns An array of CSS value strings (fallbacks first, primary last), or `null`/`undefined` for removal.
|
|
1479
|
+
*
|
|
1480
|
+
* @remarks For tuple values `[primary, fallbacks]`, duplicates among fallbacks are removed and the primary value is appended last so CSS cascade uses it as the effective value while older browsers fall back to earlier entries.
|
|
1481
|
+
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```ts
|
|
1484
|
+
* normalizeValue('red') // ['red']
|
|
1485
|
+
* normalizeValue(['red', ['blue']]) // ['blue', 'red']
|
|
1486
|
+
* normalizeValue(null) // null
|
|
1487
|
+
* ```
|
|
1488
|
+
*/
|
|
1130
1489
|
function normalizeValue(value) {
|
|
1131
1490
|
if (value == null) return value;
|
|
1132
1491
|
if (Array.isArray(value)) {
|
|
@@ -1146,13 +1505,40 @@ function normalizeValue(value) {
|
|
|
1146
1505
|
}
|
|
1147
1506
|
return [value.trim()];
|
|
1148
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Recursively walks a style definition tree, extracting each CSS property-value pair into a flat list of `ExtractedStyleContent` entries with their full selector chain.
|
|
1510
|
+
* @internal
|
|
1511
|
+
*
|
|
1512
|
+
* @param options - Extraction context: the `styleDefinition` to walk, current nesting `levels`, accumulated `result`, `defaultSelector`, and plugin transform hooks for selectors, style items, and style definitions.
|
|
1513
|
+
* @param options.styleDefinition - The style definition subtree currently being traversed.
|
|
1514
|
+
* @param options.levels - The accumulated nested selector levels leading to this subtree.
|
|
1515
|
+
* @param options.result - The mutable extraction result array being appended to.
|
|
1516
|
+
* @param options.defaultSelector - The selector substituted when extracted selectors omit the atomic-style placeholder.
|
|
1517
|
+
* @param options.transformSelectors - Hook that rewrites selector levels before normalization.
|
|
1518
|
+
* @param options.transformStyleItems - Hook that expands array style items before recursive extraction.
|
|
1519
|
+
* @param options.transformStyleDefinitions - Hook that rewrites style definition objects before traversal.
|
|
1520
|
+
* @returns The accumulated array of `ExtractedStyleContent` entries.
|
|
1521
|
+
*
|
|
1522
|
+
* @remarks Property values are identified using `isPropertyValue`. Array values are treated as style item lists (resolved via `transformStyleItems`). Object values are treated as nested style definitions and recursed into. The transform hooks allow plugins (shortcuts, selectors) to intercept and expand values during extraction.
|
|
1523
|
+
*
|
|
1524
|
+
* @example
|
|
1525
|
+
* ```ts
|
|
1526
|
+
* const contents = await extract({
|
|
1527
|
+
* styleDefinition: { color: 'red', '$hover': { color: 'blue' } },
|
|
1528
|
+
* defaultSelector: '.%',
|
|
1529
|
+
* transformSelectors: async s => s,
|
|
1530
|
+
* transformStyleItems: async i => i,
|
|
1531
|
+
* transformStyleDefinitions: async d => d,
|
|
1532
|
+
* })
|
|
1533
|
+
* ```
|
|
1534
|
+
*/
|
|
1149
1535
|
async function extract({ styleDefinition, levels = [], result = [], defaultSelector, transformSelectors, transformStyleItems, transformStyleDefinitions }) {
|
|
1150
1536
|
for (const definition of await transformStyleDefinitions([styleDefinition])) for (const [k, v] of Object.entries(definition)) if (isPropertyValue(v)) {
|
|
1151
1537
|
const selector = normalizeSelectors({
|
|
1152
1538
|
selectors: await transformSelectors(levels),
|
|
1153
1539
|
defaultSelector
|
|
1154
1540
|
});
|
|
1155
|
-
if (selector.length === 0 || selector.every((s) => s.includes(
|
|
1541
|
+
if (selector.length === 0 || selector.every((s) => s.includes("%") === false)) selector.push(defaultSelector);
|
|
1156
1542
|
result.push({
|
|
1157
1543
|
selector,
|
|
1158
1544
|
property: toKebab(k),
|
|
@@ -1181,13 +1567,35 @@ async function extract({ styleDefinition, levels = [], result = [], defaultSelec
|
|
|
1181
1567
|
});
|
|
1182
1568
|
return result;
|
|
1183
1569
|
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Creates a bound extraction function that closes over the default selector and plugin transform hooks.
|
|
1572
|
+
* @internal
|
|
1573
|
+
*
|
|
1574
|
+
* @param options - The extraction options: `defaultSelector`, `transformSelectors`, `transformStyleItems`, and `transformStyleDefinitions`.
|
|
1575
|
+
* @param options.defaultSelector - The selector used when no explicit atomic placeholder selector remains.
|
|
1576
|
+
* @param options.transformSelectors - Hook that rewrites selector arrays before normalization.
|
|
1577
|
+
* @param options.transformStyleItems - Hook that rewrites or expands style item arrays.
|
|
1578
|
+
* @param options.transformStyleDefinitions - Hook that rewrites style definition objects before extraction.
|
|
1579
|
+
* @returns An `ExtractFn` that accepts a style definition and returns extracted contents.
|
|
1580
|
+
*
|
|
1581
|
+
* @remarks Called once during engine construction. The returned function is stored as `engine.extract` and used for all subsequent `engine.use()` calls.
|
|
1582
|
+
*
|
|
1583
|
+
* @example
|
|
1584
|
+
* ```ts
|
|
1585
|
+
* const extractFn = createExtractFn({
|
|
1586
|
+
* defaultSelector: '.%',
|
|
1587
|
+
* transformSelectors: async s => s,
|
|
1588
|
+
* transformStyleItems: async i => i,
|
|
1589
|
+
* transformStyleDefinitions: async d => d,
|
|
1590
|
+
* })
|
|
1591
|
+
* ```
|
|
1592
|
+
*/
|
|
1184
1593
|
function createExtractFn(options) {
|
|
1185
1594
|
return (styleDefinition) => extract({
|
|
1186
1595
|
styleDefinition,
|
|
1187
1596
|
...options
|
|
1188
1597
|
});
|
|
1189
1598
|
}
|
|
1190
|
-
|
|
1191
1599
|
//#endregion
|
|
1192
1600
|
//#region src/internal/plugin.ts
|
|
1193
1601
|
function getPluginHook(plugin, hook) {
|
|
@@ -1195,7 +1603,7 @@ function getPluginHook(plugin, hook) {
|
|
|
1195
1603
|
return typeof hookFn === "function" ? hookFn : null;
|
|
1196
1604
|
}
|
|
1197
1605
|
function applyHookPayload(current, next) {
|
|
1198
|
-
return next
|
|
1606
|
+
return next ?? current;
|
|
1199
1607
|
}
|
|
1200
1608
|
function logHookStart(kind, hook) {
|
|
1201
1609
|
log.debug(`Executing ${kind.toLowerCase()} hook: ${hook}`);
|
|
@@ -1212,6 +1620,23 @@ function logPluginHookEnd(plugin, hook) {
|
|
|
1212
1620
|
function logPluginHookError(plugin, hook, error) {
|
|
1213
1621
|
log.error(`Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : error}`, error);
|
|
1214
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Executes an async hook across all plugins in order, piping the payload through each plugin's handler.
|
|
1625
|
+
* @internal
|
|
1626
|
+
*
|
|
1627
|
+
* @typeParam P - The payload/return type flowing through the hook pipeline.
|
|
1628
|
+
* @param plugins - The ordered list of engine plugins to execute.
|
|
1629
|
+
* @param hook - The name of the async hook to invoke.
|
|
1630
|
+
* @param payload - The initial payload to pass into the first plugin.
|
|
1631
|
+
* @returns The final payload after all plugins have processed it.
|
|
1632
|
+
*
|
|
1633
|
+
* @remarks Each plugin's hook receives the current payload and may return a replacement. If a plugin's hook throws, the error is logged and the current payload is preserved for subsequent plugins.
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* ```ts
|
|
1637
|
+
* const config = await execAsyncHook(plugins, 'configureRawConfig', rawConfig)
|
|
1638
|
+
* ```
|
|
1639
|
+
*/
|
|
1215
1640
|
async function execAsyncHook(plugins, hook, payload) {
|
|
1216
1641
|
logHookStart("Async", hook);
|
|
1217
1642
|
let current = payload;
|
|
@@ -1229,6 +1654,23 @@ async function execAsyncHook(plugins, hook, payload) {
|
|
|
1229
1654
|
logHookEnd("Async", hook);
|
|
1230
1655
|
return current;
|
|
1231
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Executes a synchronous hook across all plugins in order, piping the payload through each plugin's handler.
|
|
1659
|
+
* @internal
|
|
1660
|
+
*
|
|
1661
|
+
* @typeParam P - The payload/return type flowing through the hook pipeline.
|
|
1662
|
+
* @param plugins - The ordered list of engine plugins to execute.
|
|
1663
|
+
* @param hook - The name of the sync hook to invoke.
|
|
1664
|
+
* @param payload - The initial payload to pass into the first plugin.
|
|
1665
|
+
* @returns The final payload after all plugins have processed it.
|
|
1666
|
+
*
|
|
1667
|
+
* @remarks Functions identically to `execAsyncHook` but without awaiting. Used for notification-style hooks like `preflightUpdated` or `atomicStyleAdded`.
|
|
1668
|
+
*
|
|
1669
|
+
* @example
|
|
1670
|
+
* ```ts
|
|
1671
|
+
* execSyncHook(plugins, 'atomicStyleAdded', atomicStyle)
|
|
1672
|
+
* ```
|
|
1673
|
+
*/
|
|
1232
1674
|
function execSyncHook(plugins, hook, payload) {
|
|
1233
1675
|
logHookStart("Sync", hook);
|
|
1234
1676
|
let current = payload;
|
|
@@ -1246,6 +1688,18 @@ function execSyncHook(plugins, hook, payload) {
|
|
|
1246
1688
|
logHookEnd("Sync", hook);
|
|
1247
1689
|
return current;
|
|
1248
1690
|
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Pre-built hook dispatcher object mapping each hook name to a function that delegates to `execAsyncHook` or `execSyncHook`.
|
|
1693
|
+
* @internal
|
|
1694
|
+
*
|
|
1695
|
+
* @remarks Provides a convenient, type-safe interface for calling any engine hook by name without manually selecting between `execAsyncHook` and `execSyncHook`. Used throughout the `Engine` class.
|
|
1696
|
+
*
|
|
1697
|
+
* @example
|
|
1698
|
+
* ```ts
|
|
1699
|
+
* const config = await hooks.configureRawConfig(plugins, rawConfig)
|
|
1700
|
+
* hooks.preflightUpdated(plugins)
|
|
1701
|
+
* ```
|
|
1702
|
+
*/
|
|
1249
1703
|
const hooks = {
|
|
1250
1704
|
configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config),
|
|
1251
1705
|
rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config),
|
|
@@ -1263,15 +1717,45 @@ const orderMap = new Map([
|
|
|
1263
1717
|
["pre", 0],
|
|
1264
1718
|
["post", 2]
|
|
1265
1719
|
]);
|
|
1720
|
+
/**
|
|
1721
|
+
* Sorts an array of plugins by their `order` property: `'pre'` first, default in the middle, `'post'` last.
|
|
1722
|
+
* @internal
|
|
1723
|
+
*
|
|
1724
|
+
* @param plugins - The unordered array of engine plugins.
|
|
1725
|
+
* @returns A new array sorted by execution order.
|
|
1726
|
+
*
|
|
1727
|
+
* @remarks The original array is not mutated. Plugins with the same order retain their relative insertion order (stable sort).
|
|
1728
|
+
*
|
|
1729
|
+
* @example
|
|
1730
|
+
* ```ts
|
|
1731
|
+
* const ordered = resolvePlugins([postPlugin, prePlugin, normalPlugin])
|
|
1732
|
+
* // [prePlugin, normalPlugin, postPlugin]
|
|
1733
|
+
* ```
|
|
1734
|
+
*/
|
|
1266
1735
|
function resolvePlugins(plugins) {
|
|
1267
1736
|
return [...plugins].sort((a, b) => orderMap.get(a.order) - orderMap.get(b.order));
|
|
1268
1737
|
}
|
|
1269
1738
|
/* c8 ignore start */
|
|
1739
|
+
/**
|
|
1740
|
+
* Identity helper that returns the plugin object as-is, providing TypeScript type inference for plugin definitions.
|
|
1741
|
+
*
|
|
1742
|
+
* @param plugin - The engine plugin definition.
|
|
1743
|
+
* @returns The same plugin object, unchanged.
|
|
1744
|
+
*
|
|
1745
|
+
* @remarks This is a compile-time-only helper; it has no runtime effect. Using it ensures type checking and IDE autocompletion for hook names and payloads.
|
|
1746
|
+
*
|
|
1747
|
+
* @example
|
|
1748
|
+
* ```ts
|
|
1749
|
+
* export default defineEnginePlugin({
|
|
1750
|
+
* name: 'my-plugin',
|
|
1751
|
+
* configureRawConfig: (config) => ({ ...config, important: true }),
|
|
1752
|
+
* })
|
|
1753
|
+
* ```
|
|
1754
|
+
*/
|
|
1270
1755
|
function defineEnginePlugin(plugin) {
|
|
1271
1756
|
return plugin;
|
|
1272
1757
|
}
|
|
1273
1758
|
/* c8 ignore end */
|
|
1274
|
-
|
|
1275
1759
|
//#endregion
|
|
1276
1760
|
//#region src/internal/plugins/important.ts
|
|
1277
1761
|
function appendImportant(v) {
|
|
@@ -1282,6 +1766,18 @@ function modifyPropertyValue(value) {
|
|
|
1282
1766
|
if (Array.isArray(value)) return [appendImportant(value[0]), value[1].map((i) => appendImportant(i))];
|
|
1283
1767
|
return appendImportant(value);
|
|
1284
1768
|
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Built-in engine plugin that appends `!important` to generated CSS declarations.
|
|
1771
|
+
*
|
|
1772
|
+
* @returns An `EnginePlugin` that intercepts `transformStyleDefinitions` to conditionally append `!important` to every property value.
|
|
1773
|
+
*
|
|
1774
|
+
* @remarks When `EngineConfig.important.default` is `true`, all property values receive `!important` unless the style definition explicitly sets `__important: false`. Individual style definitions can also opt-in with `__important: true` regardless of the default.
|
|
1775
|
+
*
|
|
1776
|
+
* @example
|
|
1777
|
+
* ```ts
|
|
1778
|
+
* createEngine({ plugins: [important()] })
|
|
1779
|
+
* ```
|
|
1780
|
+
*/
|
|
1285
1781
|
function important() {
|
|
1286
1782
|
let defaultValue;
|
|
1287
1783
|
return defineEnginePlugin({
|
|
@@ -1298,8 +1794,7 @@ function important() {
|
|
|
1298
1794
|
transformStyleDefinitions(styleDefinitions) {
|
|
1299
1795
|
return styleDefinitions.map((styleDefinition) => {
|
|
1300
1796
|
const { __important, ...rest } = styleDefinition;
|
|
1301
|
-
|
|
1302
|
-
if ((value == null ? defaultValue : value) === false) return rest;
|
|
1797
|
+
if ((__important ?? defaultValue) === false) return rest;
|
|
1303
1798
|
return Object.fromEntries(Object.entries(rest).map(([k, v]) => {
|
|
1304
1799
|
if (isPropertyValue(v)) return [k, modifyPropertyValue(v)];
|
|
1305
1800
|
return [k, v];
|
|
@@ -1308,9 +1803,20 @@ function important() {
|
|
|
1308
1803
|
}
|
|
1309
1804
|
});
|
|
1310
1805
|
}
|
|
1311
|
-
|
|
1312
1806
|
//#endregion
|
|
1313
1807
|
//#region src/internal/plugins/keyframes.ts
|
|
1808
|
+
/**
|
|
1809
|
+
* Built-in engine plugin that provides CSS `@keyframes` registration, autocomplete integration, and smart pruning.
|
|
1810
|
+
*
|
|
1811
|
+
* @returns An `EnginePlugin` that registers keyframes definitions, wires up `animationName`/`animation` autocomplete entries, and emits a preflight containing only the `@keyframes` rules actually referenced by atomic styles.
|
|
1812
|
+
*
|
|
1813
|
+
* @remarks Reads `EngineConfig.keyframes` during `rawConfigConfigured` and attaches the `engine.keyframes` management interface during `configureEngine`. Unused keyframes are pruned from the output unless `pruneUnused: false` is set on the individual definition or globally.
|
|
1814
|
+
*
|
|
1815
|
+
* @example
|
|
1816
|
+
* ```ts
|
|
1817
|
+
* createEngine({ plugins: [keyframes()] })
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1314
1820
|
function keyframes() {
|
|
1315
1821
|
let resolveKeyframesConfig;
|
|
1316
1822
|
let configList;
|
|
@@ -1330,7 +1836,7 @@ function keyframes() {
|
|
|
1330
1836
|
if (frames != null) engine.keyframes.store.set(name, resolved);
|
|
1331
1837
|
engine.appendAutocomplete({ cssProperties: {
|
|
1332
1838
|
animationName: name,
|
|
1333
|
-
animation: autocompleteAnimation
|
|
1839
|
+
animation: autocompleteAnimation.length > 0 ? [`${name} `, ...autocompleteAnimation] : `${name} `
|
|
1334
1840
|
} });
|
|
1335
1841
|
});
|
|
1336
1842
|
engine.notifyPreflightUpdated();
|
|
@@ -1386,17 +1892,35 @@ function createResolveConfigFn({ pruneUnused: defaultPruneUnused = true } = {})
|
|
|
1386
1892
|
};
|
|
1387
1893
|
};
|
|
1388
1894
|
}
|
|
1389
|
-
|
|
1390
1895
|
//#endregion
|
|
1391
1896
|
//#region src/internal/resolver.ts
|
|
1392
1897
|
function stripGlobalFlag(re) {
|
|
1393
1898
|
if (!re.global) return re;
|
|
1394
1899
|
return new RegExp(re.source, re.flags.replace("g", ""));
|
|
1395
1900
|
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Base resolver class that manages static and dynamic rules and caches resolution results.
|
|
1903
|
+
* @internal
|
|
1904
|
+
*
|
|
1905
|
+
* @typeParam T - The type of resolved values.
|
|
1906
|
+
*
|
|
1907
|
+
* @remarks Subclasses override resolution behavior (e.g. `RecursiveResolver` adds recursive expansion). The base class handles rule storage, cache lookup, and the static-then-dynamic matching order. Results are cached in `_resolvedResultsMap` for subsequent lookups.
|
|
1908
|
+
*
|
|
1909
|
+
* @example
|
|
1910
|
+
* ```ts
|
|
1911
|
+
* class MyResolver extends AbstractResolver<string> { }
|
|
1912
|
+
* const r = new MyResolver()
|
|
1913
|
+
* r.addStaticRule({ key: 'x', string: 'x', resolved: 'X' })
|
|
1914
|
+
* ```
|
|
1915
|
+
*/
|
|
1396
1916
|
var AbstractResolver = class {
|
|
1917
|
+
/** Cache of previously resolved input-string → result pairs. */
|
|
1397
1918
|
_resolvedResultsMap = /* @__PURE__ */ new Map();
|
|
1919
|
+
/** Registry of static rules keyed by their unique key. */
|
|
1398
1920
|
staticRulesMap = /* @__PURE__ */ new Map();
|
|
1921
|
+
/** Registry of dynamic rules keyed by their unique key. */
|
|
1399
1922
|
dynamicRulesMap = /* @__PURE__ */ new Map();
|
|
1923
|
+
/** Callback invoked after a successful resolution, receiving the input string, rule type, and result. */
|
|
1400
1924
|
onResolved = () => {};
|
|
1401
1925
|
get staticRules() {
|
|
1402
1926
|
return [...this.staticRulesMap.values()];
|
|
@@ -1404,11 +1928,37 @@ var AbstractResolver = class {
|
|
|
1404
1928
|
get dynamicRules() {
|
|
1405
1929
|
return [...this.dynamicRulesMap.values()];
|
|
1406
1930
|
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Registers a static rule in the resolver.
|
|
1933
|
+
*
|
|
1934
|
+
* @param rule - The static rule to register.
|
|
1935
|
+
* @returns `this` for chaining.
|
|
1936
|
+
*
|
|
1937
|
+
* @remarks Overwrites any existing static rule with the same key.
|
|
1938
|
+
*
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* resolver.addStaticRule({ key: 'dark', string: 'dark', resolved: ['.dark &'] })
|
|
1942
|
+
* ```
|
|
1943
|
+
*/
|
|
1407
1944
|
addStaticRule(rule) {
|
|
1408
1945
|
log.debug(`Adding static rule: ${rule.key}`);
|
|
1409
1946
|
this.staticRulesMap.set(rule.key, rule);
|
|
1410
1947
|
return this;
|
|
1411
1948
|
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Removes a static rule and its cached resolution result.
|
|
1951
|
+
*
|
|
1952
|
+
* @param key - The key of the static rule to remove.
|
|
1953
|
+
* @returns `this` for chaining.
|
|
1954
|
+
*
|
|
1955
|
+
* @remarks Logs a warning if the key does not exist. Also evicts the cached result for the rule's input string.
|
|
1956
|
+
*
|
|
1957
|
+
* @example
|
|
1958
|
+
* ```ts
|
|
1959
|
+
* resolver.removeStaticRule('dark')
|
|
1960
|
+
* ```
|
|
1961
|
+
*/
|
|
1412
1962
|
removeStaticRule(key) {
|
|
1413
1963
|
const rule = this.staticRulesMap.get(key);
|
|
1414
1964
|
if (rule == null) {
|
|
@@ -1420,11 +1970,37 @@ var AbstractResolver = class {
|
|
|
1420
1970
|
this._resolvedResultsMap.delete(rule.string);
|
|
1421
1971
|
return this;
|
|
1422
1972
|
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Registers a dynamic rule in the resolver.
|
|
1975
|
+
*
|
|
1976
|
+
* @param rule - The dynamic rule to register.
|
|
1977
|
+
* @returns `this` for chaining.
|
|
1978
|
+
*
|
|
1979
|
+
* @remarks Overwrites any existing dynamic rule with the same key.
|
|
1980
|
+
*
|
|
1981
|
+
* @example
|
|
1982
|
+
* ```ts
|
|
1983
|
+
* resolver.addDynamicRule({ key: 'bp', stringPattern: /^bp-(\d+)$/, createResolved: m => [`@media (min-width: ${m[1]}px)`] })
|
|
1984
|
+
* ```
|
|
1985
|
+
*/
|
|
1423
1986
|
addDynamicRule(rule) {
|
|
1424
1987
|
log.debug(`Adding dynamic rule: ${rule.key}`);
|
|
1425
1988
|
this.dynamicRulesMap.set(rule.key, rule);
|
|
1426
1989
|
return this;
|
|
1427
1990
|
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Removes a dynamic rule and evicts all cached results that its pattern matched.
|
|
1993
|
+
*
|
|
1994
|
+
* @param key - The key of the dynamic rule to remove.
|
|
1995
|
+
* @returns `this` for chaining.
|
|
1996
|
+
*
|
|
1997
|
+
* @remarks Iterates through all cached results and deletes any whose input string matches the removed rule's pattern. Logs a warning if the key does not exist.
|
|
1998
|
+
*
|
|
1999
|
+
* @example
|
|
2000
|
+
* ```ts
|
|
2001
|
+
* resolver.removeDynamicRule('bp')
|
|
2002
|
+
* ```
|
|
2003
|
+
*/
|
|
1428
2004
|
removeDynamicRule(key) {
|
|
1429
2005
|
const rule = this.dynamicRulesMap.get(key);
|
|
1430
2006
|
if (rule == null) {
|
|
@@ -1441,6 +2017,19 @@ var AbstractResolver = class {
|
|
|
1441
2017
|
log.debug(` - Cleared ${matchedResolvedStringList.length} cached results`);
|
|
1442
2018
|
return this;
|
|
1443
2019
|
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Attempts to resolve an input string by checking cached results, then static rules, then dynamic rules in order.
|
|
2022
|
+
*
|
|
2023
|
+
* @param string - The input string to resolve.
|
|
2024
|
+
* @returns The resolved result wrapper, or `null`/`undefined` if no rule matches.
|
|
2025
|
+
*
|
|
2026
|
+
* @remarks Results are cached for subsequent calls. Invokes `onResolved` after a successful match. Dynamic rule matching is async because `createResolved` may return a `Promise`.
|
|
2027
|
+
*
|
|
2028
|
+
* @example
|
|
2029
|
+
* ```ts
|
|
2030
|
+
* const result = await resolver._resolve('hover')
|
|
2031
|
+
* ```
|
|
2032
|
+
*/
|
|
1444
2033
|
async _resolve(string) {
|
|
1445
2034
|
const existedResult = this._resolvedResultsMap.get(string);
|
|
1446
2035
|
if (existedResult != null) {
|
|
@@ -1474,6 +2063,19 @@ var AbstractResolver = class {
|
|
|
1474
2063
|
}
|
|
1475
2064
|
log.debug(`Resolution failed for: ${string}`);
|
|
1476
2065
|
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Updates or creates the cached resolved result for a given input string.
|
|
2068
|
+
*
|
|
2069
|
+
* @param string - The input string whose cached result should be updated.
|
|
2070
|
+
* @param resolved - The new resolved value to store.
|
|
2071
|
+
*
|
|
2072
|
+
* @remarks If a cached `ResolvedResult` already exists for `string`, its `value` property is mutated in place. Otherwise a new entry is created. This allows `RecursiveResolver` to retroactively update partially resolved values without allocating a new wrapper.
|
|
2073
|
+
*
|
|
2074
|
+
* @example
|
|
2075
|
+
* ```ts
|
|
2076
|
+
* resolver._setResolvedResult('hover', ['&:hover'])
|
|
2077
|
+
* ```
|
|
2078
|
+
*/
|
|
1477
2079
|
_setResolvedResult(string, resolved) {
|
|
1478
2080
|
const resolvedResult = this._resolvedResultsMap.get(string);
|
|
1479
2081
|
if (resolvedResult) {
|
|
@@ -1483,7 +2085,36 @@ var AbstractResolver = class {
|
|
|
1483
2085
|
this._resolvedResultsMap.set(string, { value: resolved });
|
|
1484
2086
|
}
|
|
1485
2087
|
};
|
|
2088
|
+
/**
|
|
2089
|
+
* Resolver subclass that recursively expands resolved values until all string references are fully resolved.
|
|
2090
|
+
* @internal
|
|
2091
|
+
*
|
|
2092
|
+
* @typeParam T - The element type of the final resolved array.
|
|
2093
|
+
*
|
|
2094
|
+
* @remarks Each resolution step may return a mix of final values and string references. The `resolve` method recurses into string values, flattening nested references while detecting circular dependencies via a visited set.
|
|
2095
|
+
*
|
|
2096
|
+
* @example
|
|
2097
|
+
* ```ts
|
|
2098
|
+
* class SelectorResolver extends RecursiveResolver<string> { }
|
|
2099
|
+
* const result = await resolver.resolve('hover-focus')
|
|
2100
|
+
* // ['&:hover', '&:focus'] after recursive expansion
|
|
2101
|
+
* ```
|
|
2102
|
+
*/
|
|
1486
2103
|
var RecursiveResolver = class extends AbstractResolver {
|
|
2104
|
+
/**
|
|
2105
|
+
* Recursively resolves an input string into a flat array of final values.
|
|
2106
|
+
*
|
|
2107
|
+
* @param string - The input string to resolve.
|
|
2108
|
+
* @param _visited - Accumulator set for cycle detection; callers should omit this.
|
|
2109
|
+
* @returns A flat array of resolved values. If no rule matches, returns `[string]` cast to `T`.
|
|
2110
|
+
*
|
|
2111
|
+
* @remarks Detects circular references and short-circuits by returning the unresolved string. After full expansion, the cache is updated with the final flat result via `_setResolvedResult`.
|
|
2112
|
+
*
|
|
2113
|
+
* @example
|
|
2114
|
+
* ```ts
|
|
2115
|
+
* const selectors = await resolver.resolve('hover')
|
|
2116
|
+
* ```
|
|
2117
|
+
*/
|
|
1487
2118
|
async resolve(string, _visited) {
|
|
1488
2119
|
const visited = _visited ?? /* @__PURE__ */ new Set();
|
|
1489
2120
|
if (visited.has(string)) {
|
|
@@ -1502,6 +2133,26 @@ var RecursiveResolver = class extends AbstractResolver {
|
|
|
1502
2133
|
return result;
|
|
1503
2134
|
}
|
|
1504
2135
|
};
|
|
2136
|
+
/**
|
|
2137
|
+
* Normalizes a user-supplied rule shorthand into a `ResolvedRuleConfig`, a plain redirect string, or `undefined`.
|
|
2138
|
+
* @internal
|
|
2139
|
+
*
|
|
2140
|
+
* @typeParam T - The element type of the rule's resolved value array.
|
|
2141
|
+
* @param config - The raw rule configuration: a string redirect, a tuple (`[string, value]` or `[RegExp, fn, autocomplete?]`), or an object with `keyName` and `value` properties.
|
|
2142
|
+
* @param keyName - The property name on an object-form config that holds the match key or pattern.
|
|
2143
|
+
* @returns A `ResolvedRuleConfig<T>` for valid static/dynamic configs, the original string for redirect configs, or `undefined` if the config shape is unrecognized.
|
|
2144
|
+
*
|
|
2145
|
+
* @remarks Handles three config shapes:
|
|
2146
|
+
* - **String**: returned as-is for the caller to treat as a redirect to another rule.
|
|
2147
|
+
* - **Tuple**: `[string, T | T[]]` for static rules, `[RegExp, fn, autocomplete?]` for dynamic rules.
|
|
2148
|
+
* - **Object**: `{ [keyName]: string | RegExp, value: T | fn, autocomplete?: string[] }`.
|
|
2149
|
+
*
|
|
2150
|
+
* @example
|
|
2151
|
+
* ```ts
|
|
2152
|
+
* resolveRuleConfig(['hover', '&:hover'], 'selector')
|
|
2153
|
+
* // { type: 'static', rule: { key: 'hover', ... }, autocomplete: ['hover'] }
|
|
2154
|
+
* ```
|
|
2155
|
+
*/
|
|
1505
2156
|
function resolveRuleConfig(config, keyName) {
|
|
1506
2157
|
if (typeof config === "string") return config;
|
|
1507
2158
|
if (Array.isArray(config)) {
|
|
@@ -1552,9 +2203,20 @@ function resolveRuleConfig(config, keyName) {
|
|
|
1552
2203
|
};
|
|
1553
2204
|
}
|
|
1554
2205
|
}
|
|
1555
|
-
|
|
1556
2206
|
//#endregion
|
|
1557
2207
|
//#region src/internal/plugins/selectors.ts
|
|
2208
|
+
/**
|
|
2209
|
+
* Built-in engine plugin that provides the selector resolution system.
|
|
2210
|
+
*
|
|
2211
|
+
* @returns An `EnginePlugin` that registers the `selectors` resolver on the engine and hooks into `transformSelectors` to expand selector names into resolved CSS selectors.
|
|
2212
|
+
*
|
|
2213
|
+
* @remarks Reads `EngineConfig.selectors` during `rawConfigConfigured`, attaches a `RecursiveResolver` to `engine.selectors` during `configureEngine`, and resolves all selector strings in the `transformSelectors` hook.
|
|
2214
|
+
*
|
|
2215
|
+
* @example
|
|
2216
|
+
* ```ts
|
|
2217
|
+
* createEngine({ plugins: [selectors()] })
|
|
2218
|
+
* ```
|
|
2219
|
+
*/
|
|
1558
2220
|
function selectors() {
|
|
1559
2221
|
let engine;
|
|
1560
2222
|
let configList;
|
|
@@ -1575,8 +2237,11 @@ function selectors() {
|
|
|
1575
2237
|
engine.appendAutocomplete({ selectors: resolved });
|
|
1576
2238
|
return;
|
|
1577
2239
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
2240
|
+
const addRule = {
|
|
2241
|
+
static: () => engine.selectors.resolver.addStaticRule(resolved.rule),
|
|
2242
|
+
dynamic: () => engine.selectors.resolver.addDynamicRule(resolved.rule)
|
|
2243
|
+
}[resolved.type];
|
|
2244
|
+
addRule?.();
|
|
1580
2245
|
engine.appendAutocomplete({ selectors: resolved.autocomplete });
|
|
1581
2246
|
});
|
|
1582
2247
|
}
|
|
@@ -1594,12 +2259,36 @@ function selectors() {
|
|
|
1594
2259
|
});
|
|
1595
2260
|
}
|
|
1596
2261
|
var SelectorResolver = class extends RecursiveResolver {};
|
|
2262
|
+
/**
|
|
2263
|
+
* Normalizes a `Selector` configuration into a `ResolvedRuleConfig`, a redirect string, or `undefined`.
|
|
2264
|
+
*
|
|
2265
|
+
* @param config - The selector rule configuration to resolve.
|
|
2266
|
+
* @returns A resolved static/dynamic rule config, a redirect string, or `undefined` if the shape is unrecognized.
|
|
2267
|
+
*
|
|
2268
|
+
* @remarks Delegates to the generic `resolveRuleConfig` with `'selector'` as the key name.
|
|
2269
|
+
*
|
|
2270
|
+
* @example
|
|
2271
|
+
* ```ts
|
|
2272
|
+
* const resolved = resolveSelectorConfig(['hover', '&:hover'])
|
|
2273
|
+
* ```
|
|
2274
|
+
*/
|
|
1597
2275
|
function resolveSelectorConfig(config) {
|
|
1598
2276
|
return resolveRuleConfig(config, "selector");
|
|
1599
2277
|
}
|
|
1600
|
-
|
|
1601
2278
|
//#endregion
|
|
1602
2279
|
//#region src/internal/plugins/shortcuts.ts
|
|
2280
|
+
/**
|
|
2281
|
+
* Built-in engine plugin that provides the shortcut resolution system.
|
|
2282
|
+
*
|
|
2283
|
+
* @returns An `EnginePlugin` that registers the `shortcuts` resolver on the engine and hooks into `transformStyleItems` and `transformStyleDefinitions` to expand shortcut names into style items.
|
|
2284
|
+
*
|
|
2285
|
+
* @remarks Reads `EngineConfig.shortcuts` during `rawConfigConfigured`, attaches a `RecursiveResolver` to `engine.shortcuts` during `configureEngine`, and expands shortcut references in both `transformStyleItems` (string style items) and `transformStyleDefinitions` (the `__shortcut` pseudo-property).
|
|
2286
|
+
*
|
|
2287
|
+
* @example
|
|
2288
|
+
* ```ts
|
|
2289
|
+
* createEngine({ plugins: [shortcuts()] })
|
|
2290
|
+
* ```
|
|
2291
|
+
*/
|
|
1603
2292
|
function shortcuts() {
|
|
1604
2293
|
let engine;
|
|
1605
2294
|
let configList;
|
|
@@ -1617,20 +2306,23 @@ function shortcuts() {
|
|
|
1617
2306
|
const resolved = resolveShortcutConfig(config);
|
|
1618
2307
|
if (resolved == null) return;
|
|
1619
2308
|
if (typeof resolved === "string") {
|
|
1620
|
-
engine.appendAutocomplete({
|
|
2309
|
+
engine.appendAutocomplete({ shortcuts: resolved });
|
|
1621
2310
|
return;
|
|
1622
2311
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
2312
|
+
const addRule = {
|
|
2313
|
+
static: () => engine.shortcuts.resolver.addStaticRule(resolved.rule),
|
|
2314
|
+
dynamic: () => engine.shortcuts.resolver.addDynamicRule(resolved.rule)
|
|
2315
|
+
}[resolved.type];
|
|
2316
|
+
addRule?.();
|
|
2317
|
+
engine.appendAutocomplete({ shortcuts: resolved.autocomplete });
|
|
1626
2318
|
});
|
|
1627
2319
|
}
|
|
1628
2320
|
};
|
|
1629
2321
|
engine.shortcuts.add(...configList);
|
|
1630
2322
|
engine.shortcuts.resolver.onResolved = (string, type) => {
|
|
1631
|
-
if (type === "dynamic") engine.appendAutocomplete({
|
|
2323
|
+
if (type === "dynamic") engine.appendAutocomplete({ shortcuts: string });
|
|
1632
2324
|
};
|
|
1633
|
-
const unionType = ["(string & {})", "Autocomplete['
|
|
2325
|
+
const unionType = ["(string & {})", "Autocomplete['Shortcut']"].join(" | ");
|
|
1634
2326
|
engine.appendAutocomplete({
|
|
1635
2327
|
extraProperties: "__shortcut",
|
|
1636
2328
|
properties: { __shortcut: [unionType, `(${unionType})[]`] }
|
|
@@ -1666,22 +2358,8 @@ var ShortcutResolver = class extends RecursiveResolver {};
|
|
|
1666
2358
|
function resolveShortcutConfig(config) {
|
|
1667
2359
|
return resolveRuleConfig(config, "shortcut");
|
|
1668
2360
|
}
|
|
1669
|
-
|
|
1670
2361
|
//#endregion
|
|
1671
2362
|
//#region src/internal/generated-property-semantics.ts
|
|
1672
|
-
const VARIABLE_SEMANTIC_FAMILIES = [
|
|
1673
|
-
"color",
|
|
1674
|
-
"length",
|
|
1675
|
-
"time",
|
|
1676
|
-
"number",
|
|
1677
|
-
"percentage",
|
|
1678
|
-
"angle",
|
|
1679
|
-
"image",
|
|
1680
|
-
"url",
|
|
1681
|
-
"position",
|
|
1682
|
-
"easing",
|
|
1683
|
-
"font-family"
|
|
1684
|
-
];
|
|
1685
2363
|
const VARIABLE_SEMANTIC_FAMILY_PROPERTIES = {
|
|
1686
2364
|
"angle": [],
|
|
1687
2365
|
"color": [
|
|
@@ -1759,10 +2437,20 @@ const VARIABLE_SEMANTIC_FAMILY_PROPERTIES = {
|
|
|
1759
2437
|
],
|
|
1760
2438
|
"url": []
|
|
1761
2439
|
};
|
|
1762
|
-
|
|
1763
2440
|
//#endregion
|
|
1764
2441
|
//#region src/internal/plugins/variables.ts
|
|
1765
|
-
|
|
2442
|
+
/**
|
|
2443
|
+
* Built-in engine plugin that provides CSS custom properties (variables) with smart pruning and autocomplete integration.
|
|
2444
|
+
*
|
|
2445
|
+
* @returns An `EnginePlugin` that registers variable definitions, manages a preflight for emitting `:root` / scoped variables, and prunes unused variables from the output.
|
|
2446
|
+
*
|
|
2447
|
+
* @remarks Reads `EngineConfig.variables` during `rawConfigConfigured` and attaches the `engine.variables` management interface during `configureEngine`. A preflight is registered that collects variable references from atomic styles and other preflights, transitively expands dependencies, and emits only used (or safe-listed) variables.
|
|
2448
|
+
*
|
|
2449
|
+
* @example
|
|
2450
|
+
* ```ts
|
|
2451
|
+
* createEngine({ plugins: [variables()] })
|
|
2452
|
+
* ```
|
|
2453
|
+
*/
|
|
1766
2454
|
function variables() {
|
|
1767
2455
|
let resolveVariables;
|
|
1768
2456
|
let rawVariables;
|
|
@@ -1803,7 +2491,7 @@ function variables() {
|
|
|
1803
2491
|
value.flatMap(extractUsedVarNames).forEach((name) => used.add(normalizeVariableName(name)));
|
|
1804
2492
|
});
|
|
1805
2493
|
const otherPreflights = engine.config.preflights.filter((p) => p.id !== "core:variables");
|
|
1806
|
-
(await Promise.all(otherPreflights.map(({ fn }) => Promise.resolve(fn(engine, false)).catch(() => null)))).forEach((result) => {
|
|
2494
|
+
(await Promise.all(otherPreflights.map(({ fn }) => Promise.resolve().then(() => fn(engine, false)).catch(() => null)))).forEach((result) => {
|
|
1807
2495
|
if (result == null) return;
|
|
1808
2496
|
extractUsedVarNamesFromPreflightResult(result).forEach((name) => used.add(name));
|
|
1809
2497
|
});
|
|
@@ -1815,8 +2503,8 @@ function variables() {
|
|
|
1815
2503
|
const entries = varMap.get(name);
|
|
1816
2504
|
if (!entries) continue;
|
|
1817
2505
|
for (const { value } of entries) {
|
|
1818
|
-
|
|
1819
|
-
for (const refName of extractUsedVarNames(
|
|
2506
|
+
const referencedValue = Array.isArray(value) ? value.join(" ") : String(value);
|
|
2507
|
+
for (const refName of extractUsedVarNames(referencedValue).map(normalizeVariableName)) if (!used.has(refName)) {
|
|
1820
2508
|
used.add(refName);
|
|
1821
2509
|
queue.push(refName);
|
|
1822
2510
|
}
|
|
@@ -1880,7 +2568,7 @@ function resolveAutocompleteValueTargets({ name, asValueOf, semanticType }) {
|
|
|
1880
2568
|
const targets = /* @__PURE__ */ new Set();
|
|
1881
2569
|
if (asValueOf == null && semanticTypes.length === 0) targets.add("*");
|
|
1882
2570
|
explicitTargets.forEach((target) => {
|
|
1883
|
-
|
|
2571
|
+
targets.add(target);
|
|
1884
2572
|
});
|
|
1885
2573
|
semanticTypes.forEach((family) => {
|
|
1886
2574
|
const properties = VARIABLE_SEMANTIC_FAMILY_PROPERTIES[family];
|
|
@@ -1888,42 +2576,128 @@ function resolveAutocompleteValueTargets({ name, asValueOf, semanticType }) {
|
|
|
1888
2576
|
properties.forEach((property) => targets.add(property));
|
|
1889
2577
|
return;
|
|
1890
2578
|
}
|
|
1891
|
-
|
|
2579
|
+
log.warn(`Unknown semanticType "${family}" for variable "${name}". Skipping semantic autocomplete expansion.`);
|
|
1892
2580
|
});
|
|
1893
2581
|
if (targets.has("*")) return ["*"];
|
|
1894
2582
|
return [...targets];
|
|
1895
2583
|
}
|
|
1896
2584
|
const VAR_NAME_RE = /var\((--[\w-]+)/g;
|
|
2585
|
+
/**
|
|
2586
|
+
* Extracts all CSS variable names referenced via `var(--*)` calls in a string.
|
|
2587
|
+
*
|
|
2588
|
+
* @param input - The CSS value string to scan.
|
|
2589
|
+
* @returns An array of variable names (including the `--` prefix) found in `var()` expressions.
|
|
2590
|
+
*
|
|
2591
|
+
* @remarks Uses a global regex to find all `var(--name)` occurrences. Nested `var()` calls are matched independently.
|
|
2592
|
+
*
|
|
2593
|
+
* @example
|
|
2594
|
+
* ```ts
|
|
2595
|
+
* extractUsedVarNames('color: var(--primary)') // ['--primary']
|
|
2596
|
+
* extractUsedVarNames('var(--a) var(--b)') // ['--a', '--b']
|
|
2597
|
+
* ```
|
|
2598
|
+
*/
|
|
1897
2599
|
function extractUsedVarNames(input) {
|
|
1898
2600
|
return Array.from(input.matchAll(VAR_NAME_RE), (m) => m[1]);
|
|
1899
2601
|
}
|
|
2602
|
+
/**
|
|
2603
|
+
* Ensures a variable name has the `--` prefix.
|
|
2604
|
+
*
|
|
2605
|
+
* @param name - The variable name, with or without the `--` prefix.
|
|
2606
|
+
* @returns The name with a guaranteed `--` prefix.
|
|
2607
|
+
*
|
|
2608
|
+
* @remarks A no-op when the name already starts with `--`.
|
|
2609
|
+
*
|
|
2610
|
+
* @example
|
|
2611
|
+
* ```ts
|
|
2612
|
+
* normalizeVariableName('color') // '--color'
|
|
2613
|
+
* normalizeVariableName('--color') // '--color'
|
|
2614
|
+
* ```
|
|
2615
|
+
*/
|
|
1900
2616
|
function normalizeVariableName(name) {
|
|
1901
2617
|
if (name.startsWith("--")) return name;
|
|
1902
2618
|
return `--${name}`;
|
|
1903
2619
|
}
|
|
1904
2620
|
/**
|
|
1905
|
-
* Recursively
|
|
1906
|
-
*
|
|
2621
|
+
* Recursively extracts all CSS variable names referenced in a preflight result.
|
|
2622
|
+
*
|
|
2623
|
+
* @param result - A preflight output: either a raw CSS string or a nested `PreflightDefinition` object.
|
|
2624
|
+
* @returns A flat array of normalized variable names found in the result.
|
|
2625
|
+
*
|
|
2626
|
+
* @remarks For string results, scans for `var(--*)` references. For object results, recursively traverses selector scopes and string/number values. All returned names are normalized with the `--` prefix.
|
|
2627
|
+
*
|
|
2628
|
+
* @example
|
|
2629
|
+
* ```ts
|
|
2630
|
+
* extractUsedVarNamesFromPreflightResult({ ':root': { color: 'var(--primary)' } })
|
|
2631
|
+
* // ['--primary']
|
|
2632
|
+
* ```
|
|
1907
2633
|
*/
|
|
1908
2634
|
function extractUsedVarNamesFromPreflightResult(result) {
|
|
1909
2635
|
if (typeof result === "string") return extractUsedVarNames(result).map(normalizeVariableName);
|
|
1910
2636
|
const names = [];
|
|
1911
2637
|
for (const value of Object.values(result)) {
|
|
1912
2638
|
if (value == null) continue;
|
|
1913
|
-
if (typeof value === "string" || typeof value === "number")
|
|
1914
|
-
|
|
2639
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2640
|
+
extractUsedVarNames(String(value)).forEach((n) => names.push(normalizeVariableName(n)));
|
|
2641
|
+
continue;
|
|
2642
|
+
}
|
|
2643
|
+
extractUsedVarNamesFromPreflightResult(value).forEach((n) => names.push(n));
|
|
1915
2644
|
}
|
|
1916
2645
|
return names;
|
|
1917
2646
|
}
|
|
1918
|
-
|
|
1919
2647
|
//#endregion
|
|
1920
2648
|
//#region src/internal/engine.ts
|
|
2649
|
+
/**
|
|
2650
|
+
* Default CSS layer name for preflight styles.
|
|
2651
|
+
* @internal
|
|
2652
|
+
*
|
|
2653
|
+
* @remarks Used as the layer name wrapping all unlayered preflight output when the layer exists in `config.layers`.
|
|
2654
|
+
*
|
|
2655
|
+
* @example
|
|
2656
|
+
* ```ts
|
|
2657
|
+
* // 'preflights'
|
|
2658
|
+
* ```
|
|
2659
|
+
*/
|
|
1921
2660
|
const DEFAULT_PREFLIGHTS_LAYER = "preflights";
|
|
2661
|
+
/**
|
|
2662
|
+
* Default CSS layer name for utility (atomic) styles.
|
|
2663
|
+
* @internal
|
|
2664
|
+
*
|
|
2665
|
+
* @remarks Atomic styles without an explicit layer are placed into this layer when it exists in `config.layers`.
|
|
2666
|
+
*
|
|
2667
|
+
* @example
|
|
2668
|
+
* ```ts
|
|
2669
|
+
* // 'utilities'
|
|
2670
|
+
* ```
|
|
2671
|
+
*/
|
|
1922
2672
|
const DEFAULT_UTILITIES_LAYER = "utilities";
|
|
2673
|
+
/**
|
|
2674
|
+
* Default layer ordering map: `preflights` at weight 1, `utilities` at weight 10.
|
|
2675
|
+
* @internal
|
|
2676
|
+
*
|
|
2677
|
+
* @remarks Merged with any user-supplied `config.layers` during engine config resolution. Numeric weights determine the `@layer` declaration order.
|
|
2678
|
+
*
|
|
2679
|
+
* @example
|
|
2680
|
+
* ```ts
|
|
2681
|
+
* // { preflights: 1, utilities: 10 }
|
|
2682
|
+
* ```
|
|
2683
|
+
*/
|
|
1923
2684
|
const DEFAULT_LAYERS = {
|
|
1924
2685
|
[DEFAULT_PREFLIGHTS_LAYER]: 1,
|
|
1925
2686
|
[DEFAULT_UTILITIES_LAYER]: 10
|
|
1926
2687
|
};
|
|
2688
|
+
/**
|
|
2689
|
+
* Creates and initializes a PikaCSS engine with the given configuration.
|
|
2690
|
+
*
|
|
2691
|
+
* @param config - The engine configuration, including plugins, selectors, shortcuts, variables, keyframes, preflights, and layer settings.
|
|
2692
|
+
* @returns A fully initialized `Engine` instance.
|
|
2693
|
+
*
|
|
2694
|
+
* @remarks Core plugins (`important`, `variables`, `keyframes`, `selectors`, `shortcuts`) are prepended automatically. The function resolves plugins, runs all configuration hooks in sequence, and returns the ready-to-use engine.
|
|
2695
|
+
*
|
|
2696
|
+
* @example
|
|
2697
|
+
* ```ts
|
|
2698
|
+
* const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
|
|
2699
|
+
* ```
|
|
2700
|
+
*/
|
|
1927
2701
|
async function createEngine(config = {}) {
|
|
1928
2702
|
log.debug("Creating engine with config:", config);
|
|
1929
2703
|
const corePlugins = [
|
|
@@ -1941,7 +2715,7 @@ async function createEngine(config = {}) {
|
|
|
1941
2715
|
};
|
|
1942
2716
|
log.debug(`Total plugins resolved: ${plugins.length}`);
|
|
1943
2717
|
config = await hooks.configureRawConfig(config.plugins, config);
|
|
1944
|
-
hooks.rawConfigConfigured(resolvePlugins(config.plugins
|
|
2718
|
+
hooks.rawConfigConfigured(resolvePlugins(config.plugins), config);
|
|
1945
2719
|
let resolvedConfig = await resolveEngineConfig(config);
|
|
1946
2720
|
log.debug("Engine config resolved with prefix:", resolvedConfig.prefix);
|
|
1947
2721
|
resolvedConfig = await hooks.configureResolvedConfig(resolvedConfig.plugins, resolvedConfig);
|
|
@@ -1955,11 +2729,39 @@ async function createEngine(config = {}) {
|
|
|
1955
2729
|
log.debug("Engine initialized successfully");
|
|
1956
2730
|
return engine;
|
|
1957
2731
|
}
|
|
2732
|
+
/**
|
|
2733
|
+
* The PikaCSS engine: manages atomic style resolution, rendering, preflights, and plugin hooks.
|
|
2734
|
+
*
|
|
2735
|
+
* @remarks Constructed via `createEngine()`. Holds the resolved configuration, the atomic style store, and exposes methods for processing style items (`use`), rendering CSS output (`renderPreflights`, `renderAtomicStyles`, `renderLayerOrderDeclaration`), and managing runtime extensions (`addPreflight`, `appendAutocomplete`, `appendCssImport`).
|
|
2736
|
+
*
|
|
2737
|
+
* @example
|
|
2738
|
+
* ```ts
|
|
2739
|
+
* const engine = await createEngine({ prefix: 'pk-' })
|
|
2740
|
+
* const ids = await engine.use({ color: 'red' })
|
|
2741
|
+
* const css = await engine.renderAtomicStyles(true)
|
|
2742
|
+
* ```
|
|
2743
|
+
*/
|
|
1958
2744
|
var Engine = class {
|
|
2745
|
+
/** The fully resolved engine configuration. */
|
|
1959
2746
|
config;
|
|
2747
|
+
/** Reference to the plugin hook dispatcher for invoking lifecycle hooks. */
|
|
1960
2748
|
pluginHooks = hooks;
|
|
2749
|
+
/** The extraction function that decomposes style definitions into atomic style contents. */
|
|
1961
2750
|
extract;
|
|
2751
|
+
/** The engine's runtime store holding registered atomic styles and their ID mappings. */
|
|
1962
2752
|
store = createEngineStore();
|
|
2753
|
+
/**
|
|
2754
|
+
* Creates an engine instance from a resolved configuration.
|
|
2755
|
+
*
|
|
2756
|
+
* @param config - The fully resolved engine configuration.
|
|
2757
|
+
*
|
|
2758
|
+
* @remarks Initializes the `extract` function by wiring it to the plugin hook pipeline for selectors, style items, and style definitions.
|
|
2759
|
+
*
|
|
2760
|
+
* @example
|
|
2761
|
+
* ```ts
|
|
2762
|
+
* const engine = new Engine(resolvedConfig)
|
|
2763
|
+
* ```
|
|
2764
|
+
*/
|
|
1963
2765
|
constructor(config) {
|
|
1964
2766
|
this.config = config;
|
|
1965
2767
|
this.extract = createExtractFn({
|
|
@@ -1969,30 +2771,113 @@ var Engine = class {
|
|
|
1969
2771
|
transformStyleDefinitions: (styleDefinitions) => hooks.transformStyleDefinitions(this.config.plugins, styleDefinitions)
|
|
1970
2772
|
});
|
|
1971
2773
|
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Fires the `preflightUpdated` hook to notify plugins that preflight content has changed.
|
|
2776
|
+
*
|
|
2777
|
+
*
|
|
2778
|
+
* @remarks Called automatically after `addPreflight` or when plugins modify preflight-contributing state (e.g. variables, keyframes).
|
|
2779
|
+
*
|
|
2780
|
+
* @example
|
|
2781
|
+
* ```ts
|
|
2782
|
+
* engine.notifyPreflightUpdated()
|
|
2783
|
+
* ```
|
|
2784
|
+
*/
|
|
1972
2785
|
notifyPreflightUpdated() {
|
|
1973
2786
|
hooks.preflightUpdated(this.config.plugins);
|
|
1974
2787
|
}
|
|
2788
|
+
/**
|
|
2789
|
+
* Fires the `atomicStyleAdded` hook to notify plugins that a new atomic style was registered.
|
|
2790
|
+
*
|
|
2791
|
+
* @param atomicStyle - The atomic style that was just added to the store.
|
|
2792
|
+
*
|
|
2793
|
+
* @remarks Called automatically by `use()` when a previously unseen atomic style is resolved.
|
|
2794
|
+
*
|
|
2795
|
+
* @example
|
|
2796
|
+
* ```ts
|
|
2797
|
+
* engine.notifyAtomicStyleAdded(atomicStyle)
|
|
2798
|
+
* ```
|
|
2799
|
+
*/
|
|
1975
2800
|
notifyAtomicStyleAdded(atomicStyle) {
|
|
1976
2801
|
hooks.atomicStyleAdded(this.config.plugins, atomicStyle);
|
|
1977
2802
|
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Fires the `autocompleteConfigUpdated` hook to notify plugins that autocomplete entries changed.
|
|
2805
|
+
*
|
|
2806
|
+
*
|
|
2807
|
+
* @remarks Called automatically after `appendAutocomplete` when the contribution modifies the resolved autocomplete config.
|
|
2808
|
+
*
|
|
2809
|
+
* @example
|
|
2810
|
+
* ```ts
|
|
2811
|
+
* engine.notifyAutocompleteConfigUpdated()
|
|
2812
|
+
* ```
|
|
2813
|
+
*/
|
|
1978
2814
|
notifyAutocompleteConfigUpdated() {
|
|
1979
2815
|
hooks.autocompleteConfigUpdated(this.config.plugins);
|
|
1980
2816
|
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Merges an autocomplete contribution into the resolved autocomplete config.
|
|
2819
|
+
*
|
|
2820
|
+
* @param contribution - The autocomplete entries to append (selectors, properties, CSS properties, etc.).
|
|
2821
|
+
*
|
|
2822
|
+
* @remarks Delegates to the `appendAutocomplete` utility and fires `autocompleteConfigUpdated` if the config was actually modified.
|
|
2823
|
+
*
|
|
2824
|
+
* @example
|
|
2825
|
+
* ```ts
|
|
2826
|
+
* engine.appendAutocomplete({ selectors: 'hover', cssProperties: { color: 'red' } })
|
|
2827
|
+
* ```
|
|
2828
|
+
*/
|
|
1981
2829
|
appendAutocomplete(contribution) {
|
|
1982
2830
|
if (appendAutocomplete(this.config, contribution)) this.notifyAutocompleteConfigUpdated();
|
|
1983
2831
|
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Appends a CSS `@import` statement to the preflight output.
|
|
2834
|
+
*
|
|
2835
|
+
* @param cssImport - The raw `@import` string (a trailing semicolon is appended if missing).
|
|
2836
|
+
*
|
|
2837
|
+
* @remarks Deduplicates imports. Fires `preflightUpdated` when a new import is added.
|
|
2838
|
+
*
|
|
2839
|
+
* @example
|
|
2840
|
+
* ```ts
|
|
2841
|
+
* engine.appendCssImport('@import url("https://fonts.googleapis.com/css2?family=Inter")')
|
|
2842
|
+
* ```
|
|
2843
|
+
*/
|
|
1984
2844
|
appendCssImport(cssImport) {
|
|
1985
2845
|
const normalized = normalizeCssImport(cssImport);
|
|
1986
2846
|
if (normalized == null || this.config.cssImports.includes(normalized)) return;
|
|
1987
2847
|
this.config.cssImports.push(normalized);
|
|
1988
2848
|
this.notifyPreflightUpdated();
|
|
1989
2849
|
}
|
|
2850
|
+
/**
|
|
2851
|
+
* Registers a new preflight that will be rendered before atomic styles.
|
|
2852
|
+
*
|
|
2853
|
+
* @param preflight - A preflight definition: a function, a static string/object, or a wrapper with `layer`/`id` metadata.
|
|
2854
|
+
*
|
|
2855
|
+
* @remarks The preflight is resolved into a `ResolvedPreflight` (extracting optional `layer` and `id`) and appended to `config.preflights`. Fires `preflightUpdated` so plugins and the integration layer know to re-render.
|
|
2856
|
+
*
|
|
2857
|
+
* @example
|
|
2858
|
+
* ```ts
|
|
2859
|
+
* engine.addPreflight({ layer: 'base', preflight: '*, *::before { box-sizing: border-box; }' })
|
|
2860
|
+
* ```
|
|
2861
|
+
*/
|
|
1990
2862
|
addPreflight(preflight) {
|
|
1991
2863
|
log.debug("Adding preflight");
|
|
1992
2864
|
this.config.preflights.push(resolvePreflight(preflight));
|
|
1993
2865
|
log.debug(`Total preflights: ${this.config.preflights.length}`);
|
|
1994
2866
|
this.notifyPreflightUpdated();
|
|
1995
2867
|
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Processes style items through the plugin pipeline and registers the resulting atomic styles in the store.
|
|
2870
|
+
*
|
|
2871
|
+
* @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
|
|
2872
|
+
* @returns An array of atomic style IDs (and unresolved string references) in insertion order.
|
|
2873
|
+
*
|
|
2874
|
+
* @remarks Runs `transformStyleItems` and `extractStyleDefinition` hooks, resolves each extracted content into an atomic style, deduplicates by base key, and fires `atomicStyleAdded` for new entries.
|
|
2875
|
+
*
|
|
2876
|
+
* @example
|
|
2877
|
+
* ```ts
|
|
2878
|
+
* const ids = await engine.use({ color: 'red' }, { padding: '1rem' })
|
|
2879
|
+
* ```
|
|
2880
|
+
*/
|
|
1996
2881
|
async use(...itemList) {
|
|
1997
2882
|
log.debug(`Processing ${itemList.length} style items`);
|
|
1998
2883
|
const { unknown, contents } = await resolveStyleItemList({
|
|
@@ -2019,6 +2904,19 @@ var Engine = class {
|
|
|
2019
2904
|
log.debug(`Resolved ${resolvedIds.length} atomic styles, ${unknown.size} unknown items`);
|
|
2020
2905
|
return [...unknown, ...resolvedIds];
|
|
2021
2906
|
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Renders all registered preflight definitions into a CSS string.
|
|
2909
|
+
*
|
|
2910
|
+
* @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
|
|
2911
|
+
* @returns The rendered preflight CSS, including `@import` statements, optional `@layer` wrappers, and all preflight content.
|
|
2912
|
+
*
|
|
2913
|
+
* @remarks Evaluates each preflight function, groups output by layer, wraps unlayered preflights in the default preflights layer (when present), and respects configured layer ordering.
|
|
2914
|
+
*
|
|
2915
|
+
* @example
|
|
2916
|
+
* ```ts
|
|
2917
|
+
* const css = await engine.renderPreflights(true)
|
|
2918
|
+
* ```
|
|
2919
|
+
*/
|
|
2022
2920
|
async renderPreflights(isFormatted) {
|
|
2023
2921
|
log.debug("Rendering preflights...");
|
|
2024
2922
|
const lineEnd = isFormatted ? "\n" : "";
|
|
@@ -2055,6 +2953,22 @@ var Engine = class {
|
|
|
2055
2953
|
}));
|
|
2056
2954
|
return outputParts.join(lineEnd);
|
|
2057
2955
|
}
|
|
2956
|
+
/**
|
|
2957
|
+
* Renders atomic styles into a CSS string, optionally filtered by ID and grouped by layer.
|
|
2958
|
+
*
|
|
2959
|
+
* @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
|
|
2960
|
+
* @param options - Optional filtering: `atomicStyleIds` to render a subset, `isPreview` to use placeholder IDs.
|
|
2961
|
+
* @param options.atomicStyleIds - Specific atomic style IDs to render instead of the full store.
|
|
2962
|
+
* @param options.isPreview - Whether to keep placeholder IDs instead of substituting real class names.
|
|
2963
|
+
* @returns The rendered atomic-style CSS.
|
|
2964
|
+
*
|
|
2965
|
+
* @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered. When `isPreview` is true, atomic style IDs remain as placeholders for tooling previews.
|
|
2966
|
+
*
|
|
2967
|
+
* @example
|
|
2968
|
+
* ```ts
|
|
2969
|
+
* const css = await engine.renderAtomicStyles(true)
|
|
2970
|
+
* ```
|
|
2971
|
+
*/
|
|
2058
2972
|
async renderAtomicStyles(isFormatted, options = {}) {
|
|
2059
2973
|
log.debug("Rendering atomic styles...");
|
|
2060
2974
|
const { atomicStyleIds = null, isPreview = false } = options;
|
|
@@ -2069,16 +2983,58 @@ var Engine = class {
|
|
|
2069
2983
|
defaultUtilitiesLayer: this.config.defaultUtilitiesLayer
|
|
2070
2984
|
});
|
|
2071
2985
|
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Renders the CSS `@layer` order declaration for all configured layers.
|
|
2988
|
+
*
|
|
2989
|
+
* @returns A `@layer` statement listing layer names in weight order, or an empty string if no layers are configured.
|
|
2990
|
+
*
|
|
2991
|
+
* @remarks Ensures the browser applies the intended cascade priority for `preflights`, `utilities`, and any user-defined layers.
|
|
2992
|
+
*
|
|
2993
|
+
* @example
|
|
2994
|
+
* ```ts
|
|
2995
|
+
* engine.renderLayerOrderDeclaration()
|
|
2996
|
+
* // '@layer preflights, utilities;'
|
|
2997
|
+
* ```
|
|
2998
|
+
*/
|
|
2072
2999
|
renderLayerOrderDeclaration() {
|
|
2073
3000
|
const { layers } = this.config;
|
|
2074
3001
|
if (Object.keys(layers).length === 0) return "";
|
|
2075
3002
|
return `@layer ${sortLayerNames(layers).join(", ")};`;
|
|
2076
3003
|
}
|
|
2077
3004
|
};
|
|
3005
|
+
/**
|
|
3006
|
+
* Computes a numeric rendering weight for an atomic style based on its selector depth.
|
|
3007
|
+
* @internal
|
|
3008
|
+
*
|
|
3009
|
+
* @param style - The atomic style to weigh.
|
|
3010
|
+
* @param defaultSelector - The engine's default selector pattern.
|
|
3011
|
+
* @returns `0` for styles using only the default selector; otherwise the number of selector segments.
|
|
3012
|
+
*
|
|
3013
|
+
* @remarks Used to sort atomic styles so that simpler selectors appear before more specific ones in the CSS output, preserving deterministic cascade ordering.
|
|
3014
|
+
*
|
|
3015
|
+
* @example
|
|
3016
|
+
* ```ts
|
|
3017
|
+
* calcAtomicStyleRenderingWeight(style, '.pk-__PLACEHOLDER__')
|
|
3018
|
+
* ```
|
|
3019
|
+
*/
|
|
2078
3020
|
function calcAtomicStyleRenderingWeight(style, defaultSelector) {
|
|
2079
3021
|
const { selector } = splitLayerSelector(style.content.selector);
|
|
2080
3022
|
return selector.length === 1 && selector[0] === defaultSelector ? 0 : selector.length;
|
|
2081
3023
|
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Sorts layer names by their numeric weight, then alphabetically for ties.
|
|
3026
|
+
*
|
|
3027
|
+
* @param layers - A record mapping layer names to numeric weights.
|
|
3028
|
+
* @returns An array of layer names in ascending weight order.
|
|
3029
|
+
*
|
|
3030
|
+
* @remarks Used to produce the `@layer` declaration order and to order layer group rendering.
|
|
3031
|
+
*
|
|
3032
|
+
* @example
|
|
3033
|
+
* ```ts
|
|
3034
|
+
* sortLayerNames({ utilities: 10, preflights: 1 })
|
|
3035
|
+
* // ['preflights', 'utilities']
|
|
3036
|
+
* ```
|
|
3037
|
+
*/
|
|
2082
3038
|
function sortLayerNames(layers) {
|
|
2083
3039
|
return Object.entries(layers).sort((a, b) => a[1] - b[1] || a[0].localeCompare(b[0])).map(([name]) => name);
|
|
2084
3040
|
}
|
|
@@ -2087,7 +3043,7 @@ function appendLayerGroupItem(layerGroups, layer, item) {
|
|
|
2087
3043
|
layerGroups.get(layer).push(item);
|
|
2088
3044
|
}
|
|
2089
3045
|
function getOrderedLayerNamesForGroups(layerGroups, layerOrder) {
|
|
2090
|
-
return [...layerOrder.filter((name) => (layerGroups.get(name)?.length ?? 0) > 0), ...[...layerGroups.keys()].filter((name) => !layerOrder.includes(name) &&
|
|
3046
|
+
return [...layerOrder.filter((name) => (layerGroups.get(name)?.length ?? 0) > 0), ...[...layerGroups.keys()].filter((name) => !layerOrder.includes(name) && layerGroups.get(name).length > 0)];
|
|
2091
3047
|
}
|
|
2092
3048
|
function renderLayerBlocks({ layerGroups, layerOrder, isFormatted, render }) {
|
|
2093
3049
|
const lineEnd = isFormatted ? "\n" : "";
|
|
@@ -2118,11 +3074,11 @@ function groupRenderedPreflightsByLayer(rendered) {
|
|
|
2118
3074
|
}
|
|
2119
3075
|
function splitLayerSelector(selector) {
|
|
2120
3076
|
const [first, ...rest] = selector;
|
|
2121
|
-
if (first == null || first.startsWith(
|
|
3077
|
+
if (first == null || first.startsWith("@layer ") === false) return {
|
|
2122
3078
|
layer: void 0,
|
|
2123
3079
|
selector
|
|
2124
3080
|
};
|
|
2125
|
-
const layer = first.slice(
|
|
3081
|
+
const layer = first.slice(7).trim();
|
|
2126
3082
|
if (layer.length === 0) return {
|
|
2127
3083
|
layer: void 0,
|
|
2128
3084
|
selector
|
|
@@ -2138,8 +3094,8 @@ function prependLayerSelector(selector, layer) {
|
|
|
2138
3094
|
function groupAtomicStylesByLayer({ styles, layerOrder, defaultUtilitiesLayer }) {
|
|
2139
3095
|
const unlayeredStyles = [];
|
|
2140
3096
|
const layerGroups = new Map(layerOrder.map((name) => [name, []]));
|
|
2141
|
-
const candidateDefaultLayer = defaultUtilitiesLayer ?? layerOrder
|
|
2142
|
-
const defaultLayer = candidateDefaultLayer != null && layerGroups.has(candidateDefaultLayer) ? candidateDefaultLayer : layerOrder
|
|
3097
|
+
const candidateDefaultLayer = defaultUtilitiesLayer ?? layerOrder.at(-1);
|
|
3098
|
+
const defaultLayer = candidateDefaultLayer != null && layerGroups.has(candidateDefaultLayer) ? candidateDefaultLayer : layerOrder.at(-1);
|
|
2143
3099
|
for (const style of styles) {
|
|
2144
3100
|
const { layer } = splitLayerSelector(style.content.selector);
|
|
2145
3101
|
if (layer != null && layerGroups.has(layer)) {
|
|
@@ -2172,6 +3128,20 @@ function isWithId(p) {
|
|
|
2172
3128
|
const record = p;
|
|
2173
3129
|
return typeof record.id === "string" && record.preflight !== void 0;
|
|
2174
3130
|
}
|
|
3131
|
+
/**
|
|
3132
|
+
* Normalizes a `Preflight` input into a `ResolvedPreflight` by extracting optional `layer` and `id` wrappers.
|
|
3133
|
+
* @internal
|
|
3134
|
+
*
|
|
3135
|
+
* @param preflight - A preflight value: a function, a static string/`PreflightDefinition`, or a wrapper with `layer`/`id` metadata.
|
|
3136
|
+
* @returns A `ResolvedPreflight` with separated `layer`, `id`, and `fn`.
|
|
3137
|
+
*
|
|
3138
|
+
* @remarks Handles nested wrappers: a `{ layer, preflight: { id, preflight: fn } }` shape is unwrapped in order.
|
|
3139
|
+
*
|
|
3140
|
+
* @example
|
|
3141
|
+
* ```ts
|
|
3142
|
+
* resolvePreflight({ layer: 'base', id: 'reset', preflight: '* { margin: 0 }' })
|
|
3143
|
+
* ```
|
|
3144
|
+
*/
|
|
2175
3145
|
function resolvePreflight(preflight) {
|
|
2176
3146
|
let layer;
|
|
2177
3147
|
let id;
|
|
@@ -2189,11 +3159,25 @@ function resolvePreflight(preflight) {
|
|
|
2189
3159
|
fn: typeof preflight === "function" ? preflight : () => preflight
|
|
2190
3160
|
};
|
|
2191
3161
|
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Resolves a raw `EngineConfig` into a fully normalized `ResolvedEngineConfig`.
|
|
3164
|
+
* @internal
|
|
3165
|
+
*
|
|
3166
|
+
* @param config - The raw engine configuration.
|
|
3167
|
+
* @returns A `ResolvedEngineConfig` with defaults applied, plugins sorted, preflights resolved, and autocomplete initialized.
|
|
3168
|
+
*
|
|
3169
|
+
* @remarks Merges `DEFAULT_LAYERS`, normalizes CSS imports, resolves preflight definitions, and initializes the empty autocomplete sets/maps.
|
|
3170
|
+
*
|
|
3171
|
+
* @example
|
|
3172
|
+
* ```ts
|
|
3173
|
+
* const resolved = await resolveEngineConfig({ prefix: 'pk-' })
|
|
3174
|
+
* ```
|
|
3175
|
+
*/
|
|
2192
3176
|
async function resolveEngineConfig(config) {
|
|
2193
|
-
const { prefix =
|
|
3177
|
+
const { prefix = "pk-", defaultSelector = `.%`, plugins = [], cssImports = [], preflights = [] } = config;
|
|
2194
3178
|
const layers = Object.assign({}, DEFAULT_LAYERS, config.layers);
|
|
2195
|
-
const defaultPreflightsLayer = config.defaultPreflightsLayer ??
|
|
2196
|
-
const defaultUtilitiesLayer = config.defaultUtilitiesLayer ??
|
|
3179
|
+
const defaultPreflightsLayer = config.defaultPreflightsLayer ?? "preflights";
|
|
3180
|
+
const defaultUtilitiesLayer = config.defaultUtilitiesLayer ?? "utilities";
|
|
2197
3181
|
log.debug(`Resolving engine config with prefix: "${prefix}", plugins: ${plugins.length}, preflights: ${preflights.length}`);
|
|
2198
3182
|
const resolvedConfig = {
|
|
2199
3183
|
rawConfig: config,
|
|
@@ -2207,14 +3191,14 @@ async function resolveEngineConfig(config) {
|
|
|
2207
3191
|
defaultUtilitiesLayer,
|
|
2208
3192
|
autocomplete: {
|
|
2209
3193
|
selectors: /* @__PURE__ */ new Set(),
|
|
2210
|
-
|
|
3194
|
+
shortcuts: /* @__PURE__ */ new Set(),
|
|
2211
3195
|
extraProperties: /* @__PURE__ */ new Set(),
|
|
2212
3196
|
extraCssProperties: /* @__PURE__ */ new Set(),
|
|
2213
3197
|
properties: /* @__PURE__ */ new Map(),
|
|
2214
3198
|
cssProperties: /* @__PURE__ */ new Map(),
|
|
2215
3199
|
patterns: {
|
|
2216
3200
|
selectors: /* @__PURE__ */ new Set(),
|
|
2217
|
-
|
|
3201
|
+
shortcuts: /* @__PURE__ */ new Set(),
|
|
2218
3202
|
properties: /* @__PURE__ */ new Map(),
|
|
2219
3203
|
cssProperties: /* @__PURE__ */ new Map()
|
|
2220
3204
|
}
|
|
@@ -2239,6 +3223,26 @@ function extractLayerFromStyleItem(item) {
|
|
|
2239
3223
|
definition: rest
|
|
2240
3224
|
};
|
|
2241
3225
|
}
|
|
3226
|
+
/**
|
|
3227
|
+
* Transforms and extracts a list of style items into deduplicated atomic style contents.
|
|
3228
|
+
* @internal
|
|
3229
|
+
*
|
|
3230
|
+
* @param options - An object containing:
|
|
3231
|
+
* - `itemList` — the raw style items to process.
|
|
3232
|
+
* - `transformStyleItems` — the plugin hook for transforming style items.
|
|
3233
|
+
* - `extractStyleDefinition` — the function that decomposes a style definition into extracted contents.
|
|
3234
|
+
* @param options.itemList - The raw style items to process.
|
|
3235
|
+
* @param options.transformStyleItems - Hook that expands or rewrites style items before extraction.
|
|
3236
|
+
* @param options.extractStyleDefinition - Function that decomposes a style definition into extracted style contents.
|
|
3237
|
+
* @returns An object with `unknown` (unresolved string references) and `contents` (optimized extracted style contents).
|
|
3238
|
+
*
|
|
3239
|
+
* @remarks String items that survive the `transformStyleItems` hook are collected into the `unknown` set. Object items are extracted, optionally layer-prepended, and optimized for duplicate property merging.
|
|
3240
|
+
*
|
|
3241
|
+
* @example
|
|
3242
|
+
* ```ts
|
|
3243
|
+
* const { unknown, contents } = await resolveStyleItemList({ itemList, transformStyleItems, extractStyleDefinition })
|
|
3244
|
+
* ```
|
|
3245
|
+
*/
|
|
2242
3246
|
async function resolveStyleItemList({ itemList, transformStyleItems, extractStyleDefinition }) {
|
|
2243
3247
|
const unknown = /* @__PURE__ */ new Set();
|
|
2244
3248
|
const list = [];
|
|
@@ -2263,7 +3267,7 @@ function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
|
|
|
2263
3267
|
const blocks = /* @__PURE__ */ new Map();
|
|
2264
3268
|
atomicStyles.forEach(({ id, content: { selector: rawSelector, property, value } }) => {
|
|
2265
3269
|
const { selector } = splitLayerSelector(rawSelector);
|
|
2266
|
-
if (selector.some((s) => s.includes(
|
|
3270
|
+
if (selector.some((s) => s.includes("%")) === false || value == null) return;
|
|
2267
3271
|
const renderObject = {
|
|
2268
3272
|
selector: isPreview ? selector : selector.map((s) => s.replace(ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, id)),
|
|
2269
3273
|
properties: value.map((v) => ({
|
|
@@ -2284,6 +3288,26 @@ function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
|
|
|
2284
3288
|
});
|
|
2285
3289
|
return renderCSSStyleBlocks(blocks, isFormatted);
|
|
2286
3290
|
}
|
|
3291
|
+
/**
|
|
3292
|
+
* Standalone function that renders atomic styles into CSS with layer grouping.
|
|
3293
|
+
* @internal
|
|
3294
|
+
*
|
|
3295
|
+
* @param payload - An object containing `atomicStyles`, `isPreview`, `isFormatted`, `defaultSelector`, and optional `layers`/`defaultUtilitiesLayer`.
|
|
3296
|
+
* @param payload.atomicStyles - The atomic styles to render.
|
|
3297
|
+
* @param payload.isPreview - Whether placeholder IDs should be preserved for preview output.
|
|
3298
|
+
* @param payload.isFormatted - Whether to render with indentation and line breaks.
|
|
3299
|
+
* @param payload.defaultSelector - The engine default selector used when computing render order.
|
|
3300
|
+
* @param payload.layers - Optional configured CSS layers to group atomic styles into.
|
|
3301
|
+
* @param payload.defaultUtilitiesLayer - Optional fallback layer for unlayered utility styles.
|
|
3302
|
+
* @returns The rendered CSS string.
|
|
3303
|
+
*
|
|
3304
|
+
* @remarks Sorts styles by rendering weight, groups them into `@layer` blocks when layers are configured, and renders each group. Used by both the `Engine.renderAtomicStyles` method and external consumers.
|
|
3305
|
+
*
|
|
3306
|
+
* @example
|
|
3307
|
+
* ```ts
|
|
3308
|
+
* const css = renderAtomicStyles({ atomicStyles, isPreview: false, isFormatted: true, defaultSelector: '.pk-__ID__', layers: { utilities: 10 } })
|
|
3309
|
+
* ```
|
|
3310
|
+
*/
|
|
2287
3311
|
function renderAtomicStyles(payload) {
|
|
2288
3312
|
const { atomicStyles, isPreview, isFormatted, defaultSelector, layers, defaultUtilitiesLayer } = payload;
|
|
2289
3313
|
const sortedStyles = sortAtomicStyles(atomicStyles, defaultSelector);
|
|
@@ -2317,6 +3341,26 @@ function renderAtomicStyles(payload) {
|
|
|
2317
3341
|
}));
|
|
2318
3342
|
return parts.join(lineEnd);
|
|
2319
3343
|
}
|
|
3344
|
+
/**
|
|
3345
|
+
* Recursively converts a `PreflightDefinition` object tree into CSS style blocks.
|
|
3346
|
+
* @internal
|
|
3347
|
+
*
|
|
3348
|
+
* @param options - An object containing:
|
|
3349
|
+
* - `engine` — the engine instance (used for selector transformation).
|
|
3350
|
+
* - `preflightDefinition` — the nested object tree of selectors and CSS properties.
|
|
3351
|
+
* - `blocks` — accumulator map for the resulting CSS blocks.
|
|
3352
|
+
* @param options.engine - The engine instance used to run selector transforms.
|
|
3353
|
+
* @param options.preflightDefinition - The nested preflight definition object to convert into CSS blocks.
|
|
3354
|
+
* @param options.blocks - Optional accumulator map reused during recursive descent.
|
|
3355
|
+
* @returns The accumulated `CSSStyleBlocks` map.
|
|
3356
|
+
*
|
|
3357
|
+
* @remarks Each key in the definition is either a CSS property (when its value is a property value) or a nested selector scope (when its value is an object). Selector keys are expanded through `hooks.transformSelectors`. The resulting blocks map is consumable by `renderCSSStyleBlocks`.
|
|
3358
|
+
*
|
|
3359
|
+
* @example
|
|
3360
|
+
* ```ts
|
|
3361
|
+
* const blocks = await _renderPreflightDefinition({ engine, preflightDefinition: { ':root': { '--color': 'red' } } })
|
|
3362
|
+
* ```
|
|
3363
|
+
*/
|
|
2320
3364
|
async function _renderPreflightDefinition({ engine, preflightDefinition, blocks = /* @__PURE__ */ new Map() }) {
|
|
2321
3365
|
for (const [selector, propertiesOrDefinition] of Object.entries(preflightDefinition)) {
|
|
2322
3366
|
if (propertiesOrDefinition == null) continue;
|
|
@@ -2355,6 +3399,23 @@ async function _renderPreflightDefinition({ engine, preflightDefinition, blocks
|
|
|
2355
3399
|
}
|
|
2356
3400
|
return blocks;
|
|
2357
3401
|
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Renders a `PreflightDefinition` into a CSS string via the engine's selector pipeline.
|
|
3404
|
+
* @internal
|
|
3405
|
+
*
|
|
3406
|
+
* @param payload - An object with the `engine`, the `preflightDefinition` to render, and `isFormatted` flag.
|
|
3407
|
+
* @param payload.engine - The engine instance whose selector pipeline should be applied.
|
|
3408
|
+
* @param payload.preflightDefinition - The preflight definition tree to render.
|
|
3409
|
+
* @param payload.isFormatted - Whether the rendered CSS should include indentation and line breaks.
|
|
3410
|
+
* @returns The rendered CSS string.
|
|
3411
|
+
*
|
|
3412
|
+
* @remarks A convenience wrapper that calls `_renderPreflightDefinition` and pipes the result through `renderCSSStyleBlocks`.
|
|
3413
|
+
*
|
|
3414
|
+
* @example
|
|
3415
|
+
* ```ts
|
|
3416
|
+
* const css = await renderPreflightDefinition({ engine, preflightDefinition: { ':root': { color: 'red' } }, isFormatted: true })
|
|
3417
|
+
* ```
|
|
3418
|
+
*/
|
|
2358
3419
|
async function renderPreflightDefinition(payload) {
|
|
2359
3420
|
const { engine, preflightDefinition, isFormatted } = payload;
|
|
2360
3421
|
return renderCSSStyleBlocks(await _renderPreflightDefinition({
|
|
@@ -2362,31 +3423,127 @@ async function renderPreflightDefinition(payload) {
|
|
|
2362
3423
|
preflightDefinition
|
|
2363
3424
|
}), isFormatted);
|
|
2364
3425
|
}
|
|
2365
|
-
|
|
2366
3426
|
//#endregion
|
|
2367
3427
|
//#region src/index.ts
|
|
3428
|
+
/**
|
|
3429
|
+
* Identity helper that returns the engine configuration as-is, providing TypeScript type inference and autocompletion.
|
|
3430
|
+
*
|
|
3431
|
+
* @typeParam T - The exact literal type of the configuration object.
|
|
3432
|
+
* @param config - The engine configuration object.
|
|
3433
|
+
* @returns The same configuration object, unchanged.
|
|
3434
|
+
*
|
|
3435
|
+
* @remarks A compile-time-only helper with no runtime effect. Useful in `pika.config.ts` files for IDE support.
|
|
3436
|
+
*
|
|
3437
|
+
* @example
|
|
3438
|
+
* ```ts
|
|
3439
|
+
* export default defineEngineConfig({ prefix: 'pk-', plugins: [myPlugin()] })
|
|
3440
|
+
* ```
|
|
3441
|
+
*/
|
|
2368
3442
|
function defineEngineConfig(config) {
|
|
2369
3443
|
return config;
|
|
2370
3444
|
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Identity helper that returns the style definition as-is, providing TypeScript type inference and autocompletion.
|
|
3447
|
+
*
|
|
3448
|
+
* @typeParam T - The exact literal type of the style definition.
|
|
3449
|
+
* @param styleDefinition - A style definition object.
|
|
3450
|
+
* @returns The same style definition, unchanged.
|
|
3451
|
+
*
|
|
3452
|
+
* @remarks A compile-time-only helper with no runtime effect. Useful for extracting a reusable style definition with full type safety.
|
|
3453
|
+
*
|
|
3454
|
+
* @example
|
|
3455
|
+
* ```ts
|
|
3456
|
+
* const card = defineStyleDefinition({ padding: '1rem', borderRadius: '0.5rem' })
|
|
3457
|
+
* ```
|
|
3458
|
+
*/
|
|
2371
3459
|
function defineStyleDefinition(styleDefinition) {
|
|
2372
3460
|
return styleDefinition;
|
|
2373
3461
|
}
|
|
3462
|
+
/**
|
|
3463
|
+
* Identity helper that returns the preflight as-is, providing TypeScript type inference and autocompletion.
|
|
3464
|
+
*
|
|
3465
|
+
* @typeParam T - The exact literal type of the preflight.
|
|
3466
|
+
* @param preflight - A preflight definition: a function, a static string/object, or a wrapper with `layer`/`id` metadata.
|
|
3467
|
+
* @returns The same preflight, unchanged.
|
|
3468
|
+
*
|
|
3469
|
+
* @remarks A compile-time-only helper with no runtime effect. Useful for defining reusable preflight values with type safety.
|
|
3470
|
+
*
|
|
3471
|
+
* @example
|
|
3472
|
+
* ```ts
|
|
3473
|
+
* const reset = definePreflight('*, *::before { box-sizing: border-box; }')
|
|
3474
|
+
* ```
|
|
3475
|
+
*/
|
|
2374
3476
|
function definePreflight(preflight) {
|
|
2375
3477
|
return preflight;
|
|
2376
3478
|
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Identity helper that returns the keyframes definition as-is, providing TypeScript type inference and autocompletion.
|
|
3481
|
+
*
|
|
3482
|
+
* @typeParam T - The exact literal type of the keyframes configuration.
|
|
3483
|
+
* @param keyframes - A keyframes definition: a name string, a tuple, or an object form.
|
|
3484
|
+
* @returns The same keyframes definition, unchanged.
|
|
3485
|
+
*
|
|
3486
|
+
* @remarks A compile-time-only helper with no runtime effect.
|
|
3487
|
+
*
|
|
3488
|
+
* @example
|
|
3489
|
+
* ```ts
|
|
3490
|
+
* const spin = defineKeyframes(['spin', { from: { transform: 'rotate(0deg)' }, to: { transform: 'rotate(360deg)' } }])
|
|
3491
|
+
* ```
|
|
3492
|
+
*/
|
|
2377
3493
|
function defineKeyframes(keyframes) {
|
|
2378
3494
|
return keyframes;
|
|
2379
3495
|
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Identity helper that returns the selector definition as-is, providing TypeScript type inference and autocompletion.
|
|
3498
|
+
*
|
|
3499
|
+
* @typeParam T - The exact literal type of the selector configuration.
|
|
3500
|
+
* @param selector - A selector definition: a string redirect, tuple, or object form.
|
|
3501
|
+
* @returns The same selector definition, unchanged.
|
|
3502
|
+
*
|
|
3503
|
+
* @remarks A compile-time-only helper with no runtime effect.
|
|
3504
|
+
*
|
|
3505
|
+
* @example
|
|
3506
|
+
* ```ts
|
|
3507
|
+
* const hover = defineSelector(['hover', '&:hover'])
|
|
3508
|
+
* ```
|
|
3509
|
+
*/
|
|
2380
3510
|
function defineSelector(selector) {
|
|
2381
3511
|
return selector;
|
|
2382
3512
|
}
|
|
3513
|
+
/**
|
|
3514
|
+
* Identity helper that returns the shortcut definition as-is, providing TypeScript type inference and autocompletion.
|
|
3515
|
+
*
|
|
3516
|
+
* @typeParam T - The exact literal type of the shortcut configuration.
|
|
3517
|
+
* @param shortcut - A shortcut definition: a string redirect, tuple, or object form.
|
|
3518
|
+
* @returns The same shortcut definition, unchanged.
|
|
3519
|
+
*
|
|
3520
|
+
* @remarks A compile-time-only helper with no runtime effect.
|
|
3521
|
+
*
|
|
3522
|
+
* @example
|
|
3523
|
+
* ```ts
|
|
3524
|
+
* const btn = defineShortcut(['btn', { padding: '0.5rem 1rem', borderRadius: '0.25rem' }])
|
|
3525
|
+
* ```
|
|
3526
|
+
*/
|
|
2383
3527
|
function defineShortcut(shortcut) {
|
|
2384
3528
|
return shortcut;
|
|
2385
3529
|
}
|
|
3530
|
+
/**
|
|
3531
|
+
* Identity helper that returns the variables definition as-is, providing TypeScript type inference and autocompletion.
|
|
3532
|
+
*
|
|
3533
|
+
* @typeParam T - The exact literal type of the variables definition.
|
|
3534
|
+
* @param variables - A nested record of CSS custom property definitions.
|
|
3535
|
+
* @returns The same variables definition, unchanged.
|
|
3536
|
+
*
|
|
3537
|
+
* @remarks A compile-time-only helper with no runtime effect.
|
|
3538
|
+
*
|
|
3539
|
+
* @example
|
|
3540
|
+
* ```ts
|
|
3541
|
+
* const vars = defineVariables({ '--color-primary': '#3b82f6', '.dark': { '--color-primary': '#60a5fa' } })
|
|
3542
|
+
* ```
|
|
3543
|
+
*/
|
|
2386
3544
|
function defineVariables(variables) {
|
|
2387
3545
|
return variables;
|
|
2388
3546
|
}
|
|
2389
3547
|
/* c8 ignore end */
|
|
2390
|
-
|
|
2391
3548
|
//#endregion
|
|
2392
|
-
export { appendAutocomplete, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, defineKeyframes, definePreflight, defineSelector, defineShortcut, defineStyleDefinition, defineVariables, log, renderCSSStyleBlocks, sortLayerNames };
|
|
3549
|
+
export { appendAutocomplete, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, defineKeyframes, definePreflight, defineSelector, defineShortcut, defineStyleDefinition, defineVariables, log, renderCSSStyleBlocks, sortLayerNames };
|