windrunner 1.0.3 → 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/LICENSE +15 -15
- package/README.md +328 -240
- package/dist/index.d.ts +62 -33
- package/dist/index.esm.js +235 -5
- package/dist/index.js +238 -5
- package/dist/index.min.js +3 -3
- package/package.json +58 -58
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
theme?:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
};
|