22// Tailwind utility classes in Vue templates, (3) theme-file token parity.
33// Fail messages below describe each rule when triggered.
44
5- const { execSync } = require ( 'child_process' )
65const { log, error } = require ( 'console' )
76const fs = require ( 'fs' )
87const path = require ( 'path' )
98
109const root = path . resolve ( __dirname , '..' , 'frontend' , 'src' )
10+
11+ // Cross-platform replacement for `grep -rnE`: recursively walk `dir`, match each
12+ // line of every file whose extension is in `extensions` against `pattern`, and
13+ // return `relativePath:lineNumber:lineText` entries (relative to `root`).
14+ function grepLines ( dir , pattern , extensions ) {
15+ const re = new RegExp ( pattern )
16+ const matches = [ ]
17+ const walk = ( current ) => {
18+ for ( const entry of fs . readdirSync ( current , { withFileTypes : true } ) ) {
19+ const full = path . join ( current , entry . name )
20+ if ( entry . isDirectory ( ) ) {
21+ walk ( full )
22+ } else if ( extensions . includes ( path . extname ( entry . name ) ) ) {
23+ const rel = path . relative ( root , full )
24+ const content = fs . readFileSync ( full , 'utf8' )
25+ const lines = content . split ( / \r ? \n / )
26+ for ( let i = 0 ; i < lines . length ; i ++ ) {
27+ if ( re . test ( lines [ i ] ) ) {
28+ matches . push ( `${ rel } :${ i + 1 } :${ lines [ i ] } ` )
29+ }
30+ }
31+ }
32+ }
33+ }
34+ walk ( dir )
35+ return matches
36+ }
1137const stylesheetsDir = path . join ( root , 'ui-components' , 'stylesheets' )
1238const lightThemeFile = path . join ( stylesheetsDir , 'ff-theme-light.scss' )
1339const darkThemeFile = path . join ( stylesheetsDir , 'ff-theme-dark.scss' )
@@ -24,19 +50,7 @@ const ALLOWLIST_FILES = /(ff-colors|ff-palette|ff-theme-light|ff-theme-dark|ff-h
2450const ALLOW_HEX_COMMENT = / \/ \/ \s * a l l o w - h e x : /
2551
2652function checkBannedReferences ( ) {
27- let raw = ''
28- try {
29- raw = execSync (
30- `grep -rnE '${ BANNED_PATTERN } ' --include='*.scss' --include='*.vue' --include='*.css' "${ root } "` ,
31- { encoding : 'utf8' }
32- )
33- } catch ( e ) {
34- if ( e . status === 1 ) return [ ]
35- throw e
36- }
37- return raw
38- . split ( '\n' )
39- . filter ( Boolean )
53+ return grepLines ( root , BANNED_PATTERN , [ '.scss' , '.vue' , '.css' ] )
4054 . filter ( line => ! ALLOWLIST_FILES . test ( line . split ( ':' ) [ 0 ] ) )
4155 . filter ( line => ! ALLOW_HEX_COMMENT . test ( line ) )
4256}
@@ -46,17 +60,7 @@ const BANNED_TAILWIND_CLASSES = '\\b(bg-black|text-(red|yellow|blue|green|indigo
4660const TAILWIND_IN_TEMPLATE = `(:?class)\\s*=\\s*"[^"]*${ BANNED_TAILWIND_CLASSES } [^"]*"`
4761
4862function checkTemplateClasses ( ) {
49- let raw = ''
50- try {
51- raw = execSync (
52- `grep -rnE '${ TAILWIND_IN_TEMPLATE } ' --include='*.vue' "${ root } "` ,
53- { encoding : 'utf8' }
54- )
55- } catch ( e ) {
56- if ( e . status === 1 ) return [ ]
57- throw e
58- }
59- return raw . split ( '\n' ) . filter ( Boolean )
63+ return grepLines ( root , TAILWIND_IN_TEMPLATE , [ '.vue' ] )
6064}
6165
6266// Check 3 — theme-file parity
0 commit comments