From 45c4ca806ac3dad503b35bac56a7d65d3fbaabe1 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Wed, 21 Oct 2026 15:10:47 +0100 Subject: [PATCH 1/6] chore(Lint): Lint only staged lines in the pre-commit hook Co-Authored-By: Claude Opus 5.5 (1M context) --- .husky/pre-commit | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 5d7770045e2b..599731e2ab3c 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -18,4 +18,5 @@ else cd ./frontend fi -npx lint-staged --allow-empty +# LINT_STAGED makes eslint check only the staged lines (see .eslintrc.js). +LINT_STAGED=true npx lint-staged --allow-empty From f9d5e635fccb9bc831e0c027d3f2072712b7924b Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Wed, 21 Oct 2026 15:10:47 +0100 Subject: [PATCH 2/6] chore(Lint): Add ESLint rules for patterns we catch in review Co-Authored-By: Claude Opus 5.5 (1M context) --- frontend/.eslintignore | 1 - frontend/.eslintrc.js | 145 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 4 deletions(-) diff --git a/frontend/.eslintignore b/frontend/.eslintignore index 358f1d635eca..1a4067df224b 100644 --- a/frontend/.eslintignore +++ b/frontend/.eslintignore @@ -6,6 +6,5 @@ public tests_output web/static web/project/libs.js -e2e webpack api diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 2d2591f5ee4e..cc1a8b672da4 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,3 +1,90 @@ +// Patterns we keep catching in review. CI and pre-commit lint changed lines +// only, so existing uses don't fail a PR. For a real exception, disable the +// rule on that line and say why. +const colourLiterals = [ + { + message: 'Use a colour token, not a hex, rgb or hsl literal.', + selector: + 'Literal[value=/^\\s*(#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$|(rgba?|hsla?)\\()/]', + }, + { + message: 'Use a colour token, not a hex, rgb or hsl literal.', + selector: + 'TemplateElement[value.raw=/#[0-9a-fA-F]{6}\\b|(rgba?|hsla?)\\(/]', + }, +] + +const jsxPatterns = [ + { + message: + 'Use a SCSS class with tokens. For a runtime value, set a CSS custom property.', + selector: "JSXAttribute[name.name='style']", + }, + { + message: 'Give the element a role or accessible name instead of data-test.', + selector: "JSXAttribute[name.name='data-test']", + }, + { + message: 'Use IconButton.', + selector: + "JSXAttribute[name.name='className'] Literal[value=/(^|\\s)btn-with-icon(\\s|$)/]", + }, + { + message: 'Use IconButton.', + selector: + "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='theme'][value.value='icon']", + }, + { + message: 'Pass icons as children.', + selector: 'JSXAttribute[name.name=/^icon(Left|Right)$/]', + }, + { + message: "Use BareButton instead of role='button'.", + selector: "JSXAttribute[name.name='role'][value.value='button']", + }, + { + message: + 'Use the Bootstrap 5 name (fw-*, ms-*, me-*, ps-*, pe-*, float-start, text-end).', + selector: + "JSXAttribute[name.name='className'] Literal[value=/(^|\\s)(font-weight-[a-z]+|float-(left|right)|text-(left|right)|[mp][lr]-([0-9]|auto))(\\s|$)/]", + }, +] + +const e2eSelectors = [ + { + message: 'Use getByRole or getByLabel.', + selector: "CallExpression[callee.name='byId']", + }, + { + message: 'Use getByRole or getByLabel.', + selector: + "CallExpression[callee.property.name='locator'] :matches(Literal[value=/data-test/], TemplateElement[value.raw=/data-test/])", + }, +] + +const iconLibraries = { + paths: [ + '@heroicons/react', + '@mui/icons-material', + '@phosphor-icons/react', + 'lucide-react', + 'react-icons', + ].map((name) => ({ + message: 'Add the SVG to the project and render it through Icon.', + name, + })), +} +// A warning, not an error: some data is only in the stores until the RTK +// migration, and touching a line that already reads one shouldn't fail CI. +const fluxStores = { + group: ['common/stores/*', '**/stores/*-store'], + message: 'Read data through RTK Query services, not Flux stores.', +} +const fetchGlobal = { + message: 'Add an RTK Query endpoint in common/services/ instead of fetch().', + name: 'fetch', +} + module.exports = { 'env': { 'browser': true, @@ -11,9 +98,9 @@ module.exports = { 'plugin:prettier/recommended', 'plugin:@dword-design/import-alias/recommended', 'plugin:storybook/recommended', - // Lints only changed lines in CI (no-op locally), so the pre-existing - // eslint backlog doesn't fail PRs that merely touch a file. - 'plugin:diff/ci', + // Lints only changed lines in CI and in the pre-commit hook (which sets + // LINT_STAGED), so the pre-existing backlog doesn't fail a commit or PR. + process.env.LINT_STAGED ? 'plugin:diff/staged' : 'plugin:diff/ci', ], 'globals': { '$': true, @@ -99,6 +186,45 @@ module.exports = { 'zE': true, }, 'ignorePatterns': ['server/index.js', 'next.config.js', 'babel.config.js'], + 'overrides': [ + { + // Where raw colour values are defined or are brand colours. + 'files': [ + '.storybook/**', + 'common/constants.ts', + 'common/theme/**', + 'documentation/**', + 'web/components/icons/**', + ], + 'rules': { + 'no-restricted-syntax': ['error', ...jsxPatterns], + }, + }, + { + 'files': ['common/stores/**', 'common/providers/**'], + 'rules': { + '@typescript-eslint/no-restricted-imports': 'off', + }, + }, + { + // The legacy request layer RTK Query is built on. + 'files': ['common/data/base/**'], + 'rules': { + 'no-restricted-globals': 'off', + }, + }, + { + // E2E was outside lint until these rules, so it keeps its own formatting + // and relative imports. + 'files': ['e2e/**'], + 'rules': { + '@dword-design/import-alias/prefer-alias': 'off', + 'no-restricted-globals': 'off', + 'no-restricted-syntax': ['error', ...colourLiterals, ...e2eSelectors], + 'prettier/prettier': 'off', + }, + }, + ], 'parser': '@typescript-eslint/parser', 'parserOptions': { 'ecmaFeatures': { @@ -129,9 +255,18 @@ module.exports = { ], '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { 'assertionStyle': 'never' }, + ], '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-restricted-imports': [ + 'warn', + { 'patterns': [fluxStores] }, + ], '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/no-var-requires': 'off', 'default-case': 'error', @@ -144,6 +279,9 @@ module.exports = { 'no-empty': 'off', 'no-empty-pattern': 'off', 'no-nested-ternary': 'error', + 'no-restricted-globals': ['error', fetchGlobal], + 'no-restricted-imports': ['error', iconLibraries], + 'no-restricted-syntax': ['error', ...colourLiterals, ...jsxPatterns], 'no-unused-vars': 'off', 'no-var': 'error', 'object-curly-spacing': ['error', 'always'], @@ -158,6 +296,7 @@ module.exports = { }, ], 'react/jsx-uses-react': 'off', + 'react/no-danger': 'error', 'react/no-direct-mutation-state': 'off', 'react/no-find-dom-node': 'off', 'react/no-string-refs': 'off', From 3b1f91fd548584ed9c6ec759b7f41538243ef0fc Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Wed, 21 Oct 2026 15:10:47 +0100 Subject: [PATCH 3/6] chore(Lint): Block hex colours and .dark selectors in changed SCSS lines Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/frontend-pull-request.yml | 5 ++ frontend/bin/check-scss.js | 63 +++++++++++++++++++++ frontend/package.json | 2 + 3 files changed, 70 insertions(+) create mode 100644 frontend/bin/check-scss.js diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index 6ec2808363ac..5c0e4cc45058 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -64,3 +64,8 @@ jobs: env: ESLINT_PLUGIN_DIFF_COMMIT: ${{ github.event.pull_request.base.sha }} run: npm run lint + + - name: Check changed SCSS lines + env: + SCSS_DIFF_BASE: ${{ github.event.pull_request.base.sha }} + run: npm run lint:scss diff --git a/frontend/bin/check-scss.js b/frontend/bin/check-scss.js new file mode 100644 index 000000000000..3e5b37fb5124 --- /dev/null +++ b/frontend/bin/check-scss.js @@ -0,0 +1,63 @@ +// Fails on added SCSS lines with a hex colour or a .dark selector. Checks the +// staged diff by default, or the diff from SCSS_DIFF_BASE in CI, so existing +// lines don't fail a commit or PR. +const { execFileSync } = require('child_process') + +// Where raw colour values and the dark theme are defined. +const allowed = [ + /^\.storybook\//, + /^documentation\//, + /^web\/styles\/_primitives\.scss$/, + /^web\/styles\/_tokens\.scss$/, +] + +const checks = [ + { + message: 'Use a colour token, not a hex literal.', + pattern: /#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/, + }, + { + message: 'Use a token whose value already changes in dark mode.', + pattern: /\.dark\b/, + }, +] + +const base = process.env.SCSS_DIFF_BASE +const files = process.argv.slice(2) +const diff = execFileSync( + 'git', + [ + 'diff', + '--relative', + '--unified=0', + ...(base ? [`${base}...HEAD`] : ['--cached']), + '--', + ...(files.length ? files : ['*.scss']), + ], + { encoding: 'utf8' }, +) + +const problems = [] +let file +let line +for (const row of diff.split('\n')) { + if (row.startsWith('+++ ')) { + file = row.replace(/^\+\+\+ b\//, '') + } else if (row.startsWith('@@')) { + line = Number(row.match(/\+(\d+)/)[1]) + } else if (row.startsWith('+')) { + const isAllowed = allowed.some((pattern) => pattern.test(file)) + // Comments can mention a colour. + const code = row.slice(1).replace(/\/\/.*$/, '') + checks + .filter(({ pattern }) => !isAllowed && pattern.test(code)) + .forEach(({ message }) => problems.push(`${file}:${line} ${message}`)) + line++ + } +} + +if (problems.length) { + // eslint-disable-next-line no-console + console.error(problems.join('\n')) + process.exit(1) +} diff --git a/frontend/package.json b/frontend/package.json index 90d2c0918cf4..c0df3a2a6a1a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "env": "node ./bin/env.js", "lint": "eslint .", "lint:fix": "npx eslint --fix .", + "lint:scss": "node bin/check-scss.js", "start": "cross-env NODE_ENV=production node ./api/index", "typecheck": "tsc", "dev": "cross-env npm run env && npx rspack serve -c ./rspack/rspack.config.local.js", @@ -186,6 +187,7 @@ }, "lint-staged": { "*.{js,ts,tsx}": "eslint --fix", + "*.scss": "node bin/check-scss.js", "common/theme/tokens.json": "npm run generate:tokens && git add common/theme/tokens.ts web/styles/_tokens.scss web/styles/_primitives.scss web/styles/_token-utilities.scss documentation/TokenReference.generated.stories.tsx" }, "optionalDependencies": { From bcaf4e7a5219025b8227a6332ee89acc8e8d62a6 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Wed, 21 Oct 2026 17:21:35 +0100 Subject: [PATCH 4/6] fix(Lint): Allow only Icon for icons, catch window.fetch and stop matching .dark-* Co-Authored-By: Claude Opus 5.5 (1M context) --- frontend/.eslintrc.js | 39 ++++++++++++++++++++++++-------------- frontend/bin/check-scss.js | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index cc1a8b672da4..fefb521bbca4 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -14,7 +14,20 @@ const colourLiterals = [ }, ] +// The project's own SVGs, through Icon, are the only icon set. +const onlyIcon = 'Add the SVG to the project and render it through Icon.' +const iconLibraries = { + paths: [ + { importNames: ['IonIcon'], message: onlyIcon, name: '@ionic/react' }, + ], + patterns: [{ group: ['ionicons', 'ionicons/*'], message: onlyIcon }], +} + const jsxPatterns = [ + { + message: onlyIcon, + selector: "JSXOpeningElement[name.name='IonIcon']", + }, { message: 'Use a SCSS class with tokens. For a runtime value, set a CSS custom property.', @@ -62,24 +75,17 @@ const e2eSelectors = [ }, ] -const iconLibraries = { - paths: [ - '@heroicons/react', - '@mui/icons-material', - '@phosphor-icons/react', - 'lucide-react', - 'react-icons', - ].map((name) => ({ - message: 'Add the SVG to the project and render it through Icon.', - name, - })), -} // A warning, not an error: some data is only in the stores until the RTK // migration, and touching a line that already reads one shouldn't fail CI. const fluxStores = { group: ['common/stores/*', '**/stores/*-store'], message: 'Read data through RTK Query services, not Flux stores.', } +const fetchCalls = { + message: 'Add an RTK Query endpoint in common/services/ instead of fetch().', + selector: + "CallExpression[callee.object.name=/^(window|globalThis|self)$/][callee.property.name='fetch']", +} const fetchGlobal = { message: 'Add an RTK Query endpoint in common/services/ instead of fetch().', name: 'fetch', @@ -197,7 +203,7 @@ module.exports = { 'web/components/icons/**', ], 'rules': { - 'no-restricted-syntax': ['error', ...jsxPatterns], + 'no-restricted-syntax': ['error', ...jsxPatterns, fetchCalls], }, }, { @@ -281,7 +287,12 @@ module.exports = { 'no-nested-ternary': 'error', 'no-restricted-globals': ['error', fetchGlobal], 'no-restricted-imports': ['error', iconLibraries], - 'no-restricted-syntax': ['error', ...colourLiterals, ...jsxPatterns], + 'no-restricted-syntax': [ + 'error', + ...colourLiterals, + ...jsxPatterns, + fetchCalls, + ], 'no-unused-vars': 'off', 'no-var': 'error', 'object-curly-spacing': ['error', 'always'], diff --git a/frontend/bin/check-scss.js b/frontend/bin/check-scss.js index 3e5b37fb5124..372c8f8f63ef 100644 --- a/frontend/bin/check-scss.js +++ b/frontend/bin/check-scss.js @@ -18,7 +18,7 @@ const checks = [ }, { message: 'Use a token whose value already changes in dark mode.', - pattern: /\.dark\b/, + pattern: /\.dark(?![\w-])/, }, ] From 7a9df0d5ec5f3be6f5ad6dd766c4d17161a594ea Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Thu, 22 Oct 2026 09:21:23 +0100 Subject: [PATCH 5/6] chore(Lint): Lint changed SCSS lines with Stylelint and reviewdog Replaces bin/check-scss.js. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/frontend-pull-request.yml | 18 +- frontend/.stylelintrc.cjs | 27 + frontend/bin/check-scss.js | 63 -- frontend/package-lock.json | 642 ++++++++++++++++++++ frontend/package.json | 4 +- 5 files changed, 685 insertions(+), 69 deletions(-) create mode 100644 frontend/.stylelintrc.cjs delete mode 100644 frontend/bin/check-scss.js diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index 5c0e4cc45058..8a81aecaea3d 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -38,6 +38,10 @@ jobs: lint: name: Lint changed files runs-on: ubuntu-latest + permissions: + contents: read + # reviewdog reports Stylelint results as a check run. + checks: write defaults: run: @@ -65,7 +69,13 @@ jobs: ESLINT_PLUGIN_DIFF_COMMIT: ${{ github.event.pull_request.base.sha }} run: npm run lint - - name: Check changed SCSS lines - env: - SCSS_DIFF_BASE: ${{ github.event.pull_request.base.sha }} - run: npm run lint:scss + # Fails only on problems in lines this PR added, so existing styles + # don't fail it. + - name: Lint changed SCSS lines + uses: reviewdog/action-stylelint@v1.33.0 + with: + workdir: frontend + stylelint_input: '**/*.scss' + reporter: github-pr-check + filter_mode: added + fail_level: error diff --git a/frontend/.stylelintrc.cjs b/frontend/.stylelintrc.cjs new file mode 100644 index 000000000000..83ae31a10014 --- /dev/null +++ b/frontend/.stylelintrc.cjs @@ -0,0 +1,27 @@ +// Only added lines are checked, by reviewdog in CI, so existing styles don't +// fail a PR. +module.exports = { + customSyntax: 'postcss-scss', + overrides: [ + { + // Where raw colour values and the dark theme are defined. + files: [ + '.storybook/**', + 'documentation/**', + 'web/styles/_primitives.scss', + 'web/styles/_tokens.scss', + ], + rules: { + 'color-no-hex': null, + 'selector-disallowed-list': null, + }, + }, + ], + rules: { + 'color-no-hex': [true, { message: 'Use a colour token, not a hex literal.' }], + 'selector-disallowed-list': [ + [/\.dark(?![\w-])/], + { message: 'Use a token whose value already changes in dark mode.' }, + ], + }, +} diff --git a/frontend/bin/check-scss.js b/frontend/bin/check-scss.js deleted file mode 100644 index 372c8f8f63ef..000000000000 --- a/frontend/bin/check-scss.js +++ /dev/null @@ -1,63 +0,0 @@ -// Fails on added SCSS lines with a hex colour or a .dark selector. Checks the -// staged diff by default, or the diff from SCSS_DIFF_BASE in CI, so existing -// lines don't fail a commit or PR. -const { execFileSync } = require('child_process') - -// Where raw colour values and the dark theme are defined. -const allowed = [ - /^\.storybook\//, - /^documentation\//, - /^web\/styles\/_primitives\.scss$/, - /^web\/styles\/_tokens\.scss$/, -] - -const checks = [ - { - message: 'Use a colour token, not a hex literal.', - pattern: /#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/, - }, - { - message: 'Use a token whose value already changes in dark mode.', - pattern: /\.dark(?![\w-])/, - }, -] - -const base = process.env.SCSS_DIFF_BASE -const files = process.argv.slice(2) -const diff = execFileSync( - 'git', - [ - 'diff', - '--relative', - '--unified=0', - ...(base ? [`${base}...HEAD`] : ['--cached']), - '--', - ...(files.length ? files : ['*.scss']), - ], - { encoding: 'utf8' }, -) - -const problems = [] -let file -let line -for (const row of diff.split('\n')) { - if (row.startsWith('+++ ')) { - file = row.replace(/^\+\+\+ b\//, '') - } else if (row.startsWith('@@')) { - line = Number(row.match(/\+(\d+)/)[1]) - } else if (row.startsWith('+')) { - const isAllowed = allowed.some((pattern) => pattern.test(file)) - // Comments can mention a colour. - const code = row.slice(1).replace(/\/\/.*$/, '') - checks - .filter(({ pattern }) => !isAllowed && pattern.test(code)) - .forEach(({ message }) => problems.push(`${file}:${line} ${message}`)) - line++ - } -} - -if (problems.length) { - // eslint-disable-next-line no-console - console.error(problems.join('\n')) - process.exit(1) -} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 60ee6f285bfb..1f0101f6d280 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -138,11 +138,13 @@ "minimist": "^1.2.8", "nodemon": "^3.0.1", "playwright": "^1.58.2", + "postcss-scss": "4.0.9", "prettier": "^2.5.1", "raw-loader": "^4.0.2", "react-refresh": "^0.14.2", "ssgrtk": "^0.3.5", "storybook": "^10.3.3", + "stylelint": "16.10.0", "ts-jest": "^29.4.6", "typescript": "5.9.3" }, @@ -2368,6 +2370,73 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1" + } + }, "node_modules/@datadog/framepost": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@datadog/framepost/-/framepost-0.3.1.tgz", @@ -2483,6 +2552,17 @@ "tslib": "^2.6.2" } }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz", + "integrity": "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/JounQin" + } + }, "node_modules/@dword-design/endent": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/@dword-design/endent/-/endent-1.4.7.tgz", @@ -10842,6 +10922,13 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/colord": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", + "dev": true, + "license": "MIT" + }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -11098,6 +11185,16 @@ "node": ">= 8" } }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/css-loader": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", @@ -11140,6 +11237,20 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/css-what": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", @@ -11902,6 +12013,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -13216,6 +13337,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, "node_modules/fastq": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", @@ -13953,6 +14084,47 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -14007,6 +14179,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -14280,6 +14459,19 @@ "node": ">=12" } }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -14719,6 +14911,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -16408,6 +16607,13 @@ "node": ">=6" } }, + "node_modules/known-css-properties": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -16782,6 +16988,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -16977,6 +17190,17 @@ "node": ">= 0.4" } }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -17049,6 +17273,13 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", @@ -17081,6 +17312,19 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -19153,6 +19397,67 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.7.tgz", + "integrity": "sha512-lFcugRCQXWManq7nyZ0dDtir52wc7gKYJfWpSuz4GxvnGc7dtlpXcZYX4A0asatzTuqpQ3ngDdyD+QG/szeXWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.1.0.tgz", + "integrity": "sha512-1WzZxRLaAFwEh6Do+zyGpjWV3nGJNxxhuh7Ubu/q1ICImMgZJnLwhoaEpRbG4pJppp2Y1ncL9ffA2f+LhrefQg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, "node_modules/postcss-selector-parser": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", @@ -21593,6 +21898,246 @@ "inline-style-parser": "0.1.1" } }, + "node_modules/stylelint": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.10.0.tgz", + "integrity": "sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1", + "@csstools/media-query-list-parser": "^3.0.1", + "@csstools/selector-specificity": "^4.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.0.0", + "debug": "^4.3.7", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.1.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^6.0.2", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.34.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.47", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.1.0", + "svg-tags": "^1.0.0", + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint/node_modules/@csstools/selector-specificity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.1.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", + "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", + "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/is-plain-object": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.1.0.tgz", + "integrity": "sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylelint/node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -21638,6 +22183,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, "node_modules/swc-loader": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz", @@ -21674,6 +22225,97 @@ "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", "license": "MIT" }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index c0df3a2a6a1a..b1dfe0770781 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,6 @@ "env": "node ./bin/env.js", "lint": "eslint .", "lint:fix": "npx eslint --fix .", - "lint:scss": "node bin/check-scss.js", "start": "cross-env NODE_ENV=production node ./api/index", "typecheck": "tsc", "dev": "cross-env npm run env && npx rspack serve -c ./rspack/rspack.config.local.js", @@ -177,17 +176,18 @@ "minimist": "^1.2.8", "nodemon": "^3.0.1", "playwright": "^1.58.2", + "postcss-scss": "4.0.9", "prettier": "^2.5.1", "raw-loader": "^4.0.2", "react-refresh": "^0.14.2", "ssgrtk": "^0.3.5", "storybook": "^10.3.3", + "stylelint": "16.10.0", "ts-jest": "^29.4.6", "typescript": "5.9.3" }, "lint-staged": { "*.{js,ts,tsx}": "eslint --fix", - "*.scss": "node bin/check-scss.js", "common/theme/tokens.json": "npm run generate:tokens && git add common/theme/tokens.ts web/styles/_tokens.scss web/styles/_primitives.scss web/styles/_token-utilities.scss documentation/TokenReference.generated.stories.tsx" }, "optionalDependencies": { From 742715dc19268b7bcc6e13cbf59384f3a164383f Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Thu, 22 Oct 2026 09:24:57 +0100 Subject: [PATCH 6/6] fix(Lint): Let Stylelint expand the SCSS glob in CI Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/frontend-pull-request.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index 8a81aecaea3d..6111a8c6a587 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -75,7 +75,8 @@ jobs: uses: reviewdog/action-stylelint@v1.33.0 with: workdir: frontend - stylelint_input: '**/*.scss' + # Quoted so Stylelint expands the glob, not the action's shell. + stylelint_input: "'**/*.scss'" reporter: github-pr-check filter_mode: added fail_level: error