@pikacss/core 0.0.57 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,26 @@ const css = await engine.renderAtomicStyles(true, { atomicStyleIds: ids })
25
25
 
26
26
  See the [full documentation](https://pikacss.github.io/api/core).
27
27
 
28
+ ## Diagnostics
29
+
30
+ `@pikacss/core` is platform-neutral and performs no console I/O by default. Explicitly install an instance-scoped handler when diagnostics should be surfaced:
31
+
32
+ ```ts
33
+ const engine = await createEngine(config, {
34
+ onDiagnostic(diagnostic) {
35
+ // Route to your host logger, editor UI, telemetry, or test assertions.
36
+ },
37
+ })
38
+ ```
39
+
40
+ Plugin lifecycle failures are reported and rethrown; warnings are delivered only through the configured handler.
41
+
42
+ Diagnostics and optional tracing are separate host-controlled channels, so configuring both does not duplicate the same runtime event.
43
+
44
+ ## Optional tracing
45
+
46
+ The exported `log` and `createLogger()` APIs independently default to no-op output handlers. A host explicitly controls which debug, info, warning, and error sinks tracing uses.
47
+
28
48
  ## License
29
49
 
30
50
  MIT
package/dist/index.d.mts CHANGED
@@ -151,6 +151,46 @@ interface ResolvedKeyframesConfig {
151
151
  autocomplete: string[];
152
152
  }
153
153
  //#endregion
154
+ //#region src/diagnostics.d.ts
155
+ /** Severity of a PikaCSS diagnostic. */
156
+ type DiagnosticLevel = 'warning' | 'error';
157
+ /**
158
+ * Structured diagnostic emitted by the PikaCSS engine or an engine plugin.
159
+ *
160
+ * @remarks Diagnostics are data only. The core package never assumes a console,
161
+ * logger, browser, or Node.js runtime. Hosts decide how diagnostics are displayed.
162
+ */
163
+ interface Diagnostic {
164
+ /** Diagnostic severity. */
165
+ level: DiagnosticLevel;
166
+ /** Stable machine-readable identifier. */
167
+ code: string;
168
+ /** Human-readable explanation. */
169
+ message: string;
170
+ /** Original error or value related to the diagnostic. */
171
+ cause?: unknown;
172
+ /** Plugin that produced the diagnostic, when applicable. */
173
+ plugin?: string;
174
+ /** Plugin hook that produced the diagnostic, when applicable. */
175
+ hook?: string;
176
+ }
177
+ /** Callback used by a host to receive structured diagnostics. */
178
+ type DiagnosticHandler = (diagnostic: Diagnostic) => void;
179
+ /** Runtime-only options accepted by {@link createEngine}. */
180
+ interface CreateEngineOptions {
181
+ /**
182
+ * Receives warnings and errors produced by this engine instance.
183
+ *
184
+ * @default A no-op handler.
185
+ */
186
+ onDiagnostic?: DiagnosticHandler;
187
+ }
188
+ /** Context passed to plugin hooks by the engine. */
189
+ interface EnginePluginContext {
190
+ /** Instance-scoped diagnostic handler. */
191
+ onDiagnostic: DiagnosticHandler;
192
+ }
193
+ //#endregion
154
194
  //#region src/resolver.d.ts
155
195
  /**
156
196
  * Wrapper holding a resolved value, used as the cache entry in resolver maps.
@@ -231,6 +271,7 @@ interface DynamicRule<T> {
231
271
  * ```
232
272
  */
233
273
  declare abstract class AbstractResolver<T> {
274
+ readonly onDiagnostic: DiagnosticHandler;
234
275
  /** Cache of previously resolved input-string → result pairs. */
235
276
  _resolvedResultsMap: Map<string, ResolvedResult<T>>;
236
277
  /** Negative cache of input strings that matched no rule at all. Retryable-unresolved dynamic results (a matched dynamic rule whose value fn returned nullish) are never stored here. */
@@ -243,6 +284,9 @@ declare abstract class AbstractResolver<T> {
243
284
  dynamicRulesMap: Map<string, DynamicRule<T>>;
244
285
  /** Callback invoked after a successful resolution, receiving the input string, rule type, and result. */
245
286
  onResolved: (string: string, type: 'static' | 'dynamic', result: ResolvedResult<T>) => void;
287
+ constructor(onDiagnostic?: DiagnosticHandler);
288
+ /** Reports through the host handler, falling back to the optional logger only for standalone resolvers. */
289
+ reportDiagnostic(diagnostic: Diagnostic): void;
246
290
  get staticRules(): StaticRule<T>[];
247
291
  get dynamicRules(): DynamicRule<T>[];
248
292
  /**
@@ -1284,6 +1328,7 @@ type ExtractFn = (styleDefinition: InternalStyleDefinition) => Promise<Extracted
1284
1328
  * Creates and initializes a PikaCSS engine with the given configuration.
1285
1329
  *
1286
1330
  * @param config - The engine configuration, including plugins, selectors, shortcuts, variables, keyframes, preflights, and layer settings.
1331
+ * @param options - Runtime-only host capabilities, including the instance-scoped diagnostic handler.
1287
1332
  * @returns A fully initialized `Engine` instance.
1288
1333
  *
1289
1334
  * @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.
@@ -1293,7 +1338,7 @@ type ExtractFn = (styleDefinition: InternalStyleDefinition) => Promise<Extracted
1293
1338
  * const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
1294
1339
  * ```
1295
1340
  */
1296
- declare function createEngine(config?: EngineConfig): Promise<Engine>;
1341
+ declare function createEngine(config?: EngineConfig, options?: CreateEngineOptions): Promise<Engine>;
1297
1342
  /**
1298
1343
  * The PikaCSS engine: manages atomic style resolution, rendering, preflights, and plugin hooks.
1299
1344
  *
@@ -1309,19 +1354,10 @@ declare function createEngine(config?: EngineConfig): Promise<Engine>;
1309
1354
  declare class Engine {
1310
1355
  /** The fully resolved engine configuration. */
1311
1356
  config: ResolvedEngineConfig;
1312
- /** Reference to the plugin hook dispatcher for invoking lifecycle hooks. */
1313
- pluginHooks: {
1314
- configureRawConfig: (plugins: EnginePlugin[], payload: EngineConfig) => Promise<EngineConfig>;
1315
- rawConfigConfigured: (plugins: EnginePlugin[], payload: EngineConfig) => void;
1316
- configureResolvedConfig: (plugins: EnginePlugin[], payload: ResolvedEngineConfig) => Promise<ResolvedEngineConfig>;
1317
- configureEngine: (plugins: EnginePlugin[], payload: Engine) => Promise<Engine>;
1318
- transformSelectors: (plugins: EnginePlugin[], payload: string[]) => Promise<string[]>;
1319
- transformStyleItems: (plugins: EnginePlugin[], payload: StyleItem[]) => Promise<StyleItem[]>;
1320
- transformStyleDefinitions: (plugins: EnginePlugin[], payload: StyleDefinition[]) => Promise<StyleDefinition[]>;
1321
- preflightUpdated: (plugins: EnginePlugin[]) => void;
1322
- atomicStyleAdded: (plugins: EnginePlugin[], payload: AtomicStyle) => AtomicStyle;
1323
- autocompleteConfigUpdated: (plugins: EnginePlugin[]) => void;
1324
- };
1357
+ /** Instance-scoped diagnostic handler supplied by the host. */
1358
+ readonly onDiagnostic: DiagnosticHandler;
1359
+ /** Reference to the instance-scoped plugin hook dispatcher. */
1360
+ pluginHooks: ReturnType<typeof createEngineHooks>;
1325
1361
  /** The extraction function that decomposes style definitions into atomic style contents. */
1326
1362
  extract: ExtractFn;
1327
1363
  /** The engine's runtime store holding registered atomic styles and their ID mappings. */
@@ -1344,7 +1380,13 @@ declare class Engine {
1344
1380
  * const engine = new Engine(resolvedConfig)
1345
1381
  * ```
1346
1382
  */
1347
- constructor(config: ResolvedEngineConfig);
1383
+ constructor(config: ResolvedEngineConfig, onDiagnostic?: DiagnosticHandler, pluginHooks?: ReturnType<typeof createEngineHooks>);
1384
+ /**
1385
+ * Reports a structured diagnostic to this engine instance's host handler.
1386
+ *
1387
+ * @param diagnostic - The structured warning or error to deliver.
1388
+ */
1389
+ reportDiagnostic(diagnostic: Diagnostic): void;
1348
1390
  /**
1349
1391
  * Invokes a preflight function, memoizing the result in the given render-pass context.
1350
1392
  *
@@ -1549,45 +1591,28 @@ type EngineHooksDefinition = DefineHooks<{
1549
1591
  autocompleteConfigUpdated: ['sync', void];
1550
1592
  }>;
1551
1593
  type HookParams<H extends [type: 'sync' | 'async', payload: any, returnValue?: any]> = H[1] extends void ? [] : [payload: H[1]];
1552
- type EnginePluginHooksOptions = { [K in keyof EngineHooksDefinition]?: EngineHooksDefinition[K][0] extends 'async' ? (...params: HookParams<EngineHooksDefinition[K]>) => Awaitable<EngineHooksDefinition[K][1] | void> : (...params: HookParams<EngineHooksDefinition[K]>) => EngineHooksDefinition[K][1] | void };
1594
+ type PluginHookParams<H extends [type: 'sync' | 'async', payload: any, returnValue?: any]> = H[1] extends void ? [context?: EnginePluginContext] : [payload: H[1], context?: EnginePluginContext];
1595
+ 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
+ type EngineHooks = { [K in keyof EngineHooksDefinition]: (plugins: EnginePlugin[], ...params: HookParams<EngineHooksDefinition[K]>) => HookReturnType<EngineHooksDefinition[K]> };
1553
1597
  /**
1554
- * Describes an engine plugin that can hook into the PikaCSS engine lifecycle.
1555
- *
1556
- * @remarks Plugins implement optional hook methods corresponding to engine lifecycle events. Hooks run in plugin registration order, optionally reordered by the `order` property.
1598
+ * Creates an engine-local hook dispatcher bound to one diagnostic context.
1557
1599
  *
1558
- * @example
1559
- * ```ts
1560
- * const myPlugin: EnginePlugin = {
1561
- * name: 'my-plugin',
1562
- * configureRawConfig: (config) => ({ ...config, important: { default: true } }),
1563
- * }
1564
- * ```
1600
+ * @internal
1565
1601
  */
1602
+ declare function createEngineHooks(context: EnginePluginContext): EngineHooks;
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. */
1566
1605
  interface EnginePlugin extends EnginePluginHooksOptions {
1567
- /** The unique human-readable name identifying this plugin in logs and diagnostics. */
1606
+ /** The unique human-readable name identifying this plugin in diagnostics. */
1568
1607
  name: string;
1569
- /**
1570
- * Controls plugin execution order relative to other plugins.
1571
- *
1572
- * @default undefined (normal order)
1573
- */
1608
+ /** Controls execution order relative to other plugins. */
1574
1609
  order?: 'pre' | 'post';
1575
1610
  }
1576
1611
  /**
1577
- * Identity helper that returns the plugin object as-is, providing TypeScript type inference for plugin definitions.
1578
- *
1579
- * @param plugin - The engine plugin definition.
1580
- * @returns The same plugin object, unchanged.
1612
+ * Identity helper that provides type inference for an engine plugin definition.
1581
1613
  *
1582
- * @remarks This is a compile-time-only helper; it has no runtime effect. Using it ensures type checking and IDE autocompletion for hook names and payloads.
1583
- *
1584
- * @example
1585
- * ```ts
1586
- * export default defineEnginePlugin({
1587
- * name: 'my-plugin',
1588
- * configureRawConfig: (config) => ({ ...config, important: { default: true } }),
1589
- * })
1590
- * ```
1614
+ * @param plugin - The plugin definition to return unchanged.
1615
+ * @returns The same plugin instance.
1591
1616
  */
1592
1617
  declare function defineEnginePlugin(plugin: EnginePlugin): EnginePlugin;
1593
1618
  //#endregion
@@ -37336,7 +37361,7 @@ interface ResolvedEngineConfig {
37336
37361
  * @param prefix - Label prepended to every log message (e.g. `'[PikaCSS]'`).
37337
37362
  * @returns A logger object with `debug`, `info`, `warn`, `error` methods and configuration setters.
37338
37363
  *
37339
- * @remarks Debug messages are suppressed by default. Call `log.toggleDebug()` to enable them. Each log level can be replaced with a custom implementation via the `set*Fn` methods, which is useful for redirecting output in non-browser environments.
37364
+ * @remarks All output handlers are no-ops by default, and debug messages are additionally disabled. Hosts may install output functions through the `set*Fn` methods; engine warnings and errors are reported through `createEngine(..., { onDiagnostic })` instead.
37340
37365
  *
37341
37366
  * @example
37342
37367
  * ```ts
@@ -37469,4 +37494,4 @@ declare function renderCSSStyleBlocks(blocks: CSSStyleBlocks, isFormatted: boole
37469
37494
  */
37470
37495
  declare function defineEngineConfig<const T extends EngineConfig>(config: T): T;
37471
37496
  //#endregion
37472
- export { Arrayable, type AutocompleteConfig, type AutocompleteContribution, type AutocompletePatternsConfig, Awaitable, type CSSProperty, type CSSSelector, type CSSStyleBlockBody, type CSSStyleBlocks, type DefineAutocomplete, type Engine, type EngineConfig, type EnginePlugin, 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 };
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 };
package/dist/index.mjs CHANGED
@@ -1231,7 +1231,7 @@ function hasPropertyEffectOverlap(left, right) {
1231
1231
  * @param prefix - Label prepended to every log message (e.g. `'[PikaCSS]'`).
1232
1232
  * @returns A logger object with `debug`, `info`, `warn`, `error` methods and configuration setters.
1233
1233
  *
1234
- * @remarks Debug messages are suppressed by default. Call `log.toggleDebug()` to enable them. Each log level can be replaced with a custom implementation via the `set*Fn` methods, which is useful for redirecting output in non-browser environments.
1234
+ * @remarks All output handlers are no-ops by default, and debug messages are additionally disabled. Hosts may install output functions through the `set*Fn` methods; engine warnings and errors are reported through `createEngine(..., { onDiagnostic })` instead.
1235
1235
  *
1236
1236
  * @example
1237
1237
  * ```ts
@@ -1244,10 +1244,11 @@ function hasPropertyEffectOverlap(left, right) {
1244
1244
  function createLogger(prefix) {
1245
1245
  let currentPrefix = prefix;
1246
1246
  let enabledDebug = false;
1247
- let _debug = console.log;
1248
- let _info = console.log;
1249
- let _warn = console.warn;
1250
- let _error = console.error;
1247
+ const noop = (_prefix, ..._args) => {};
1248
+ let _debug = noop;
1249
+ let _info = noop;
1250
+ let _warn = noop;
1251
+ let _error = noop;
1251
1252
  return {
1252
1253
  debug: (...args) => {
1253
1254
  if (!enabledDebug) return;
@@ -1927,6 +1928,20 @@ function replaceAtomicStyleIdPlaceholder(selector, atomicStyleId) {
1927
1928
  return transformOutsideQuotes(selector, (segment) => segment.replace(ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, () => atomicStyleId));
1928
1929
  }
1929
1930
  //#endregion
1931
+ //#region src/diagnostics.ts
1932
+ /** Default diagnostic handler used by the platform-neutral core. */
1933
+ const noopDiagnosticHandler = (_diagnostic) => {};
1934
+ /**
1935
+ * Delivers a diagnostic without allowing a faulty host handler to alter engine execution.
1936
+ *
1937
+ * @internal
1938
+ */
1939
+ function emitDiagnostic(handler, diagnostic) {
1940
+ try {
1941
+ handler(diagnostic);
1942
+ } catch {}
1943
+ }
1944
+ //#endregion
1930
1945
  //#region src/extractor.ts
1931
1946
  function replaceBySplitAndJoin(str, split, mapFn, join) {
1932
1947
  let splitted = str.split(split);
@@ -2092,10 +2107,15 @@ function createExtractFn(options) {
2092
2107
  }
2093
2108
  //#endregion
2094
2109
  //#region src/plugin.ts
2110
+ const VOID_HOOKS = new Set(["preflightUpdated", "autocompleteConfigUpdated"]);
2111
+ const DEFAULT_PLUGIN_CONTEXT = { onDiagnostic: noopDiagnosticHandler };
2095
2112
  function getPluginHook(plugin, hook) {
2096
2113
  const hookFn = plugin[hook];
2097
2114
  return typeof hookFn === "function" ? hookFn : null;
2098
2115
  }
2116
+ function invokePluginHook(hookFn, hook, payload, context) {
2117
+ return VOID_HOOKS.has(hook) ? hookFn(context) : hookFn(payload, context);
2118
+ }
2099
2119
  function applyHookPayload(current, next) {
2100
2120
  return next ?? current;
2101
2121
  }
@@ -2111,27 +2131,29 @@ function logPluginHookStart(plugin, hook) {
2111
2131
  function logPluginHookEnd(plugin, hook) {
2112
2132
  log.debug(` - Plugin "${plugin.name}" completed ${hook}`);
2113
2133
  }
2114
- function logPluginHookError(plugin, hook, error) {
2115
- log.error(`Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : error}`, error);
2134
+ function reportPluginHookError(context, plugin, hook, error) {
2135
+ const message = `Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : String(error)}`;
2136
+ if (context.onDiagnostic === noopDiagnosticHandler) {
2137
+ log.error(message, error);
2138
+ return;
2139
+ }
2140
+ emitDiagnostic(context.onDiagnostic, {
2141
+ level: "error",
2142
+ code: "plugin-hook-error",
2143
+ message,
2144
+ cause: error,
2145
+ plugin: plugin.name,
2146
+ hook
2147
+ });
2116
2148
  }
2117
2149
  /**
2118
- * Executes an async hook across all plugins in order, piping the payload through each plugin's handler.
2119
- * @internal
2120
- *
2121
- * @typeParam P - The payload/return type flowing through the hook pipeline.
2122
- * @param plugins - The ordered list of engine plugins to execute.
2123
- * @param hook - The name of the async hook to invoke.
2124
- * @param payload - The initial payload to pass into the first plugin.
2125
- * @returns The final payload after all plugins have processed it.
2150
+ * Executes an async hook across all plugins in order, piping the payload through each handler.
2126
2151
  *
2127
- * @remarks Each plugin's hook receives the current payload and may return a replacement. If a plugin's hook throws, the error is logged and the current payload is preserved for subsequent plugins.
2128
- *
2129
- * @example
2130
- * ```ts
2131
- * const config = await execAsyncHook(plugins, 'configureRawConfig', rawConfig)
2132
- * ```
2152
+ * @internal
2153
+ * @remarks A thrown plugin error is reported through the supplied diagnostic context and then
2154
+ * rethrown. The engine never converts a failed lifecycle into a silently partial result.
2133
2155
  */
2134
- async function execAsyncHook(plugins, hook, payload) {
2156
+ async function execAsyncHook(plugins, hook, payload, context = DEFAULT_PLUGIN_CONTEXT) {
2135
2157
  logHookStart("Async", hook);
2136
2158
  let current = payload;
2137
2159
  for (const plugin of plugins) {
@@ -2139,33 +2161,24 @@ async function execAsyncHook(plugins, hook, payload) {
2139
2161
  if (hookFn == null) continue;
2140
2162
  try {
2141
2163
  logPluginHookStart(plugin, hook);
2142
- current = applyHookPayload(current, await hookFn(current));
2164
+ current = applyHookPayload(current, await invokePluginHook(hookFn, hook, current, context));
2143
2165
  logPluginHookEnd(plugin, hook);
2144
2166
  } catch (error) {
2145
- logPluginHookError(plugin, hook, error);
2167
+ reportPluginHookError(context, plugin, hook, error);
2168
+ throw error;
2146
2169
  }
2147
2170
  }
2148
2171
  logHookEnd("Async", hook);
2149
2172
  return current;
2150
2173
  }
2151
2174
  /**
2152
- * Executes a synchronous hook across all plugins in order, piping the payload through each plugin's handler.
2153
- * @internal
2175
+ * Executes a synchronous hook across all plugins in order, piping the payload through each handler.
2154
2176
  *
2155
- * @typeParam P - The payload/return type flowing through the hook pipeline.
2156
- * @param plugins - The ordered list of engine plugins to execute.
2157
- * @param hook - The name of the sync hook to invoke.
2158
- * @param payload - The initial payload to pass into the first plugin.
2159
- * @returns The final payload after all plugins have processed it.
2160
- *
2161
- * @remarks Functions identically to `execAsyncHook` but without awaiting. Used for notification-style hooks like `preflightUpdated` or `atomicStyleAdded`.
2162
- *
2163
- * @example
2164
- * ```ts
2165
- * execSyncHook(plugins, 'atomicStyleAdded', atomicStyle)
2166
- * ```
2177
+ * @internal
2178
+ * @remarks A thrown plugin error is reported through the supplied diagnostic context and then
2179
+ * rethrown. Notification hooks therefore cannot fail silently.
2167
2180
  */
2168
- function execSyncHook(plugins, hook, payload) {
2181
+ function execSyncHook(plugins, hook, payload, context = DEFAULT_PLUGIN_CONTEXT) {
2169
2182
  logHookStart("Sync", hook);
2170
2183
  let current = payload;
2171
2184
  for (const plugin of plugins) {
@@ -2173,78 +2186,55 @@ function execSyncHook(plugins, hook, payload) {
2173
2186
  if (hookFn == null) continue;
2174
2187
  try {
2175
2188
  logPluginHookStart(plugin, hook);
2176
- current = applyHookPayload(current, hookFn(current));
2189
+ current = applyHookPayload(current, invokePluginHook(hookFn, hook, current, context));
2177
2190
  logPluginHookEnd(plugin, hook);
2178
2191
  } catch (error) {
2179
- logPluginHookError(plugin, hook, error);
2192
+ reportPluginHookError(context, plugin, hook, error);
2193
+ throw error;
2180
2194
  }
2181
2195
  }
2182
2196
  logHookEnd("Sync", hook);
2183
2197
  return current;
2184
2198
  }
2185
2199
  /**
2186
- * Pre-built hook dispatcher object mapping each hook name to a function that delegates to `execAsyncHook` or `execSyncHook`.
2187
- * @internal
2188
- *
2189
- * @remarks Provides a convenient, type-safe interface for calling any engine hook by name without manually selecting between `execAsyncHook` and `execSyncHook`. Used throughout the `Engine` class.
2200
+ * Creates an engine-local hook dispatcher bound to one diagnostic context.
2190
2201
  *
2191
- * @example
2192
- * ```ts
2193
- * const config = await hooks.configureRawConfig(plugins, rawConfig)
2194
- * hooks.preflightUpdated(plugins)
2195
- * ```
2202
+ * @internal
2196
2203
  */
2197
- const hooks = {
2198
- configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config),
2199
- rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config),
2200
- configureResolvedConfig: (plugins, resolvedConfig) => execAsyncHook(plugins, "configureResolvedConfig", resolvedConfig),
2201
- configureEngine: (plugins, engine) => execAsyncHook(plugins, "configureEngine", engine),
2202
- transformSelectors: (plugins, selectors) => execAsyncHook(plugins, "transformSelectors", selectors),
2203
- transformStyleItems: (plugins, styleItems) => execAsyncHook(plugins, "transformStyleItems", styleItems),
2204
- transformStyleDefinitions: (plugins, styleDefinitions) => execAsyncHook(plugins, "transformStyleDefinitions", styleDefinitions),
2205
- preflightUpdated: (plugins) => execSyncHook(plugins, "preflightUpdated", void 0),
2206
- atomicStyleAdded: (plugins, atomicStyle) => execSyncHook(plugins, "atomicStyleAdded", atomicStyle),
2207
- autocompleteConfigUpdated: (plugins) => execSyncHook(plugins, "autocompleteConfigUpdated", void 0)
2208
- };
2204
+ function createEngineHooks(context) {
2205
+ return {
2206
+ configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config, context),
2207
+ rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config, context),
2208
+ configureResolvedConfig: (plugins, resolvedConfig) => execAsyncHook(plugins, "configureResolvedConfig", resolvedConfig, context),
2209
+ configureEngine: (plugins, engine) => execAsyncHook(plugins, "configureEngine", engine, context),
2210
+ transformSelectors: (plugins, selectors) => execAsyncHook(plugins, "transformSelectors", selectors, context),
2211
+ transformStyleItems: (plugins, styleItems) => execAsyncHook(plugins, "transformStyleItems", styleItems, context),
2212
+ transformStyleDefinitions: (plugins, styleDefinitions) => execAsyncHook(plugins, "transformStyleDefinitions", styleDefinitions, context),
2213
+ preflightUpdated: (plugins) => execSyncHook(plugins, "preflightUpdated", void 0, context),
2214
+ atomicStyleAdded: (plugins, atomicStyle) => execSyncHook(plugins, "atomicStyleAdded", atomicStyle, context),
2215
+ autocompleteConfigUpdated: (plugins) => execSyncHook(plugins, "autocompleteConfigUpdated", void 0, context)
2216
+ };
2217
+ }
2218
+ createEngineHooks(DEFAULT_PLUGIN_CONTEXT);
2209
2219
  const orderMap = new Map([
2210
2220
  [void 0, 1],
2211
2221
  ["pre", 0],
2212
2222
  ["post", 2]
2213
2223
  ]);
2214
2224
  /**
2215
- * Sorts an array of plugins by their `order` property: `'pre'` first, default in the middle, `'post'` last.
2216
- * @internal
2217
- *
2218
- * @param plugins - The unordered array of engine plugins.
2219
- * @returns A new array sorted by execution order.
2220
- *
2221
- * @remarks The original array is not mutated. Plugins with the same order retain their relative insertion order (stable sort).
2225
+ * Sorts plugins by `pre`, default, then `post` order without mutating the input.
2222
2226
  *
2223
- * @example
2224
- * ```ts
2225
- * const ordered = resolvePlugins([postPlugin, prePlugin, normalPlugin])
2226
- * // [prePlugin, normalPlugin, postPlugin]
2227
- * ```
2227
+ * @internal
2228
2228
  */
2229
2229
  function resolvePlugins(plugins) {
2230
2230
  return [...plugins].sort((a, b) => orderMap.get(a.order) - orderMap.get(b.order));
2231
2231
  }
2232
2232
  /* c8 ignore start */
2233
2233
  /**
2234
- * Identity helper that returns the plugin object as-is, providing TypeScript type inference for plugin definitions.
2235
- *
2236
- * @param plugin - The engine plugin definition.
2237
- * @returns The same plugin object, unchanged.
2238
- *
2239
- * @remarks This is a compile-time-only helper; it has no runtime effect. Using it ensures type checking and IDE autocompletion for hook names and payloads.
2234
+ * Identity helper that provides type inference for an engine plugin definition.
2240
2235
  *
2241
- * @example
2242
- * ```ts
2243
- * export default defineEnginePlugin({
2244
- * name: 'my-plugin',
2245
- * configureRawConfig: (config) => ({ ...config, important: { default: true } }),
2246
- * })
2247
- * ```
2236
+ * @param plugin - The plugin definition to return unchanged.
2237
+ * @returns The same plugin instance.
2248
2238
  */
2249
2239
  function defineEnginePlugin(plugin) {
2250
2240
  return plugin;
@@ -2436,6 +2426,19 @@ var AbstractResolver = class {
2436
2426
  dynamicRulesMap = /* @__PURE__ */ new Map();
2437
2427
  /** Callback invoked after a successful resolution, receiving the input string, rule type, and result. */
2438
2428
  onResolved = () => {};
2429
+ constructor(onDiagnostic = noopDiagnosticHandler) {
2430
+ this.onDiagnostic = onDiagnostic;
2431
+ }
2432
+ /** Reports through the host handler, falling back to the optional logger only for standalone resolvers. */
2433
+ reportDiagnostic(diagnostic) {
2434
+ if (this.onDiagnostic === noopDiagnosticHandler) {
2435
+ const args = diagnostic.cause == null ? [] : [diagnostic.cause];
2436
+ if (diagnostic.level === "error") log.error(diagnostic.message, ...args);
2437
+ else log.warn(diagnostic.message, ...args);
2438
+ return;
2439
+ }
2440
+ emitDiagnostic(this.onDiagnostic, diagnostic);
2441
+ }
2439
2442
  get staticRules() {
2440
2443
  return [...this.staticRulesMap.values()];
2441
2444
  }
@@ -2483,7 +2486,12 @@ var AbstractResolver = class {
2483
2486
  removeStaticRule(key) {
2484
2487
  const rule = this.staticRulesMap.get(key);
2485
2488
  if (rule == null) {
2486
- log.warn(`Static rule not found for removal: ${key}`);
2489
+ const message = `Static rule not found for removal: ${key}`;
2490
+ this.reportDiagnostic({
2491
+ level: "warning",
2492
+ code: "resolver-static-rule-not-found",
2493
+ message
2494
+ });
2487
2495
  return this;
2488
2496
  }
2489
2497
  log.debug(`Removing static rule: ${key}`);
@@ -2542,7 +2550,12 @@ var AbstractResolver = class {
2542
2550
  */
2543
2551
  removeDynamicRule(key) {
2544
2552
  if (this.dynamicRulesMap.get(key) == null) {
2545
- log.warn(`Dynamic rule not found for removal: ${key}`);
2553
+ const message = `Dynamic rule not found for removal: ${key}`;
2554
+ this.reportDiagnostic({
2555
+ level: "warning",
2556
+ code: "resolver-dynamic-rule-not-found",
2557
+ message
2558
+ });
2546
2559
  return this;
2547
2560
  }
2548
2561
  log.debug(`Removing dynamic rule: ${key}`);
@@ -2663,12 +2676,25 @@ var RecursiveResolver = class extends AbstractResolver {
2663
2676
  async resolve(string, _visited) {
2664
2677
  const visited = _visited ?? /* @__PURE__ */ new Set();
2665
2678
  if (visited.has(string)) {
2666
- log.warn(`Circular reference detected for "${string}", returning as-is`);
2679
+ const message = `Circular reference detected for "${string}", returning as-is`;
2680
+ this.reportDiagnostic({
2681
+ level: "warning",
2682
+ code: "resolver-circular-reference",
2683
+ message
2684
+ });
2685
+ log.warn(message);
2667
2686
  return [string];
2668
2687
  }
2669
2688
  visited.add(string);
2670
2689
  const resolved = await this._resolve(string).catch((error) => {
2671
- log.warn(`Failed to resolve "${string}": ${error.message}`, error);
2690
+ const message = `Failed to resolve "${string}": ${error instanceof Error ? error.message : String(error)}`;
2691
+ this.reportDiagnostic({
2692
+ level: "warning",
2693
+ code: "resolver-resolution-error",
2694
+ message,
2695
+ cause: error
2696
+ });
2697
+ log.warn(message, error);
2672
2698
  });
2673
2699
  if (resolved == null) return [string];
2674
2700
  const result = [];
@@ -2763,7 +2789,7 @@ function selectors() {
2763
2789
  configureEngine(_engine) {
2764
2790
  engine = _engine;
2765
2791
  engine.selectors = {
2766
- resolver: new SelectorResolver(),
2792
+ resolver: new SelectorResolver(engine.onDiagnostic),
2767
2793
  add: (...list) => {
2768
2794
  list.forEach((config) => {
2769
2795
  const resolved = resolveSelectorConfig(config);
@@ -2832,7 +2858,7 @@ function shortcuts() {
2832
2858
  configureEngine(_engine) {
2833
2859
  engine = _engine;
2834
2860
  engine.shortcuts = {
2835
- resolver: new ShortcutResolver(),
2861
+ resolver: new ShortcutResolver(engine.onDiagnostic),
2836
2862
  add: (...list) => {
2837
2863
  list.forEach((config) => {
2838
2864
  const resolved = resolveShortcutConfig(config);
@@ -2911,8 +2937,11 @@ function variables() {
2911
2937
  let safeSet;
2912
2938
  return defineEnginePlugin({
2913
2939
  name: "core:variables",
2914
- rawConfigConfigured(config) {
2915
- resolveVariables = createResolveVariablesFn({ pruneUnused: config.variables?.pruneUnused });
2940
+ rawConfigConfigured(config, context) {
2941
+ resolveVariables = createResolveVariablesFn({
2942
+ pruneUnused: config.variables?.pruneUnused,
2943
+ onDiagnostic: context?.onDiagnostic
2944
+ });
2916
2945
  rawVariables = normalizeVariablesConfig(config.variables);
2917
2946
  safeSet = new Set(config.variables?.safeList ?? []);
2918
2947
  },
@@ -3001,7 +3030,7 @@ function mergeVariablesDefinition(target, source) {
3001
3030
  }
3002
3031
  return target;
3003
3032
  }
3004
- function createResolveVariablesFn({ pruneUnused: defaultPruneUnused = true } = {}) {
3033
+ function createResolveVariablesFn({ pruneUnused: defaultPruneUnused = true, onDiagnostic = noopDiagnosticHandler } = {}) {
3005
3034
  function _resolveVariables(variables, levels, result) {
3006
3035
  for (const [key, value] of Object.entries(variables)) if (key.startsWith("--")) {
3007
3036
  const { value: varValue, autocomplete = {}, pruneUnused = defaultPruneUnused } = isPlainObjectRecord(value) ? value : { value };
@@ -3017,7 +3046,13 @@ function createResolveVariablesFn({ pruneUnused: defaultPruneUnused = true } = {
3017
3046
  });
3018
3047
  } else {
3019
3048
  if (!isPlainObjectRecord(value)) {
3020
- log.warn(`Invalid variables scope for selector "${key}". Expected a nested object, received ${typeof value}. Skipping.`);
3049
+ const message = `Invalid variables scope for selector "${key}". Expected a nested object, received ${typeof value}. Skipping.`;
3050
+ if (onDiagnostic === noopDiagnosticHandler) log.warn(message);
3051
+ else emitDiagnostic(onDiagnostic, {
3052
+ level: "warning",
3053
+ code: "variables-invalid-scope",
3054
+ message
3055
+ });
3021
3056
  continue;
3022
3057
  }
3023
3058
  _resolveVariables(value, [...levels, key], result);
@@ -3147,6 +3182,7 @@ const DEFAULT_LAYERS = {
3147
3182
  * Creates and initializes a PikaCSS engine with the given configuration.
3148
3183
  *
3149
3184
  * @param config - The engine configuration, including plugins, selectors, shortcuts, variables, keyframes, preflights, and layer settings.
3185
+ * @param options - Runtime-only host capabilities, including the instance-scoped diagnostic handler.
3150
3186
  * @returns A fully initialized `Engine` instance.
3151
3187
  *
3152
3188
  * @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.
@@ -3156,7 +3192,10 @@ const DEFAULT_LAYERS = {
3156
3192
  * const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
3157
3193
  * ```
3158
3194
  */
3159
- async function createEngine(config = {}) {
3195
+ async function createEngine(config = {}, options = {}) {
3196
+ const hostOnDiagnostic = options.onDiagnostic ?? noopDiagnosticHandler;
3197
+ const onDiagnostic = (diagnostic) => emitDiagnostic(hostOnDiagnostic, diagnostic);
3198
+ const pluginHooks = createEngineHooks({ onDiagnostic });
3160
3199
  log.debug("Creating engine with config:", config);
3161
3200
  const corePlugins = [
3162
3201
  variables(),
@@ -3172,18 +3211,18 @@ async function createEngine(config = {}) {
3172
3211
  plugins
3173
3212
  };
3174
3213
  log.debug(`Total plugins resolved: ${plugins.length}`);
3175
- config = await hooks.configureRawConfig(config.plugins, config);
3176
- hooks.rawConfigConfigured(resolvePlugins(config.plugins), config);
3214
+ config = await pluginHooks.configureRawConfig(config.plugins, config);
3215
+ pluginHooks.rawConfigConfigured(resolvePlugins(config.plugins), config);
3177
3216
  let resolvedConfig = await resolveEngineConfig(config);
3178
3217
  log.debug("Engine config resolved with prefix:", resolvedConfig.prefix);
3179
- resolvedConfig = await hooks.configureResolvedConfig(resolvedConfig.plugins, resolvedConfig);
3180
- let engine = new Engine(resolvedConfig);
3218
+ resolvedConfig = await pluginHooks.configureResolvedConfig(resolvedConfig.plugins, resolvedConfig);
3219
+ let engine = new Engine(resolvedConfig, hostOnDiagnostic, pluginHooks);
3181
3220
  engine.appendAutocomplete({
3182
3221
  extraProperties: "__layer",
3183
3222
  properties: { __layer: "Autocomplete['Layer']" }
3184
3223
  });
3185
3224
  log.debug("Engine instance created");
3186
- engine = await hooks.configureEngine(engine.config.plugins, engine);
3225
+ engine = await pluginHooks.configureEngine(engine.config.plugins, engine);
3187
3226
  log.debug("Engine initialized successfully");
3188
3227
  return engine;
3189
3228
  }
@@ -3202,8 +3241,10 @@ async function createEngine(config = {}) {
3202
3241
  var Engine = class {
3203
3242
  /** The fully resolved engine configuration. */
3204
3243
  config;
3205
- /** Reference to the plugin hook dispatcher for invoking lifecycle hooks. */
3206
- pluginHooks = hooks;
3244
+ /** Instance-scoped diagnostic handler supplied by the host. */
3245
+ onDiagnostic;
3246
+ /** Reference to the instance-scoped plugin hook dispatcher. */
3247
+ pluginHooks;
3207
3248
  /** The extraction function that decomposes style definitions into atomic style contents. */
3208
3249
  extract;
3209
3250
  /** The engine's runtime store holding registered atomic styles and their ID mappings. */
@@ -3226,16 +3267,27 @@ var Engine = class {
3226
3267
  * const engine = new Engine(resolvedConfig)
3227
3268
  * ```
3228
3269
  */
3229
- constructor(config) {
3270
+ constructor(config, onDiagnostic = noopDiagnosticHandler, pluginHooks) {
3271
+ const safeOnDiagnostic = (diagnostic) => emitDiagnostic(onDiagnostic, diagnostic);
3230
3272
  this.config = config;
3273
+ this.onDiagnostic = safeOnDiagnostic;
3274
+ this.pluginHooks = pluginHooks ?? createEngineHooks({ onDiagnostic: safeOnDiagnostic });
3231
3275
  this.extract = createExtractFn({
3232
3276
  defaultSelector: this.config.defaultSelector,
3233
- transformSelectors: (selectors) => hooks.transformSelectors(this.config.plugins, selectors),
3234
- transformStyleItems: (styleItems) => hooks.transformStyleItems(this.config.plugins, styleItems),
3235
- transformStyleDefinitions: (styleDefinitions) => hooks.transformStyleDefinitions(this.config.plugins, styleDefinitions)
3277
+ transformSelectors: (selectors) => this.pluginHooks.transformSelectors(this.config.plugins, selectors),
3278
+ transformStyleItems: (styleItems) => this.pluginHooks.transformStyleItems(this.config.plugins, styleItems),
3279
+ transformStyleDefinitions: (styleDefinitions) => this.pluginHooks.transformStyleDefinitions(this.config.plugins, styleDefinitions)
3236
3280
  });
3237
3281
  }
3238
3282
  /**
3283
+ * Reports a structured diagnostic to this engine instance's host handler.
3284
+ *
3285
+ * @param diagnostic - The structured warning or error to deliver.
3286
+ */
3287
+ reportDiagnostic(diagnostic) {
3288
+ emitDiagnostic(this.onDiagnostic, diagnostic);
3289
+ }
3290
+ /**
3239
3291
  * Invokes a preflight function, memoizing the result in the given render-pass context.
3240
3292
  *
3241
3293
  * @param fn - The preflight function to invoke.
@@ -3286,7 +3338,7 @@ var Engine = class {
3286
3338
  * ```
3287
3339
  */
3288
3340
  notifyPreflightUpdated() {
3289
- hooks.preflightUpdated(this.config.plugins);
3341
+ this.pluginHooks.preflightUpdated(this.config.plugins);
3290
3342
  }
3291
3343
  /**
3292
3344
  * Fires the `atomicStyleAdded` hook to notify plugins that a new atomic style was registered.
@@ -3301,7 +3353,7 @@ var Engine = class {
3301
3353
  * ```
3302
3354
  */
3303
3355
  notifyAtomicStyleAdded(atomicStyle) {
3304
- hooks.atomicStyleAdded(this.config.plugins, atomicStyle);
3356
+ this.pluginHooks.atomicStyleAdded(this.config.plugins, atomicStyle);
3305
3357
  }
3306
3358
  /**
3307
3359
  * Fires the `autocompleteConfigUpdated` hook to notify plugins that autocomplete entries changed.
@@ -3315,7 +3367,7 @@ var Engine = class {
3315
3367
  * ```
3316
3368
  */
3317
3369
  notifyAutocompleteConfigUpdated() {
3318
- hooks.autocompleteConfigUpdated(this.config.plugins);
3370
+ this.pluginHooks.autocompleteConfigUpdated(this.config.plugins);
3319
3371
  }
3320
3372
  /**
3321
3373
  * Merges an autocomplete contribution into the resolved autocomplete config.
@@ -3385,7 +3437,7 @@ var Engine = class {
3385
3437
  log.debug(`Processing ${itemList.length} style items`);
3386
3438
  const { unknown, contents } = await resolveStyleItemList({
3387
3439
  itemList,
3388
- transformStyleItems: (styleItems) => hooks.transformStyleItems(this.config.plugins, styleItems),
3440
+ transformStyleItems: (styleItems) => this.pluginHooks.transformStyleItems(this.config.plugins, styleItems),
3389
3441
  extractStyleDefinition: (styleDefinition) => this.extract(styleDefinition)
3390
3442
  });
3391
3443
  const resolvedIds = [];
@@ -3484,6 +3536,7 @@ var Engine = class {
3484
3536
  const { atomicStyleIds = null, isPreview = false } = options;
3485
3537
  const atomicStyles = atomicStyleIds == null ? [...this.store.atomicStyles.values()] : atomicStyleIds.map((id) => this.store.atomicStyles.get(id)).filter(isNotNullish);
3486
3538
  log.debug(`Rendering ${atomicStyles.length} atomic styles (preview: ${isPreview})`);
3539
+ reportUnknownAtomicStyleLayers(this, atomicStyles);
3487
3540
  return renderAtomicStyles({
3488
3541
  atomicStyles,
3489
3542
  isPreview,
@@ -3601,6 +3654,20 @@ function splitLayerSelector(selector) {
3601
3654
  function prependLayerSelector(selector, layer) {
3602
3655
  return [`${LAYER_SELECTOR_PREFIX}${layer}`, ...selector];
3603
3656
  }
3657
+ function reportUnknownAtomicStyleLayers(engine, styles) {
3658
+ const knownLayers = new Set(Object.keys(engine.config.layers));
3659
+ const reportedLayers = /* @__PURE__ */ new Set();
3660
+ for (const style of styles) {
3661
+ const { layer } = splitLayerSelector(style.content.selector);
3662
+ if (layer == null || knownLayers.has(layer) || reportedLayers.has(layer)) continue;
3663
+ reportedLayers.add(layer);
3664
+ engine.reportDiagnostic({
3665
+ level: "warning",
3666
+ code: "atomic-style-unknown-layer",
3667
+ message: `Unknown layer "${layer}" encountered in atomic style; falling back to unlayered output.`
3668
+ });
3669
+ }
3670
+ }
3604
3671
  function groupAtomicStylesByLayer({ styles, layerOrder, defaultUtilitiesLayer }) {
3605
3672
  const unlayeredStyles = [];
3606
3673
  const layerGroups = new Map(layerOrder.map((name) => [name, []]));
@@ -3613,7 +3680,6 @@ function groupAtomicStylesByLayer({ styles, layerOrder, defaultUtilitiesLayer })
3613
3680
  continue;
3614
3681
  }
3615
3682
  if (layer != null) {
3616
- log.warn(`Unknown layer "${layer}" encountered in atomic style; falling back to unlayered output.`);
3617
3683
  unlayeredStyles.push(style);
3618
3684
  continue;
3619
3685
  }
@@ -3859,7 +3925,7 @@ function renderAtomicStyles(payload) {
3859
3925
  * @param options.blocks - Optional accumulator map reused during recursive descent.
3860
3926
  * @returns The accumulated `CSSStyleBlocks` map.
3861
3927
  *
3862
- * @remarks Each key in the definition is either a CSS property (when its value is a property value) or a nested selector scope (when its value is an object). Selector keys are expanded through `hooks.transformSelectors`. The resulting blocks map is consumable by `renderCSSStyleBlocks`.
3928
+ * @remarks Each key in the definition is either a CSS property (when its value is a property value) or a nested selector scope (when its value is an object). Selector keys are expanded through the engine-local `pluginHooks.transformSelectors` dispatcher. The resulting blocks map is consumable by `renderCSSStyleBlocks`.
3863
3929
  *
3864
3930
  * @example
3865
3931
  * ```ts
@@ -3870,7 +3936,7 @@ async function _renderPreflightDefinition({ engine, preflightDefinition, blocks
3870
3936
  for (const [selector, propertiesOrDefinition] of Object.entries(preflightDefinition)) {
3871
3937
  if (propertiesOrDefinition == null) continue;
3872
3938
  const selectors = normalizeSelectors({
3873
- selectors: await hooks.transformSelectors(engine.config.plugins, [selector]),
3939
+ selectors: await engine.pluginHooks.transformSelectors(engine.config.plugins, [selector]),
3874
3940
  defaultSelector: ""
3875
3941
  }).filter(Boolean);
3876
3942
  if (selectors.length === 0) continue;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pikacss/core",
3
3
  "type": "module",
4
- "version": "0.0.57",
4
+ "version": "0.0.58",
5
5
  "author": "DevilTea <ch19980814@gmail.com>",
6
6
  "license": "MIT",
7
7
  "homepage": "https://pikacss.github.io",
@@ -36,9 +36,6 @@
36
36
  "files": [
37
37
  "dist"
38
38
  ],
39
- "engines": {
40
- "node": ">=22"
41
- },
42
39
  "scripts": {
43
40
  "build": "tsdown",
44
41
  "build:watch": "tsdown --watch",