windrunner 1.0.2 → 1.1.0

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.ts CHANGED
@@ -1,33 +1,62 @@
1
- export interface WindrunnerOptions {
2
- id?: string;
3
- autoStart?: boolean;
4
- preflight?: boolean;
5
- compatMode?: "none" | "full";
6
- compatStyleId?: string;
7
- compatGenerateCss?: (options: Record<string, any>) => string;
8
- theme?: Record<string, any>;
9
- onReady?: () => void;
10
- [key: string]: any;
11
- }
12
-
13
- export interface Runtime {
14
- processClassName(className: string): string | undefined;
15
- processClassList(classList: string | string[] | ArrayLike<string>): string[];
16
- processElement(el: Element | null): void;
17
- scan(root?: Document | Element): void;
18
- observe(root?: Element): void;
19
- flush(): void;
20
- start(): void;
21
- disconnect(): void;
22
- isCompatLoaded(): boolean;
23
- getCacheSize(): number;
24
- getInsertedRuleCount(): number;
25
- }
26
-
27
- export function createWindrunner(options?: WindrunnerOptions): Runtime;
28
- export function compileRuntimeClassNameWithContext(className: string, context: any): string;
29
- export function compileClass(className: string, options?: any): string;
30
- export function parseClass(className: string, screens?: Record<string,string>, containers?: Record<string,string>): { original: string; baseToken: string; variants: string[]; breakpoint: string | null; containerBreakpoint: string | null; important: boolean; starting: boolean } | null;
31
-
32
- declare function windrunner(options?: WindrunnerOptions): Runtime;
33
- export default windrunner;
1
+ // ─── Plugin Types ─────────────────────────────────────────────────────────────
2
+
3
+ export interface PluginAPI {
4
+ addUtility(pattern: string | RegExp, handler: string | ((match: RegExpMatchArray, theme: any) => string)): void;
5
+ addUtilities(utilities: Record<string, string | ((match: RegExpMatchArray, theme: any) => string)>): void;
6
+ addVariant(name: string, handler: (selector: string) => string): void;
7
+ addVariants(variants: Record<string, (selector: string) => string>): void;
8
+ theme(key?: string): any;
9
+ config(): any;
10
+ }
11
+
12
+ export interface Plugin {
13
+ __isWindrunnerPlugin: true;
14
+ handler: (api: PluginAPI) => void;
15
+ }
16
+
17
+ export function plugin(handler: (api: PluginAPI) => void): Plugin;
18
+
19
+ export function defineUtilities(definitions: Record<string, string | Record<string, string>>): Record<string, string>;
20
+
21
+ export function defineResponsiveUtilities(
22
+ base: string,
23
+ values: Record<string, string>,
24
+ toDeclaration: (key: string, value: string) => string
25
+ ): Record<string, string>;
26
+
27
+ // ─── Runtime Options with Plugins ─────────────────────────────────────────────
28
+
29
+ export interface WindrunnerOptions {
30
+ id?: string;
31
+ autoStart?: boolean;
32
+ preflight?: boolean;
33
+ compatMode?: "none" | "full";
34
+ compatStyleId?: string;
35
+ compatGenerateCss?: (options: Record<string, any>) => string;
36
+ theme?: Record<string, any>;
37
+ plugins?: Plugin[];
38
+ onReady?: () => void;
39
+ [key: string]: any;
40
+ }
41
+
42
+ export interface Runtime {
43
+ processClassName(className: string): string | undefined;
44
+ processClassList(classList: string | string[] | ArrayLike<string>): string[];
45
+ processElement(el: Element | null): void;
46
+ scan(root?: Document | Element): void;
47
+ observe(root?: Element): void;
48
+ flush(): void;
49
+ start(): void;
50
+ disconnect(): void;
51
+ isCompatLoaded(): boolean;
52
+ getCacheSize(): number;
53
+ getInsertedRuleCount(): number;
54
+ }
55
+
56
+ export function createWindrunner(options?: WindrunnerOptions): Runtime;
57
+ export function compileRuntimeClassNameWithContext(className: string, context: any): string;
58
+ export function compileClass(className: string, options?: any): string;
59
+ export function parseClass(className: string, screens?: Record<string,string>, containers?: Record<string,string>): { original: string; baseToken: string; variants: string[]; breakpoint: string | null; containerBreakpoint: string | null; important: boolean; starting: boolean } | null;
60
+
61
+ declare function windrunner(options?: WindrunnerOptions): Runtime;
62
+ export default windrunner;
package/dist/index.esm.js CHANGED
@@ -3260,13 +3260,160 @@ function buildDivideDeclaration(baseToken, theme2) {
3260
3260
  return void 0;
3261
3261
  }
