@pikacss/core 0.0.60 → 0.0.62
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 +166 -21
- package/dist/index.mjs +239 -43
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -37,7 +37,7 @@ declare module '@pikacss/core' {
|
|
|
37
37
|
* createEngine({ plugins: [important()] })
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
declare function important(): EnginePlugin
|
|
40
|
+
declare function important(): EnginePlugin<void>;
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/plugins/keyframes.d.ts
|
|
43
43
|
/**
|
|
@@ -143,7 +143,7 @@ declare module '@pikacss/core' {
|
|
|
143
143
|
* createEngine({ plugins: [keyframes()] })
|
|
144
144
|
* ```
|
|
145
145
|
*/
|
|
146
|
-
declare function keyframes(): EnginePlugin
|
|
146
|
+
declare function keyframes(): EnginePlugin<void>;
|
|
147
147
|
interface ResolvedKeyframesConfig {
|
|
148
148
|
name: string;
|
|
149
149
|
frames: KeyframesProgress | Nullish;
|
|
@@ -176,6 +176,26 @@ interface Diagnostic {
|
|
|
176
176
|
}
|
|
177
177
|
/** Callback used by a host to receive structured diagnostics. */
|
|
178
178
|
type DiagnosticHandler = (diagnostic: Diagnostic) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Host semantic metadata for one engine, supplied by the integration/bundler
|
|
181
|
+
* host and transported — never interpreted — by the platform-neutral core.
|
|
182
|
+
*
|
|
183
|
+
* @remarks
|
|
184
|
+
* The host (Vite adapter, Nuxt module, a programmatic caller) is the
|
|
185
|
+
* authority for these values. Plugins consume them through
|
|
186
|
+
* `context.host` so project-relative resources resolve against the same
|
|
187
|
+
* effective PikaCSS project root as config discovery, scans, and generated
|
|
188
|
+
* artifacts — never against `process.cwd()` once a host supplied a more
|
|
189
|
+
* specific root (#118). Core adds no filesystem, path, or bundler APIs here.
|
|
190
|
+
*/
|
|
191
|
+
interface EngineHostContext {
|
|
192
|
+
/**
|
|
193
|
+
* The effective PikaCSS project root for this engine (e.g. Vite's
|
|
194
|
+
* `config.root`, Nuxt's `rootDir`). Absent for standalone `createEngine()`
|
|
195
|
+
* callers that supply no host context.
|
|
196
|
+
*/
|
|
197
|
+
projectRoot?: string;
|
|
198
|
+
}
|
|
179
199
|
/** Runtime-only options accepted by {@link createEngine}. */
|
|
180
200
|
interface CreateEngineOptions {
|
|
181
201
|
/**
|
|
@@ -184,11 +204,40 @@ interface CreateEngineOptions {
|
|
|
184
204
|
* @default A no-op handler.
|
|
185
205
|
*/
|
|
186
206
|
onDiagnostic?: DiagnosticHandler;
|
|
207
|
+
/**
|
|
208
|
+
* Host semantic metadata for this engine (e.g. the effective project
|
|
209
|
+
* root). Exposed to plugins as `context.host`.
|
|
210
|
+
*
|
|
211
|
+
* @default An empty context.
|
|
212
|
+
*/
|
|
213
|
+
host?: EngineHostContext;
|
|
187
214
|
}
|
|
188
|
-
/**
|
|
189
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Context passed to plugin hooks by the engine.
|
|
217
|
+
*
|
|
218
|
+
* @remarks
|
|
219
|
+
* One context object exists per plugin definition **per engine** (#116): the
|
|
220
|
+
* same object is passed to every hook invocation of that plugin/engine pair,
|
|
221
|
+
* from `configureRawConfig` through committed notifications. Long-lived
|
|
222
|
+
* callbacks a plugin registers (shortcut resolvers, preflight functions,
|
|
223
|
+
* engine service methods) should close over this context — never over mutable
|
|
224
|
+
* plugin-factory closure variables, which are shared across every engine
|
|
225
|
+
* reusing the definition.
|
|
226
|
+
*/
|
|
227
|
+
interface EnginePluginContext<State = void> {
|
|
190
228
|
/** Instance-scoped diagnostic handler. */
|
|
191
229
|
onDiagnostic: DiagnosticHandler;
|
|
230
|
+
/**
|
|
231
|
+
* Engine-local plugin state, created once per plugin/engine pair by the
|
|
232
|
+
* plugin's `createState()` initializer. `undefined` (typed `void`) for
|
|
233
|
+
* stateless plugins that omit the initializer.
|
|
234
|
+
*/
|
|
235
|
+
state: State;
|
|
236
|
+
/**
|
|
237
|
+
* Host semantic metadata for this engine (#118). Read-only from a
|
|
238
|
+
* plugin's perspective; empty when no host context was supplied.
|
|
239
|
+
*/
|
|
240
|
+
host: EngineHostContext;
|
|
192
241
|
}
|
|
193
242
|
//#endregion
|
|
194
243
|
//#region src/resolver.d.ts
|
|
@@ -514,7 +563,7 @@ declare module '@pikacss/core' {
|
|
|
514
563
|
* createEngine({ plugins: [selectors()] })
|
|
515
564
|
* ```
|
|
516
565
|
*/
|
|
517
|
-
declare function selectors$1(): EnginePlugin
|
|
566
|
+
declare function selectors$1(): EnginePlugin<void>;
|
|
518
567
|
declare class SelectorResolver extends RecursiveResolver<string> {}
|
|
519
568
|
/**
|
|
520
569
|
* Normalizes a `Selector` configuration into a `ResolvedRuleConfig`, a redirect string, or `undefined`.
|
|
@@ -598,7 +647,7 @@ declare module '@pikacss/core' {
|
|
|
598
647
|
* createEngine({ plugins: [shortcuts()] })
|
|
599
648
|
* ```
|
|
600
649
|
*/
|
|
601
|
-
declare function shortcuts(): EnginePlugin
|
|
650
|
+
declare function shortcuts(): EnginePlugin<void>;
|
|
602
651
|
declare class ShortcutResolver extends RecursiveResolver<InternalStyleItem> {}
|
|
603
652
|
//#endregion
|
|
604
653
|
//#region src/plugins/variables.d.ts
|
|
@@ -749,7 +798,7 @@ declare module '@pikacss/core' {
|
|
|
749
798
|
* createEngine({ plugins: [variables()] })
|
|
750
799
|
* ```
|
|
751
800
|
*/
|
|
752
|
-
declare function variables(): EnginePlugin
|
|
801
|
+
declare function variables(): EnginePlugin<void>;
|
|
753
802
|
interface ResolvedVariable {
|
|
754
803
|
name: string;
|
|
755
804
|
value: InternalPropertyValue;
|
|
@@ -1333,6 +1382,8 @@ type ExtractFn = (styleDefinition: InternalStyleDefinition) => Promise<Extracted
|
|
|
1333
1382
|
*
|
|
1334
1383
|
* @remarks Core plugins (`important`, `variables`, `keyframes`, `selectors`, `shortcuts`) are prepended automatically. The function resolves plugins, runs all configuration hooks in sequence, and returns the ready-to-use engine.
|
|
1335
1384
|
*
|
|
1385
|
+
* The caller-owned `config` graph is treated as immutable input (#117): the engine clones it into an engine-local working copy before any plugin configuration hook runs, so plugin hooks that mutate their config (`config.layers ??= {}` and friends) never write back into caller-owned objects, and the same config object can be reused across sequential or concurrent `createEngine()` calls without accumulating setup mutations. Ordinary config data (plain objects/arrays, `Map`/`Set` contents, `Date`, `RegExp`) is recursively isolated — module-augmented plugin fields included; functions and other opaque class instances keep their identity and are treated as immutable values; the `plugins` array is copied while plugin definition objects keep their identity (#116).
|
|
1386
|
+
*
|
|
1336
1387
|
* @example
|
|
1337
1388
|
* ```ts
|
|
1338
1389
|
* const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
|
|
@@ -1408,7 +1459,7 @@ declare class Engine {
|
|
|
1408
1459
|
*
|
|
1409
1460
|
* @param path - The file path (ideally absolute) the current config was derived from.
|
|
1410
1461
|
*
|
|
1411
|
-
* @remarks Call from a plugin
|
|
1462
|
+
* @remarks Call from a plugin after loading data from disk — typically in `configureEngine`, but registering during later hooks (e.g. while resolving inside `engine.use()`) is fully supported: each genuinely new path fires the `configDependencyAdded` committed notification so integration layers can extend an already-running watcher (#122). Integration layers watch registered paths and rebuild the engine when any of them changes.
|
|
1412
1463
|
*
|
|
1413
1464
|
* @example
|
|
1414
1465
|
* ```ts
|
|
@@ -1433,7 +1484,7 @@ declare class Engine {
|
|
|
1433
1484
|
*
|
|
1434
1485
|
* @param atomicStyle - The atomic style that was just added to the store.
|
|
1435
1486
|
*
|
|
1436
|
-
* @remarks Called automatically by `
|
|
1487
|
+
* @remarks Called automatically by `commitUse()` when a previously unseen atomic style is registered. This is a committed notification: the style's ID, cache keys, and store indices are already established, so mutating the payload is unsupported — plugins that need to transform styles must use the provisional hooks (`transformStyleItems`, `transformStyleDefinitions`, `transformSelectors`, `transformStyleContents`) instead (#114).
|
|
1437
1488
|
*
|
|
1438
1489
|
* @example
|
|
1439
1490
|
* ```ts
|
|
@@ -1492,13 +1543,55 @@ declare class Engine {
|
|
|
1492
1543
|
* ```
|
|
1493
1544
|
*/
|
|
1494
1545
|
addPreflight(preflight: Preflight): void;
|
|
1546
|
+
/**
|
|
1547
|
+
* Provisionally resolves style items into a commit-ready plan without touching committed engine state.
|
|
1548
|
+
*
|
|
1549
|
+
* @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
|
|
1550
|
+
* @returns A promise of the {@link StyleUsePlan} to pass to `commitUse()`.
|
|
1551
|
+
*
|
|
1552
|
+
* @remarks
|
|
1553
|
+
* Runs the full provisional pipeline: `transformStyleItems`, extraction
|
|
1554
|
+
* (`transformStyleDefinitions`/`transformSelectors`), normalization, and the
|
|
1555
|
+
* normalized-content seam `transformStyleContents`. It allocates no atomic
|
|
1556
|
+
* style IDs, mutates no `EngineStore` state, and fires no committed
|
|
1557
|
+
* notifications — a rejection anywhere leaves the engine exactly as it was.
|
|
1558
|
+
* Plans deliberately carry no IDs: reuse-vs-fresh decisions read live store
|
|
1559
|
+
* state and are only valid inside `commitUse()` (#114).
|
|
1560
|
+
*
|
|
1561
|
+
* @example
|
|
1562
|
+
* ```ts
|
|
1563
|
+
* const plan = await engine.prepareUse({ color: 'red' })
|
|
1564
|
+
* const ids = engine.commitUse(plan)
|
|
1565
|
+
* ```
|
|
1566
|
+
*/
|
|
1567
|
+
prepareUse(...itemList: InternalStyleItem[]): Promise<StyleUsePlan>;
|
|
1568
|
+
/**
|
|
1569
|
+
* Commits a prepared plan: allocates/reuses atomic style IDs and registers new styles in the store.
|
|
1570
|
+
*
|
|
1571
|
+
* @param plan - A plan produced by `prepareUse()`.
|
|
1572
|
+
* @returns An array containing any unresolved string references first, followed by atomic style IDs in resolution order.
|
|
1573
|
+
*
|
|
1574
|
+
* @remarks
|
|
1575
|
+
* This is the short, mutation-critical section and MUST stay synchronous:
|
|
1576
|
+
* integration layers commit whole modules inside a revision/epoch-checked
|
|
1577
|
+
* synchronous block, so an `await` here would reopen the stale-commit race
|
|
1578
|
+
* (#114). `atomicStyleAdded` fires per newly registered style as a committed
|
|
1579
|
+
* notification; a throwing observer is reported through the diagnostic
|
|
1580
|
+
* context but never rolls back the already-committed registration.
|
|
1581
|
+
*
|
|
1582
|
+
* @example
|
|
1583
|
+
* ```ts
|
|
1584
|
+
* const ids = engine.commitUse(await engine.prepareUse({ color: 'red' }))
|
|
1585
|
+
* ```
|
|
1586
|
+
*/
|
|
1587
|
+
commitUse(plan: StyleUsePlan): string[];
|
|
1495
1588
|
/**
|
|
1496
1589
|
* Processes style items through the plugin pipeline and registers the resulting atomic styles in the store.
|
|
1497
1590
|
*
|
|
1498
1591
|
* @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
|
|
1499
1592
|
* @returns An array containing any unresolved string references first, followed by atomic style IDs in resolution order.
|
|
1500
1593
|
*
|
|
1501
|
-
* @remarks
|
|
1594
|
+
* @remarks Equivalent to `commitUse(await prepareUse(...itemList))` — the convenience path for direct consumers. Integration layers that need whole-module transactionality call the two phases separately (#114).
|
|
1502
1595
|
*
|
|
1503
1596
|
* @example
|
|
1504
1597
|
* ```ts
|
|
@@ -1529,12 +1622,11 @@ declare class Engine {
|
|
|
1529
1622
|
* Renders atomic styles into a CSS string, optionally filtered by ID and grouped by layer.
|
|
1530
1623
|
*
|
|
1531
1624
|
* @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
|
|
1532
|
-
* @param options - Optional filtering: `atomicStyleIds` to render a subset
|
|
1625
|
+
* @param options - Optional filtering: `atomicStyleIds` to render a subset.
|
|
1533
1626
|
* @param options.atomicStyleIds - Specific atomic style IDs to render instead of the full store.
|
|
1534
|
-
* @param options.isPreview - Whether to keep placeholder IDs instead of substituting real class names.
|
|
1535
1627
|
* @returns The rendered atomic-style CSS.
|
|
1536
1628
|
*
|
|
1537
|
-
* @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered.
|
|
1629
|
+
* @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered.
|
|
1538
1630
|
*
|
|
1539
1631
|
* @example
|
|
1540
1632
|
* ```ts
|
|
@@ -1543,7 +1635,6 @@ declare class Engine {
|
|
|
1543
1635
|
*/
|
|
1544
1636
|
renderAtomicStyles(isFormatted: boolean, options?: {
|
|
1545
1637
|
atomicStyleIds?: string[];
|
|
1546
|
-
isPreview?: boolean;
|
|
1547
1638
|
}): Promise<string>;
|
|
1548
1639
|
/**
|
|
1549
1640
|
* Renders the CSS `@layer` order declaration for all configured layers.
|
|
@@ -1575,6 +1666,23 @@ declare class Engine {
|
|
|
1575
1666
|
* ```
|
|
1576
1667
|
*/
|
|
1577
1668
|
declare function sortLayerNames(layers: Record<string, number>): string[];
|
|
1669
|
+
/**
|
|
1670
|
+
* The provisional result of `engine.prepareUse()`: fully transformed, extracted,
|
|
1671
|
+
* and normalized style contents plus unresolved string references, ready to be
|
|
1672
|
+
* committed via `engine.commitUse()`.
|
|
1673
|
+
*
|
|
1674
|
+
* @remarks
|
|
1675
|
+
* A plan deliberately carries no atomic style IDs and no base-key resolutions:
|
|
1676
|
+
* reuse-vs-fresh-ID decisions read live `EngineStore` state and are only valid
|
|
1677
|
+
* at the moment `commitUse()` runs. Discarding an uncommitted plan has no
|
|
1678
|
+
* effect on the engine (#114).
|
|
1679
|
+
*/
|
|
1680
|
+
interface StyleUsePlan {
|
|
1681
|
+
/** String references no plugin resolved; echoed back verbatim by `commitUse()`. */
|
|
1682
|
+
unknown: Set<string>;
|
|
1683
|
+
/** Deduplicated, normalized style contents in resolution order. */
|
|
1684
|
+
contents: StyleContent[];
|
|
1685
|
+
}
|
|
1578
1686
|
//#endregion
|
|
1579
1687
|
//#region src/plugin.d.ts
|
|
1580
1688
|
type DefineHooks<Hooks extends Record<string, [type: 'sync' | 'async', payload: unknown, returnValue?: unknown]>> = Hooks;
|
|
@@ -1586,35 +1694,72 @@ type EngineHooksDefinition = DefineHooks<{
|
|
|
1586
1694
|
transformSelectors: ['async', selectors: string[]];
|
|
1587
1695
|
transformStyleItems: ['async', styleItems: ResolvedStyleItem[]];
|
|
1588
1696
|
transformStyleDefinitions: ['async', styleDefinitions: ResolvedStyleDefinition[]];
|
|
1697
|
+
transformStyleContents: ['async', styleContents: StyleContent[]];
|
|
1589
1698
|
preflightUpdated: ['sync', void];
|
|
1590
1699
|
atomicStyleAdded: ['sync', AtomicStyle];
|
|
1591
1700
|
autocompleteConfigUpdated: ['sync', void];
|
|
1701
|
+
configDependencyAdded: ['sync', path: string];
|
|
1592
1702
|
}>;
|
|
1593
1703
|
type HookParams<H extends [type: 'sync' | 'async', payload: any, returnValue?: any]> = H[1] extends void ? [] : [payload: H[1]];
|
|
1594
|
-
type PluginHookParams<H extends [type: 'sync' | 'async', payload: any, returnValue?: any]> = H[1] extends void ? [context
|
|
1704
|
+
type PluginHookParams<H extends [type: 'sync' | 'async', payload: any, returnValue?: any], State = any> = H[1] extends void ? [context: EnginePluginContext<State>] : [payload: H[1], context: EnginePluginContext<State>];
|
|
1595
1705
|
type HookReturnType<H extends [type: 'sync' | 'async', payload: any, returnValue?: any]> = H extends [any, any, infer R] ? H[0] extends 'async' ? Promise<R> : R : H[0] extends 'async' ? Promise<H[1]> : H[1];
|
|
1596
1706
|
type EngineHooks = { [K in keyof EngineHooksDefinition]: (plugins: EnginePlugin[], ...params: HookParams<EngineHooksDefinition[K]>) => HookReturnType<EngineHooksDefinition[K]> };
|
|
1597
1707
|
/**
|
|
1598
1708
|
* Creates an engine-local hook dispatcher bound to one diagnostic context.
|
|
1599
1709
|
*
|
|
1600
1710
|
* @internal
|
|
1711
|
+
* @remarks
|
|
1712
|
+
* Each dispatcher instance owns one plugin-context store: every plugin
|
|
1713
|
+
* definition gets exactly one `EnginePluginContext` (with `state` initialized
|
|
1714
|
+
* lazily via `createState()`) per dispatcher — i.e. per engine, since
|
|
1715
|
+
* `createEngine` creates one dispatcher per engine (#116). The same plugin
|
|
1716
|
+
* definition used with another dispatcher/engine gets a distinct context and
|
|
1717
|
+
* distinct state.
|
|
1718
|
+
*/
|
|
1719
|
+
declare function createEngineHooks(context: Pick<EnginePluginContext, 'onDiagnostic'> & Partial<Pick<EnginePluginContext, 'host'>>): EngineHooks;
|
|
1720
|
+
type EnginePluginHooksOptions<State = any> = { [K in keyof EngineHooksDefinition]?: EngineHooksDefinition[K][0] extends 'async' ? (...params: PluginHookParams<EngineHooksDefinition[K], State>) => Awaitable<EngineHooksDefinition[K][1] | void> : (...params: PluginHookParams<EngineHooksDefinition[K], State>) => EngineHooksDefinition[K][1] | void };
|
|
1721
|
+
/**
|
|
1722
|
+
* Describes an engine plugin that can hook into the PikaCSS engine lifecycle.
|
|
1723
|
+
*
|
|
1724
|
+
* @remarks
|
|
1725
|
+
* A plugin object is a reusable **definition**, not a single-engine resource
|
|
1726
|
+
* (#116): the same object may be passed to any number of `createEngine()`
|
|
1727
|
+
* calls, sequentially or concurrently. Mutable per-engine data therefore must
|
|
1728
|
+
* never live in the plugin factory's closure — declare it via `createState`
|
|
1729
|
+
* and read/write it through `context.state`, which the engine keeps isolated
|
|
1730
|
+
* per plugin/engine pair. Factory arguments that are never mutated may stay in
|
|
1731
|
+
* the closure as immutable definition configuration.
|
|
1601
1732
|
*/
|
|
1602
|
-
|
|
1603
|
-
type EnginePluginHooksOptions = { [K in keyof EngineHooksDefinition]?: EngineHooksDefinition[K][0] extends 'async' ? (...params: PluginHookParams<EngineHooksDefinition[K]>) => Awaitable<EngineHooksDefinition[K][1] | void> : (...params: PluginHookParams<EngineHooksDefinition[K]>) => EngineHooksDefinition[K][1] | void };
|
|
1604
|
-
/** Describes an engine plugin that can hook into the PikaCSS engine lifecycle. */
|
|
1605
|
-
interface EnginePlugin extends EnginePluginHooksOptions {
|
|
1733
|
+
interface EnginePlugin<State = any> extends EnginePluginHooksOptions<State> {
|
|
1606
1734
|
/** The unique human-readable name identifying this plugin in diagnostics. */
|
|
1607
1735
|
name: string;
|
|
1608
1736
|
/** Controls execution order relative to other plugins. */
|
|
1609
1737
|
order?: 'pre' | 'post';
|
|
1738
|
+
/**
|
|
1739
|
+
* Initializes this plugin's engine-local state.
|
|
1740
|
+
*
|
|
1741
|
+
* @returns The fresh state for one engine.
|
|
1742
|
+
*
|
|
1743
|
+
* @remarks
|
|
1744
|
+
* Invoked by the engine at most once per plugin definition **per engine**,
|
|
1745
|
+
* before the first hook of this plugin runs for that engine; every hook
|
|
1746
|
+
* invocation of that plugin/engine pair then receives the same object via
|
|
1747
|
+
* `context.state`. Another engine reusing the same definition gets a
|
|
1748
|
+
* distinct state object. Stateless plugins simply omit this.
|
|
1749
|
+
*/
|
|
1750
|
+
createState?: () => State;
|
|
1610
1751
|
}
|
|
1611
1752
|
/**
|
|
1612
1753
|
* Identity helper that provides type inference for an engine plugin definition.
|
|
1613
1754
|
*
|
|
1614
1755
|
* @param plugin - The plugin definition to return unchanged.
|
|
1615
1756
|
* @returns The same plugin instance.
|
|
1757
|
+
*
|
|
1758
|
+
* @remarks
|
|
1759
|
+
* When the plugin declares `createState`, the state type is inferred from its
|
|
1760
|
+
* return value and every hook's `context.state` is typed accordingly.
|
|
1616
1761
|
*/
|
|
1617
|
-
declare function defineEnginePlugin(plugin: EnginePlugin): EnginePlugin
|
|
1762
|
+
declare function defineEnginePlugin<State = void>(plugin: EnginePlugin<State>): EnginePlugin<State>;
|
|
1618
1763
|
//#endregion
|
|
1619
1764
|
//#region src/generated/csstype.d.ts
|
|
1620
1765
|
type UnionString$1 = string & {};
|
|
@@ -37494,4 +37639,4 @@ declare function renderCSSStyleBlocks(blocks: CSSStyleBlocks, isFormatted: boole
|
|
|
37494
37639
|
*/
|
|
37495
37640
|
declare function defineEngineConfig<const T extends EngineConfig>(config: T): T;
|
|
37496
37641
|
//#endregion
|
|
37497
|
-
export { Arrayable, type AutocompleteConfig, type AutocompleteContribution, type AutocompletePatternsConfig, Awaitable, type CSSProperty, type CSSSelector, type CSSStyleBlockBody, type CSSStyleBlocks, type CreateEngineOptions, type DefineAutocomplete, type Diagnostic, type DiagnosticHandler, type DiagnosticLevel, type Engine, type EngineConfig, type EnginePlugin, type EnginePluginContext, FromKebab, GetValue, ImportantConfig, IsEqual, IsNever, Keyframes, KeyframesConfig, KeyframesProgress, Nullish, type PikaAugment, type Preflight, type PreflightDefinition, type PreflightFn, type Properties, type PropertyValue, ResolveFrom, type ResolvedLayerName, type ResolvedPreflight, Selector, SelectorsConfig, Shortcut, ShortcutsConfig, Simplify, type StyleDefinition, type StyleDefinitionMap, type StyleItem, ToKebab, UnionString, UnionToIntersection, Variable, VariableAutocomplete, VariableObject, VariablesConfig, VariablesDefinition, appendAutocomplete, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, escapeRegExp, extractUsedVarNames, extractUsedVarNamesFromPreflightResult, important, isPlainObjectRecord, keyframes, log, normalizeVariableName, renderCSSStyleBlocks, resolveSelectorConfig, selectors$1 as selectors, shortcuts, sortLayerNames, variables };
|
|
37642
|
+
export { Arrayable, type AutocompleteConfig, type AutocompleteContribution, type AutocompletePatternsConfig, Awaitable, type CSSProperty, type CSSSelector, type CSSStyleBlockBody, type CSSStyleBlocks, type CreateEngineOptions, type DefineAutocomplete, type Diagnostic, type DiagnosticHandler, type DiagnosticLevel, type Engine, type EngineConfig, type EngineHostContext, type EnginePlugin, type EnginePluginContext, FromKebab, GetValue, ImportantConfig, IsEqual, IsNever, Keyframes, KeyframesConfig, KeyframesProgress, Nullish, type PikaAugment, type Preflight, type PreflightDefinition, type PreflightFn, type Properties, type PropertyValue, ResolveFrom, type ResolvedLayerName, type ResolvedPreflight, Selector, SelectorsConfig, Shortcut, ShortcutsConfig, Simplify, type StyleDefinition, type StyleDefinitionMap, type StyleItem, type StyleUsePlan, ToKebab, UnionString, UnionToIntersection, Variable, VariableAutocomplete, VariableObject, VariablesConfig, VariablesDefinition, appendAutocomplete, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, escapeRegExp, extractUsedVarNames, extractUsedVarNamesFromPreflightResult, important, isPlainObjectRecord, keyframes, log, normalizeVariableName, renderCSSStyleBlocks, resolveSelectorConfig, selectors$1 as selectors, shortcuts, sortLayerNames, variables };
|
package/dist/index.mjs
CHANGED
|
@@ -1873,6 +1873,93 @@ function getOrderSensitiveDependencyKeys(scoped, property) {
|
|
|
1873
1873
|
return dependencyKeys;
|
|
1874
1874
|
}
|
|
1875
1875
|
//#endregion
|
|
1876
|
+
//#region src/config-clone.ts
|
|
1877
|
+
/**
|
|
1878
|
+
* Deep-copies ordinary config data while preserving behavioral identities.
|
|
1879
|
+
*
|
|
1880
|
+
* Recursively isolated (fresh copies): plain objects (null or Object
|
|
1881
|
+
* prototype, third-party augmented fields included), arrays, `Map` keys and
|
|
1882
|
+
* values, `Set` values, `Date`, `RegExp` (with `lastIndex`).
|
|
1883
|
+
*
|
|
1884
|
+
* Identity-preserved (returned as-is): primitives, functions/callbacks, and
|
|
1885
|
+
* any other non-plain instance (class instances, typed arrays, promises, …) —
|
|
1886
|
+
* Core cannot know a safe clone semantic for those, so they are treated as
|
|
1887
|
+
* opaque immutable values.
|
|
1888
|
+
*
|
|
1889
|
+
* Cycles and diamond references between plain objects/arrays/Maps/Sets are
|
|
1890
|
+
* preserved through `seen`; `Date`/`RegExp` diamonds become independent
|
|
1891
|
+
* value copies (they are immutable-by-convention config data).
|
|
1892
|
+
*/
|
|
1893
|
+
function cloneConfigValue(value, seen) {
|
|
1894
|
+
if (typeof value !== "object" || value == null) return value;
|
|
1895
|
+
const cached = seen.get(value);
|
|
1896
|
+
if (cached != null) return cached;
|
|
1897
|
+
if (Array.isArray(value)) {
|
|
1898
|
+
const copy = [];
|
|
1899
|
+
seen.set(value, copy);
|
|
1900
|
+
for (const item of value) copy.push(cloneConfigValue(item, seen));
|
|
1901
|
+
return copy;
|
|
1902
|
+
}
|
|
1903
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
1904
|
+
if (value instanceof RegExp) {
|
|
1905
|
+
const copy = new RegExp(value.source, value.flags);
|
|
1906
|
+
copy.lastIndex = value.lastIndex;
|
|
1907
|
+
return copy;
|
|
1908
|
+
}
|
|
1909
|
+
if (value instanceof Map) {
|
|
1910
|
+
const copy = /* @__PURE__ */ new Map();
|
|
1911
|
+
seen.set(value, copy);
|
|
1912
|
+
for (const [key, entry] of value) copy.set(cloneConfigValue(key, seen), cloneConfigValue(entry, seen));
|
|
1913
|
+
return copy;
|
|
1914
|
+
}
|
|
1915
|
+
if (value instanceof Set) {
|
|
1916
|
+
const copy = /* @__PURE__ */ new Set();
|
|
1917
|
+
seen.set(value, copy);
|
|
1918
|
+
for (const item of value) copy.add(cloneConfigValue(item, seen));
|
|
1919
|
+
return copy;
|
|
1920
|
+
}
|
|
1921
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1922
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
1923
|
+
const copy = prototype === null ? Object.create(null) : {};
|
|
1924
|
+
seen.set(value, copy);
|
|
1925
|
+
for (const [key, entry] of Object.entries(value)) if (key === "__proto__") Object.defineProperty(copy, key, {
|
|
1926
|
+
value: cloneConfigValue(entry, seen),
|
|
1927
|
+
enumerable: true,
|
|
1928
|
+
writable: true,
|
|
1929
|
+
configurable: true
|
|
1930
|
+
});
|
|
1931
|
+
else copy[key] = cloneConfigValue(entry, seen);
|
|
1932
|
+
return copy;
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Creates the engine-local mutable working copy of a caller-owned config.
|
|
1936
|
+
* @internal
|
|
1937
|
+
*
|
|
1938
|
+
* @param config - The caller-owned engine configuration.
|
|
1939
|
+
* @returns An independent working config for one `createEngine()` invocation.
|
|
1940
|
+
*
|
|
1941
|
+
* @remarks
|
|
1942
|
+
* `createEngine(config)` treats the caller's `EngineConfig` graph as
|
|
1943
|
+
* immutable input (#117): plugin configuration hooks mutate this working
|
|
1944
|
+
* copy, never the caller's objects, so one caller config can be reused
|
|
1945
|
+
* across sequential or concurrent engine creations without accumulating
|
|
1946
|
+
* setup mutations. Ordinary config data is recursively isolated —
|
|
1947
|
+
* including module-augmented third-party fields; functions and opaque
|
|
1948
|
+
* class instances keep their identity; and the `plugins` array is copied
|
|
1949
|
+
* while the `EnginePlugin` definition objects inside it keep their
|
|
1950
|
+
* identity, per the #116 reusable-definition contract (per-engine plugin
|
|
1951
|
+
* state is keyed by definition identity).
|
|
1952
|
+
*/
|
|
1953
|
+
function cloneEngineConfig(config) {
|
|
1954
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
1955
|
+
const plugins = config.plugins;
|
|
1956
|
+
if (plugins != null) {
|
|
1957
|
+
seen.set(plugins, [...plugins]);
|
|
1958
|
+
for (const plugin of plugins) if (typeof plugin === "object" && plugin != null) seen.set(plugin, plugin);
|
|
1959
|
+
}
|
|
1960
|
+
return cloneConfigValue(config, seen);
|
|
1961
|
+
}
|
|
1962
|
+
//#endregion
|
|
1876
1963
|
//#region src/constants.ts
|
|
1877
1964
|
/**
|
|
1878
1965
|
* CSS `@layer` at-rule prefix used when constructing layer-scoped selectors
|
|
@@ -2108,7 +2195,14 @@ function createExtractFn(options) {
|
|
|
2108
2195
|
//#endregion
|
|
2109
2196
|
//#region src/plugin.ts
|
|
2110
2197
|
const VOID_HOOKS = new Set(["preflightUpdated", "autocompleteConfigUpdated"]);
|
|
2111
|
-
const DEFAULT_PLUGIN_CONTEXT = {
|
|
2198
|
+
const DEFAULT_PLUGIN_CONTEXT = {
|
|
2199
|
+
onDiagnostic: noopDiagnosticHandler,
|
|
2200
|
+
state: void 0,
|
|
2201
|
+
host: {}
|
|
2202
|
+
};
|
|
2203
|
+
function resolvePluginContext(source, plugin) {
|
|
2204
|
+
return typeof source === "function" ? source(plugin) : source;
|
|
2205
|
+
}
|
|
2112
2206
|
function getPluginHook(plugin, hook) {
|
|
2113
2207
|
const hookFn = plugin[hook];
|
|
2114
2208
|
return typeof hookFn === "function" ? hookFn : null;
|
|
@@ -2159,12 +2253,13 @@ async function execAsyncHook(plugins, hook, payload, context = DEFAULT_PLUGIN_CO
|
|
|
2159
2253
|
for (const plugin of plugins) {
|
|
2160
2254
|
const hookFn = getPluginHook(plugin, hook);
|
|
2161
2255
|
if (hookFn == null) continue;
|
|
2256
|
+
const pluginContext = resolvePluginContext(context, plugin);
|
|
2162
2257
|
try {
|
|
2163
2258
|
logPluginHookStart(plugin, hook);
|
|
2164
|
-
current = applyHookPayload(current, await invokePluginHook(hookFn, hook, current,
|
|
2259
|
+
current = applyHookPayload(current, await invokePluginHook(hookFn, hook, current, pluginContext));
|
|
2165
2260
|
logPluginHookEnd(plugin, hook);
|
|
2166
2261
|
} catch (error) {
|
|
2167
|
-
reportPluginHookError(
|
|
2262
|
+
reportPluginHookError(pluginContext, plugin, hook, error);
|
|
2168
2263
|
throw error;
|
|
2169
2264
|
}
|
|
2170
2265
|
}
|
|
@@ -2184,12 +2279,13 @@ function execSyncHook(plugins, hook, payload, context = DEFAULT_PLUGIN_CONTEXT)
|
|
|
2184
2279
|
for (const plugin of plugins) {
|
|
2185
2280
|
const hookFn = getPluginHook(plugin, hook);
|
|
2186
2281
|
if (hookFn == null) continue;
|
|
2282
|
+
const pluginContext = resolvePluginContext(context, plugin);
|
|
2187
2283
|
try {
|
|
2188
2284
|
logPluginHookStart(plugin, hook);
|
|
2189
|
-
current = applyHookPayload(current, invokePluginHook(hookFn, hook, current,
|
|
2285
|
+
current = applyHookPayload(current, invokePluginHook(hookFn, hook, current, pluginContext));
|
|
2190
2286
|
logPluginHookEnd(plugin, hook);
|
|
2191
2287
|
} catch (error) {
|
|
2192
|
-
reportPluginHookError(
|
|
2288
|
+
reportPluginHookError(pluginContext, plugin, hook, error);
|
|
2193
2289
|
throw error;
|
|
2194
2290
|
}
|
|
2195
2291
|
}
|
|
@@ -2200,19 +2296,60 @@ function execSyncHook(plugins, hook, payload, context = DEFAULT_PLUGIN_CONTEXT)
|
|
|
2200
2296
|
* Creates an engine-local hook dispatcher bound to one diagnostic context.
|
|
2201
2297
|
*
|
|
2202
2298
|
* @internal
|
|
2299
|
+
* @remarks
|
|
2300
|
+
* Each dispatcher instance owns one plugin-context store: every plugin
|
|
2301
|
+
* definition gets exactly one `EnginePluginContext` (with `state` initialized
|
|
2302
|
+
* lazily via `createState()`) per dispatcher — i.e. per engine, since
|
|
2303
|
+
* `createEngine` creates one dispatcher per engine (#116). The same plugin
|
|
2304
|
+
* definition used with another dispatcher/engine gets a distinct context and
|
|
2305
|
+
* distinct state.
|
|
2203
2306
|
*/
|
|
2204
2307
|
function createEngineHooks(context) {
|
|
2308
|
+
const pluginContexts = /* @__PURE__ */ new WeakMap();
|
|
2309
|
+
const host = context.host ?? {};
|
|
2310
|
+
const contextFor = (plugin) => {
|
|
2311
|
+
let entry = pluginContexts.get(plugin);
|
|
2312
|
+
if (entry == null) {
|
|
2313
|
+
try {
|
|
2314
|
+
entry = {
|
|
2315
|
+
status: "ok",
|
|
2316
|
+
context: {
|
|
2317
|
+
onDiagnostic: context.onDiagnostic,
|
|
2318
|
+
state: plugin.createState?.(),
|
|
2319
|
+
host
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
} catch (error) {
|
|
2323
|
+
entry = {
|
|
2324
|
+
status: "failed",
|
|
2325
|
+
error
|
|
2326
|
+
};
|
|
2327
|
+
emitDiagnostic(context.onDiagnostic, {
|
|
2328
|
+
level: "error",
|
|
2329
|
+
code: "plugin-state-init-error",
|
|
2330
|
+
message: `Plugin "${plugin.name}" failed to initialize its engine-local state: ${error instanceof Error ? error.message : String(error)}`,
|
|
2331
|
+
cause: error,
|
|
2332
|
+
plugin: plugin.name
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
pluginContexts.set(plugin, entry);
|
|
2336
|
+
}
|
|
2337
|
+
if (entry.status === "failed") throw entry.error;
|
|
2338
|
+
return entry.context;
|
|
2339
|
+
};
|
|
2205
2340
|
return {
|
|
2206
|
-
configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config,
|
|
2207
|
-
rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config,
|
|
2208
|
-
configureResolvedConfig: (plugins, resolvedConfig) => execAsyncHook(plugins, "configureResolvedConfig", resolvedConfig,
|
|
2209
|
-
configureEngine: (plugins, engine) => execAsyncHook(plugins, "configureEngine", engine,
|
|
2210
|
-
transformSelectors: (plugins, selectors) => execAsyncHook(plugins, "transformSelectors", selectors,
|
|
2211
|
-
transformStyleItems: (plugins, styleItems) => execAsyncHook(plugins, "transformStyleItems", styleItems,
|
|
2212
|
-
transformStyleDefinitions: (plugins, styleDefinitions) => execAsyncHook(plugins, "transformStyleDefinitions", styleDefinitions,
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2341
|
+
configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config, contextFor),
|
|
2342
|
+
rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config, contextFor),
|
|
2343
|
+
configureResolvedConfig: (plugins, resolvedConfig) => execAsyncHook(plugins, "configureResolvedConfig", resolvedConfig, contextFor),
|
|
2344
|
+
configureEngine: (plugins, engine) => execAsyncHook(plugins, "configureEngine", engine, contextFor),
|
|
2345
|
+
transformSelectors: (plugins, selectors) => execAsyncHook(plugins, "transformSelectors", selectors, contextFor),
|
|
2346
|
+
transformStyleItems: (plugins, styleItems) => execAsyncHook(plugins, "transformStyleItems", styleItems, contextFor),
|
|
2347
|
+
transformStyleDefinitions: (plugins, styleDefinitions) => execAsyncHook(plugins, "transformStyleDefinitions", styleDefinitions, contextFor),
|
|
2348
|
+
transformStyleContents: (plugins, styleContents) => execAsyncHook(plugins, "transformStyleContents", styleContents, contextFor),
|
|
2349
|
+
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)
|
|
2216
2353
|
};
|
|
2217
2354
|
}
|
|
2218
2355
|
createEngineHooks(DEFAULT_PLUGIN_CONTEXT);
|
|
@@ -2235,6 +2372,10 @@ function resolvePlugins(plugins) {
|
|
|
2235
2372
|
*
|
|
2236
2373
|
* @param plugin - The plugin definition to return unchanged.
|
|
2237
2374
|
* @returns The same plugin instance.
|
|
2375
|
+
*
|
|
2376
|
+
* @remarks
|
|
2377
|
+
* When the plugin declares `createState`, the state type is inferred from its
|
|
2378
|
+
* return value and every hook's `context.state` is typed accordingly.
|
|
2238
2379
|
*/
|
|
2239
2380
|
function defineEnginePlugin(plugin) {
|
|
2240
2381
|
return plugin;
|
|
@@ -3187,15 +3328,21 @@ const DEFAULT_LAYERS = {
|
|
|
3187
3328
|
*
|
|
3188
3329
|
* @remarks Core plugins (`important`, `variables`, `keyframes`, `selectors`, `shortcuts`) are prepended automatically. The function resolves plugins, runs all configuration hooks in sequence, and returns the ready-to-use engine.
|
|
3189
3330
|
*
|
|
3331
|
+
* The caller-owned `config` graph is treated as immutable input (#117): the engine clones it into an engine-local working copy before any plugin configuration hook runs, so plugin hooks that mutate their config (`config.layers ??= {}` and friends) never write back into caller-owned objects, and the same config object can be reused across sequential or concurrent `createEngine()` calls without accumulating setup mutations. Ordinary config data (plain objects/arrays, `Map`/`Set` contents, `Date`, `RegExp`) is recursively isolated — module-augmented plugin fields included; functions and other opaque class instances keep their identity and are treated as immutable values; the `plugins` array is copied while plugin definition objects keep their identity (#116).
|
|
3332
|
+
*
|
|
3190
3333
|
* @example
|
|
3191
3334
|
* ```ts
|
|
3192
3335
|
* const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
|
|
3193
3336
|
* ```
|
|
3194
3337
|
*/
|
|
3195
3338
|
async function createEngine(config = {}, options = {}) {
|
|
3339
|
+
config = cloneEngineConfig(config);
|
|
3196
3340
|
const hostOnDiagnostic = options.onDiagnostic ?? noopDiagnosticHandler;
|
|
3197
3341
|
const onDiagnostic = (diagnostic) => emitDiagnostic(hostOnDiagnostic, diagnostic);
|
|
3198
|
-
const pluginHooks = createEngineHooks({
|
|
3342
|
+
const pluginHooks = createEngineHooks({
|
|
3343
|
+
onDiagnostic,
|
|
3344
|
+
host: options.host ?? {}
|
|
3345
|
+
});
|
|
3199
3346
|
log.debug("Creating engine with config:", config);
|
|
3200
3347
|
const corePlugins = [
|
|
3201
3348
|
variables(),
|
|
@@ -3316,7 +3463,7 @@ var Engine = class {
|
|
|
3316
3463
|
*
|
|
3317
3464
|
* @param path - The file path (ideally absolute) the current config was derived from.
|
|
3318
3465
|
*
|
|
3319
|
-
* @remarks Call from a plugin
|
|
3466
|
+
* @remarks Call from a plugin after loading data from disk — typically in `configureEngine`, but registering during later hooks (e.g. while resolving inside `engine.use()`) is fully supported: each genuinely new path fires the `configDependencyAdded` committed notification so integration layers can extend an already-running watcher (#122). Integration layers watch registered paths and rebuild the engine when any of them changes.
|
|
3320
3467
|
*
|
|
3321
3468
|
* @example
|
|
3322
3469
|
* ```ts
|
|
@@ -3324,7 +3471,11 @@ var Engine = class {
|
|
|
3324
3471
|
* ```
|
|
3325
3472
|
*/
|
|
3326
3473
|
addConfigDependency(path) {
|
|
3474
|
+
if (this.configDependencies.has(path)) return;
|
|
3327
3475
|
this.configDependencies.add(path);
|
|
3476
|
+
try {
|
|
3477
|
+
this.pluginHooks.configDependencyAdded(this.config.plugins, path);
|
|
3478
|
+
} catch {}
|
|
3328
3479
|
}
|
|
3329
3480
|
/**
|
|
3330
3481
|
* Fires the `preflightUpdated` hook to notify plugins that preflight content has changed.
|
|
@@ -3345,7 +3496,7 @@ var Engine = class {
|
|
|
3345
3496
|
*
|
|
3346
3497
|
* @param atomicStyle - The atomic style that was just added to the store.
|
|
3347
3498
|
*
|
|
3348
|
-
* @remarks Called automatically by `
|
|
3499
|
+
* @remarks Called automatically by `commitUse()` when a previously unseen atomic style is registered. This is a committed notification: the style's ID, cache keys, and store indices are already established, so mutating the payload is unsupported — plugins that need to transform styles must use the provisional hooks (`transformStyleItems`, `transformStyleDefinitions`, `transformSelectors`, `transformStyleContents`) instead (#114).
|
|
3349
3500
|
*
|
|
3350
3501
|
* @example
|
|
3351
3502
|
* ```ts
|
|
@@ -3421,28 +3572,61 @@ var Engine = class {
|
|
|
3421
3572
|
this.notifyPreflightUpdated();
|
|
3422
3573
|
}
|
|
3423
3574
|
/**
|
|
3424
|
-
*
|
|
3575
|
+
* Provisionally resolves style items into a commit-ready plan without touching committed engine state.
|
|
3425
3576
|
*
|
|
3426
3577
|
* @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
|
|
3427
|
-
* @returns
|
|
3578
|
+
* @returns A promise of the {@link StyleUsePlan} to pass to `commitUse()`.
|
|
3428
3579
|
*
|
|
3429
|
-
* @remarks
|
|
3580
|
+
* @remarks
|
|
3581
|
+
* Runs the full provisional pipeline: `transformStyleItems`, extraction
|
|
3582
|
+
* (`transformStyleDefinitions`/`transformSelectors`), normalization, and the
|
|
3583
|
+
* normalized-content seam `transformStyleContents`. It allocates no atomic
|
|
3584
|
+
* style IDs, mutates no `EngineStore` state, and fires no committed
|
|
3585
|
+
* notifications — a rejection anywhere leaves the engine exactly as it was.
|
|
3586
|
+
* Plans deliberately carry no IDs: reuse-vs-fresh decisions read live store
|
|
3587
|
+
* state and are only valid inside `commitUse()` (#114).
|
|
3430
3588
|
*
|
|
3431
3589
|
* @example
|
|
3432
3590
|
* ```ts
|
|
3433
|
-
* const
|
|
3591
|
+
* const plan = await engine.prepareUse({ color: 'red' })
|
|
3592
|
+
* const ids = engine.commitUse(plan)
|
|
3434
3593
|
* ```
|
|
3435
3594
|
*/
|
|
3436
|
-
async
|
|
3437
|
-
log.debug(`
|
|
3595
|
+
async prepareUse(...itemList) {
|
|
3596
|
+
log.debug(`Preparing ${itemList.length} style items`);
|
|
3438
3597
|
const { unknown, contents } = await resolveStyleItemList({
|
|
3439
3598
|
itemList,
|
|
3440
3599
|
transformStyleItems: (styleItems) => this.pluginHooks.transformStyleItems(this.config.plugins, styleItems),
|
|
3441
3600
|
extractStyleDefinition: (styleDefinition) => this.extract(styleDefinition)
|
|
3442
3601
|
});
|
|
3602
|
+
return {
|
|
3603
|
+
unknown,
|
|
3604
|
+
contents: optimizeAtomicStyleContents(await this.pluginHooks.transformStyleContents(this.config.plugins, contents))
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Commits a prepared plan: allocates/reuses atomic style IDs and registers new styles in the store.
|
|
3609
|
+
*
|
|
3610
|
+
* @param plan - A plan produced by `prepareUse()`.
|
|
3611
|
+
* @returns An array containing any unresolved string references first, followed by atomic style IDs in resolution order.
|
|
3612
|
+
*
|
|
3613
|
+
* @remarks
|
|
3614
|
+
* This is the short, mutation-critical section and MUST stay synchronous:
|
|
3615
|
+
* integration layers commit whole modules inside a revision/epoch-checked
|
|
3616
|
+
* synchronous block, so an `await` here would reopen the stale-commit race
|
|
3617
|
+
* (#114). `atomicStyleAdded` fires per newly registered style as a committed
|
|
3618
|
+
* notification; a throwing observer is reported through the diagnostic
|
|
3619
|
+
* context but never rolls back the already-committed registration.
|
|
3620
|
+
*
|
|
3621
|
+
* @example
|
|
3622
|
+
* ```ts
|
|
3623
|
+
* const ids = engine.commitUse(await engine.prepareUse({ color: 'red' }))
|
|
3624
|
+
* ```
|
|
3625
|
+
*/
|
|
3626
|
+
commitUse(plan) {
|
|
3443
3627
|
const resolvedIds = [];
|
|
3444
3628
|
const resolvedIdsByBaseKey = /* @__PURE__ */ new Map();
|
|
3445
|
-
for (const content of contents) {
|
|
3629
|
+
for (const content of plan.contents) {
|
|
3446
3630
|
const { id, atomicStyle } = resolveAtomicStyle({
|
|
3447
3631
|
content,
|
|
3448
3632
|
prefix: this.config.prefix,
|
|
@@ -3453,11 +3637,29 @@ var Engine = class {
|
|
|
3453
3637
|
resolvedIdsByBaseKey.set(getAtomicStyleBaseKey(content), id);
|
|
3454
3638
|
if (atomicStyle != null) {
|
|
3455
3639
|
log.debug(`Atomic style added: ${id}`);
|
|
3456
|
-
|
|
3640
|
+
try {
|
|
3641
|
+
this.notifyAtomicStyleAdded(atomicStyle);
|
|
3642
|
+
} catch {}
|
|
3457
3643
|
}
|
|
3458
3644
|
}
|
|
3459
|
-
log.debug(`Resolved ${resolvedIds.length} atomic styles, ${unknown.size} unknown items`);
|
|
3460
|
-
return [...unknown, ...resolvedIds];
|
|
3645
|
+
log.debug(`Resolved ${resolvedIds.length} atomic styles, ${plan.unknown.size} unknown items`);
|
|
3646
|
+
return [...plan.unknown, ...resolvedIds];
|
|
3647
|
+
}
|
|
3648
|
+
/**
|
|
3649
|
+
* Processes style items through the plugin pipeline and registers the resulting atomic styles in the store.
|
|
3650
|
+
*
|
|
3651
|
+
* @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
|
|
3652
|
+
* @returns An array containing any unresolved string references first, followed by atomic style IDs in resolution order.
|
|
3653
|
+
*
|
|
3654
|
+
* @remarks Equivalent to `commitUse(await prepareUse(...itemList))` — the convenience path for direct consumers. Integration layers that need whole-module transactionality call the two phases separately (#114).
|
|
3655
|
+
*
|
|
3656
|
+
* @example
|
|
3657
|
+
* ```ts
|
|
3658
|
+
* const ids = await engine.use({ color: 'red' }, { padding: '1rem' })
|
|
3659
|
+
* ```
|
|
3660
|
+
*/
|
|
3661
|
+
async use(...itemList) {
|
|
3662
|
+
return this.commitUse(await this.prepareUse(...itemList));
|
|
3461
3663
|
}
|
|
3462
3664
|
/**
|
|
3463
3665
|
* Renders all registered preflight definitions into a CSS string.
|
|
@@ -3519,12 +3721,11 @@ var Engine = class {
|
|
|
3519
3721
|
* Renders atomic styles into a CSS string, optionally filtered by ID and grouped by layer.
|
|
3520
3722
|
*
|
|
3521
3723
|
* @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
|
|
3522
|
-
* @param options - Optional filtering: `atomicStyleIds` to render a subset
|
|
3724
|
+
* @param options - Optional filtering: `atomicStyleIds` to render a subset.
|
|
3523
3725
|
* @param options.atomicStyleIds - Specific atomic style IDs to render instead of the full store.
|
|
3524
|
-
* @param options.isPreview - Whether to keep placeholder IDs instead of substituting real class names.
|
|
3525
3726
|
* @returns The rendered atomic-style CSS.
|
|
3526
3727
|
*
|
|
3527
|
-
* @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered.
|
|
3728
|
+
* @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered.
|
|
3528
3729
|
*
|
|
3529
3730
|
* @example
|
|
3530
3731
|
* ```ts
|
|
@@ -3533,13 +3734,12 @@ var Engine = class {
|
|
|
3533
3734
|
*/
|
|
3534
3735
|
async renderAtomicStyles(isFormatted, options = {}) {
|
|
3535
3736
|
log.debug("Rendering atomic styles...");
|
|
3536
|
-
const { atomicStyleIds = null
|
|
3737
|
+
const { atomicStyleIds = null } = options;
|
|
3537
3738
|
const atomicStyles = atomicStyleIds == null ? [...this.store.atomicStyles.values()] : atomicStyleIds.map((id) => this.store.atomicStyles.get(id)).filter(isNotNullish);
|
|
3538
|
-
log.debug(`Rendering ${atomicStyles.length} atomic styles
|
|
3739
|
+
log.debug(`Rendering ${atomicStyles.length} atomic styles`);
|
|
3539
3740
|
reportUnknownAtomicStyleLayers(this, atomicStyles);
|
|
3540
3741
|
return renderAtomicStyles({
|
|
3541
3742
|
atomicStyles,
|
|
3542
|
-
isPreview,
|
|
3543
3743
|
isFormatted,
|
|
3544
3744
|
defaultSelector: this.config.defaultSelector,
|
|
3545
3745
|
layers: this.config.layers,
|
|
@@ -3834,13 +4034,13 @@ async function resolveStyleItemList({ itemList, transformStyleItems, extractStyl
|
|
|
3834
4034
|
function sortAtomicStyles(styles, defaultSelector) {
|
|
3835
4035
|
return [...styles].sort((a, b) => calcAtomicStyleRenderingWeight(a, defaultSelector) - calcAtomicStyleRenderingWeight(b, defaultSelector));
|
|
3836
4036
|
}
|
|
3837
|
-
function renderAtomicStylesCss({ atomicStyles,
|
|
4037
|
+
function renderAtomicStylesCss({ atomicStyles, isFormatted }) {
|
|
3838
4038
|
const blocks = /* @__PURE__ */ new Map();
|
|
3839
4039
|
atomicStyles.forEach(({ id, content: { selector: rawSelector, property, value } }) => {
|
|
3840
4040
|
const { selector } = splitLayerSelector(rawSelector);
|
|
3841
4041
|
if (selector.some((s) => hasAtomicStyleIdPlaceholder(s)) === false || value == null) return;
|
|
3842
4042
|
const renderObject = {
|
|
3843
|
-
selector:
|
|
4043
|
+
selector: selector.map((s) => replaceAtomicStyleIdPlaceholder(s, id)),
|
|
3844
4044
|
properties: value.map((v) => ({
|
|
3845
4045
|
property,
|
|
3846
4046
|
value: v
|
|
@@ -3863,9 +4063,8 @@ function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
|
|
|
3863
4063
|
* Standalone function that renders atomic styles into CSS with layer grouping.
|
|
3864
4064
|
* @internal
|
|
3865
4065
|
*
|
|
3866
|
-
* @param payload - An object containing `atomicStyles`, `
|
|
4066
|
+
* @param payload - An object containing `atomicStyles`, `isFormatted`, `defaultSelector`, and optional `layers`/`defaultUtilitiesLayer`.
|
|
3867
4067
|
* @param payload.atomicStyles - The atomic styles to render.
|
|
3868
|
-
* @param payload.isPreview - Whether placeholder IDs should be preserved for preview output.
|
|
3869
4068
|
* @param payload.isFormatted - Whether to render with indentation and line breaks.
|
|
3870
4069
|
* @param payload.defaultSelector - The engine default selector used when computing render order.
|
|
3871
4070
|
* @param payload.layers - Optional configured CSS layers to group atomic styles into.
|
|
@@ -3876,15 +4075,14 @@ function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
|
|
|
3876
4075
|
*
|
|
3877
4076
|
* @example
|
|
3878
4077
|
* ```ts
|
|
3879
|
-
* const css = renderAtomicStyles({ atomicStyles,
|
|
4078
|
+
* const css = renderAtomicStyles({ atomicStyles, isFormatted: true, defaultSelector: '.pk-__ID__', layers: { utilities: 10 } })
|
|
3880
4079
|
* ```
|
|
3881
4080
|
*/
|
|
3882
4081
|
function renderAtomicStyles(payload) {
|
|
3883
|
-
const { atomicStyles,
|
|
4082
|
+
const { atomicStyles, isFormatted, defaultSelector, layers, defaultUtilitiesLayer } = payload;
|
|
3884
4083
|
const sortedStyles = sortAtomicStyles(atomicStyles, defaultSelector);
|
|
3885
4084
|
if (layers == null) return renderAtomicStylesCss({
|
|
3886
4085
|
atomicStyles: sortedStyles,
|
|
3887
|
-
isPreview,
|
|
3888
4086
|
isFormatted
|
|
3889
4087
|
});
|
|
3890
4088
|
const layerOrder = sortLayerNames(layers);
|
|
@@ -3897,7 +4095,6 @@ function renderAtomicStyles(payload) {
|
|
|
3897
4095
|
const parts = [];
|
|
3898
4096
|
if (unlayeredStyles.length > 0) parts.push(renderAtomicStylesCss({
|
|
3899
4097
|
atomicStyles: unlayeredStyles,
|
|
3900
|
-
isPreview,
|
|
3901
4098
|
isFormatted
|
|
3902
4099
|
}));
|
|
3903
4100
|
parts.push(...renderLayerBlocks({
|
|
@@ -3906,7 +4103,6 @@ function renderAtomicStyles(payload) {
|
|
|
3906
4103
|
isFormatted,
|
|
3907
4104
|
render: (styles) => renderAtomicStylesCss({
|
|
3908
4105
|
atomicStyles: styles,
|
|
3909
|
-
isPreview,
|
|
3910
4106
|
isFormatted
|
|
3911
4107
|
})
|
|
3912
4108
|
}));
|