@pikacss/core 0.0.62 → 0.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +734 -1021
- package/dist/index.mjs +1116 -558
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1362,25 +1362,6 @@ function isNotNullish(value) {
|
|
|
1362
1362
|
return value != null;
|
|
1363
1363
|
}
|
|
1364
1364
|
/**
|
|
1365
|
-
* Type-narrowing guard that returns `true` when the value is not a string, narrowing the type to `Exclude<V, string>`.
|
|
1366
|
-
* @internal
|
|
1367
|
-
*
|
|
1368
|
-
* @typeParam V - The union type of the input value.
|
|
1369
|
-
* @param value - The value to test.
|
|
1370
|
-
* @returns `true` if the value is not a `string`.
|
|
1371
|
-
*
|
|
1372
|
-
* @remarks Useful for filtering processed style items to separate resolved definition objects from unresolved string references.
|
|
1373
|
-
*
|
|
1374
|
-
* @example
|
|
1375
|
-
* ```ts
|
|
1376
|
-
* const items: (string | object)[] = ['btn', { color: 'red' }]
|
|
1377
|
-
* const objects = items.filter(isNotString) // [{ color: 'red' }]
|
|
1378
|
-
* ```
|
|
1379
|
-
*/
|
|
1380
|
-
function isNotString(value) {
|
|
1381
|
-
return typeof value !== "string";
|
|
1382
|
-
}
|
|
1383
|
-
/**
|
|
1384
1365
|
* Type-narrowing guard that returns `true` when the value is a plain object record (non-null, non-array object).
|
|
1385
1366
|
* @internal
|
|
1386
1367
|
*
|
|
@@ -1513,7 +1494,7 @@ function serialize(value) {
|
|
|
1513
1494
|
* @param values - Values to add.
|
|
1514
1495
|
* @returns `true` if at least one new element was added (the set grew).
|
|
1515
1496
|
*
|
|
1516
|
-
* @remarks The boolean return
|
|
1497
|
+
* @remarks The boolean return lets callers detect whether adding values changed the set.
|
|
1517
1498
|
*
|
|
1518
1499
|
* @example
|
|
1519
1500
|
* ```ts
|
|
@@ -1528,95 +1509,6 @@ function addToSet(set, ...values) {
|
|
|
1528
1509
|
return set.size !== before;
|
|
1529
1510
|
}
|
|
1530
1511
|
/**
|
|
1531
|
-
* Flattens an `Arrayable<string>` value and adds all entries to a `Set`, returning whether the set grew.
|
|
1532
|
-
* @internal
|
|
1533
|
-
*
|
|
1534
|
-
* @param set - The target set to append to.
|
|
1535
|
-
* @param values - A single string or array of strings to add, or `undefined`/`null` to skip.
|
|
1536
|
-
* @returns `true` if at least one new entry was added; `false` if the input was nullish or all entries already existed.
|
|
1537
|
-
*
|
|
1538
|
-
* @remarks Short-circuits on nullish input for convenience, since many autocomplete contribution fields are optional.
|
|
1539
|
-
*
|
|
1540
|
-
* @example
|
|
1541
|
-
* ```ts
|
|
1542
|
-
* const s = new Set<string>()
|
|
1543
|
-
* appendAutocompleteEntries(s, 'hover') // true
|
|
1544
|
-
* appendAutocompleteEntries(s, ['hover']) // false (already present)
|
|
1545
|
-
* appendAutocompleteEntries(s, undefined) // false
|
|
1546
|
-
* ```
|
|
1547
|
-
*/
|
|
1548
|
-
function appendAutocompleteEntries(set, values) {
|
|
1549
|
-
if (values == null) return false;
|
|
1550
|
-
return addToSet(set, ...[values].flat());
|
|
1551
|
-
}
|
|
1552
|
-
/**
|
|
1553
|
-
* Merges a record of `Arrayable<string>` values into a `Map<string, string[]>`, returning whether any entry was added.
|
|
1554
|
-
* @internal
|
|
1555
|
-
*
|
|
1556
|
-
* @param map - The target map to append entries to.
|
|
1557
|
-
* @param entries - A record mapping keys to single or arrayed string values, or `undefined` to skip.
|
|
1558
|
-
* @returns `true` if at least one entry was added or extended; `false` if the input was nullish or empty.
|
|
1559
|
-
*
|
|
1560
|
-
* @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. Values already present for a key are skipped, and the function returns `false` when nothing new was added.
|
|
1561
|
-
*
|
|
1562
|
-
* @example
|
|
1563
|
-
* ```ts
|
|
1564
|
-
* const map = new Map<string, string[]>()
|
|
1565
|
-
* appendAutocompleteRecordEntries(map, { color: ['red', 'blue'] }) // true
|
|
1566
|
-
* appendAutocompleteRecordEntries(map, { color: 'green' }) // true (now ['red','blue','green'])
|
|
1567
|
-
* appendAutocompleteRecordEntries(map, { color: 'green' }) // false (no change)
|
|
1568
|
-
* ```
|
|
1569
|
-
*/
|
|
1570
|
-
function appendAutocompleteRecordEntries(map, entries) {
|
|
1571
|
-
if (entries == null) return false;
|
|
1572
|
-
let changed = false;
|
|
1573
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
1574
|
-
const current = map.get(key);
|
|
1575
|
-
const existing = new Set(current);
|
|
1576
|
-
const added = [value].flat().filter((v) => !existing.has(v));
|
|
1577
|
-
if (added.length === 0) continue;
|
|
1578
|
-
map.set(key, [...current ?? [], ...added]);
|
|
1579
|
-
changed = true;
|
|
1580
|
-
}
|
|
1581
|
-
return changed;
|
|
1582
|
-
}
|
|
1583
|
-
function normalizeAutocompleteRecordEntries(entries) {
|
|
1584
|
-
if (entries == null) return void 0;
|
|
1585
|
-
return Array.isArray(entries) ? Object.fromEntries(entries) : entries;
|
|
1586
|
-
}
|
|
1587
|
-
/**
|
|
1588
|
-
* Merges an `AutocompleteContribution` or `AutocompleteConfig` into the resolved autocomplete state, returning whether any entry changed.
|
|
1589
|
-
*
|
|
1590
|
-
* @param config - The resolved engine config (or a subset with the `autocomplete` field) to mutate.
|
|
1591
|
-
* @param contribution - The autocomplete entries to merge in.
|
|
1592
|
-
* @returns `true` if any selector, shortcut, property, CSS property, or pattern entry was added or extended.
|
|
1593
|
-
*
|
|
1594
|
-
* @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.
|
|
1595
|
-
*
|
|
1596
|
-
* @example
|
|
1597
|
-
* ```ts
|
|
1598
|
-
* const changed = appendAutocomplete(resolvedConfig, {
|
|
1599
|
-
* selectors: 'dark',
|
|
1600
|
-
* cssProperties: { color: 'primary' },
|
|
1601
|
-
* })
|
|
1602
|
-
* ```
|
|
1603
|
-
*/
|
|
1604
|
-
function appendAutocomplete(config, contribution) {
|
|
1605
|
-
const { patterns, properties, cssProperties, ...literals } = contribution;
|
|
1606
|
-
return [
|
|
1607
|
-
appendAutocompleteEntries(config.autocomplete.selectors, literals.selectors),
|
|
1608
|
-
appendAutocompleteEntries(config.autocomplete.shortcuts, literals.shortcuts),
|
|
1609
|
-
appendAutocompleteEntries(config.autocomplete.extraProperties, literals.extraProperties),
|
|
1610
|
-
appendAutocompleteEntries(config.autocomplete.extraCssProperties, literals.extraCssProperties),
|
|
1611
|
-
appendAutocompleteRecordEntries(config.autocomplete.properties, normalizeAutocompleteRecordEntries(properties)),
|
|
1612
|
-
appendAutocompleteRecordEntries(config.autocomplete.cssProperties, normalizeAutocompleteRecordEntries(cssProperties)),
|
|
1613
|
-
appendAutocompleteEntries(config.autocomplete.patterns.selectors, patterns?.selectors),
|
|
1614
|
-
appendAutocompleteEntries(config.autocomplete.patterns.shortcuts, patterns?.shortcuts),
|
|
1615
|
-
appendAutocompleteRecordEntries(config.autocomplete.patterns.properties, patterns?.properties),
|
|
1616
|
-
appendAutocompleteRecordEntries(config.autocomplete.patterns.cssProperties, patterns?.cssProperties)
|
|
1617
|
-
].some(Boolean);
|
|
1618
|
-
}
|
|
1619
|
-
/**
|
|
1620
1512
|
* Serializes a `CSSStyleBlocks` tree into a CSS string, optionally formatted with indentation and newlines.
|
|
1621
1513
|
*
|
|
1622
1514
|
* @param blocks - The CSS block tree to render.
|
|
@@ -1688,21 +1580,22 @@ function createEngineStore() {
|
|
|
1688
1580
|
* Assigns or retrieves a compact atomic style ID for the given resolved style content.
|
|
1689
1581
|
* @internal
|
|
1690
1582
|
*
|
|
1691
|
-
* @param options - Object containing the style `content`, the engine `prefix`,
|
|
1583
|
+
* @param options - Object containing the style `content`, the engine `prefix`, the `stored` ID map, and the resolved allocation strategy.
|
|
1692
1584
|
* @param options.content - The resolved style content to hash and identify.
|
|
1693
1585
|
* @param options.prefix - The class-name prefix used when constructing a new atomic style ID.
|
|
1694
1586
|
* @param options.stored - The map that caches assigned IDs by serialized key.
|
|
1587
|
+
* @param options.atomicStyleIdStrategy - Engine-owned strategy used only when a new ID is required.
|
|
1695
1588
|
* @returns The short alphabetic ID string (e.g. `'pk-a'`, `'pk-bA'`).
|
|
1696
1589
|
*
|
|
1697
1590
|
* @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.
|
|
1698
1591
|
*
|
|
1699
1592
|
* @example
|
|
1700
1593
|
* ```ts
|
|
1701
|
-
* const id = getAtomicStyleId({ content, prefix: 'pk-', stored: store.atomicStyleIds })
|
|
1594
|
+
* const id = getAtomicStyleId({ content, prefix: 'pk-', stored: store.atomicStyleIds, atomicStyleIdStrategy })
|
|
1702
1595
|
* // 'pk-a'
|
|
1703
1596
|
* ```
|
|
1704
1597
|
*/
|
|
1705
|
-
function getAtomicStyleId({ content, prefix, stored }) {
|
|
1598
|
+
function getAtomicStyleId({ content, prefix, stored, atomicStyleIdStrategy }) {
|
|
1706
1599
|
const baseKey = getAtomicStyleBaseKey(content);
|
|
1707
1600
|
if (isOrderSensitiveContent(content) === false) {
|
|
1708
1601
|
const cached = stored.get(baseKey);
|
|
@@ -1711,12 +1604,15 @@ function getAtomicStyleId({ content, prefix, stored }) {
|
|
|
1711
1604
|
return cached;
|
|
1712
1605
|
}
|
|
1713
1606
|
}
|
|
1714
|
-
const
|
|
1715
|
-
const id =
|
|
1607
|
+
const index = stored.size;
|
|
1608
|
+
const id = atomicStyleIdStrategy({
|
|
1609
|
+
index,
|
|
1610
|
+
prefix
|
|
1611
|
+
});
|
|
1716
1612
|
const key = getAtomicStyleStoredKey({
|
|
1717
1613
|
content,
|
|
1718
1614
|
baseKey,
|
|
1719
|
-
num
|
|
1615
|
+
num: index
|
|
1720
1616
|
});
|
|
1721
1617
|
stored.set(key, id);
|
|
1722
1618
|
log.debug(`Generated new atomic style ID: ${id}`);
|
|
@@ -1726,11 +1622,12 @@ function getAtomicStyleId({ content, prefix, stored }) {
|
|
|
1726
1622
|
* Resolves a `StyleContent` into an atomic style: either reusing an existing ID or creating a new `AtomicStyle` entry in the store.
|
|
1727
1623
|
* @internal
|
|
1728
1624
|
*
|
|
1729
|
-
* @param options - Object containing the style `content`, `prefix`, `store`,
|
|
1625
|
+
* @param options - Object containing the style `content`, `prefix`, `store`, the per-use-call `resolvedIdsByBaseKey` map, and the engine-owned allocation strategy.
|
|
1730
1626
|
* @param options.content - The style content to resolve into a cached or newly registered atomic style.
|
|
1731
1627
|
* @param options.prefix - The atomic style ID prefix for any newly created IDs.
|
|
1732
1628
|
* @param options.store - The engine store holding existing atomic styles and lookup maps.
|
|
1733
1629
|
* @param options.resolvedIdsByBaseKey - Per-call memoization map for reusing order-sensitive IDs within one `engine.use()` execution.
|
|
1630
|
+
* @param options.atomicStyleIdStrategy - Engine-owned strategy used only at the new-allocation boundary.
|
|
1734
1631
|
* @returns An `AtomicStyleResolution` with the assigned `id` and optionally the newly created `atomicStyle` (absent when the ID was already registered).
|
|
1735
1632
|
*
|
|
1736
1633
|
* @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.
|
|
@@ -1738,11 +1635,11 @@ function getAtomicStyleId({ content, prefix, stored }) {
|
|
|
1738
1635
|
* @example
|
|
1739
1636
|
* ```ts
|
|
1740
1637
|
* const { id, atomicStyle } = resolveAtomicStyle({
|
|
1741
|
-
* content, prefix: 'pk-', store, resolvedIdsByBaseKey,
|
|
1638
|
+
* content, prefix: 'pk-', store, resolvedIdsByBaseKey, atomicStyleIdStrategy,
|
|
1742
1639
|
* })
|
|
1743
1640
|
* ```
|
|
1744
1641
|
*/
|
|
1745
|
-
function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey }) {
|
|
1642
|
+
function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey, atomicStyleIdStrategy }) {
|
|
1746
1643
|
const reusableId = findReusableAtomicStyleId({
|
|
1747
1644
|
content,
|
|
1748
1645
|
store,
|
|
@@ -1755,7 +1652,8 @@ function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey }) {
|
|
|
1755
1652
|
const id = getAtomicStyleId({
|
|
1756
1653
|
content,
|
|
1757
1654
|
prefix,
|
|
1758
|
-
stored: store.atomicStyleIds
|
|
1655
|
+
stored: store.atomicStyleIds,
|
|
1656
|
+
atomicStyleIdStrategy
|
|
1759
1657
|
});
|
|
1760
1658
|
if (store.atomicStyles.has(id)) return { id };
|
|
1761
1659
|
const atomicStyle = {
|
|
@@ -2193,8 +2091,222 @@ function createExtractFn(options) {
|
|
|
2193
2091
|
});
|
|
2194
2092
|
}
|
|
2195
2093
|
//#endregion
|
|
2094
|
+
//#region src/finalization.ts
|
|
2095
|
+
const finalizers = /* @__PURE__ */ new WeakMap();
|
|
2096
|
+
/** @internal */
|
|
2097
|
+
function registerCoreEngineFinalizer(engine, finalizer) {
|
|
2098
|
+
const tasks = finalizers.get(engine) ?? [];
|
|
2099
|
+
tasks.push(finalizer);
|
|
2100
|
+
finalizers.set(engine, tasks);
|
|
2101
|
+
}
|
|
2102
|
+
/** @internal */
|
|
2103
|
+
async function runCoreEngineFinalizers(engine) {
|
|
2104
|
+
const tasks = finalizers.get(engine) ?? [];
|
|
2105
|
+
finalizers.delete(engine);
|
|
2106
|
+
for (const task of tasks) await task();
|
|
2107
|
+
}
|
|
2108
|
+
//#endregion
|
|
2109
|
+
//#region src/pika.ts
|
|
2110
|
+
const states$1 = /* @__PURE__ */ new WeakMap();
|
|
2111
|
+
/** @internal */
|
|
2112
|
+
function createPikaManager() {
|
|
2113
|
+
const manager = {
|
|
2114
|
+
hasStatic(name) {
|
|
2115
|
+
return states$1.get(manager).entries.has(name);
|
|
2116
|
+
},
|
|
2117
|
+
getStatic(name) {
|
|
2118
|
+
return states$1.get(manager).entries.get(name)?.implementation;
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
states$1.set(manager, {
|
|
2122
|
+
entries: /* @__PURE__ */ new Map(),
|
|
2123
|
+
finalized: false
|
|
2124
|
+
});
|
|
2125
|
+
return manager;
|
|
2126
|
+
}
|
|
2127
|
+
/** @internal */
|
|
2128
|
+
function createPikaRegistrationController(manager, owner) {
|
|
2129
|
+
const gate = { open: false };
|
|
2130
|
+
return {
|
|
2131
|
+
capability: Object.freeze({ extendStatic(name, implementation) {
|
|
2132
|
+
const state = states$1.get(manager);
|
|
2133
|
+
if (state.finalized) throw new Error("Pika static extensions are finalized and cannot be modified");
|
|
2134
|
+
if (!gate.open) throw new Error("Pika static extensions may only be registered during this plugin configureEngine hook");
|
|
2135
|
+
if (typeof name !== "string" || name.trim().length === 0) throw new Error("Pika static extension name must be a non-empty string");
|
|
2136
|
+
if (state.entries.has(name)) throw new Error(`Pika static extension root "${name}" is already registered`);
|
|
2137
|
+
state.entries.set(name, {
|
|
2138
|
+
owner,
|
|
2139
|
+
implementation
|
|
2140
|
+
});
|
|
2141
|
+
} }),
|
|
2142
|
+
open: () => {
|
|
2143
|
+
gate.open = true;
|
|
2144
|
+
},
|
|
2145
|
+
close: () => {
|
|
2146
|
+
gate.open = false;
|
|
2147
|
+
}
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
/** @internal */
|
|
2151
|
+
function getPikaStaticOwner(manager, name) {
|
|
2152
|
+
return states$1.get(manager).entries.get(name)?.owner;
|
|
2153
|
+
}
|
|
2154
|
+
/** @internal */
|
|
2155
|
+
function finalizePikaManager(manager) {
|
|
2156
|
+
const state = states$1.get(manager);
|
|
2157
|
+
state.finalized = true;
|
|
2158
|
+
Object.freeze(manager);
|
|
2159
|
+
}
|
|
2160
|
+
//#endregion
|
|
2161
|
+
//#region src/typegen/registry.ts
|
|
2162
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
2163
|
+
const snapshotRenderOverrides = /* @__PURE__ */ new WeakMap();
|
|
2164
|
+
const MANAGED_REF_KEYS = [
|
|
2165
|
+
"selectors",
|
|
2166
|
+
"properties",
|
|
2167
|
+
"cssProperties",
|
|
2168
|
+
"cssPropertyValues",
|
|
2169
|
+
"propertyConstraints"
|
|
2170
|
+
];
|
|
2171
|
+
function compareStrings$3(a, b) {
|
|
2172
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2173
|
+
}
|
|
2174
|
+
function validateContribution(contribution) {
|
|
2175
|
+
if (contribution == null || typeof contribution !== "object" || Array.isArray(contribution)) throw new Error("Typegen contribution must be an object");
|
|
2176
|
+
if (typeof contribution.id !== "string" || contribution.id.trim().length === 0) throw new Error("Typegen contribution id must be a non-empty string");
|
|
2177
|
+
if (contribution.declarations !== void 0 && typeof contribution.declarations !== "string") throw new Error("Typegen declarations must be a string when provided");
|
|
2178
|
+
for (const key of MANAGED_REF_KEYS) {
|
|
2179
|
+
const ref = contribution[key];
|
|
2180
|
+
if (ref !== void 0 && (typeof ref !== "string" || ref.trim().length === 0)) throw new Error(`Typegen managed attachment "${key}" must be a non-empty string when provided`);
|
|
2181
|
+
}
|
|
2182
|
+
if (contribution.pika !== void 0) {
|
|
2183
|
+
if (contribution.pika == null || typeof contribution.pika !== "object" || Array.isArray(contribution.pika)) throw new Error("Typegen Pika attachment must be an object when provided");
|
|
2184
|
+
for (const [root, ref] of Object.entries(contribution.pika)) {
|
|
2185
|
+
if (root.trim().length === 0) throw new Error("Typegen Pika root must be a non-empty string");
|
|
2186
|
+
if (typeof ref !== "string" || ref.trim().length === 0) throw new Error(`Typegen Pika root "${root}" must reference a non-empty TypeScript expression`);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
function freezePikaAttachment(pika) {
|
|
2191
|
+
return Object.freeze(Object.fromEntries(Object.entries(pika).sort(([a], [b]) => compareStrings$3(a, b))));
|
|
2192
|
+
}
|
|
2193
|
+
function freezeContribution(contribution) {
|
|
2194
|
+
const result = {
|
|
2195
|
+
id: contribution.id,
|
|
2196
|
+
...contribution.declarations === void 0 ? {} : { declarations: contribution.declarations },
|
|
2197
|
+
...contribution.pika === void 0 ? {} : { pika: freezePikaAttachment(contribution.pika) },
|
|
2198
|
+
...contribution.selectors === void 0 ? {} : { selectors: contribution.selectors },
|
|
2199
|
+
...contribution.properties === void 0 ? {} : { properties: contribution.properties },
|
|
2200
|
+
...contribution.cssProperties === void 0 ? {} : { cssProperties: contribution.cssProperties },
|
|
2201
|
+
...contribution.cssPropertyValues === void 0 ? {} : { cssPropertyValues: contribution.cssPropertyValues },
|
|
2202
|
+
...contribution.propertyConstraints === void 0 ? {} : { propertyConstraints: contribution.propertyConstraints }
|
|
2203
|
+
};
|
|
2204
|
+
return Object.freeze(result);
|
|
2205
|
+
}
|
|
2206
|
+
/** @internal */
|
|
2207
|
+
function createTypegenManager() {
|
|
2208
|
+
const manager = { get snapshot() {
|
|
2209
|
+
const snapshot = states.get(manager).snapshot;
|
|
2210
|
+
if (snapshot == null) throw new Error("Typegen snapshot is not available until Engine finalization");
|
|
2211
|
+
return snapshot;
|
|
2212
|
+
} };
|
|
2213
|
+
states.set(manager, {
|
|
2214
|
+
contributions: [],
|
|
2215
|
+
ids: /* @__PURE__ */ new Set(),
|
|
2216
|
+
pikaOwners: /* @__PURE__ */ new Map(),
|
|
2217
|
+
previewAssets: /* @__PURE__ */ new Map(),
|
|
2218
|
+
renderOverrides: /* @__PURE__ */ new Map(),
|
|
2219
|
+
finalized: false
|
|
2220
|
+
});
|
|
2221
|
+
return manager;
|
|
2222
|
+
}
|
|
2223
|
+
/** @internal */
|
|
2224
|
+
function createTypegenRegistrationController(manager, owner) {
|
|
2225
|
+
const gate = { open: false };
|
|
2226
|
+
return {
|
|
2227
|
+
capability: Object.freeze({ add(contribution) {
|
|
2228
|
+
const state = states.get(manager);
|
|
2229
|
+
if (state.finalized) throw new Error("Typegen contributions are finalized and cannot be modified");
|
|
2230
|
+
if (!gate.open) throw new Error("Typegen contributions may only be registered during this plugin configureEngine hook");
|
|
2231
|
+
validateContribution(contribution);
|
|
2232
|
+
if (state.ids.has(contribution.id)) throw new Error(`Typegen contribution id "${contribution.id}" is already registered`);
|
|
2233
|
+
for (const root of Object.keys(contribution.pika ?? {}).sort(compareStrings$3)) if (state.pikaOwners.has(root)) throw new Error(`Typegen Pika root "${root}" is already registered`);
|
|
2234
|
+
const value = freezeContribution(contribution);
|
|
2235
|
+
state.ids.add(value.id);
|
|
2236
|
+
for (const root of Object.keys(value.pika ?? {}).sort(compareStrings$3)) state.pikaOwners.set(root, owner);
|
|
2237
|
+
state.contributions.push({
|
|
2238
|
+
owner,
|
|
2239
|
+
value
|
|
2240
|
+
});
|
|
2241
|
+
} }),
|
|
2242
|
+
open: () => {
|
|
2243
|
+
gate.open = true;
|
|
2244
|
+
},
|
|
2245
|
+
close: () => {
|
|
2246
|
+
gate.open = false;
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Replaces one Core-owned generated declaration and attaches path-free render
|
|
2252
|
+
* metadata after plugin configuration settles but before Typegen finalization.
|
|
2253
|
+
* Third-party raw `declarations` never pass through this seam.
|
|
2254
|
+
* @internal
|
|
2255
|
+
*/
|
|
2256
|
+
function setCoreGeneratedTypegenContribution(manager, id, options) {
|
|
2257
|
+
const state = states.get(manager);
|
|
2258
|
+
if (state.finalized) throw new Error("Typegen contributions are finalized and cannot be modified");
|
|
2259
|
+
const index = state.contributions.findIndex(({ value }) => value.id === id);
|
|
2260
|
+
if (index < 0) throw new Error(`Typegen contribution id "${id}" is not registered`);
|
|
2261
|
+
const validatedAssets = /* @__PURE__ */ new Map();
|
|
2262
|
+
for (const asset of options.previewAssets ?? []) {
|
|
2263
|
+
if (asset == null || typeof asset !== "object" || typeof asset.id !== "string" || asset.id.length === 0) throw new Error("Typegen preview asset id must be a non-empty string");
|
|
2264
|
+
if (typeof asset.content !== "string" || typeof asset.mediaType !== "string" || asset.mediaType.length === 0) throw new Error(`Typegen preview asset "${asset.id}" must provide string content and a non-empty mediaType`);
|
|
2265
|
+
const previous = validatedAssets.get(asset.id) ?? state.previewAssets.get(asset.id);
|
|
2266
|
+
if (previous != null && (previous.content !== asset.content || previous.mediaType !== asset.mediaType)) throw new Error(`Typegen preview asset id "${asset.id}" is already registered with different content`);
|
|
2267
|
+
validatedAssets.set(asset.id, Object.freeze({ ...asset }));
|
|
2268
|
+
}
|
|
2269
|
+
const existing = state.contributions[index];
|
|
2270
|
+
state.contributions[index] = {
|
|
2271
|
+
owner: existing.owner,
|
|
2272
|
+
value: Object.freeze({
|
|
2273
|
+
...existing.value,
|
|
2274
|
+
declarations: options.declarations
|
|
2275
|
+
})
|
|
2276
|
+
};
|
|
2277
|
+
state.renderOverrides.set(id, options.renderDeclarations);
|
|
2278
|
+
for (const [assetId, asset] of validatedAssets) state.previewAssets.set(assetId, asset);
|
|
2279
|
+
}
|
|
2280
|
+
/** @internal */
|
|
2281
|
+
function renderTypegenContributionDeclarations(snapshot, contribution, bindings) {
|
|
2282
|
+
return snapshotRenderOverrides.get(snapshot)?.get(contribution.id)?.(bindings) ?? contribution.declarations;
|
|
2283
|
+
}
|
|
2284
|
+
/** @internal */
|
|
2285
|
+
function validateTypegenPikaOwners(manager, getRuntimeOwner) {
|
|
2286
|
+
const state = states.get(manager);
|
|
2287
|
+
for (const root of [...state.pikaOwners.keys()].sort(compareStrings$3)) {
|
|
2288
|
+
const typegenOwner = state.pikaOwners.get(root);
|
|
2289
|
+
const runtimeOwner = getRuntimeOwner(root);
|
|
2290
|
+
if (runtimeOwner != null && runtimeOwner !== typegenOwner) throw new Error(`Pika root "${root}" has different runtime and Typegen owners`);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
/** @internal */
|
|
2294
|
+
function finalizeTypegenManager(manager) {
|
|
2295
|
+
const state = states.get(manager);
|
|
2296
|
+
const contributions = Object.freeze(state.contributions.map(({ value }) => value).sort((a, b) => compareStrings$3(a.id, b.id)));
|
|
2297
|
+
const previewAssets = Object.freeze([...state.previewAssets.values()].sort((a, b) => compareStrings$3(a.id, b.id)));
|
|
2298
|
+
const snapshot = Object.freeze({
|
|
2299
|
+
contributions,
|
|
2300
|
+
previewAssets
|
|
2301
|
+
});
|
|
2302
|
+
state.snapshot = snapshot;
|
|
2303
|
+
snapshotRenderOverrides.set(snapshot, new Map(state.renderOverrides));
|
|
2304
|
+
state.finalized = true;
|
|
2305
|
+
Object.freeze(manager);
|
|
2306
|
+
}
|
|
2307
|
+
//#endregion
|
|
2196
2308
|
//#region src/plugin.ts
|
|
2197
|
-
const VOID_HOOKS = new Set(["preflightUpdated"
|
|
2309
|
+
const VOID_HOOKS = new Set(["preflightUpdated"]);
|
|
2198
2310
|
const DEFAULT_PLUGIN_CONTEXT = {
|
|
2199
2311
|
onDiagnostic: noopDiagnosticHandler,
|
|
2200
2312
|
state: void 0,
|
|
@@ -2337,19 +2449,48 @@ function createEngineHooks(context) {
|
|
|
2337
2449
|
if (entry.status === "failed") throw entry.error;
|
|
2338
2450
|
return entry.context;
|
|
2339
2451
|
};
|
|
2452
|
+
const configureEngine = async (plugins, engine) => {
|
|
2453
|
+
logHookStart("Async", "configureEngine");
|
|
2454
|
+
for (const plugin of plugins) {
|
|
2455
|
+
const hookFn = getPluginHook(plugin, "configureEngine");
|
|
2456
|
+
if (hookFn == null) continue;
|
|
2457
|
+
const pluginContext = contextFor(plugin);
|
|
2458
|
+
const pikaRegistration = createPikaRegistrationController(engine.pika, plugin);
|
|
2459
|
+
const typegenRegistration = createTypegenRegistrationController(engine.typegen, plugin);
|
|
2460
|
+
const configurator = Object.freeze({
|
|
2461
|
+
...pluginContext,
|
|
2462
|
+
runtime: engine,
|
|
2463
|
+
pika: pikaRegistration.capability,
|
|
2464
|
+
typegen: typegenRegistration.capability
|
|
2465
|
+
});
|
|
2466
|
+
pikaRegistration.open();
|
|
2467
|
+
typegenRegistration.open();
|
|
2468
|
+
try {
|
|
2469
|
+
logPluginHookStart(plugin, "configureEngine");
|
|
2470
|
+
await hookFn(configurator);
|
|
2471
|
+
logPluginHookEnd(plugin, "configureEngine");
|
|
2472
|
+
} catch (error) {
|
|
2473
|
+
reportPluginHookError(pluginContext, plugin, "configureEngine", error);
|
|
2474
|
+
throw error;
|
|
2475
|
+
} finally {
|
|
2476
|
+
pikaRegistration.close();
|
|
2477
|
+
typegenRegistration.close();
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
logHookEnd("Async", "configureEngine");
|
|
2481
|
+
return engine;
|
|
2482
|
+
};
|
|
2340
2483
|
return {
|
|
2341
2484
|
configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config, contextFor),
|
|
2342
2485
|
rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config, contextFor),
|
|
2343
2486
|
configureResolvedConfig: (plugins, resolvedConfig) => execAsyncHook(plugins, "configureResolvedConfig", resolvedConfig, contextFor),
|
|
2344
|
-
configureEngine
|
|
2487
|
+
configureEngine,
|
|
2345
2488
|
transformSelectors: (plugins, selectors) => execAsyncHook(plugins, "transformSelectors", selectors, contextFor),
|
|
2346
2489
|
transformStyleItems: (plugins, styleItems) => execAsyncHook(plugins, "transformStyleItems", styleItems, contextFor),
|
|
2347
2490
|
transformStyleDefinitions: (plugins, styleDefinitions) => execAsyncHook(plugins, "transformStyleDefinitions", styleDefinitions, contextFor),
|
|
2348
2491
|
transformStyleContents: (plugins, styleContents) => execAsyncHook(plugins, "transformStyleContents", styleContents, contextFor),
|
|
2349
2492
|
preflightUpdated: (plugins) => execSyncHook(plugins, "preflightUpdated", void 0, contextFor),
|
|
2350
|
-
atomicStyleAdded: (plugins, atomicStyle) => execSyncHook(plugins, "atomicStyleAdded", atomicStyle, contextFor)
|
|
2351
|
-
autocompleteConfigUpdated: (plugins) => execSyncHook(plugins, "autocompleteConfigUpdated", void 0, contextFor),
|
|
2352
|
-
configDependencyAdded: (plugins, path) => execSyncHook(plugins, "configDependencyAdded", path, contextFor)
|
|
2493
|
+
atomicStyleAdded: (plugins, atomicStyle) => execSyncHook(plugins, "atomicStyleAdded", atomicStyle, contextFor)
|
|
2353
2494
|
};
|
|
2354
2495
|
}
|
|
2355
2496
|
createEngineHooks(DEFAULT_PLUGIN_CONTEXT);
|
|
@@ -2398,7 +2539,7 @@ function modifyPropertyValue(value) {
|
|
|
2398
2539
|
*
|
|
2399
2540
|
* @returns An `EnginePlugin` that intercepts `transformStyleDefinitions` to conditionally append `!important` to every property value.
|
|
2400
2541
|
*
|
|
2401
|
-
* @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. An explicit `__important` flag is propagated into nested selector blocks (which may override it with their own explicit flag).
|
|
2542
|
+
* @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. An explicit `__important` flag is propagated into nested selector blocks (which may override it with their own explicit flag).
|
|
2402
2543
|
*
|
|
2403
2544
|
* @example
|
|
2404
2545
|
* ```ts
|
|
@@ -2406,7 +2547,6 @@ function modifyPropertyValue(value) {
|
|
|
2406
2547
|
* ```
|
|
2407
2548
|
*/
|
|
2408
2549
|
function important() {
|
|
2409
|
-
let defaultValue;
|
|
2410
2550
|
function propagateExplicitFlag(v, flag) {
|
|
2411
2551
|
if (Array.isArray(v)) return v.map((item) => typeof item === "object" && item !== null && !Array.isArray(item) ? {
|
|
2412
2552
|
__important: flag,
|
|
@@ -2419,23 +2559,28 @@ function important() {
|
|
|
2419
2559
|
}
|
|
2420
2560
|
return defineEnginePlugin({
|
|
2421
2561
|
name: "core:important",
|
|
2422
|
-
|
|
2423
|
-
|
|
2562
|
+
createState: () => ({ defaultValue: false }),
|
|
2563
|
+
rawConfigConfigured(config, context) {
|
|
2564
|
+
context.state.defaultValue = config.important?.default ?? false;
|
|
2424
2565
|
},
|
|
2425
|
-
configureEngine(
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2566
|
+
configureEngine(configurator) {
|
|
2567
|
+
configurator.typegen.add({
|
|
2568
|
+
id: "core:important",
|
|
2569
|
+
declarations: [
|
|
2570
|
+
"interface __PikaImportantProperties {",
|
|
2571
|
+
" __important?: boolean",
|
|
2572
|
+
"}"
|
|
2573
|
+
].join("\n"),
|
|
2574
|
+
properties: "__PikaImportantProperties"
|
|
2429
2575
|
});
|
|
2430
2576
|
},
|
|
2431
|
-
transformStyleDefinitions(styleDefinitions) {
|
|
2577
|
+
transformStyleDefinitions(styleDefinitions, context) {
|
|
2432
2578
|
return styleDefinitions.map((styleDefinition) => {
|
|
2433
2579
|
const { __important, ...rest } = styleDefinition;
|
|
2434
2580
|
const explicit = __important;
|
|
2435
|
-
const important = explicit ?? defaultValue;
|
|
2581
|
+
const important = explicit ?? context.state.defaultValue;
|
|
2436
2582
|
if (important === false && explicit == null) return rest;
|
|
2437
2583
|
return Object.fromEntries(Object.entries(rest).map(([k, v]) => {
|
|
2438
|
-
if (k === "__shortcut") return [k, v];
|
|
2439
2584
|
if (isPropertyValue(v)) return [k, important ? modifyPropertyValue(v) : v];
|
|
2440
2585
|
return [k, explicit == null ? v : propagateExplicitFlag(v, explicit)];
|
|
2441
2586
|
}));
|
|
@@ -2444,45 +2589,147 @@ function important() {
|
|
|
2444
2589
|
});
|
|
2445
2590
|
}
|
|
2446
2591
|
//#endregion
|
|
2447
|
-
//#region src/
|
|
2592
|
+
//#region src/typegen/jsdoc.ts
|
|
2593
|
+
const LEFT_TO_RIGHT_MARK = "";
|
|
2594
|
+
const RE_LEADING_INDENT = /^(\s*)/;
|
|
2595
|
+
const RE_USER_JSDOC_TAG = /^(\s*)@/;
|
|
2596
|
+
function sanitizeJSDocText(text, neutralizeTags) {
|
|
2597
|
+
return text.replaceAll("*/", `*${LEFT_TO_RIGHT_MARK}/`).split("\n").map((line) => {
|
|
2598
|
+
return `${LEFT_TO_RIGHT_MARK}${(neutralizeTags ? line.replace(RE_USER_JSDOC_TAG, `$1${LEFT_TO_RIGHT_MARK}@`) : line).replace(RE_LEADING_INDENT, `$1${LEFT_TO_RIGHT_MARK}`)}`;
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
function markdownImage(alt, href) {
|
|
2602
|
+
return ``;
|
|
2603
|
+
}
|
|
2448
2604
|
/**
|
|
2449
|
-
*
|
|
2605
|
+
* Renders one lexical-safe JSDoc block from path-free semantic documentation.
|
|
2450
2606
|
*
|
|
2451
|
-
* @
|
|
2607
|
+
* @remarks
|
|
2608
|
+
* The renderer preserves the historical `### PikaCSS Preview` fenced-CSS
|
|
2609
|
+
* convention and U+200E safety workaround. Arbitrary descriptions are prevented
|
|
2610
|
+
* from becoming semantic JSDoc `@tags`. Preview-image hrefs are supplied only at
|
|
2611
|
+
* final render time, so semantic snapshots never contain host paths or URIs.
|
|
2452
2612
|
*
|
|
2453
|
-
* @
|
|
2613
|
+
* @param documentation - Path-free description, preview, and semantic tags to render.
|
|
2614
|
+
* @param bindings - Host callbacks used to resolve semantic preview asset IDs to hrefs.
|
|
2615
|
+
* @param indent - Prefix applied to every line of the generated JSDoc block.
|
|
2454
2616
|
*
|
|
2455
|
-
* @
|
|
2456
|
-
* ```ts
|
|
2457
|
-
* createEngine({ plugins: [keyframes()] })
|
|
2458
|
-
* ```
|
|
2617
|
+
* @internal
|
|
2459
2618
|
*/
|
|
2619
|
+
function renderTypegenJSDoc(documentation, bindings = {}, indent = "") {
|
|
2620
|
+
const body = [];
|
|
2621
|
+
if (documentation.description != null && documentation.description.length > 0) body.push(...sanitizeJSDocText(documentation.description, true));
|
|
2622
|
+
const imageLines = (documentation.previewImages ?? []).flatMap((image) => {
|
|
2623
|
+
const href = bindings.resolvePreviewImageHref?.(image.assetId);
|
|
2624
|
+
if (href == null) return [];
|
|
2625
|
+
return sanitizeJSDocText(markdownImage(image.alt ?? "PikaCSS Preview", href), true);
|
|
2626
|
+
});
|
|
2627
|
+
const hasPreview = (documentation.previewCss?.length ?? 0) > 0 || imageLines.length > 0;
|
|
2628
|
+
if (hasPreview && body.length > 0) body.push("");
|
|
2629
|
+
if (hasPreview) body.push(`${LEFT_TO_RIGHT_MARK}### PikaCSS Preview`);
|
|
2630
|
+
body.push(...imageLines);
|
|
2631
|
+
if ((documentation.previewCss?.length ?? 0) > 0) {
|
|
2632
|
+
body.push(`${LEFT_TO_RIGHT_MARK}\`\`\`css`);
|
|
2633
|
+
body.push(...sanitizeJSDocText(documentation.previewCss, false));
|
|
2634
|
+
body.push(`${LEFT_TO_RIGHT_MARK}\`\`\``);
|
|
2635
|
+
}
|
|
2636
|
+
const tags = (documentation.tags ?? []).flatMap((tag) => {
|
|
2637
|
+
if (!/^[A-Z][\w-]*$/i.test(tag.name)) throw new Error(`Invalid Typegen JSDoc tag name: ${tag.name}`);
|
|
2638
|
+
const text = tag.text == null || tag.text.length === 0 ? [] : sanitizeJSDocText(tag.text, true);
|
|
2639
|
+
if (text.length === 0) return [`@${tag.name}`];
|
|
2640
|
+
return [`@${tag.name} ${text[0]}`, ...text.slice(1)];
|
|
2641
|
+
});
|
|
2642
|
+
if (tags.length > 0 && body.length > 0) body.push("");
|
|
2643
|
+
body.push(...tags);
|
|
2644
|
+
if (body.length === 0) return [];
|
|
2645
|
+
return [
|
|
2646
|
+
`${indent}/**`,
|
|
2647
|
+
...body.map((line) => `${indent} * ${line}`),
|
|
2648
|
+
`${indent} */`
|
|
2649
|
+
];
|
|
2650
|
+
}
|
|
2651
|
+
//#endregion
|
|
2652
|
+
//#region src/plugins/keyframes.ts
|
|
2653
|
+
function resolveKeyframesConfig(config, defaultPruneUnused) {
|
|
2654
|
+
if ("external" in config) {
|
|
2655
|
+
if (typeof config.external !== "string" || config.external.trim().length === 0) return void 0;
|
|
2656
|
+
return {
|
|
2657
|
+
name: config.external,
|
|
2658
|
+
animationValues: [config.animationValues ?? []].flat(),
|
|
2659
|
+
description: config.description,
|
|
2660
|
+
external: true,
|
|
2661
|
+
pruneUnused: false
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
if (typeof config.name !== "string" || config.name.trim().length === 0 || config.frames == null) return void 0;
|
|
2665
|
+
return {
|
|
2666
|
+
name: config.name,
|
|
2667
|
+
frames: config.frames,
|
|
2668
|
+
animationValues: [config.animationValues ?? []].flat(),
|
|
2669
|
+
description: config.description,
|
|
2670
|
+
external: false,
|
|
2671
|
+
pruneUnused: config.pruneUnused ?? defaultPruneUnused
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
function renderKeyframesPreview(name, frames) {
|
|
2675
|
+
const lines = [`@keyframes ${name} {`];
|
|
2676
|
+
for (const [stop, properties] of Object.entries(frames)) {
|
|
2677
|
+
lines.push(` ${stop} {`);
|
|
2678
|
+
for (const [property, rawValue] of Object.entries(properties)) {
|
|
2679
|
+
const values = normalizeValue(rawValue) ?? [];
|
|
2680
|
+
for (const value of values) lines.push(` ${toKebab(property)}: ${value};`);
|
|
2681
|
+
}
|
|
2682
|
+
lines.push(" }");
|
|
2683
|
+
}
|
|
2684
|
+
lines.push("}");
|
|
2685
|
+
return lines.join("\n");
|
|
2686
|
+
}
|
|
2687
|
+
function renderKeyframesDeclarations(definitions) {
|
|
2688
|
+
const ordered = [...definitions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
2689
|
+
const lines = ["interface __PikaKeyframes {"];
|
|
2690
|
+
for (const definition of ordered) {
|
|
2691
|
+
lines.push(...renderTypegenJSDoc({
|
|
2692
|
+
description: definition.description,
|
|
2693
|
+
previewCss: definition.frames == null ? void 0 : renderKeyframesPreview(definition.name, definition.frames)
|
|
2694
|
+
}, {}, " "));
|
|
2695
|
+
lines.push(` ${JSON.stringify(definition.name)}: ${JSON.stringify(definition.name)}`);
|
|
2696
|
+
}
|
|
2697
|
+
lines.push("}");
|
|
2698
|
+
const names = ordered.map(({ name }) => JSON.stringify(name));
|
|
2699
|
+
const animationValues = ordered.flatMap(({ name, animationValues }) => [name, ...animationValues]).filter((value, index, list) => list.indexOf(value) === index).map((value) => JSON.stringify(value));
|
|
2700
|
+
lines.push("interface __PikaKeyframePropertyValues {");
|
|
2701
|
+
lines.push(` animationName: ${names.length === 0 ? "never" : names.join(" | ")}`);
|
|
2702
|
+
lines.push(` animation: ${animationValues.length === 0 ? "never" : animationValues.join(" | ")}`);
|
|
2703
|
+
lines.push("}");
|
|
2704
|
+
return lines.join("\n");
|
|
2705
|
+
}
|
|
2706
|
+
/** Built-in keyframes subsystem with config-only semantic ingress. */
|
|
2460
2707
|
function keyframes() {
|
|
2461
|
-
let resolveKeyframesConfig;
|
|
2462
|
-
let configList;
|
|
2463
2708
|
return defineEnginePlugin({
|
|
2464
2709
|
name: "core:keyframes",
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2710
|
+
createState: () => ({
|
|
2711
|
+
definitions: [],
|
|
2712
|
+
defaultPruneUnused: true,
|
|
2713
|
+
store: /* @__PURE__ */ new Map()
|
|
2714
|
+
}),
|
|
2715
|
+
rawConfigConfigured(config, context) {
|
|
2716
|
+
context.state.definitions = config.keyframes?.definitions ?? [];
|
|
2717
|
+
context.state.defaultPruneUnused = config.keyframes?.pruneUnused ?? true;
|
|
2468
2718
|
},
|
|
2469
|
-
configureEngine(
|
|
2470
|
-
engine
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
engine.keyframes.add(...configList);
|
|
2719
|
+
configureEngine(configurator) {
|
|
2720
|
+
const engine = configurator.runtime;
|
|
2721
|
+
const resolved = configurator.state.definitions.map((definition) => resolveKeyframesConfig(definition, configurator.state.defaultPruneUnused)).filter((definition) => definition != null);
|
|
2722
|
+
configurator.state.store.clear();
|
|
2723
|
+
for (const definition of resolved) if (!definition.external) configurator.state.store.set(definition.name, definition);
|
|
2724
|
+
const namespace = Object.freeze(Object.fromEntries(resolved.map(({ name }) => [name, name])));
|
|
2725
|
+
configurator.pika.extendStatic("kf", namespace);
|
|
2726
|
+
configurator.typegen.add({
|
|
2727
|
+
id: "core:keyframes",
|
|
2728
|
+
declarations: renderKeyframesDeclarations(resolved),
|
|
2729
|
+
pika: { kf: "__PikaKeyframes" },
|
|
2730
|
+
cssPropertyValues: "__PikaKeyframePropertyValues"
|
|
2731
|
+
});
|
|
2732
|
+
const state = configurator.state;
|
|
2486
2733
|
engine.addPreflight((engine, _isFormatted, ctx) => {
|
|
2487
2734
|
const maybeUsedName = /* @__PURE__ */ new Set();
|
|
2488
2735
|
engine.store.atomicStyles.forEach(({ content: { property, value } }, id) => {
|
|
@@ -2497,41 +2744,41 @@ function keyframes() {
|
|
|
2497
2744
|
});
|
|
2498
2745
|
});
|
|
2499
2746
|
});
|
|
2500
|
-
const maybeUsedKeyframes = Array.from(engine.keyframes.store.values()).filter(({ name, frames, pruneUnused }) => (pruneUnused === false || maybeUsedName.has(name)) && frames != null);
|
|
2501
2747
|
const preflightDefinition = {};
|
|
2502
|
-
|
|
2748
|
+
for (const { name, frames, pruneUnused } of state.store.values()) {
|
|
2749
|
+
if (frames == null || pruneUnused !== false && !maybeUsedName.has(name)) continue;
|
|
2503
2750
|
preflightDefinition[`@keyframes ${name}`] = frames;
|
|
2504
|
-
}
|
|
2751
|
+
}
|
|
2505
2752
|
return preflightDefinition;
|
|
2506
2753
|
});
|
|
2507
2754
|
}
|
|
2508
2755
|
});
|
|
2509
2756
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2757
|
+
//#endregion
|
|
2758
|
+
//#region src/plugins/layers.ts
|
|
2759
|
+
function compareStrings$2(a, b) {
|
|
2760
|
+
return a < b ? -1 : 1;
|
|
2761
|
+
}
|
|
2762
|
+
function renderLayerDeclarations(names) {
|
|
2763
|
+
return [
|
|
2764
|
+
`type __PikaLayerName = ${[...[...new Set(names)].sort(compareStrings$2).map((name) => JSON.stringify(name)), "(string & {})"].join(" | ")}`,
|
|
2765
|
+
"interface __PikaLayerProperties {",
|
|
2766
|
+
" __layer?: __PikaLayerName",
|
|
2767
|
+
"}"
|
|
2768
|
+
].join("\n");
|
|
2769
|
+
}
|
|
2770
|
+
/** Internal Core owner for the `__layer` authoring directive Typegen surface. */
|
|
2771
|
+
function layers() {
|
|
2772
|
+
return defineEnginePlugin({
|
|
2773
|
+
name: "core:layers",
|
|
2774
|
+
configureEngine(configurator) {
|
|
2775
|
+
configurator.typegen.add({
|
|
2776
|
+
id: "core:layers",
|
|
2777
|
+
declarations: renderLayerDeclarations(Object.keys(configurator.runtime.config.layers)),
|
|
2778
|
+
properties: "__PikaLayerProperties"
|
|
2779
|
+
});
|
|
2526
2780
|
}
|
|
2527
|
-
|
|
2528
|
-
return {
|
|
2529
|
-
name,
|
|
2530
|
-
frames,
|
|
2531
|
-
autocomplete,
|
|
2532
|
-
pruneUnused
|
|
2533
|
-
};
|
|
2534
|
-
};
|
|
2781
|
+
});
|
|
2535
2782
|
}
|
|
2536
2783
|
//#endregion
|
|
2537
2784
|
//#region src/resolver.ts
|
|
@@ -2539,6 +2786,12 @@ function stripGlobalFlag(re) {
|
|
|
2539
2786
|
if (!re.global) return re;
|
|
2540
2787
|
return new RegExp(re.source, re.flags.replace("g", ""));
|
|
2541
2788
|
}
|
|
2789
|
+
/** Tests one input without observing or mutating the caller's RegExp lastIndex. */
|
|
2790
|
+
function matchesRulePattern(pattern, input) {
|
|
2791
|
+
const isolated = new RegExp(pattern.source, pattern.flags);
|
|
2792
|
+
isolated.lastIndex = 0;
|
|
2793
|
+
return isolated.test(input);
|
|
2794
|
+
}
|
|
2542
2795
|
/**
|
|
2543
2796
|
* Base resolver class that manages static and dynamic rules and caches resolution results.
|
|
2544
2797
|
* @internal
|
|
@@ -2853,308 +3106,311 @@ function createDynamicResolvedFactory(fn) {
|
|
|
2853
3106
|
};
|
|
2854
3107
|
}
|
|
2855
3108
|
/**
|
|
2856
|
-
* Normalizes
|
|
2857
|
-
*
|
|
2858
|
-
*
|
|
2859
|
-
*
|
|
2860
|
-
*
|
|
2861
|
-
* @param keyName - The property name on an object-form config that holds the match key or pattern.
|
|
2862
|
-
* @returns A `ResolvedRuleConfig<T>` for valid static/dynamic configs, the original string for redirect configs, or `undefined` if the config shape is unrecognized.
|
|
2863
|
-
*
|
|
2864
|
-
* @remarks Handles three config shapes:
|
|
2865
|
-
* - **String**: returned as-is for the caller to treat as a redirect to another rule.
|
|
2866
|
-
* - **Tuple**: `[string, T | T[]]` for static rules, `[RegExp, fn, autocomplete?]` for dynamic rules.
|
|
2867
|
-
* - **Object**: `{ [keyName]: string | RegExp, value: T | fn, autocomplete?: string[] }`.
|
|
2868
|
-
*
|
|
2869
|
-
* A dynamic rule's value function may return `undefined`/`null` to signal a retryable-unresolved result: the resolver treats the input as unresolved and stores no cache entry, so the rule is re-invoked on a later resolve call.
|
|
2870
|
-
*
|
|
2871
|
-
* @example
|
|
2872
|
-
* ```ts
|
|
2873
|
-
* resolveRuleConfig(['hover', '$:hover'], 'selector')
|
|
2874
|
-
* // { type: 'static', rule: { key: 'hover', ... }, autocomplete: ['hover'] }
|
|
2875
|
-
* ```
|
|
3109
|
+
* Normalizes the frozen object-only selector/shortcut rule grammar.
|
|
3110
|
+
* Static definitions use `{ name, value }`; dynamic definitions use
|
|
3111
|
+
* `{ pattern, inputType, resolve, autocomplete? }`. `inputType` and rich
|
|
3112
|
+
* documentation metadata are semantic Typegen inputs and intentionally do not
|
|
3113
|
+
* affect runtime matching here.
|
|
2876
3114
|
*/
|
|
2877
|
-
function resolveRuleConfig(config
|
|
2878
|
-
if (typeof config
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
key: config[0],
|
|
2882
|
-
value: config[1],
|
|
2883
|
-
autocomplete: config[2]
|
|
2884
|
-
} : {
|
|
2885
|
-
key: config[keyName],
|
|
2886
|
-
value: config.value,
|
|
2887
|
-
autocomplete: config.autocomplete
|
|
2888
|
-
};
|
|
2889
|
-
if (typeof key === "string" && typeof value !== "function") return {
|
|
3115
|
+
function resolveRuleConfig(config) {
|
|
3116
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) return void 0;
|
|
3117
|
+
const definition = config;
|
|
3118
|
+
if (typeof definition.name === "string" && definition.name.trim().length > 0 && "value" in definition) return {
|
|
2890
3119
|
type: "static",
|
|
2891
3120
|
rule: {
|
|
2892
|
-
key,
|
|
2893
|
-
string:
|
|
2894
|
-
resolved: [value].flat(1)
|
|
3121
|
+
key: definition.name,
|
|
3122
|
+
string: definition.name,
|
|
3123
|
+
resolved: [definition.value].flat(1)
|
|
2895
3124
|
},
|
|
2896
|
-
autocomplete: [
|
|
3125
|
+
autocomplete: [definition.name]
|
|
2897
3126
|
};
|
|
2898
|
-
if (
|
|
3127
|
+
if (definition.pattern instanceof RegExp && typeof definition.inputType === "string" && definition.inputType.trim().length > 0 && typeof definition.resolve === "function") return {
|
|
2899
3128
|
type: "dynamic",
|
|
2900
3129
|
rule: {
|
|
2901
|
-
key:
|
|
2902
|
-
stringPattern: stripGlobalFlag(
|
|
2903
|
-
createResolved: createDynamicResolvedFactory(
|
|
3130
|
+
key: definition.pattern.source,
|
|
3131
|
+
stringPattern: stripGlobalFlag(definition.pattern),
|
|
3132
|
+
createResolved: createDynamicResolvedFactory(definition.resolve)
|
|
2904
3133
|
},
|
|
2905
|
-
autocomplete: autocomplete
|
|
3134
|
+
autocomplete: definition.autocomplete == null ? [] : [definition.autocomplete].flat(1)
|
|
2906
3135
|
};
|
|
2907
3136
|
}
|
|
2908
3137
|
//#endregion
|
|
2909
3138
|
//#region src/plugins/selectors.ts
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
3139
|
+
function renderSelectorDeclarations(definitions, onInvalidAutocomplete) {
|
|
3140
|
+
const explicit = /* @__PURE__ */ new Map();
|
|
3141
|
+
const dynamicTypes = [];
|
|
3142
|
+
for (const definition of definitions) {
|
|
3143
|
+
if ("name" in definition) {
|
|
3144
|
+
explicit.set(definition.name, definition.description);
|
|
3145
|
+
continue;
|
|
3146
|
+
}
|
|
3147
|
+
dynamicTypes.push(definition.inputType);
|
|
3148
|
+
for (const value of [definition.autocomplete ?? []].flat()) {
|
|
3149
|
+
if (!matchesRulePattern(definition.pattern, value)) {
|
|
3150
|
+
onInvalidAutocomplete(value, definition.pattern);
|
|
3151
|
+
continue;
|
|
3152
|
+
}
|
|
3153
|
+
explicit.set(value, definition.description);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
const lines = ["interface __PikaExplicitSelectors {"];
|
|
3157
|
+
for (const [name, description] of [...explicit].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
3158
|
+
lines.push(...renderTypegenJSDoc({ description }, {}, " "));
|
|
3159
|
+
lines.push(` ${JSON.stringify(name)}?: __StyleDefinition | __StyleItem[]`);
|
|
3160
|
+
}
|
|
3161
|
+
lines.push("}");
|
|
3162
|
+
if (dynamicTypes.length === 0) lines.push("type __PikaSelectors = __PikaExplicitSelectors");
|
|
3163
|
+
else {
|
|
3164
|
+
lines.push(`type __PikaDynamicSelectorInput = ${dynamicTypes.join(" | ")}`);
|
|
3165
|
+
lines.push("type __PikaDynamicSelectors = { [K in __PikaDynamicSelectorInput]?: __StyleDefinition | __StyleItem[] }");
|
|
3166
|
+
lines.push("type __PikaSelectors = __PikaExplicitSelectors & __PikaDynamicSelectors");
|
|
3167
|
+
}
|
|
3168
|
+
return lines.join("\n");
|
|
3169
|
+
}
|
|
3170
|
+
/** Built-in selector subsystem. Effective raw config is its only semantic ingress. */
|
|
2922
3171
|
function selectors() {
|
|
2923
|
-
let engine;
|
|
2924
|
-
let configList;
|
|
2925
3172
|
return defineEnginePlugin({
|
|
2926
3173
|
name: "core:selectors",
|
|
2927
|
-
|
|
2928
|
-
|
|
3174
|
+
createState: () => ({ definitions: [] }),
|
|
3175
|
+
rawConfigConfigured(config, context) {
|
|
3176
|
+
context.state.definitions = config.selectors?.definitions ?? [];
|
|
2929
3177
|
},
|
|
2930
|
-
configureEngine(
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
3178
|
+
configureEngine(configurator) {
|
|
3179
|
+
const resolver = new SelectorResolver(configurator.onDiagnostic);
|
|
3180
|
+
const acceptedDefinitions = [];
|
|
3181
|
+
for (const definition of configurator.state.definitions) {
|
|
3182
|
+
const resolved = resolveSelectorConfig(definition);
|
|
3183
|
+
if (resolved == null) continue;
|
|
3184
|
+
acceptedDefinitions.push(definition);
|
|
3185
|
+
if (resolved.type === "static") resolver.addStaticRule(resolved.rule);
|
|
3186
|
+
else resolver.addDynamicRule(resolved.rule);
|
|
3187
|
+
}
|
|
3188
|
+
configurator.state.definitions = acceptedDefinitions;
|
|
3189
|
+
configurator.state.resolver = resolver;
|
|
3190
|
+
const declarations = renderSelectorDeclarations(acceptedDefinitions, (value, pattern) => {
|
|
3191
|
+
configurator.onDiagnostic({
|
|
3192
|
+
level: "warning",
|
|
3193
|
+
code: "selector-autocomplete-pattern-mismatch",
|
|
3194
|
+
message: `Selector autocomplete value "${value}" does not match ${pattern}`
|
|
3195
|
+
});
|
|
3196
|
+
});
|
|
3197
|
+
configurator.typegen.add({
|
|
3198
|
+
id: "core:selectors",
|
|
3199
|
+
declarations,
|
|
3200
|
+
selectors: "__PikaSelectors"
|
|
3201
|
+
});
|
|
2952
3202
|
},
|
|
2953
|
-
async transformSelectors(selectors) {
|
|
3203
|
+
async transformSelectors(selectors, context) {
|
|
3204
|
+
const resolver = context.state.resolver;
|
|
3205
|
+
if (resolver == null) return selectors;
|
|
2954
3206
|
const result = [];
|
|
2955
|
-
for (const selector of selectors) result.push(...await
|
|
3207
|
+
for (const selector of selectors) result.push(...await resolver.resolve(selector));
|
|
2956
3208
|
return result;
|
|
2957
3209
|
}
|
|
2958
3210
|
});
|
|
2959
3211
|
}
|
|
2960
3212
|
var SelectorResolver = class extends RecursiveResolver {};
|
|
2961
|
-
/**
|
|
2962
|
-
* Normalizes a `Selector` configuration into a `ResolvedRuleConfig`, a redirect string, or `undefined`.
|
|
2963
|
-
*
|
|
2964
|
-
* @param config - The selector rule configuration to resolve.
|
|
2965
|
-
* @returns A resolved static/dynamic rule config, a redirect string, or `undefined` if the shape is unrecognized.
|
|
2966
|
-
*
|
|
2967
|
-
* @remarks Delegates to the generic `resolveRuleConfig` with `'selector'` as the key name.
|
|
2968
|
-
*
|
|
2969
|
-
* @example
|
|
2970
|
-
* ```ts
|
|
2971
|
-
* const resolved = resolveSelectorConfig(['hover', '$:hover'])
|
|
2972
|
-
* ```
|
|
2973
|
-
*/
|
|
3213
|
+
/** @internal */
|
|
2974
3214
|
function resolveSelectorConfig(config) {
|
|
2975
|
-
return resolveRuleConfig(config
|
|
3215
|
+
return resolveRuleConfig(config);
|
|
2976
3216
|
}
|
|
2977
3217
|
//#endregion
|
|
2978
3218
|
//#region src/plugins/shortcuts.ts
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
3219
|
+
function createStrictShortcutNamespace(definitions) {
|
|
3220
|
+
const staticNames = new Set(definitions.flatMap((definition) => "name" in definition ? [definition.name] : []));
|
|
3221
|
+
const dynamicPatterns = definitions.flatMap((definition) => "pattern" in definition ? [definition.pattern] : []);
|
|
3222
|
+
return new Proxy(Object.create(null), { get(_target, property) {
|
|
3223
|
+
if (typeof property !== "string") return void 0;
|
|
3224
|
+
return staticNames.has(property) || dynamicPatterns.some((pattern) => matchesRulePattern(pattern, property)) ? property : void 0;
|
|
3225
|
+
} });
|
|
3226
|
+
}
|
|
3227
|
+
function renderShortcutDeclarations(definitions, onInvalidAutocomplete, documentation = /* @__PURE__ */ new Map(), bindings = {}) {
|
|
3228
|
+
const explicit = /* @__PURE__ */ new Map();
|
|
3229
|
+
const dynamicTypes = [];
|
|
3230
|
+
for (const definition of definitions) {
|
|
3231
|
+
if ("name" in definition) {
|
|
3232
|
+
explicit.set(definition.name, { description: definition.description });
|
|
3233
|
+
continue;
|
|
3234
|
+
}
|
|
3235
|
+
dynamicTypes.push(definition.inputType);
|
|
3236
|
+
for (const value of [definition.autocomplete ?? []].flat()) {
|
|
3237
|
+
if (!matchesRulePattern(definition.pattern, value)) {
|
|
3238
|
+
onInvalidAutocomplete(value, definition.pattern);
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
explicit.set(value, documentation.get(value) ?? { description: definition.description });
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
const lines = ["interface __PikaExplicitShortcuts {"];
|
|
3245
|
+
for (const [name, docs] of [...explicit].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
3246
|
+
lines.push(...renderTypegenJSDoc(docs, bindings, " "));
|
|
3247
|
+
lines.push(` ${JSON.stringify(name)}: string`);
|
|
3248
|
+
}
|
|
3249
|
+
lines.push("}");
|
|
3250
|
+
if (dynamicTypes.length === 0) lines.push("type __PikaShortcuts = __PikaExplicitShortcuts");
|
|
3251
|
+
else {
|
|
3252
|
+
lines.push(`type __PikaDynamicShortcutInput = ${dynamicTypes.join(" | ")}`);
|
|
3253
|
+
lines.push("type __PikaDynamicShortcuts = { [K in __PikaDynamicShortcutInput]: string }");
|
|
3254
|
+
lines.push("type __PikaShortcuts = __PikaExplicitShortcuts & __PikaDynamicShortcuts");
|
|
3255
|
+
}
|
|
3256
|
+
return lines.join("\n");
|
|
3257
|
+
}
|
|
3258
|
+
function resolveShortcutConfigForContext(config, context) {
|
|
3259
|
+
if (context != null && typeof config === "object" && config != null && !Array.isArray(config) && "pattern" in config) return resolveRuleConfig({
|
|
3260
|
+
...config,
|
|
3261
|
+
resolve: (matched) => config.resolve(matched, context)
|
|
3262
|
+
});
|
|
3263
|
+
return resolveRuleConfig(config);
|
|
3264
|
+
}
|
|
3265
|
+
function createShortcutResolver(definitions, onDiagnostic, context) {
|
|
3266
|
+
const resolver = new ShortcutResolver(onDiagnostic);
|
|
3267
|
+
for (const definition of definitions) {
|
|
3268
|
+
const resolved = resolveShortcutConfigForContext(definition, context);
|
|
3269
|
+
if (resolved?.type === "static") resolver.addStaticRule(resolved.rule);
|
|
3270
|
+
else if (resolved?.type === "dynamic") resolver.addDynamicRule(resolved.rule);
|
|
3271
|
+
}
|
|
3272
|
+
return resolver;
|
|
3273
|
+
}
|
|
3274
|
+
function renderPreviewCss(contents) {
|
|
3275
|
+
if (contents.length === 0) return "";
|
|
3276
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
3277
|
+
for (const { selector: rawSelector, property, value } of contents) {
|
|
3278
|
+
const selector = [...rawSelector];
|
|
3279
|
+
if (selector[0]?.startsWith("@layer ")) {
|
|
3280
|
+
const layer = selector.shift().slice(7).trim();
|
|
3281
|
+
if (layer.length > 0) selector.unshift(`@layer ${layer}`);
|
|
3282
|
+
}
|
|
3283
|
+
const renderedSelectors = selector.map((part) => replaceAtomicStyleIdPlaceholder(part, "pika-preview"));
|
|
3284
|
+
if (renderedSelectors.length === 0) continue;
|
|
3285
|
+
let current = blocks;
|
|
3286
|
+
for (let index = 0; index < renderedSelectors.length; index++) {
|
|
3287
|
+
const currentSelector = renderedSelectors[index];
|
|
3288
|
+
const body = current.get(currentSelector) ?? { properties: [] };
|
|
3289
|
+
if (index === renderedSelectors.length - 1) body.properties.push(...value.map((v) => ({
|
|
3290
|
+
property,
|
|
3291
|
+
value: v
|
|
3292
|
+
})));
|
|
3293
|
+
else body.children ??= /* @__PURE__ */ new Map();
|
|
3294
|
+
current.set(currentSelector, body);
|
|
3295
|
+
if (index < renderedSelectors.length - 1) current = body.children;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
return renderCSSStyleBlocks(blocks, true).trim();
|
|
3299
|
+
}
|
|
3300
|
+
async function finalizeShortcutTypegen(engine, definitions, onDiagnostic) {
|
|
3301
|
+
const documentation = /* @__PURE__ */ new Map();
|
|
3302
|
+
const previewAssets = [];
|
|
3303
|
+
const concreteSet = /* @__PURE__ */ new Set();
|
|
3304
|
+
for (const definition of definitions) {
|
|
3305
|
+
if ("pattern" in definition === false) continue;
|
|
3306
|
+
for (const value of [definition.autocomplete ?? []].flat()) {
|
|
3307
|
+
if (!matchesRulePattern(definition.pattern, value)) {
|
|
3308
|
+
onDiagnostic({
|
|
3309
|
+
level: "warning",
|
|
3310
|
+
code: "shortcut-autocomplete-pattern-mismatch",
|
|
3311
|
+
message: `Shortcut autocomplete value "${value}" does not match ${definition.pattern}`
|
|
3312
|
+
});
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
concreteSet.add(value);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const concrete = [...concreteSet].sort();
|
|
3319
|
+
for (let memberIndex = 0; memberIndex < concrete.length; memberIndex++) {
|
|
3320
|
+
const member = concrete[memberIndex];
|
|
3321
|
+
const owner = definitions.find((definition) => "pattern" in definition && matchesRulePattern(definition.pattern, member));
|
|
3322
|
+
if (owner == null) continue;
|
|
3323
|
+
const images = [];
|
|
3324
|
+
let imageIndex = 0;
|
|
3325
|
+
const context = Object.freeze({ preview: Object.freeze({ image(image) {
|
|
3326
|
+
const assetId = `core:shortcuts:${memberIndex}:image:${imageIndex++}`;
|
|
3327
|
+
previewAssets.push(Object.freeze({
|
|
3328
|
+
id: assetId,
|
|
3329
|
+
content: image.content,
|
|
3330
|
+
mediaType: image.mediaType
|
|
3331
|
+
}));
|
|
3332
|
+
images.push(Object.freeze({
|
|
3333
|
+
assetId,
|
|
3334
|
+
...image.alt == null ? {} : { alt: image.alt }
|
|
3335
|
+
}));
|
|
3336
|
+
} }) });
|
|
3337
|
+
let previewCss;
|
|
3338
|
+
try {
|
|
3339
|
+
let resolutionFailure;
|
|
3340
|
+
const resolved = await createShortcutResolver(definitions, (diagnostic) => {
|
|
3341
|
+
if (diagnostic.code === "resolver-resolution-error") {
|
|
3342
|
+
resolutionFailure ??= diagnostic.cause ?? new Error(diagnostic.message);
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
engine.reportDiagnostic(diagnostic);
|
|
3346
|
+
}, context).resolve(member);
|
|
3347
|
+
if (resolutionFailure != null) throw resolutionFailure;
|
|
3348
|
+
const styleItems = resolved.filter((item) => typeof item !== "string");
|
|
3349
|
+
if (styleItems.length > 0) previewCss = renderPreviewCss((await engine.prepareUse(...styleItems)).contents) || void 0;
|
|
3350
|
+
} catch (cause) {
|
|
3351
|
+
onDiagnostic({
|
|
3352
|
+
level: "warning",
|
|
3353
|
+
code: "shortcut-preview-resolution-error",
|
|
3354
|
+
message: `Failed to render Typegen preview for shortcut "${member}": ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
3355
|
+
cause
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
documentation.set(member, Object.freeze({
|
|
3359
|
+
description: owner.description,
|
|
3360
|
+
...previewCss == null ? {} : { previewCss },
|
|
3361
|
+
...images.length === 0 ? {} : { previewImages: Object.freeze(images) }
|
|
3362
|
+
}));
|
|
3363
|
+
}
|
|
3364
|
+
const render = (bindings) => renderShortcutDeclarations(definitions, () => {}, documentation, bindings);
|
|
3365
|
+
setCoreGeneratedTypegenContribution(engine.typegen, "core:shortcuts", {
|
|
3366
|
+
declarations: render({}),
|
|
3367
|
+
renderDeclarations: render,
|
|
3368
|
+
previewAssets
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
/** Built-in shortcut subsystem. Effective raw config is its only semantic ingress. */
|
|
2991
3372
|
function shortcuts() {
|
|
2992
|
-
let engine;
|
|
2993
|
-
let configList;
|
|
2994
3373
|
return defineEnginePlugin({
|
|
2995
3374
|
name: "core:shortcuts",
|
|
2996
|
-
|
|
2997
|
-
|
|
3375
|
+
createState: () => ({ definitions: [] }),
|
|
3376
|
+
rawConfigConfigured(config, context) {
|
|
3377
|
+
context.state.definitions = config.shortcuts?.definitions ?? [];
|
|
2998
3378
|
},
|
|
2999
|
-
configureEngine(
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
return;
|
|
3010
|
-
}
|
|
3011
|
-
if (resolved.type === "static") engine.shortcuts.resolver.addStaticRule(resolved.rule);
|
|
3012
|
-
else engine.shortcuts.resolver.addDynamicRule(resolved.rule);
|
|
3013
|
-
engine.appendAutocomplete({ shortcuts: resolved.autocomplete });
|
|
3014
|
-
});
|
|
3015
|
-
}
|
|
3016
|
-
};
|
|
3017
|
-
engine.shortcuts.add(...configList);
|
|
3018
|
-
engine.shortcuts.resolver.onResolved = (string, type) => {
|
|
3019
|
-
if (type === "dynamic") engine.appendAutocomplete({ shortcuts: string });
|
|
3020
|
-
};
|
|
3021
|
-
const unionType = ["(string & {})", "Autocomplete['Shortcut']"].join(" | ");
|
|
3022
|
-
engine.appendAutocomplete({
|
|
3023
|
-
extraProperties: "__shortcut",
|
|
3024
|
-
properties: { __shortcut: [unionType, `(${unionType})[]`] }
|
|
3379
|
+
configureEngine(configurator) {
|
|
3380
|
+
const acceptedDefinitions = configurator.state.definitions.filter((definition) => resolveShortcutConfig(definition) != null);
|
|
3381
|
+
const resolver = createShortcutResolver(acceptedDefinitions, configurator.onDiagnostic);
|
|
3382
|
+
configurator.state.definitions = acceptedDefinitions;
|
|
3383
|
+
configurator.state.resolver = resolver;
|
|
3384
|
+
configurator.pika.extendStatic("sc", createStrictShortcutNamespace(acceptedDefinitions));
|
|
3385
|
+
configurator.typegen.add({
|
|
3386
|
+
id: "core:shortcuts",
|
|
3387
|
+
declarations: renderShortcutDeclarations(acceptedDefinitions, () => {}),
|
|
3388
|
+
pika: { sc: "__PikaShortcuts" }
|
|
3025
3389
|
});
|
|
3390
|
+
registerCoreEngineFinalizer(configurator.runtime, () => finalizeShortcutTypegen(configurator.runtime, acceptedDefinitions, configurator.onDiagnostic));
|
|
3026
3391
|
},
|
|
3027
|
-
async transformStyleItems(styleItems) {
|
|
3392
|
+
async transformStyleItems(styleItems, context) {
|
|
3393
|
+
const resolver = context.state.resolver;
|
|
3394
|
+
if (resolver == null) return styleItems;
|
|
3028
3395
|
const result = [];
|
|
3029
3396
|
for (const styleItem of styleItems) {
|
|
3030
3397
|
if (typeof styleItem === "string") {
|
|
3031
|
-
result.push(...await
|
|
3398
|
+
result.push(...await resolver.resolve(styleItem));
|
|
3032
3399
|
continue;
|
|
3033
3400
|
}
|
|
3034
3401
|
result.push(styleItem);
|
|
3035
3402
|
}
|
|
3036
3403
|
return result;
|
|
3037
|
-
},
|
|
3038
|
-
async transformStyleDefinitions(styleDefinitions) {
|
|
3039
|
-
const result = [];
|
|
3040
|
-
for (const styleDefinition of styleDefinitions) if ("__shortcut" in styleDefinition) {
|
|
3041
|
-
const { __shortcut, ...rest } = styleDefinition;
|
|
3042
|
-
const explicitImportant = rest.__important ?? null;
|
|
3043
|
-
const applied = [];
|
|
3044
|
-
for (const shortcut of __shortcut == null ? [] : [__shortcut].flat(1)) {
|
|
3045
|
-
const resolved = (await engine.shortcuts.resolver.resolve(shortcut)).filter(isNotString);
|
|
3046
|
-
applied.push(...explicitImportant == null ? resolved : resolved.map((definition) => ({
|
|
3047
|
-
__important: explicitImportant,
|
|
3048
|
-
...definition
|
|
3049
|
-
})));
|
|
3050
|
-
}
|
|
3051
|
-
result.push(...applied, rest);
|
|
3052
|
-
} else result.push(styleDefinition);
|
|
3053
|
-
return result;
|
|
3054
3404
|
}
|
|
3055
3405
|
});
|
|
3056
3406
|
}
|
|
3057
3407
|
var ShortcutResolver = class extends RecursiveResolver {};
|
|
3408
|
+
/** @internal */
|
|
3058
3409
|
function resolveShortcutConfig(config) {
|
|
3059
|
-
return
|
|
3410
|
+
return resolveShortcutConfigForContext(config);
|
|
3060
3411
|
}
|
|
3061
3412
|
//#endregion
|
|
3062
3413
|
//#region src/plugins/variables.ts
|
|
3063
|
-
/**
|
|
3064
|
-
* Built-in engine plugin that provides CSS custom properties (variables) with smart pruning and autocomplete integration.
|
|
3065
|
-
*
|
|
3066
|
-
* @returns An `EnginePlugin` that registers variable definitions, manages a preflight for emitting `:root` / scoped variables, and prunes unused variables from the output.
|
|
3067
|
-
*
|
|
3068
|
-
* @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.
|
|
3069
|
-
*
|
|
3070
|
-
* @example
|
|
3071
|
-
* ```ts
|
|
3072
|
-
* createEngine({ plugins: [variables()] })
|
|
3073
|
-
* ```
|
|
3074
|
-
*/
|
|
3075
|
-
function variables() {
|
|
3076
|
-
let resolveVariables;
|
|
3077
|
-
let rawVariables;
|
|
3078
|
-
let safeSet;
|
|
3079
|
-
return defineEnginePlugin({
|
|
3080
|
-
name: "core:variables",
|
|
3081
|
-
rawConfigConfigured(config, context) {
|
|
3082
|
-
resolveVariables = createResolveVariablesFn({
|
|
3083
|
-
pruneUnused: config.variables?.pruneUnused,
|
|
3084
|
-
onDiagnostic: context?.onDiagnostic
|
|
3085
|
-
});
|
|
3086
|
-
rawVariables = normalizeVariablesConfig(config.variables);
|
|
3087
|
-
safeSet = new Set(config.variables?.safeList ?? []);
|
|
3088
|
-
},
|
|
3089
|
-
configureEngine(engine) {
|
|
3090
|
-
engine.variables = {
|
|
3091
|
-
store: /* @__PURE__ */ new Map(),
|
|
3092
|
-
add: (variables) => {
|
|
3093
|
-
resolveVariables(variables).forEach((resolved) => {
|
|
3094
|
-
const { name, value, autocomplete: { asValueOf, asProperty } } = resolved;
|
|
3095
|
-
const cssProperties = Object.fromEntries(asValueOf.filter((p) => p !== "-").map((p) => [p, `var(${name})`]));
|
|
3096
|
-
engine.appendAutocomplete({
|
|
3097
|
-
cssProperties,
|
|
3098
|
-
extraCssProperties: asProperty ? name : void 0
|
|
3099
|
-
});
|
|
3100
|
-
if (value != null) {
|
|
3101
|
-
const list = engine.variables.store.get(name) ?? [];
|
|
3102
|
-
list.push(resolved);
|
|
3103
|
-
engine.variables.store.set(name, list);
|
|
3104
|
-
}
|
|
3105
|
-
});
|
|
3106
|
-
engine.notifyPreflightUpdated();
|
|
3107
|
-
}
|
|
3108
|
-
};
|
|
3109
|
-
rawVariables.forEach((variables) => engine.variables.add(variables));
|
|
3110
|
-
engine.addPreflight({
|
|
3111
|
-
id: "core:variables",
|
|
3112
|
-
preflight: async (engine, isFormatted, ctx) => {
|
|
3113
|
-
const used = /* @__PURE__ */ new Set();
|
|
3114
|
-
engine.store.atomicStyles.forEach(({ content: { value } }, id) => {
|
|
3115
|
-
if (ctx?.usedAtomicStyleIds != null && ctx.usedAtomicStyleIds.has(id) === false) return;
|
|
3116
|
-
value.flatMap(extractUsedVarNames).forEach((name) => used.add(normalizeVariableName(name)));
|
|
3117
|
-
});
|
|
3118
|
-
const otherPreflights = engine.config.preflights.filter((p) => p.id !== "core:variables");
|
|
3119
|
-
(await Promise.all(otherPreflights.map(({ fn }) => engine.invokePreflight(fn, isFormatted, ctx).catch(() => null)))).forEach((result) => {
|
|
3120
|
-
if (result == null) return;
|
|
3121
|
-
extractUsedVarNamesFromPreflightResult(result).forEach((name) => used.add(name));
|
|
3122
|
-
});
|
|
3123
|
-
const varMap = engine.variables.store;
|
|
3124
|
-
for (const [name, entries] of varMap.entries()) {
|
|
3125
|
-
if (used.has(name)) continue;
|
|
3126
|
-
if (safeSet.has(name) || entries.some((entry) => entry.pruneUnused === false)) used.add(name);
|
|
3127
|
-
}
|
|
3128
|
-
const queue = Array.from(used);
|
|
3129
|
-
while (queue.length > 0) {
|
|
3130
|
-
const name = queue.pop();
|
|
3131
|
-
const entries = varMap.get(name);
|
|
3132
|
-
if (!entries) continue;
|
|
3133
|
-
for (const { value } of entries) {
|
|
3134
|
-
const referencedValue = Array.isArray(value) ? value.join(" ") : String(value);
|
|
3135
|
-
for (const refName of extractUsedVarNames(referencedValue).map(normalizeVariableName)) if (!used.has(refName)) {
|
|
3136
|
-
used.add(refName);
|
|
3137
|
-
queue.push(refName);
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
}
|
|
3141
|
-
const usedVariables = Array.from(engine.variables.store.values()).flat().filter(({ name, pruneUnused, value }) => (safeSet.has(name) || pruneUnused === false || used.has(name)) && value != null);
|
|
3142
|
-
const preflightDefinition = {};
|
|
3143
|
-
for (const { name, value, selector: _selector } of usedVariables) {
|
|
3144
|
-
const selector = await engine.pluginHooks.transformSelectors(engine.config.plugins, _selector);
|
|
3145
|
-
let current = preflightDefinition;
|
|
3146
|
-
selector.forEach((s) => {
|
|
3147
|
-
current[s] ||= {};
|
|
3148
|
-
current = current[s];
|
|
3149
|
-
});
|
|
3150
|
-
Object.assign(current, { [name]: value });
|
|
3151
|
-
}
|
|
3152
|
-
return preflightDefinition;
|
|
3153
|
-
}
|
|
3154
|
-
});
|
|
3155
|
-
}
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3158
3414
|
function normalizeVariablesConfig(config) {
|
|
3159
3415
|
if (config == null) return [];
|
|
3160
3416
|
const merged = {};
|
|
@@ -3171,49 +3427,236 @@ function mergeVariablesDefinition(target, source) {
|
|
|
3171
3427
|
}
|
|
3172
3428
|
return target;
|
|
3173
3429
|
}
|
|
3430
|
+
function resolveSuggestTargets(asValueOf) {
|
|
3431
|
+
if (asValueOf === false || asValueOf == null) return [];
|
|
3432
|
+
return [...new Set([asValueOf].flat().map(String))];
|
|
3433
|
+
}
|
|
3174
3434
|
function createResolveVariablesFn({ pruneUnused: defaultPruneUnused = true, onDiagnostic = noopDiagnosticHandler } = {}) {
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3435
|
+
const warn = (code, message) => {
|
|
3436
|
+
if (onDiagnostic === noopDiagnosticHandler) log.warn(message);
|
|
3437
|
+
else emitDiagnostic(onDiagnostic, {
|
|
3438
|
+
level: "warning",
|
|
3439
|
+
code,
|
|
3440
|
+
message
|
|
3441
|
+
});
|
|
3442
|
+
};
|
|
3443
|
+
function walk(variables, levels, result) {
|
|
3444
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
3445
|
+
if (key.startsWith("--")) {
|
|
3446
|
+
if (!isPlainObjectRecord(value) || !("external" in value) && !("value" in value)) {
|
|
3447
|
+
warn("variables-invalid-leaf", `Invalid variable leaf for "${key}". Variable leaves must use the canonical object form. Skipping.`);
|
|
3448
|
+
continue;
|
|
3449
|
+
}
|
|
3450
|
+
if (value.external === true) {
|
|
3451
|
+
if (levels.length > 0) {
|
|
3452
|
+
warn("variables-scoped-external", `External variable "${key}" cannot be declared under selector scope "${levels.join(" -> ")}". Skipping.`);
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
result.push({
|
|
3456
|
+
name: key,
|
|
3457
|
+
selector: [":root"],
|
|
3458
|
+
pruneUnused: false,
|
|
3459
|
+
suggest: {
|
|
3460
|
+
asValueOf: resolveSuggestTargets(value.suggest?.asValueOf),
|
|
3461
|
+
asProperty: value.suggest?.asProperty ?? true
|
|
3462
|
+
},
|
|
3463
|
+
description: typeof value.description === "string" ? value.description : void 0,
|
|
3464
|
+
external: true
|
|
3465
|
+
});
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
const leaf = value;
|
|
3469
|
+
result.push({
|
|
3470
|
+
name: key,
|
|
3471
|
+
value: leaf.value,
|
|
3472
|
+
selector: levels.length > 0 ? levels : [":root"],
|
|
3473
|
+
pruneUnused: leaf.pruneUnused ?? defaultPruneUnused,
|
|
3474
|
+
suggest: {
|
|
3475
|
+
asValueOf: resolveSuggestTargets(leaf.suggest?.asValueOf),
|
|
3476
|
+
asProperty: leaf.suggest?.asProperty ?? true
|
|
3477
|
+
},
|
|
3478
|
+
description: leaf.description,
|
|
3479
|
+
external: false
|
|
3196
3480
|
});
|
|
3197
3481
|
continue;
|
|
3198
3482
|
}
|
|
3199
|
-
|
|
3483
|
+
if (!isPlainObjectRecord(value)) {
|
|
3484
|
+
warn("variables-invalid-scope", `Invalid variables scope for selector "${key}". Expected a nested object, received ${typeof value}. Skipping.`);
|
|
3485
|
+
continue;
|
|
3486
|
+
}
|
|
3487
|
+
walk(value, [...levels, key], result);
|
|
3200
3488
|
}
|
|
3201
3489
|
return result;
|
|
3202
3490
|
}
|
|
3203
|
-
return
|
|
3204
|
-
|
|
3205
|
-
|
|
3491
|
+
return (variables) => walk(variables, [], []);
|
|
3492
|
+
}
|
|
3493
|
+
function compareStrings$1(a, b) {
|
|
3494
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
3495
|
+
}
|
|
3496
|
+
function groupVariablesByName(resolved) {
|
|
3497
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3498
|
+
for (const variable of resolved) {
|
|
3499
|
+
const list = groups.get(variable.name) ?? [];
|
|
3500
|
+
list.push(variable);
|
|
3501
|
+
groups.set(variable.name, list);
|
|
3502
|
+
}
|
|
3503
|
+
return groups;
|
|
3206
3504
|
}
|
|
3207
|
-
function
|
|
3208
|
-
const
|
|
3209
|
-
if (
|
|
3210
|
-
const
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3505
|
+
function renderVariablePreview(name, entries) {
|
|
3506
|
+
const emitted = entries.filter((entry) => !entry.external && entry.value != null);
|
|
3507
|
+
if (emitted.length === 0) return void 0;
|
|
3508
|
+
const lines = [];
|
|
3509
|
+
for (const entry of emitted) {
|
|
3510
|
+
entry.selector.forEach((selector, depth) => {
|
|
3511
|
+
lines.push(`${" ".repeat(depth)}${selector} {`);
|
|
3512
|
+
});
|
|
3513
|
+
const values = Array.isArray(entry.value) ? [...entry.value[1].map(String), String(entry.value[0])] : [String(entry.value)];
|
|
3514
|
+
const propertyIndent = " ".repeat(entry.selector.length);
|
|
3515
|
+
for (const value of values) lines.push(`${propertyIndent}${name}: ${value};`);
|
|
3516
|
+
for (let depth = entry.selector.length - 1; depth >= 0; depth--) lines.push(`${" ".repeat(depth)}}`);
|
|
3517
|
+
}
|
|
3518
|
+
return lines.join("\n");
|
|
3519
|
+
}
|
|
3520
|
+
function renderVariableDeclarations(resolved) {
|
|
3521
|
+
const groups = groupVariablesByName(resolved);
|
|
3522
|
+
const orderedNames = [...groups.keys()].sort(compareStrings$1);
|
|
3523
|
+
const lines = ["interface __PikaVariables {"];
|
|
3524
|
+
for (const name of orderedNames) {
|
|
3525
|
+
const entries = groups.get(name);
|
|
3526
|
+
const descriptions = [...new Set(entries.flatMap((entry) => entry.description == null ? [] : [entry.description]))].sort(compareStrings$1);
|
|
3527
|
+
const reference = `var(${name})`;
|
|
3528
|
+
lines.push(...renderTypegenJSDoc({
|
|
3529
|
+
description: [...descriptions, `CSS variable reference: ${reference}`].join("\n\n"),
|
|
3530
|
+
previewCss: renderVariablePreview(name, entries)
|
|
3531
|
+
}, {}, " "));
|
|
3532
|
+
lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(reference)}`);
|
|
3533
|
+
}
|
|
3534
|
+
lines.push("}");
|
|
3535
|
+
lines.push("interface __PikaVariableProperties {");
|
|
3536
|
+
for (const name of orderedNames) {
|
|
3537
|
+
const entries = groups.get(name);
|
|
3538
|
+
if (!entries.some((entry) => entry.suggest.asProperty)) continue;
|
|
3539
|
+
const descriptions = [...new Set(entries.flatMap((entry) => entry.description == null ? [] : [entry.description]))].sort(compareStrings$1);
|
|
3540
|
+
lines.push(...renderTypegenJSDoc({ description: descriptions.join("\n\n") || void 0 }, {}, " "));
|
|
3541
|
+
lines.push(` ${JSON.stringify(name)}?: string | [value: string, fallback: string[]] | null | undefined`);
|
|
3542
|
+
}
|
|
3543
|
+
lines.push("}");
|
|
3544
|
+
const valuesByTarget = /* @__PURE__ */ new Map();
|
|
3545
|
+
for (const [name, entries] of groups) {
|
|
3546
|
+
const reference = `var(${name})`;
|
|
3547
|
+
for (const target of entries.flatMap((entry) => entry.suggest.asValueOf)) {
|
|
3548
|
+
const values = valuesByTarget.get(target) ?? /* @__PURE__ */ new Set();
|
|
3549
|
+
values.add(reference);
|
|
3550
|
+
valuesByTarget.set(target, values);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
lines.push("interface __PikaVariablePropertyValues {");
|
|
3554
|
+
for (const target of [...valuesByTarget.keys()].sort(compareStrings$1)) {
|
|
3555
|
+
const values = [...valuesByTarget.get(target)].sort(compareStrings$1).map((value) => JSON.stringify(value));
|
|
3556
|
+
lines.push(` ${JSON.stringify(target)}: ${values.join(" | ")}`);
|
|
3557
|
+
}
|
|
3558
|
+
lines.push("}");
|
|
3559
|
+
return lines.join("\n");
|
|
3560
|
+
}
|
|
3561
|
+
function collectAtomicVariableUsage(engine, usedAtomicStyleIds) {
|
|
3562
|
+
const used = /* @__PURE__ */ new Set();
|
|
3563
|
+
engine.store.atomicStyles.forEach(({ content: { value } }, id) => {
|
|
3564
|
+
if (usedAtomicStyleIds != null && usedAtomicStyleIds.has(id) === false) return;
|
|
3565
|
+
value.flatMap(extractUsedVarNames).map(normalizeVariableName).forEach((name) => used.add(name));
|
|
3566
|
+
});
|
|
3567
|
+
return used;
|
|
3568
|
+
}
|
|
3569
|
+
function expandVariableUsage(used, store) {
|
|
3570
|
+
const queue = Array.from(used);
|
|
3571
|
+
while (queue.length > 0) {
|
|
3572
|
+
const name = queue.pop();
|
|
3573
|
+
const entries = store.get(name);
|
|
3574
|
+
if (entries == null) continue;
|
|
3575
|
+
for (const { value } of entries) {
|
|
3576
|
+
const referencedValue = Array.isArray(value) ? value.join(" ") : String(value);
|
|
3577
|
+
for (const refName of extractUsedVarNames(referencedValue).map(normalizeVariableName)) {
|
|
3578
|
+
if (used.has(refName)) continue;
|
|
3579
|
+
used.add(refName);
|
|
3580
|
+
queue.push(refName);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
return used;
|
|
3585
|
+
}
|
|
3586
|
+
/** Built-in CSS variable subsystem with config-only semantic ingress. */
|
|
3587
|
+
function variables() {
|
|
3588
|
+
return defineEnginePlugin({
|
|
3589
|
+
name: "core:variables",
|
|
3590
|
+
createState: () => ({
|
|
3591
|
+
definitions: [],
|
|
3592
|
+
defaultPruneUnused: true,
|
|
3593
|
+
safeSet: /* @__PURE__ */ new Set(),
|
|
3594
|
+
resolved: [],
|
|
3595
|
+
store: /* @__PURE__ */ new Map()
|
|
3596
|
+
}),
|
|
3597
|
+
rawConfigConfigured(config, context) {
|
|
3598
|
+
context.state.definitions = normalizeVariablesConfig(config.variables);
|
|
3599
|
+
context.state.defaultPruneUnused = config.variables?.pruneUnused ?? true;
|
|
3600
|
+
context.state.safeSet = new Set(config.variables?.safeList ?? []);
|
|
3601
|
+
},
|
|
3602
|
+
configureEngine(configurator) {
|
|
3603
|
+
const engine = configurator.runtime;
|
|
3604
|
+
const resolveVariables = createResolveVariablesFn({
|
|
3605
|
+
pruneUnused: configurator.state.defaultPruneUnused,
|
|
3606
|
+
onDiagnostic: configurator.onDiagnostic
|
|
3607
|
+
});
|
|
3608
|
+
const resolved = configurator.state.definitions.flatMap(resolveVariables);
|
|
3609
|
+
configurator.state.resolved = resolved;
|
|
3610
|
+
configurator.state.store.clear();
|
|
3611
|
+
for (const variable of resolved) {
|
|
3612
|
+
if (variable.external || variable.value == null) continue;
|
|
3613
|
+
const list = configurator.state.store.get(variable.name) ?? [];
|
|
3614
|
+
list.push(variable);
|
|
3615
|
+
configurator.state.store.set(variable.name, list);
|
|
3616
|
+
}
|
|
3617
|
+
const groups = groupVariablesByName(resolved);
|
|
3618
|
+
const namespace = Object.freeze(Object.fromEntries([...groups.keys()].sort(compareStrings$1).map((name) => [name, `var(${name})`])));
|
|
3619
|
+
configurator.pika.extendStatic("var", namespace);
|
|
3620
|
+
configurator.typegen.add({
|
|
3621
|
+
id: "core:variables",
|
|
3622
|
+
declarations: renderVariableDeclarations(resolved),
|
|
3623
|
+
pika: { var: "__PikaVariables" },
|
|
3624
|
+
cssProperties: "__PikaVariableProperties",
|
|
3625
|
+
cssPropertyValues: "__PikaVariablePropertyValues"
|
|
3626
|
+
});
|
|
3627
|
+
const state = configurator.state;
|
|
3628
|
+
engine.getUsedVariableNames = () => new Set(expandVariableUsage(collectAtomicVariableUsage(engine), state.store));
|
|
3629
|
+
engine.addPreflight({
|
|
3630
|
+
id: "core:variables",
|
|
3631
|
+
preflight: async (engine, isFormatted, ctx) => {
|
|
3632
|
+
const used = collectAtomicVariableUsage(engine, ctx?.usedAtomicStyleIds);
|
|
3633
|
+
const otherPreflights = engine.config.preflights.filter((p) => p.id !== "core:variables");
|
|
3634
|
+
(await Promise.all(otherPreflights.map(({ fn }) => engine.invokePreflight(fn, isFormatted, ctx).catch(() => null)))).forEach((result) => {
|
|
3635
|
+
if (result == null) return;
|
|
3636
|
+
extractUsedVarNamesFromPreflightResult(result).forEach((name) => used.add(name));
|
|
3637
|
+
});
|
|
3638
|
+
const varMap = state.store;
|
|
3639
|
+
for (const [name, entries] of varMap.entries()) {
|
|
3640
|
+
if (used.has(name)) continue;
|
|
3641
|
+
if (state.safeSet.has(name) || entries.some((entry) => entry.pruneUnused === false)) used.add(name);
|
|
3642
|
+
}
|
|
3643
|
+
expandVariableUsage(used, varMap);
|
|
3644
|
+
const usedVariables = Array.from(varMap.values()).flat().filter(({ name, pruneUnused, value }) => (state.safeSet.has(name) || pruneUnused === false || used.has(name)) && value != null);
|
|
3645
|
+
const preflightDefinition = {};
|
|
3646
|
+
for (const { name, value, selector: rawSelector } of usedVariables) {
|
|
3647
|
+
const selector = await engine.pluginHooks.transformSelectors(engine.config.plugins, rawSelector);
|
|
3648
|
+
let current = preflightDefinition;
|
|
3649
|
+
for (const item of selector) {
|
|
3650
|
+
current[item] ||= {};
|
|
3651
|
+
current = current[item];
|
|
3652
|
+
}
|
|
3653
|
+
Object.assign(current, { [name]: value });
|
|
3654
|
+
}
|
|
3655
|
+
return preflightDefinition;
|
|
3656
|
+
}
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3214
3659
|
});
|
|
3215
|
-
if (targets.has("*")) return ["*"];
|
|
3216
|
-
return [...targets];
|
|
3217
3660
|
}
|
|
3218
3661
|
const VAR_NAME_RE = /var\(\s*(--[\w-]+)/g;
|
|
3219
3662
|
/**
|
|
@@ -3319,6 +3762,26 @@ const DEFAULT_LAYERS = {
|
|
|
3319
3762
|
[DEFAULT_PREFLIGHTS_LAYER]: 1,
|
|
3320
3763
|
[DEFAULT_UTILITIES_LAYER]: 10
|
|
3321
3764
|
};
|
|
3765
|
+
const engineInitializationStates = /* @__PURE__ */ new WeakMap();
|
|
3766
|
+
const defaultAtomicStyleIdStrategy = ({ index, prefix }) => `${prefix}${numberToChars(index)}`;
|
|
3767
|
+
function snapshotEngineHostContext(host) {
|
|
3768
|
+
const snapshot = {};
|
|
3769
|
+
if (host?.projectRoot != null) Object.assign(snapshot, { projectRoot: host.projectRoot });
|
|
3770
|
+
if (host?.privateCssDiscriminator != null) Object.assign(snapshot, { privateCssDiscriminator: host.privateCssDiscriminator });
|
|
3771
|
+
return Object.freeze(snapshot);
|
|
3772
|
+
}
|
|
3773
|
+
function snapshotConfigDependencies(state) {
|
|
3774
|
+
return Object.freeze([...state.dependencies.keys()].sort().map((key) => Object.freeze({ ...state.dependencies.get(key) })));
|
|
3775
|
+
}
|
|
3776
|
+
async function finalizeEngineInitialization(engine) {
|
|
3777
|
+
const state = engineInitializationStates.get(engine);
|
|
3778
|
+
await runCoreEngineFinalizers(engine);
|
|
3779
|
+
validateTypegenPikaOwners(engine.typegen, (root) => getPikaStaticOwner(engine.pika, root));
|
|
3780
|
+
finalizePikaManager(engine.pika);
|
|
3781
|
+
finalizeTypegenManager(engine.typegen);
|
|
3782
|
+
state.finalizedDependencies = snapshotConfigDependencies(state);
|
|
3783
|
+
state.finalized = true;
|
|
3784
|
+
}
|
|
3322
3785
|
/**
|
|
3323
3786
|
* Creates and initializes a PikaCSS engine with the given configuration.
|
|
3324
3787
|
*
|
|
@@ -3339,14 +3802,17 @@ async function createEngine(config = {}, options = {}) {
|
|
|
3339
3802
|
config = cloneEngineConfig(config);
|
|
3340
3803
|
const hostOnDiagnostic = options.onDiagnostic ?? noopDiagnosticHandler;
|
|
3341
3804
|
const onDiagnostic = (diagnostic) => emitDiagnostic(hostOnDiagnostic, diagnostic);
|
|
3805
|
+
const host = snapshotEngineHostContext(options.host);
|
|
3806
|
+
const atomicStyleIdStrategy = options.atomicStyleIdStrategy ?? defaultAtomicStyleIdStrategy;
|
|
3342
3807
|
const pluginHooks = createEngineHooks({
|
|
3343
3808
|
onDiagnostic,
|
|
3344
|
-
host
|
|
3809
|
+
host
|
|
3345
3810
|
});
|
|
3346
3811
|
log.debug("Creating engine with config:", config);
|
|
3347
3812
|
const corePlugins = [
|
|
3348
3813
|
variables(),
|
|
3349
3814
|
keyframes(),
|
|
3815
|
+
layers(),
|
|
3350
3816
|
selectors(),
|
|
3351
3817
|
shortcuts(),
|
|
3352
3818
|
important()
|
|
@@ -3363,20 +3829,18 @@ async function createEngine(config = {}, options = {}) {
|
|
|
3363
3829
|
let resolvedConfig = await resolveEngineConfig(config);
|
|
3364
3830
|
log.debug("Engine config resolved with prefix:", resolvedConfig.prefix);
|
|
3365
3831
|
resolvedConfig = await pluginHooks.configureResolvedConfig(resolvedConfig.plugins, resolvedConfig);
|
|
3366
|
-
let engine = new Engine(resolvedConfig, hostOnDiagnostic, pluginHooks);
|
|
3367
|
-
engine.
|
|
3368
|
-
extraProperties: "__layer",
|
|
3369
|
-
properties: { __layer: "Autocomplete['Layer']" }
|
|
3370
|
-
});
|
|
3832
|
+
let engine = new Engine(resolvedConfig, hostOnDiagnostic, pluginHooks, atomicStyleIdStrategy);
|
|
3833
|
+
engineInitializationStates.get(engine).onConfigDependency = options.onConfigDependency;
|
|
3371
3834
|
log.debug("Engine instance created");
|
|
3372
3835
|
engine = await pluginHooks.configureEngine(engine.config.plugins, engine);
|
|
3836
|
+
await finalizeEngineInitialization(engine);
|
|
3373
3837
|
log.debug("Engine initialized successfully");
|
|
3374
3838
|
return engine;
|
|
3375
3839
|
}
|
|
3376
3840
|
/**
|
|
3377
3841
|
* The PikaCSS engine: manages atomic style resolution, rendering, preflights, and plugin hooks.
|
|
3378
3842
|
*
|
|
3379
|
-
* @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`, `
|
|
3843
|
+
* @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`, `appendCssImport`).
|
|
3380
3844
|
*
|
|
3381
3845
|
* @example
|
|
3382
3846
|
* ```ts
|
|
@@ -3392,16 +3856,21 @@ var Engine = class {
|
|
|
3392
3856
|
onDiagnostic;
|
|
3393
3857
|
/** Reference to the instance-scoped plugin hook dispatcher. */
|
|
3394
3858
|
pluginHooks;
|
|
3859
|
+
/** Finalized/read-side first-level Pika static authoring extension registry. */
|
|
3860
|
+
pika;
|
|
3861
|
+
/** Finalized/read-side Typegen semantic registry. */
|
|
3862
|
+
typegen;
|
|
3395
3863
|
/** The extraction function that decomposes style definitions into atomic style contents. */
|
|
3396
3864
|
extract;
|
|
3397
3865
|
/** The engine's runtime store holding registered atomic styles and their ID mappings. */
|
|
3398
3866
|
store = createEngineStore();
|
|
3399
|
-
/**
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3867
|
+
/** Finalized external file and directory-membership dependencies for this engine. */
|
|
3868
|
+
get configDependencies() {
|
|
3869
|
+
const state = engineInitializationStates.get(this);
|
|
3870
|
+
return state.finalizedDependencies ?? snapshotConfigDependencies(state);
|
|
3871
|
+
}
|
|
3872
|
+
/** Engine-owned atomic-style ID allocation strategy. */
|
|
3873
|
+
#atomicStyleIdStrategy;
|
|
3405
3874
|
/**
|
|
3406
3875
|
* Creates an engine instance from a resolved configuration.
|
|
3407
3876
|
*
|
|
@@ -3414,11 +3883,18 @@ var Engine = class {
|
|
|
3414
3883
|
* const engine = new Engine(resolvedConfig)
|
|
3415
3884
|
* ```
|
|
3416
3885
|
*/
|
|
3417
|
-
constructor(config, onDiagnostic = noopDiagnosticHandler, pluginHooks) {
|
|
3886
|
+
constructor(config, onDiagnostic = noopDiagnosticHandler, pluginHooks, atomicStyleIdStrategy = defaultAtomicStyleIdStrategy) {
|
|
3887
|
+
engineInitializationStates.set(this, {
|
|
3888
|
+
finalized: false,
|
|
3889
|
+
dependencies: /* @__PURE__ */ new Map()
|
|
3890
|
+
});
|
|
3891
|
+
this.#atomicStyleIdStrategy = atomicStyleIdStrategy;
|
|
3418
3892
|
const safeOnDiagnostic = (diagnostic) => emitDiagnostic(onDiagnostic, diagnostic);
|
|
3419
3893
|
this.config = config;
|
|
3420
3894
|
this.onDiagnostic = safeOnDiagnostic;
|
|
3421
3895
|
this.pluginHooks = pluginHooks ?? createEngineHooks({ onDiagnostic: safeOnDiagnostic });
|
|
3896
|
+
this.pika = createPikaManager();
|
|
3897
|
+
this.typegen = createTypegenManager();
|
|
3422
3898
|
this.extract = createExtractFn({
|
|
3423
3899
|
defaultSelector: this.config.defaultSelector,
|
|
3424
3900
|
transformSelectors: (selectors) => this.pluginHooks.transformSelectors(this.config.plugins, selectors),
|
|
@@ -3459,23 +3935,34 @@ var Engine = class {
|
|
|
3459
3935
|
return invocation;
|
|
3460
3936
|
}
|
|
3461
3937
|
/**
|
|
3462
|
-
* Registers
|
|
3463
|
-
*
|
|
3464
|
-
* @param path - The file path (ideally absolute) the current config was derived from.
|
|
3938
|
+
* Registers a file dependency during Engine initialization.
|
|
3465
3939
|
*
|
|
3466
|
-
* @
|
|
3467
|
-
*
|
|
3468
|
-
* @example
|
|
3469
|
-
* ```ts
|
|
3470
|
-
* engine.addConfigDependency('/project/design.md')
|
|
3471
|
-
* ```
|
|
3940
|
+
* @param path - File path whose content/existence participates in Engine configuration semantics. Missing files are allowed.
|
|
3941
|
+
* @throws If registration is attempted after Engine finalization.
|
|
3472
3942
|
*/
|
|
3473
3943
|
addConfigDependency(path) {
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3944
|
+
this.#registerConfigDependency("file", path);
|
|
3945
|
+
}
|
|
3946
|
+
/**
|
|
3947
|
+
* Registers a direct directory-membership dependency during Engine initialization.
|
|
3948
|
+
*
|
|
3949
|
+
* @param path - Directory path whose direct member create/delete/rename events invalidate Engine configuration semantics.
|
|
3950
|
+
* @throws If registration is attempted after Engine finalization.
|
|
3951
|
+
*/
|
|
3952
|
+
addConfigDirectoryMembershipDependency(path) {
|
|
3953
|
+
this.#registerConfigDependency("directory-membership", path);
|
|
3954
|
+
}
|
|
3955
|
+
#registerConfigDependency(type, path) {
|
|
3956
|
+
const state = engineInitializationStates.get(this);
|
|
3957
|
+
if (state == null || state.finalized) throw new Error("Engine config dependencies are finalized and cannot be modified");
|
|
3958
|
+
const key = `${type === "file" ? "0" : "1"}\0${path}`;
|
|
3959
|
+
if (state.dependencies.has(key)) return;
|
|
3960
|
+
const dependency = Object.freeze({
|
|
3961
|
+
type,
|
|
3962
|
+
path
|
|
3963
|
+
});
|
|
3964
|
+
state.dependencies.set(key, dependency);
|
|
3965
|
+
state.onConfigDependency?.(dependency);
|
|
3479
3966
|
}
|
|
3480
3967
|
/**
|
|
3481
3968
|
* Fires the `preflightUpdated` hook to notify plugins that preflight content has changed.
|
|
@@ -3507,35 +3994,6 @@ var Engine = class {
|
|
|
3507
3994
|
this.pluginHooks.atomicStyleAdded(this.config.plugins, atomicStyle);
|
|
3508
3995
|
}
|
|
3509
3996
|
/**
|
|
3510
|
-
* Fires the `autocompleteConfigUpdated` hook to notify plugins that autocomplete entries changed.
|
|
3511
|
-
*
|
|
3512
|
-
*
|
|
3513
|
-
* @remarks Called automatically after `appendAutocomplete` when the contribution modifies the resolved autocomplete config.
|
|
3514
|
-
*
|
|
3515
|
-
* @example
|
|
3516
|
-
* ```ts
|
|
3517
|
-
* engine.notifyAutocompleteConfigUpdated()
|
|
3518
|
-
* ```
|
|
3519
|
-
*/
|
|
3520
|
-
notifyAutocompleteConfigUpdated() {
|
|
3521
|
-
this.pluginHooks.autocompleteConfigUpdated(this.config.plugins);
|
|
3522
|
-
}
|
|
3523
|
-
/**
|
|
3524
|
-
* Merges an autocomplete contribution into the resolved autocomplete config.
|
|
3525
|
-
*
|
|
3526
|
-
* @param contribution - The autocomplete entries to append (selectors, properties, CSS properties, etc.).
|
|
3527
|
-
*
|
|
3528
|
-
* @remarks Delegates to the `appendAutocomplete` utility and fires `autocompleteConfigUpdated` if the config was actually modified.
|
|
3529
|
-
*
|
|
3530
|
-
* @example
|
|
3531
|
-
* ```ts
|
|
3532
|
-
* engine.appendAutocomplete({ selectors: 'hover', cssProperties: { color: 'red' } })
|
|
3533
|
-
* ```
|
|
3534
|
-
*/
|
|
3535
|
-
appendAutocomplete(contribution) {
|
|
3536
|
-
if (appendAutocomplete(this.config, contribution)) this.notifyAutocompleteConfigUpdated();
|
|
3537
|
-
}
|
|
3538
|
-
/**
|
|
3539
3997
|
* Appends a CSS `@import` statement to the preflight output.
|
|
3540
3998
|
*
|
|
3541
3999
|
* @param cssImport - The raw `@import` string (a trailing semicolon is appended if missing).
|
|
@@ -3631,7 +4089,8 @@ var Engine = class {
|
|
|
3631
4089
|
content,
|
|
3632
4090
|
prefix: this.config.prefix,
|
|
3633
4091
|
store: this.store,
|
|
3634
|
-
resolvedIdsByBaseKey
|
|
4092
|
+
resolvedIdsByBaseKey,
|
|
4093
|
+
atomicStyleIdStrategy: this.#atomicStyleIdStrategy
|
|
3635
4094
|
});
|
|
3636
4095
|
resolvedIds.push(id);
|
|
3637
4096
|
resolvedIdsByBaseKey.set(getAtomicStyleBaseKey(content), id);
|
|
@@ -3935,9 +4394,9 @@ function resolvePreflight(preflight) {
|
|
|
3935
4394
|
* @internal
|
|
3936
4395
|
*
|
|
3937
4396
|
* @param config - The raw engine configuration.
|
|
3938
|
-
* @returns A `ResolvedEngineConfig` with defaults applied, plugins sorted, preflights resolved,
|
|
4397
|
+
* @returns A `ResolvedEngineConfig` with defaults applied, plugins sorted, preflights resolved, .
|
|
3939
4398
|
*
|
|
3940
|
-
* @remarks Merges `DEFAULT_LAYERS`, normalizes CSS imports, resolves preflight definitions
|
|
4399
|
+
* @remarks Merges `DEFAULT_LAYERS`, normalizes CSS imports, and resolves preflight definitions.
|
|
3941
4400
|
*
|
|
3942
4401
|
* @example
|
|
3943
4402
|
* ```ts
|
|
@@ -3959,23 +4418,8 @@ async function resolveEngineConfig(config) {
|
|
|
3959
4418
|
cssImports: [...new Set(cssImports.map(normalizeCssImport).filter(isNotNullish))],
|
|
3960
4419
|
layers,
|
|
3961
4420
|
defaultPreflightsLayer,
|
|
3962
|
-
defaultUtilitiesLayer
|
|
3963
|
-
autocomplete: {
|
|
3964
|
-
selectors: /* @__PURE__ */ new Set(),
|
|
3965
|
-
shortcuts: /* @__PURE__ */ new Set(),
|
|
3966
|
-
extraProperties: /* @__PURE__ */ new Set(),
|
|
3967
|
-
extraCssProperties: /* @__PURE__ */ new Set(),
|
|
3968
|
-
properties: /* @__PURE__ */ new Map(),
|
|
3969
|
-
cssProperties: /* @__PURE__ */ new Map(),
|
|
3970
|
-
patterns: {
|
|
3971
|
-
selectors: /* @__PURE__ */ new Set(),
|
|
3972
|
-
shortcuts: /* @__PURE__ */ new Set(),
|
|
3973
|
-
properties: /* @__PURE__ */ new Map(),
|
|
3974
|
-
cssProperties: /* @__PURE__ */ new Map()
|
|
3975
|
-
}
|
|
3976
|
-
}
|
|
4421
|
+
defaultUtilitiesLayer
|
|
3977
4422
|
};
|
|
3978
|
-
appendAutocomplete(resolvedConfig, config.autocomplete ?? {});
|
|
3979
4423
|
const resolvedPreflights = preflights.map(resolvePreflight);
|
|
3980
4424
|
resolvedConfig.preflights.push(...resolvedPreflights);
|
|
3981
4425
|
log.debug(`Engine config resolved: ${resolvedPreflights.length} preflights processed`);
|
|
@@ -4191,6 +4635,115 @@ async function renderPreflightDefinition(payload) {
|
|
|
4191
4635
|
}), isFormatted);
|
|
4192
4636
|
}
|
|
4193
4637
|
//#endregion
|
|
4638
|
+
//#region src/typegen/render.ts
|
|
4639
|
+
function joinRefs(contributions, key) {
|
|
4640
|
+
const refs = contributions.flatMap((contribution) => contribution[key] == null ? [] : [contribution[key]]);
|
|
4641
|
+
return refs.length === 0 ? "never" : refs.join(" | ");
|
|
4642
|
+
}
|
|
4643
|
+
function joinIntersectionRefs(contributions, key) {
|
|
4644
|
+
const refs = contributions.flatMap((contribution) => contribution[key] == null ? [] : [contribution[key]]);
|
|
4645
|
+
return refs.length === 0 ? "{}" : refs.join(" & ");
|
|
4646
|
+
}
|
|
4647
|
+
function compareStrings(a, b) {
|
|
4648
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
4649
|
+
}
|
|
4650
|
+
function renderPikaMembers(contributions) {
|
|
4651
|
+
return contributions.flatMap((contribution) => Object.entries(contribution.pika ?? {})).sort(([a], [b]) => compareStrings(a, b)).map(([root, ref]) => ` ${JSON.stringify(root)}: ${ref}`);
|
|
4652
|
+
}
|
|
4653
|
+
function renderUnit(unit, index) {
|
|
4654
|
+
const namespace = `__PikaTypegenUnit${index}`;
|
|
4655
|
+
const contributions = [...unit.snapshot.contributions].sort((a, b) => compareStrings(a.id, b.id));
|
|
4656
|
+
const declarations = contributions.flatMap((contribution) => {
|
|
4657
|
+
const declarations = renderTypegenContributionDeclarations(unit.snapshot, contribution, unit.hostBindings ?? {});
|
|
4658
|
+
return declarations == null ? [] : [declarations];
|
|
4659
|
+
});
|
|
4660
|
+
const resultType = unit.transformedFormat === "array" ? "string[]" : "string";
|
|
4661
|
+
const selectors = joinIntersectionRefs(contributions, "selectors");
|
|
4662
|
+
const properties = joinRefs(contributions, "properties");
|
|
4663
|
+
const cssProperties = joinRefs(contributions, "cssProperties");
|
|
4664
|
+
const cssPropertyValues = joinRefs(contributions, "cssPropertyValues");
|
|
4665
|
+
const propertyConstraints = joinIntersectionRefs(contributions, "propertyConstraints");
|
|
4666
|
+
const pikaMembers = renderPikaMembers(contributions);
|
|
4667
|
+
const moduleName = JSON.stringify(unit.publicModule);
|
|
4668
|
+
return {
|
|
4669
|
+
namespace,
|
|
4670
|
+
lines: [
|
|
4671
|
+
`declare namespace ${namespace} {`,
|
|
4672
|
+
...declarations,
|
|
4673
|
+
...declarations.length === 0 ? [] : [""],
|
|
4674
|
+
" type __UnionToIntersection<T> = (T extends unknown ? (value: T) => void : never) extends ((value: infer I) => void) ? I : never",
|
|
4675
|
+
" type __Additive<T> = [T] extends [never] ? {} : __UnionToIntersection<T>",
|
|
4676
|
+
` type __SelectorContributions = ${selectors}`,
|
|
4677
|
+
` type __PropertyContributions = ${properties}`,
|
|
4678
|
+
` type __CssPropertyContributions = ${cssProperties}`,
|
|
4679
|
+
` type __CssPropertyValueContributions = ${cssPropertyValues}`,
|
|
4680
|
+
` type __PropertyConstraints = ${propertyConstraints}`,
|
|
4681
|
+
` type __CustomProperties = { [K in \`--\${string}\`]?: import(${moduleName}).TypegenCSSPropertyInputValue<__CssPropertyValueContributions, import(${moduleName}).UnionString, K> }`,
|
|
4682
|
+
` type __Properties = import(${moduleName}).TypegenCSSPropertiesInput<__CssPropertyValueContributions>`,
|
|
4683
|
+
` & import(${moduleName}).TypegenCSSPropertiesHyphenInput<__CssPropertyValueContributions>`,
|
|
4684
|
+
" & __CustomProperties",
|
|
4685
|
+
" & __Additive<__PropertyContributions>",
|
|
4686
|
+
" & __Additive<__CssPropertyContributions>",
|
|
4687
|
+
" interface __StyleDefinitionMapBase {",
|
|
4688
|
+
" [selector: string]:",
|
|
4689
|
+
` | import(${moduleName}).PropertyValue<import(${moduleName}).UnionString>`,
|
|
4690
|
+
" | __StyleDefinition",
|
|
4691
|
+
" | __StyleItem[]",
|
|
4692
|
+
" | undefined",
|
|
4693
|
+
" }",
|
|
4694
|
+
" type __ConstrainedProperties = Omit<__Properties, keyof __PropertyConstraints> & __PropertyConstraints",
|
|
4695
|
+
" type __StyleDefinitionMap = __StyleDefinitionMapBase & __SelectorContributions & __PropertyConstraints",
|
|
4696
|
+
" type __StyleDefinition = __ConstrainedProperties | __StyleDefinitionMap",
|
|
4697
|
+
` type __StyleItem = import(${moduleName}).UnionString | __StyleDefinition`,
|
|
4698
|
+
` type __StyleFn = (...params: __StyleItem[]) => ${resultType}`,
|
|
4699
|
+
...pikaMembers.length === 0 ? [" export type Pika = __StyleFn"] : [
|
|
4700
|
+
" type __StaticExtensions = {",
|
|
4701
|
+
...pikaMembers,
|
|
4702
|
+
" }",
|
|
4703
|
+
" export type Pika = __StyleFn & __StaticExtensions"
|
|
4704
|
+
],
|
|
4705
|
+
"}"
|
|
4706
|
+
]
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
/**
|
|
4710
|
+
* Renders one collision-safe TypeScript declaration document from isolated
|
|
4711
|
+
* finalized Engine Typegen snapshots and explicit project/host bindings.
|
|
4712
|
+
*
|
|
4713
|
+
* @param units - Isolated finalized snapshots and host bindings to render as one declaration document.
|
|
4714
|
+
*/
|
|
4715
|
+
function renderTypegenDocument(units) {
|
|
4716
|
+
const fnNames = /* @__PURE__ */ new Set();
|
|
4717
|
+
const rendered = units.map((unit, index) => {
|
|
4718
|
+
if (fnNames.has(unit.fnName)) throw new Error(`Typegen render unit fnName "${unit.fnName}" is duplicated`);
|
|
4719
|
+
fnNames.add(unit.fnName);
|
|
4720
|
+
return {
|
|
4721
|
+
unit,
|
|
4722
|
+
...renderUnit(unit, index)
|
|
4723
|
+
};
|
|
4724
|
+
});
|
|
4725
|
+
const vueUnits = rendered.filter(({ unit }) => unit.vueTemplateGlobals === true);
|
|
4726
|
+
return [
|
|
4727
|
+
"// Auto-generated by PikaCSS",
|
|
4728
|
+
...rendered.flatMap(({ lines }) => ["", ...lines]),
|
|
4729
|
+
"",
|
|
4730
|
+
"declare global {",
|
|
4731
|
+
...rendered.map(({ namespace, unit }) => ` const ${unit.fnName}: ${namespace}.Pika`),
|
|
4732
|
+
"}",
|
|
4733
|
+
...vueUnits.length === 0 ? [] : [
|
|
4734
|
+
"",
|
|
4735
|
+
"declare module 'vue' {",
|
|
4736
|
+
" interface ComponentCustomProperties {",
|
|
4737
|
+
...vueUnits.map(({ namespace, unit }) => ` ${unit.fnName}: ${namespace}.Pika`),
|
|
4738
|
+
" }",
|
|
4739
|
+
"}"
|
|
4740
|
+
],
|
|
4741
|
+
"",
|
|
4742
|
+
"export {}",
|
|
4743
|
+
""
|
|
4744
|
+
].join("\n");
|
|
4745
|
+
}
|
|
4746
|
+
//#endregion
|
|
4194
4747
|
//#region src/index.ts
|
|
4195
4748
|
/**
|
|
4196
4749
|
* Identity helper that returns the engine configuration as-is, providing TypeScript type inference and autocompletion.
|
|
@@ -4199,11 +4752,16 @@ async function renderPreflightDefinition(payload) {
|
|
|
4199
4752
|
* @param config - The engine configuration object.
|
|
4200
4753
|
* @returns The same configuration object, unchanged.
|
|
4201
4754
|
*
|
|
4202
|
-
* @remarks A compile-time-only helper with no runtime effect.
|
|
4755
|
+
* @remarks A compile-time-only helper with no runtime effect. Use it for low-level `EngineConfig` authoring or as a typed value nested under the canonical project `defineConfig({ engine: ... })` surface. It is not itself the default-export root for `pika.config.*`.
|
|
4203
4756
|
*
|
|
4204
4757
|
* @example
|
|
4205
4758
|
* ```ts
|
|
4206
|
-
*
|
|
4759
|
+
* import { defineEngineConfig } from '@pikacss/core'
|
|
4760
|
+
*
|
|
4761
|
+
* const engineConfig = defineEngineConfig({
|
|
4762
|
+
* prefix: 'pk-',
|
|
4763
|
+
* plugins: [],
|
|
4764
|
+
* })
|
|
4207
4765
|
* ```
|
|
4208
4766
|
*/
|
|
4209
4767
|
function defineEngineConfig(config) {
|
|
@@ -4211,4 +4769,4 @@ function defineEngineConfig(config) {
|
|
|
4211
4769
|
}
|
|
4212
4770
|
/* c8 ignore end */
|
|
4213
4771
|
//#endregion
|
|
4214
|
-
export {
|
|
4772
|
+
export { createEngine, createLogger, defineEngineConfig, defineEnginePlugin, escapeRegExp, isPlainObjectRecord, log, renderCSSStyleBlocks, renderTypegenDocument, renderTypegenJSDoc, sortLayerNames };
|