@@ -309,6 +309,165 @@ throw new Error('Product source must not execute')
309309 }
310310)
311311
312+ test . each ( [
313+ [
314+ 'Boolean class map' ,
315+ "{'bg-red-500':true}" ,
316+ "{'bg-blue-500':true}" ,
317+ 'cn(paintStyles)' ,
318+ 'paintStyles' ,
319+ true ,
320+ false ,
321+ ] ,
322+ [
323+ 'truthy string class map' ,
324+ "{'bg-red-500':'enabled'}" ,
325+ "{'bg-blue-500':'enabled'}" ,
326+ 'cn(paintStyles)' ,
327+ 'paintStyles' ,
328+ true ,
329+ false ,
330+ ] ,
331+ [
332+ 'nested class map' ,
333+ "{tone:{'bg-red-500':true}}" ,
334+ "{tone:{'bg-blue-500':true}}" ,
335+ 'cn(paintStyles.tone)' ,
336+ 'paintStyles' ,
337+ true ,
338+ false ,
339+ ] ,
340+ [
341+ 'class map without styling suffix' ,
342+ "{'bg-red-500':true}" ,
343+ "{'bg-blue-500':true}" ,
344+ 'cn(palette)' ,
345+ 'palette' ,
346+ true ,
347+ false ,
348+ ] ,
349+ [
350+ 'falsy class map' ,
351+ "{'bg-red-500':false,'rounded-lg':0,'p-2':null,'text-red-500':''}" ,
352+ "{'bg-blue-500':false,'rounded-xl':0,'p-4':null,'text-blue-500':''}" ,
353+ 'cn(paintStyles)' ,
354+ 'paintStyles' ,
355+ false ,
356+ false ,
357+ ] ,
358+ [
359+ 'variant string value' ,
360+ "{primary:'bg-red-500'}" ,
361+ "{primary:'bg-blue-500'}" ,
362+ 'cn(paintStyles.primary)' ,
363+ 'paintStyles' ,
364+ true ,
365+ false ,
366+ ] ,
367+ [
368+ 'nested variant value' ,
369+ "{tone:{primary:'bg-red-500'}}" ,
370+ "{tone:{primary:'bg-blue-500'}}" ,
371+ 'cn(paintStyles.tone.primary)' ,
372+ 'paintStyles' ,
373+ true ,
374+ false ,
375+ ] ,
376+ [
377+ 'CVA variant table' ,
378+ "{tone:{primary:'bg-red-500'}}" ,
379+ "{tone:{primary:'bg-blue-500'}}" ,
380+ "cva('',{variants:paintStyles})()" ,
381+ 'paintStyles' ,
382+ true ,
383+ false ,
384+ ] ,
385+ [
386+ 'exported string' ,
387+ "'bg-red-500'" ,
388+ "'bg-blue-500'" ,
389+ 'cn(paintStyles)' ,
390+ 'paintStyles' ,
391+ true ,
392+ false ,
393+ ] ,
394+ [
395+ 'data-only object' ,
396+ "{message:'bg-red-500'}" ,
397+ "{message:'bg-blue-500'}" ,
398+ 'JSON.stringify(paintStyles)' ,
399+ 'paintStyles' ,
400+ false ,
401+ true ,
402+ ] ,
403+ [
404+ 'shadowed data object' ,
405+ "{message:'bg-red-500'}" ,
406+ "{message:'bg-blue-500'}" ,
407+ '<shadowed>' ,
408+ 'paintStyles' ,
409+ false ,
410+ true ,
411+ ] ,
412+ [
413+ 'unknown object consumer' ,
414+ "{primary:'bg-red-500'}" ,
415+ "{primary:'bg-blue-500'}" ,
416+ 'unknownClasses(paintStyles)' ,
417+ 'paintStyles' ,
418+ false ,
419+ true ,
420+ ] ,
421+ ] as const ) (
422+ 'source-only exported styling changes follow unchanged class consumers through the real CLI: %s' ,
423+ ( _name , before , after , use , exported , notify , unchecked ) => {
424+ const { repo } = fixture ( )
425+ const sourceFile = 'packages/emcn/src/lib/styles.ts'
426+ const consumerFile = 'packages/emcn/src/components/consumer.tsx'
427+ const write = ( file : string , source : string ) => {
428+ mkdirSync ( path . dirname ( path . join ( repo , file ) ) , { recursive : true } )
429+ writeFileSync ( path . join ( repo , file ) , source )
430+ }
431+ write ( 'packages/emcn/src/index.ts' , `export {${ exported } } from './lib/styles'` )
432+ const shadow =
433+ use === '<shadowed>'
434+ ? ";declare function cva(value:unknown):unknown;function Local(){const paintStyles={'p-2':true};return cva(paintStyles)}"
435+ : ''
436+ write ( sourceFile , `export const ${ exported } =${ before } as const${ shadow } ` )
437+ write (
438+ consumerFile ,
439+ `import {${ exported } } from '../lib/styles';declare function cn(...v:unknown[]):string;declare function unknownClasses(v:unknown):string;declare function cva(base:string,config:unknown):()=>string;${ use === '<shadowed>' ? "const Consumer=()=> {const paintStyles={'p-2':true};return <div className={cn(paintStyles)}/>}" : `const Consumer=()=> <div className={${ use } }/>` } `
440+ )
441+ write (
442+ ui ,
443+ `import {${ exported } ,cn} from '@sim/emcn';const A=()=> <div className={cn(${ exported } )}/>`
444+ )
445+ const base = commit ( repo )
446+ write ( sourceFile , `export const ${ exported } =${ after } as const${ shadow } ` )
447+ const child = run ( [ '--repo' , repo , '--base' , base , '--working-tree' , '--format' , 'json' ] )
448+ expect ( [ 0 , 1 ] , child . stderr ) . toContain ( child . status )
449+ const report = JSON . parse ( child . stdout ) as Report
450+ expect ( report . status , child . stdout ) . toBe ( 'completed' )
451+ expect ( git ( repo , [ 'diff' , '--name-only' , base ] ) . toString ( ) . trim ( ) ) . toBe ( sourceFile )
452+ expect (
453+ report . findings . some (
454+ ( finding ) => finding . file === sourceFile && finding . property === 'background-color'
455+ ) ,
456+ child . stdout
457+ ) . toBe ( notify )
458+ expect (
459+ report . findings . filter ( ( finding ) => finding . file === sourceFile ) ,
460+ child . stdout
461+ ) . toHaveLength ( notify ? 1 : 0 )
462+ expect (
463+ report . unchecked . some (
464+ ( note ) => note . file === sourceFile && / o b j e c t .* c l a s s .* u n c h e c k e d / i. test ( note . reason )
465+ ) ,
466+ child . stdout
467+ ) . toBe ( unchecked )
468+ }
469+ )
470+
312471test . each ( [
313472 [ 'grid line integer' , 'gridColumnStart' , 'grid-column-start' , '1' , 1 , '' ] ,
314473 [ 'grid row integer' , 'gridRowEnd' , 'grid-row-end' , '3' , 3 , '' ] ,
0 commit comments