@@ -7,10 +7,16 @@ const chokidar = require('chokidar')
7
7
const fastGlob = require ( 'fast-glob' )
8
8
const hash = require ( 'object-hash' )
9
9
const LRU = require ( 'quick-lru' )
10
+ const dlv = require ( 'dlv' )
11
+ const Node = require ( 'postcss/lib/node' )
12
+ const selectorParser = require ( 'postcss-selector-parser' )
10
13
11
14
const resolveConfig = require ( 'tailwindcss/resolveConfig' )
12
15
const escape = require ( 'tailwindcss/lib/util/escapeClassName' ) . default
13
16
const evaluateTailwindFunctions = require ( 'tailwindcss/lib/lib/evaluateTailwindFunctions' ) . default
17
+ const parseObjectStyles = require ( 'tailwindcss/lib/util/parseObjectStyles' ) . default
18
+ const transformThemeValue = require ( 'tailwindcss/lib/util/transformThemeValue' ) . default
19
+ const prefixSelector = require ( 'tailwindcss/lib/util/prefixSelector' ) . default
14
20
15
21
const corePlugins = require ( './corePlugins' )
16
22
@@ -342,13 +348,175 @@ function rebootTemplateWatcher(context) {
342
348
}
343
349
}
344
350
351
+ function parseLegacyStyles ( styles ) {
352
+ if ( ! Array . isArray ( styles ) ) {
353
+ return parseLegacyStyles ( [ styles ] )
354
+ }
355
+
356
+ return styles . flatMap ( ( style ) => ( style instanceof Node ? style : parseObjectStyles ( style ) ) )
357
+ }
358
+
359
+ function getClasses ( selector ) {
360
+ let parser = selectorParser ( ( selectors ) => {
361
+ let allClasses = [ ]
362
+ selectors . walkClasses ( ( classNode ) => {
363
+ allClasses . push ( classNode . value )
364
+ } )
365
+ return allClasses
366
+ } )
367
+ return parser . transformSync ( selector )
368
+ }
369
+
370
+ function toPath ( value ) {
371
+ if ( Array . isArray ( value ) ) {
372
+ return value
373
+ }
374
+
375
+ let inBrackets = false
376
+ let parts = [ ]
377
+ let chunk = ''
378
+
379
+ for ( let i = 0 ; i < value . length ; i ++ ) {
380
+ let char = value [ i ]
381
+ if ( char === '[' ) {
382
+ inBrackets = true
383
+ parts . push ( chunk )
384
+ chunk = ''
385
+ continue
386
+ }
387
+ if ( char === ']' && inBrackets ) {
388
+ inBrackets = false
389
+ parts . push ( chunk )
390
+ chunk = ''
391
+ continue
392
+ }
393
+ if ( char === '.' && ! inBrackets && chunk . length > 0 ) {
394
+ parts . push ( chunk )
395
+ chunk = ''
396
+ continue
397
+ }
398
+ chunk = chunk + char
399
+ }
400
+
401
+ if ( chunk . length > 0 ) {
402
+ parts . push ( chunk )
403
+ }
404
+
405
+ return parts
406
+ }
407
+
408
+ // { .foo { color: black } }
409
+ // => [ ['foo', ['.foo', { color: 'black' }] ]
410
+ function toStaticRuleArray ( legacyStyles ) {
411
+ return parseLegacyStyles ( legacyStyles ) . flatMap ( ( node ) => {
412
+ let nodeMap = new Map ( )
413
+ let classes = getClasses ( node . selector )
414
+ return classes . map ( ( c ) => {
415
+ if ( ! nodeMap . has ( node ) ) {
416
+ let decls = Object . fromEntries (
417
+ node . nodes . map ( ( { prop, value } ) => {
418
+ return [ prop , value ]
419
+ } )
420
+ )
421
+ nodeMap . set ( node , [ node . selector , decls ] )
422
+ }
423
+ return [ c , nodeMap . get ( node ) ]
424
+ } )
425
+ } )
426
+ }
427
+
345
428
function buildPluginApi ( tailwindConfig , context , { variantList, variantMap, offsets } ) {
429
+ function getConfigValue ( path , defaultValue ) {
430
+ return path ? dlv ( tailwindConfig , path , defaultValue ) : tailwindConfig
431
+ }
432
+
433
+ function applyConfiguredPrefix ( selector ) {
434
+ return prefixSelector ( config . prefix , selector )
435
+ }
436
+
346
437
return {
347
- addBase ( nodes ) {
438
+ // Classic plugin API
439
+ addVariant ( ) {
440
+ throw new Error ( `Variant plugins aren't supported yet` )
441
+ } ,
442
+ postcss,
443
+ prefix : applyConfiguredPrefix ,
444
+ e : escape ,
445
+ config : getConfigValue ,
446
+ theme ( path , defaultValue ) {
447
+ const [ pathRoot , ...subPaths ] = toPath ( path )
448
+ const value = getConfigValue ( [ 'theme' , pathRoot , ...subPaths ] , defaultValue )
449
+ return transformThemeValue ( pathRoot ) ( value )
450
+ } ,
451
+ corePlugins : ( path ) => {
452
+ if ( Array . isArray ( tailwindConfig . corePlugins ) ) {
453
+ return tailwindConfig . corePlugins . includes ( path )
454
+ }
455
+
456
+ return getConfigValue ( [ 'corePlugins' , path ] , true )
457
+ } ,
458
+ variants : ( path , defaultValue ) => {
459
+ if ( Array . isArray ( tailwindConfig . variants ) ) {
460
+ return tailwindConfig . variants
461
+ }
462
+
463
+ return getConfigValue ( [ 'variants' , path ] , defaultValue )
464
+ } ,
465
+ addBase ( styles ) {
466
+ let nodes = parseLegacyStyles ( styles )
348
467
for ( let node of nodes ) {
349
468
context . baseRules . add ( node )
350
469
}
351
470
} ,
471
+ addComponents ( components , options ) {
472
+ let defaultOptions = {
473
+ variants : [ ] ,
474
+ respectPrefix : true ,
475
+ respectImportant : false ,
476
+ respectVariants : true ,
477
+ }
478
+
479
+ options = Object . assign (
480
+ { } ,
481
+ defaultOptions ,
482
+ Array . isArray ( options ) ? { variants : options } : options
483
+ )
484
+
485
+ for ( let [ identifier , tuple ] of toStaticRuleArray ( components ) ) {
486
+ let offset = offsets . components ++
487
+
488
+ if ( context . componentMap . has ( identifier ) ) {
489
+ context . componentMap . get ( identifier ) . push ( [ offset , tuple ] )
490
+ } else {
491
+ context . componentMap . set ( identifier , [ [ offset , tuple ] ] )
492
+ }
493
+ }
494
+ } ,
495
+ addUtilities ( utilities , options ) {
496
+ let defaultOptions = {
497
+ variants : [ ] ,
498
+ respectPrefix : true ,
499
+ respectImportant : true ,
500
+ respectVariants : true ,
501
+ }
502
+
503
+ options = Object . assign (
504
+ { } ,
505
+ defaultOptions ,
506
+ Array . isArray ( options ) ? { variants : options } : options
507
+ )
508
+
509
+ for ( let [ identifier , tuple ] of toStaticRuleArray ( utilities ) ) {
510
+ let offset = offsets . utilities ++
511
+
512
+ if ( context . utilityMap . has ( identifier ) ) {
513
+ context . utilityMap . get ( identifier ) . push ( [ offset , tuple ] )
514
+ } else {
515
+ context . utilityMap . set ( identifier , [ [ offset , tuple ] ] )
516
+ }
517
+ }
518
+ } ,
519
+ // ---
352
520
jit : {
353
521
e : escape ,
354
522
config : tailwindConfig ,
@@ -451,8 +619,7 @@ function registerPlugins(tailwindConfig, plugins, context) {
451
619
offsets,
452
620
} )
453
621
454
- for ( let pluginName in plugins ) {
455
- let plugin = corePlugins [ pluginName ]
622
+ for ( let plugin of plugins ) {
456
623
if ( Array . isArray ( plugin ) ) {
457
624
for ( let pluginItem of plugin ) {
458
625
pluginItem ( pluginApi )
@@ -585,11 +752,34 @@ function setupContext(tailwindConfig, configHash, configPath) {
585
752
screens : [ ] ,
586
753
} ,
587
754
}
588
-
589
755
contextMap . set ( configHash , context )
590
756
591
757
rebootTemplateWatcher ( context )
592
- registerPlugins ( context . tailwindConfig , corePlugins , context )
758
+
759
+ let corePluginList = Object . entries ( corePlugins )
760
+ . map ( ( [ name , plugin ] ) => {
761
+ // TODO: Make variants a real core plugin so we don't special case it
762
+ if ( name === 'variants' ) {
763
+ return plugin
764
+ }
765
+
766
+ if ( ! tailwindConfig . corePlugins . includes ( name ) ) {
767
+ return null
768
+ }
769
+
770
+ return plugin
771
+ } )
772
+ . filter ( Boolean )
773
+
774
+ let userPlugins = tailwindConfig . plugins . map ( ( plugin ) => {
775
+ if ( plugin . __isOptionsFunction ) {
776
+ plugin = plugin ( )
777
+ }
778
+
779
+ return typeof plugin === 'function' ? plugin : plugin . handler
780
+ } )
781
+
782
+ registerPlugins ( context . tailwindConfig , [ ...corePluginList , ...userPlugins ] , context )
593
783
594
784
return context
595
785
}
0 commit comments