From 77bdb2164224a9e3f9ce5828eaacf0ec977fb399 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Wed, 8 Jul 2026 12:12:09 +0100 Subject: [PATCH 1/4] remove grep requirement to make color linting cross platform --- scripts/lint-colors.js | 54 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/scripts/lint-colors.js b/scripts/lint-colors.js index 851855c4c6..137e7d8337 100644 --- a/scripts/lint-colors.js +++ b/scripts/lint-colors.js @@ -2,12 +2,38 @@ // Tailwind utility classes in Vue templates, (3) theme-file token parity. // Fail messages below describe each rule when triggered. -const { execSync } = require('child_process') const { log, error } = require('console') const fs = require('fs') const path = require('path') const root = path.resolve(__dirname, '..', 'frontend', 'src') + +// Cross-platform replacement for `grep -rnE`: recursively walk `dir`, match each +// line of every file whose extension is in `extensions` against `pattern`, and +// return `relativePath:lineNumber:lineText` entries (relative to `root`). +function grepLines (dir, pattern, extensions) { + const re = new RegExp(pattern) + const matches = [] + const walk = (current) => { + for (const entry of fs.readdirSync(current, { withFileTypes: true })) { + const full = path.join(current, entry.name) + if (entry.isDirectory()) { + walk(full) + } else if (extensions.includes(path.extname(entry.name))) { + const rel = path.relative(root, full) + const content = fs.readFileSync(full, 'utf8') + const lines = content.split(/\r?\n/) + for (let i = 0; i < lines.length; i++) { + if (re.test(lines[i])) { + matches.push(`${rel}:${i + 1}:${lines[i]}`) + } + } + } + } + } + walk(dir) + return matches +} const stylesheetsDir = path.join(root, 'ui-components', 'stylesheets') const lightThemeFile = path.join(stylesheetsDir, 'ff-theme-light.scss') const 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 const ALLOW_HEX_COMMENT = /\/\/\s*allow-hex:/ function checkBannedReferences () { - let raw = '' - try { - raw = execSync( - `grep -rnE '${BANNED_PATTERN}' --include='*.scss' --include='*.vue' --include='*.css' "${root}"`, - { encoding: 'utf8' } - ) - } catch (e) { - if (e.status === 1) return [] - throw e - } - return raw - .split('\n') - .filter(Boolean) + return grepLines(root, BANNED_PATTERN, ['.scss', '.vue', '.css']) .filter(line => !ALLOWLIST_FILES.test(line.split(':')[0])) .filter(line => !ALLOW_HEX_COMMENT.test(line)) } @@ -46,17 +60,7 @@ const BANNED_TAILWIND_CLASSES = '\\b(bg-black|text-(red|yellow|blue|green|indigo const TAILWIND_IN_TEMPLATE = `(:?class)\\s*=\\s*"[^"]*${BANNED_TAILWIND_CLASSES}[^"]*"` function checkTemplateClasses () { - let raw = '' - try { - raw = execSync( - `grep -rnE '${TAILWIND_IN_TEMPLATE}' --include='*.vue' "${root}"`, - { encoding: 'utf8' } - ) - } catch (e) { - if (e.status === 1) return [] - throw e - } - return raw.split('\n').filter(Boolean) + return grepLines(root, TAILWIND_IN_TEMPLATE, ['.vue']) } // Check 3 — theme-file parity From 66a77a669d29593efd7315550568f07a6242b935 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Wed, 8 Jul 2026 12:14:05 +0100 Subject: [PATCH 2/4] Exit without stack trace noise on color-token lint failure --- scripts/lint-colors.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/lint-colors.js b/scripts/lint-colors.js index 137e7d8337..18d4070c4e 100644 --- a/scripts/lint-colors.js +++ b/scripts/lint-colors.js @@ -156,7 +156,8 @@ function run () { } if (failed) { - throw new Error('FlowFuse color-token lint failed.') + error('FlowFuse color-token lint failed.') + process.exit(1) } log('✓ no banned color references') From ad4db66befe660bfac507be38078e6f24b6aa055 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Wed, 8 Jul 2026 12:14:33 +0100 Subject: [PATCH 3/4] TEMP CHECK: Add probe files for color linting tests --- frontend/src/__lint_probe__.scss | 15 +++++++++++++ frontend/src/__lint_probe__.vue | 21 +++++++++++++++++++ .../stylesheets/ff-theme-light.scss | 3 +++ 3 files changed, 39 insertions(+) create mode 100644 frontend/src/__lint_probe__.scss create mode 100644 frontend/src/__lint_probe__.vue diff --git a/frontend/src/__lint_probe__.scss b/frontend/src/__lint_probe__.scss new file mode 100644 index 0000000000..1c7b698b17 --- /dev/null +++ b/frontend/src/__lint_probe__.scss @@ -0,0 +1,15 @@ +// PROBE FILE — deliberate lint violations for testing scripts/lint-colors.js. DELETE ME. + +.probe { + // Check 1: banned SCSS alias + color: $ff-grey-700; + + // Check 1: hex literal in a color property + background-color: #374151; + + // Check 1: named color keyword in a color property + border: 1px solid black; + + // Check 1: raw palette var used directly in a color property + fill: var(--ff-palette-red-500); +} diff --git a/frontend/src/__lint_probe__.vue b/frontend/src/__lint_probe__.vue new file mode 100644 index 0000000000..f485aaa264 --- /dev/null +++ b/frontend/src/__lint_probe__.vue @@ -0,0 +1,21 @@ + + diff --git a/frontend/src/ui-components/stylesheets/ff-theme-light.scss b/frontend/src/ui-components/stylesheets/ff-theme-light.scss index a5cb2a729c..612aec4013 100644 --- a/frontend/src/ui-components/stylesheets/ff-theme-light.scss +++ b/frontend/src/ui-components/stylesheets/ff-theme-light.scss @@ -4,6 +4,9 @@ [data-theme="light"] { color-scheme: light; + /* PROBE: theme-parity violation — token defined in light but not dark. DELETE ME. */ + --ff-color-probe-only-light: var(--ff-palette-white); + --ff-color-bg-app: var(--ff-palette-white); --ff-color-bg-surface: var(--ff-palette-grey-50); --ff-color-bg-surface-raised: var(--ff-palette-grey-100); From 0bed3d21f236e08ecba3cdc4fca364c96cd3425e Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Wed, 8 Jul 2026 12:44:54 +0100 Subject: [PATCH 4/4] remove color lint test probes --- frontend/src/__lint_probe__.scss | 15 ------------- frontend/src/__lint_probe__.vue | 21 ------------------- .../stylesheets/ff-theme-light.scss | 3 --- 3 files changed, 39 deletions(-) delete mode 100644 frontend/src/__lint_probe__.scss delete mode 100644 frontend/src/__lint_probe__.vue diff --git a/frontend/src/__lint_probe__.scss b/frontend/src/__lint_probe__.scss deleted file mode 100644 index 1c7b698b17..0000000000 --- a/frontend/src/__lint_probe__.scss +++ /dev/null @@ -1,15 +0,0 @@ -// PROBE FILE — deliberate lint violations for testing scripts/lint-colors.js. DELETE ME. - -.probe { - // Check 1: banned SCSS alias - color: $ff-grey-700; - - // Check 1: hex literal in a color property - background-color: #374151; - - // Check 1: named color keyword in a color property - border: 1px solid black; - - // Check 1: raw palette var used directly in a color property - fill: var(--ff-palette-red-500); -} diff --git a/frontend/src/__lint_probe__.vue b/frontend/src/__lint_probe__.vue deleted file mode 100644 index f485aaa264..0000000000 --- a/frontend/src/__lint_probe__.vue +++ /dev/null @@ -1,21 +0,0 @@ - - diff --git a/frontend/src/ui-components/stylesheets/ff-theme-light.scss b/frontend/src/ui-components/stylesheets/ff-theme-light.scss index 612aec4013..a5cb2a729c 100644 --- a/frontend/src/ui-components/stylesheets/ff-theme-light.scss +++ b/frontend/src/ui-components/stylesheets/ff-theme-light.scss @@ -4,9 +4,6 @@ [data-theme="light"] { color-scheme: light; - /* PROBE: theme-parity violation — token defined in light but not dark. DELETE ME. */ - --ff-color-probe-only-light: var(--ff-palette-white); - --ff-color-bg-app: var(--ff-palette-white); --ff-color-bg-surface: var(--ff-palette-grey-50); --ff-color-bg-surface-raised: var(--ff-palette-grey-100);