3262
3262
 
3263
+ // src/plugins.js
3264
+ var PluginRegistry = class {
3265
+ constructor() {
3266
+ this.utilities = /* @__PURE__ */ new Map();
3267
+ this.variants = /* @__PURE__ */ new Map();
3268
+ }
3269
+ /**
3270
+ * Register a custom utility
3271
+ * @param {string|RegExp} pattern - Class name pattern (e.g., "glass" or /^glass-(.+)$/)
3272
+ * @param {Function|string} handler - Function that returns CSS or CSS string
3273
+ */
3274
+ addUtility(pattern, handler) {
3275
+ this.utilities.set(pattern, handler);
3276
+ }
3277
+ /**
3278
+ * Register multiple utilities at once
3279
+ * @param {Object} utilities - Object mapping patterns to handlers
3280
+ */
3281
+ addUtilities(utilities) {
3282
+ Object.entries(utilities).forEach(([pattern, handler]) => {
3283
+ this.addUtility(pattern, handler);
3284
+ });
3285
+ }
3286
+ /**
3287
+ * Register a custom variant
3288
+ * @param {string} name - Variant name (e.g., "parent-hover")
3289
+ * @param {Function} handler - Function that transforms selector
3290
+ */
3291
+ addVariant(name, handler) {
3292
+ this.variants.set(name, handler);
3293
+ }
3294
+ /**
3295
+ * Register multiple variants at once
3296
+ * @param {Object} variants - Object mapping names to handlers
3297
+ */
3298
+ addVariants(variants2) {
3299
+ Object.entries(variants2).forEach(([name, handler]) => {
3300
+ this.addVariant(name, handler);
3301
+ });
3302
+ }
3303
+ /**
3304
+ * Check if a token matches any custom utility pattern
3305
+ * @param {string} token - The base token to match
3306
+ * @returns {Object|null} - { handler, match } or null
3307
+ */
3308
+ matchUtility(token) {
3309
+ for (const [pattern, handler] of this.utilities) {
3310
+ if (pattern instanceof RegExp) {
3311
+ const match = pattern.exec(token);
3312
+ if (match) {
3313
+ return { handler, match };
3314
+ }
3315
+ } else if (pattern === token) {
3316
+ return { handler, match: [token] };
3317
+ }
3318
+ }
3319
+ return null;
3320
+ }
3321
+ /**
3322
+ * Check if a variant name matches any custom variant
3323
+ * @param {string} variantName - The variant to check
3324
+ * @returns {Function|null} - Handler function or null
3325
+ */
3326
+ matchVariant(variantName) {
3327
+ return this.variants.get(variantName) || null;
3328
+ }
3329
+ /**
3330
+ * Get all registered utility patterns (for debugging)
3331
+ */
3332
+ getUtilities() {
3333
+ return Array.from(this.utilities.keys());
3334
+ }
3335
+ /**
3336
+ * Get all registered variant names (for debugging)
3337
+ */
3338
+ getVariants() {
3339
+ return Array.from(this.variants.keys());
3340
+ }
3341
+ /**
3342
+ * Clear all registered plugins
3343
+ */
3344
+ clear() {
3345
+ this.utilities.clear();
3346
+ this.variants.clear();
3347
+ }
3348
+ };
3349
+ function plugin(handler) {
3350
+ if (typeof handler !== "function") {
3351
+ throw new Error("Plugin handler must be a function");
3352
+ }
3353
+ return {
3354
+ __isWindrunnerPlugin: true,
3355
+ handler
3356
+ };
3357
+ }
3358
+ function isPlugin(obj) {
3359
+ return obj && obj.__isWindrunnerPlugin === true && typeof obj.handler === "function";
3360
+ }
3361
+ function defineUtilities(definitions) {
3362
+ const utilities = {};
3363
+ Object.entries(definitions).forEach(([className, css]) => {
3364
+ utilities[className] = typeof css === "string" ? css : objectToCss(css);
3365
+ });
3366
+ return utilities;
3367
+ }
3368
+ function defineResponsiveUtilities(base, values, toDeclaration) {
3369
+ const utilities = {};
3370
+ Object.entries(values).forEach(([key, value]) => {
3371
+ const className = key === "DEFAULT" ? base : `${base}-${key}`;
3372
+ utilities[className] = toDeclaration(key, value);
3373
+ });
3374
+ return utilities;
3375
+ }
3376
+ function objectToCss(obj) {
3377
+ return Object.entries(obj).map(([prop, value]) => `${prop}: ${value};`).join(" ");
3378
+ }
3379
+
3263
3380
  // src/compiler.js
3264
- function compileBaseToken(baseToken, theme2) {
3381
+ function compileBaseToken(baseToken, theme2, pluginRegistry) {
3382
+ if (pluginRegistry) {
3383
+ const pluginMatch = pluginRegistry.matchUtility(baseToken);
3384
+ if (pluginMatch) {
3385
+ const { handler, match } = pluginMatch;
3386
+ try {
3387
+ if (typeof handler === "function") {
3388
+ const result = handler(match, theme2);
3389
+ if (result) return result;
3390
+ } else if (typeof handler === "string") {
3391
+ return handler;
3392
+ }
3393
+ } catch (error) {
3394
+ console.warn(`[Windrunner] Plugin utility handler error for "${baseToken}":`, error);
3395
+ }
3396
+ }
3397
+ }
3265
3398
  return buildLayoutDeclaration(baseToken, theme2) || buildPositionInsetDeclaration(baseToken, theme2) || buildSpacingDeclaration(baseToken, theme2) || buildSpaceBetweenDeclaration(baseToken, theme2) || buildGapDeclaration(baseToken, theme2) || buildDimensionDeclaration(baseToken, theme2) || buildFlexGridDeclaration(baseToken, theme2) || buildBorderDeclaration(baseToken, theme2) || buildBorderRadiusDeclaration(baseToken, theme2) || buildBorderSpacingDeclaration(baseToken, theme2) || buildDivideDeclaration(baseToken, theme2) || buildOpacityDeclaration(baseToken, theme2) || buildShadowDeclaration(baseToken, theme2) || buildInsetShadowDeclaration(baseToken, theme2) || buildInsetRingDeclaration(baseToken, theme2) || buildRingDeclaration(baseToken, theme2) || buildRingOffsetDeclaration(baseToken, theme2) || buildTextShadowDeclaration(baseToken, theme2) || buildTransitionDeclaration(baseToken) || buildTransformDeclaration(baseToken, theme2) || buildFilterDeclaration(baseToken, theme2) || buildBackgroundDeclaration(baseToken, theme2) || buildGradientDeclaration(baseToken, theme2) || buildColorDeclaration(baseToken, theme2) || buildTypographyDeclaration(baseToken, theme2) || buildBlendingDeclaration(baseToken) || buildInteractivityDeclaration(baseToken, theme2) || buildAnimationDeclaration(baseToken) || buildMaskDeclaration(baseToken) || buildContainerQueryDeclaration(baseToken) || buildScrollSnapDeclaration(baseToken) || buildAccessibilityDeclaration(baseToken) || buildZoomDeclaration(baseToken, theme2) || buildForcedColorDeclaration(baseToken);
3266
3399
  }
3267
- function applyVariants(selector, variants2) {
3400
+ function applyVariants(selector, variants2, pluginRegistry) {
3268
3401
  let currentSelector = selector;
3269
3402
  for (const variant of variants2) {
3403
+ if (pluginRegistry) {
3404
+ const customHandler = pluginRegistry.matchVariant(variant);
3405
+ if (customHandler) {
3406
+ try {
3407
+ const result = customHandler(currentSelector);
3408
+ if (result) {
3409
+ currentSelector = result;
3410
+ continue;
3411
+ }
3412
+ } catch (error) {
3413
+ console.warn(`[Windrunner] Plugin variant handler error for "${variant}":`, error);
3414
+ }
3415
+ }
3416
+ }
3270
3417
  switch (variant) {
3271
3418
  case "dark":
3272
3419
  currentSelector = `.dark ${currentSelector}`;
@@ -3307,15 +3454,66 @@ function applyVariants(selector, variants2) {
3307
3454
  case "invalid":
3308
3455
  currentSelector = `${currentSelector}:invalid`;
3309
3456
  break;
3457
+ case "target":
3458
+ currentSelector = `${currentSelector}:target`;
3459
+ break;
3460
+ case "enabled":
3461
+ currentSelector = `${currentSelector}:enabled`;
3462
+ break;
3463
+ case "default":
3464
+ currentSelector = `${currentSelector}:default`;
3465
+ break;
3466
+ case "optional":
3467
+ currentSelector = `${currentSelector}:optional`;
3468
+ break;
3469
+ case "user-valid":
3470
+ currentSelector = `${currentSelector}:user-valid`;
3471
+ break;
3472
+ case "user-invalid":
3473
+ currentSelector = `${currentSelector}:user-invalid`;
3474
+ break;
3475
+ case "in-range":
3476
+ currentSelector = `${currentSelector}:in-range`;
3477
+ break;
3478
+ case "out-of-range":
3479
+ currentSelector = `${currentSelector}:out-of-range`;
3480
+ break;
3481
+ case "placeholder-shown":
3482
+ currentSelector = `${currentSelector}:placeholder-shown`;
3483
+ break;
3484
+ case "autofill":
3485
+ currentSelector = `${currentSelector}:autofill`;
3486
+ break;
3487
+ case "details-content":
3488
+ currentSelector = `${currentSelector}:details-content`;
3489
+ break;
3310
3490
  case "placeholder":
3311
3491
  currentSelector = `${currentSelector}::placeholder`;
3312
3492
  break;
3493
+ case "backdrop":
3494
+ currentSelector = `${currentSelector}::backdrop`;
3495
+ break;
3313
3496
  case "before":
3314
3497
  currentSelector = `${currentSelector}::before`;
3315
3498
  break;
3316
3499
  case "after":
3317
3500
  currentSelector = `${currentSelector}::after`;
3318
3501
  break;
3502
+ case "first-letter":
3503
+ currentSelector = `${currentSelector}::first-letter`;
3504
+ break;
3505
+ case "first-line":
3506
+ currentSelector = `${currentSelector}::first-line`;
3507
+ break;
3508
+ case "marker":
3509
+ currentSelector = `${currentSelector}::marker`;
3510
+ break;
3511
+ case "selection":
3512
+ currentSelector = `${currentSelector}::selection`;
3513
+ break;
3514
+ case "file":
3515
+ currentSelector = `${currentSelector}::file-selector-button`;
3516
+ break;
3319
3517
  case "first":
3320
3518
  currentSelector = `${currentSelector}:first-child`;
3321
3519
  break;
@@ -3396,11 +3594,40 @@ function applyVariants(selector, variants2) {
3396
3594
  }
3397
3595
  function resolveRuntimeContext(options = {}) {
3398
3596
  const config = getConfigOptions(options, []);
3597
+ const pluginRegistry = new PluginRegistry();
3598
+ if (options.plugins && Array.isArray(options.plugins)) {
3599
+ options.plugins.forEach((pluginDef) => {
3600
+ if (isPlugin(pluginDef)) {
3601
+ try {
3602
+ pluginDef.handler({
3603
+ addUtility: (pattern, handler) => pluginRegistry.addUtility(pattern, handler),
3604
+ addUtilities: (utilities) => pluginRegistry.addUtilities(utilities),
3605
+ addVariant: (name, handler) => pluginRegistry.addVariant(name, handler),
3606
+ addVariants: (variants2) => pluginRegistry.addVariants(variants2),
3607
+ theme: (key) => {
3608
+ if (!key) return config.theme || {};
3609
+ const keys = key.split(".");
3610
+ let value = config.theme || {};
3611
+ for (const k of keys) {
3612
+ value = value[k];
3613
+ if (value === void 0) break;
3614
+ }
3615
+ return value;
3616
+ },
3617
+ config: () => config
3618
+ });
3619
+ } catch (error) {
3620
+ console.error("[Windrunner] Plugin initialization error:", error);
3621
+ }
3622
+ }
3623
+ });
3624
+ }
3399
3625
  return {
3400
3626
  config,
3401
3627
  theme: config.theme || {},
3402
3628
  screens: config.theme && config.theme.screens || config.screens || {},
3403
- containers: config.theme && config.theme.containers || config.containers || {}
3629
+ containers: config.theme && config.theme.containers || config.containers || {},
3630
+ plugins: pluginRegistry
3404
3631
  };
3405
3632
  }
3406
3633
  function getBaseTailwindOptions(options = {}) {
@@ -3444,10 +3671,10 @@ function parseClass(className, screens = {}, containers = {}) {
3444
3671
  function compileRuntimeClassNameWithContext(className, context) {
3445
3672
  const parsed = parseClass(className, context.screens, context.containers);
3446
3673
  if (!parsed) return "";
3447
- const declaration = compileBaseToken(parsed.baseToken, context.theme);
3674
+ const declaration = compileBaseToken(parsed.baseToken, context.theme, context.plugins);
3448
3675
  if (!declaration) return "";
3449
3676
  const selector = `.${escapeCssIdentifier(parsed.original)}`;
3450
- const variantSelector = applyVariants(selector, parsed.variants);
3677
+ const variantSelector = applyVariants(selector, parsed.variants, context.plugins);
3451
3678
  if (!variantSelector) return "";
3452
3679
  const finalDeclaration = appendImportant(
3453
3680
  isChildScoped(declaration) ? declaration.declaration : declaration,
@@ -3737,6 +3964,9 @@ function windrunner(options = {}) {
3737
3964
  export {
3738
3965
  compileClass,
3739
3966
  createWindrunner,
3967
+ defineResponsiveUtilities,
3968
+ defineUtilities,
3740
3969
  parseClass,
3970
+ plugin,
3741
3971
  windrunner
3742
3972
  };