From 22c20d8298aad01868bbf9e7474f242e3eec5e59 Mon Sep 17 00:00:00 2001 From: firefoxic Date: Tue, 7 May 2024 18:22:28 +0300 Subject: [PATCH] Initial commit --- .editorconfig | 15 + .gitattributes | 1 + .github/workflows/test.yml | 41 + .gitignore | 2 + .husky/pre-commit | 5 + .npmrc | 1 + CHANGELOG.md | 14 + LICENSE.md | 9 + README.md | 42 + eslint.config.js | 12 + package.json | 59 + pnpm-lock.yaml | 1447 +++++++++++++++++ stylelint.config.js | 197 +++ .../declaration-colon-newline-after.js | 57 + test/annotation-no-unknown.js | 31 + test/at-rule-empty-line-before.js | 110 ++ test/at-rule-no-unknown.js | 123 ++ test/at-rule-no-vendor-prefix.js | 51 + test/block-no-empty.js | 101 ++ ...tion-block-single-line-max-declarations.js | 28 + test/font-family-name-quotes.js | 52 + test/font-family-no-duplicate-names.js | 51 + test/import-notation.js | 25 + test/number-max-precision.js | 82 + test/property-no-unknown.js | 74 + test/value-keyword-case.js | 30 + utils/testRule.js | 46 + 27 files changed, 2706 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100755 .husky/pre-commit create mode 100644 .npmrc create mode 100644 CHANGELOG.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 eslint.config.js create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 stylelint.config.js create mode 100644 test/@stylistic/declaration-colon-newline-after.js create mode 100644 test/annotation-no-unknown.js create mode 100644 test/at-rule-empty-line-before.js create mode 100644 test/at-rule-no-unknown.js create mode 100644 test/at-rule-no-vendor-prefix.js create mode 100644 test/block-no-empty.js create mode 100644 test/declaration-block-single-line-max-declarations.js create mode 100644 test/font-family-name-quotes.js create mode 100644 test/font-family-no-duplicate-names.js create mode 100644 test/import-notation.js create mode 100644 test/number-max-precision.js create mode 100644 test/property-no-unknown.js create mode 100644 test/value-keyword-case.js create mode 100644 utils/testRule.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5bce439 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..24f6dba --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,41 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + branches: + - '*' + +env: + FORCE_COLOR: true + +jobs: + test: + name: Test + + runs-on: ubuntu-latest + + steps: + + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Restore cache + uses: actions/cache@v4 + with: + path: ~/.local/share/pnpm/store/v3 + key: cache-pnpm-${{ hashFiles('./pnpm-lock.yaml') }} + restore-keys: cache-pnpm- + + - name: Install dependencies + run: corepack enable pnpm && pnpm i + + - name: Run tests + run: pnpm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb03e3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.log diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..bef77db --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +git stash -ku + +trap "git stash pop" EXIT + +pnpm test diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a706086 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Base config for `stylelint`. + +[Unreleased]: https://github.com/firefoxic/stylelint-config/compare/v0.0.1...HEAD diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..04bf4e4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright © Sergey Artemov , 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..672e50f --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# @firefoxic/stylelint-config + +[![Test Status][test-image]][test-url] +[![License: MIT][license-image]][license-url] +[![NPM version][npm-image]][npm-url] + +Shared config for stylelint by [firefoxic](https://firefoxic.dev). + +To see the rules that this config uses, please read the [config itself](./stylelint.config.js). + +## Installation + +```shell +pnpm add -D stylelint @firefoxic/stylelint-config +``` + +## Usage + +Set your `stylelint.config.js` to: + +```js +export default { + customSyntax: "postcss-scss", // If your styles syntax is different from CSS (don't forget to install the package) + extends: "@firefoxic/stylelint-config", + rules: { + // Your overrides or additions to rules + } +} +``` + +## Useful links + +- [Changelog](CHANGELOG.md) + +[test-url]: https://github.com/firefoxic/stylelint-config/actions +[test-image]: https://github.com/firefoxic/stylelint-config/actions/workflows/test.yml/badge.svg?branch=main + +[npm-url]: https://npmjs.org/package/firefoxic/stylelint-config +[npm-image]: https://badge.fury.io/js/@firefoxic%2Fstylelint-config.svg + +[license-url]: https://github.com/firefoxic/stylelint-config/blob/main/LICENSE.md +[license-image]: https://img.shields.io/badge/License-MIT-limegreen.svg diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..8c7a0f4 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,12 @@ +import { default as firefoxicEslintConfig, globals } from "@firefoxic/eslint-config" + +export default [ + { + languageOptions: { + globals: { + ...globals.nodeBuildIn, + }, + }, + }, + ...firefoxicEslintConfig, +] diff --git a/package.json b/package.json new file mode 100644 index 0000000..a9323f6 --- /dev/null +++ b/package.json @@ -0,0 +1,59 @@ +{ + "name": "@firefoxic/stylelint-config", + "description": "Shared config for stylelint by firefoxic.", + "version": "0.0.1", + "license": "MIT", + "author": { + "name": "Sergey Artemov", + "email": "firefoxic.dev@gmail.com" + }, + "homepage": "https://github.com/firefoxic/stylelint-config#readme", + "bugs": { + "url": "https://github.com/firefoxic/stylelint-config/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/firefoxic/stylelint-config.git" + }, + "type": "module", + "exports": "./stylelint.config.js", + "files": [ + "./stylelint.config.js" + ], + "packageManager": "pnpm@9.1.0", + "engines": { + "node": "^18.12 || >=20.9" + }, + "peerDependencies": { + "stylelint": "^16.5.0" + }, + "dependencies": { + "@stylistic/stylelint-plugin": "^2.1.2" + }, + "scripts": { + "prepare": "husky", + "lint": "eslint", + "pretest": "pnpm lint", + "test": "node --test", + "preversion": "pnpm test", + "version": "update-changelog && git add CHANGELOG.md", + "postversion": "pnpm publish --access=public", + "postpublish": "git push --follow-tags" + }, + "devDependencies": { + "@firefoxic/eslint-config": "^1.0.0", + "@firefoxic/utils": "^0.1.1", + "eslint": "^9.2.0", + "husky": "^9.0.11", + "stylelint": "^16.5.0" + }, + "keywords": [ + "config", + "css", + "lint", + "linter", + "linting", + "style", + "stylelint" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..176d955 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1447 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@stylistic/stylelint-plugin': + specifier: ^2.1.2 + version: 2.1.2(stylelint@16.5.0) + devDependencies: + '@firefoxic/eslint-config': + specifier: ^1.0.0 + version: 1.0.0(eslint@9.2.0) + '@firefoxic/utils': + specifier: ^0.1.1 + version: 0.1.1 + eslint: + specifier: ^9.2.0 + version: 9.2.0 + husky: + specifier: ^9.0.11 + version: 9.0.11 + stylelint: + specifier: ^16.5.0 + version: 16.5.0 + +packages: + + '@babel/code-frame@7.24.2': + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.5': + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.5': + resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} + engines: {node: '>=6.9.0'} + + '@csstools/css-parser-algorithms@2.6.3': + resolution: {integrity: sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^2.3.1 + + '@csstools/css-tokenizer@2.3.1': + resolution: {integrity: sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==} + engines: {node: ^14 || ^16 || >=18} + + '@csstools/media-query-list-parser@2.1.11': + resolution: {integrity: sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.6.3 + '@csstools/css-tokenizer': ^2.3.1 + + '@csstools/selector-specificity@3.0.3': + resolution: {integrity: sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss-selector-parser: ^6.0.13 + + '@dual-bundle/import-meta-resolve@4.0.0': + resolution: {integrity: sha512-ZKXyJeFAzcpKM2kk8ipoGIPUqx9BX52omTGnfwjJvxOCaZTM2wtDK7zN0aIgPRbT9XYAlha0HtmZ+XKteuh0Gw==} + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.10.0': + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@3.0.2': + resolution: {integrity: sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.2.0': + resolution: {integrity: sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@firefoxic/eslint-config@1.0.0': + resolution: {integrity: sha512-5Kru1mXkDVYr+oQa/u7bqLEflb82KQvKIDr43z1x52bGVcCZW9wQC+WjG1FkRufEIy6svTWWw5Eiof0afvM5ag==} + engines: {node: ^18.12 || >=20.9} + peerDependencies: + eslint: ^9.2.0 + + '@firefoxic/utils@0.1.1': + resolution: {integrity: sha512-rWFuRjwsDIhHuem3SU+3ke3Yn6G/gEvF1uqJGwvKiQ7O2SKKjs0AXzraS6g1EHANfOA0IxXvDgcz73pTXMisIA==} + engines: {node: ^18.12 || >=20.9} + hasBin: true + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + + '@humanwhocodes/retry@0.2.4': + resolution: {integrity: sha512-Ttl/jHpxfS3st5sxwICYfk4pOH0WrLI1SpW283GgQL7sCWU7EHIOhX4b4fkIxr3tkfzwg8+FNojtzsIEE7Ecgg==} + engines: {node: '>=18.18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@stylistic/eslint-plugin-js@1.8.0': + resolution: {integrity: sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: '>=8.40.0' + + '@stylistic/stylelint-plugin@2.1.2': + resolution: {integrity: sha512-JsSqu0Y3vsX+PBl+DwULxC0cIv9C1yIcq1MXkx7pBOGtTqU26a75I8MPYMiEYvrsXgsKLi65xVgy1iLVSZquJA==} + engines: {node: ^18.12 || >=20.9} + peerDependencies: + stylelint: ^16.0.2 + + '@types/eslint@8.56.10': + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + css-functions-list@3.2.2: + resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} + engines: {node: '>=12 || >=16'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.2.0: + resolution: {integrity: sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + + espree@10.0.1: + resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.1.0: + resolution: {integrity: sha512-926gJqg+4mkxwYKiFvoomM4J0kWESfk3qfTvRL2/oc/tK/eTDBbrfcKnSa2KtfdxB5onoL7D3A3qIHQFpd4+UA==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + husky@9.0.11: + resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + engines: {node: '>=18'} + hasBin: true + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + known-css-properties@0.30.0: + resolution: {integrity: sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + postcss-resolve-nested-selector@0.1.1: + resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} + + postcss-safe-parser@7.0.0: + resolution: {integrity: sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + stylelint@16.5.0: + resolution: {integrity: sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==} + engines: {node: '>=18.12.0'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.0.0: + resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} + engines: {node: '>=14.18'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@babel/code-frame@7.24.2': + dependencies: + '@babel/highlight': 7.24.5 + picocolors: 1.0.0 + + '@babel/helper-validator-identifier@7.24.5': {} + + '@babel/highlight@7.24.5': + dependencies: + '@babel/helper-validator-identifier': 7.24.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + + '@csstools/css-parser-algorithms@2.6.3(@csstools/css-tokenizer@2.3.1)': + dependencies: + '@csstools/css-tokenizer': 2.3.1 + + '@csstools/css-tokenizer@2.3.1': {} + + '@csstools/media-query-list-parser@2.1.11(@csstools/css-parser-algorithms@2.6.3(@csstools/css-tokenizer@2.3.1))(@csstools/css-tokenizer@2.3.1)': + dependencies: + '@csstools/css-parser-algorithms': 2.6.3(@csstools/css-tokenizer@2.3.1) + '@csstools/css-tokenizer': 2.3.1 + + '@csstools/selector-specificity@3.0.3(postcss-selector-parser@6.0.16)': + dependencies: + postcss-selector-parser: 6.0.16 + + '@dual-bundle/import-meta-resolve@4.0.0': {} + + '@eslint-community/eslint-utils@4.4.0(eslint@9.2.0)': + dependencies: + eslint: 9.2.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.10.0': {} + + '@eslint/eslintrc@3.0.2': + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 10.0.1 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.2.0': {} + + '@firefoxic/eslint-config@1.0.0(eslint@9.2.0)': + dependencies: + '@eslint/js': 9.2.0 + '@stylistic/eslint-plugin-js': 1.8.0(eslint@9.2.0) + eslint: 9.2.0 + globals: 15.1.0 + + '@firefoxic/utils@0.1.1': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@humanwhocodes/retry@0.2.4': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@stylistic/eslint-plugin-js@1.8.0(eslint@9.2.0)': + dependencies: + '@types/eslint': 8.56.10 + acorn: 8.11.3 + escape-string-regexp: 4.0.0 + eslint: 9.2.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + + '@stylistic/stylelint-plugin@2.1.2(stylelint@16.5.0)': + dependencies: + '@csstools/css-parser-algorithms': 2.6.3(@csstools/css-tokenizer@2.3.1) + '@csstools/css-tokenizer': 2.3.1 + '@csstools/media-query-list-parser': 2.1.11(@csstools/css-parser-algorithms@2.6.3(@csstools/css-tokenizer@2.3.1))(@csstools/css-tokenizer@2.3.1) + is-plain-object: 5.0.0 + postcss-selector-parser: 6.0.16 + postcss-value-parser: 4.2.0 + style-search: 0.1.0 + stylelint: 16.5.0 + + '@types/eslint@8.56.10': + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.5': {} + + '@types/json-schema@7.0.15': {} + + acorn-jsx@5.3.2(acorn@8.11.3): + dependencies: + acorn: 8.11.3 + + acorn@8.11.3: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + astral-regex@2.0.0: {} + + balanced-match@1.0.2: {} + + balanced-match@2.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.2: + dependencies: + fill-range: 7.0.1 + + callsites@3.1.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + colord@2.9.3: {} + + concat-map@0.0.1: {} + + cosmiconfig@9.0.0: + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-functions-list@3.2.2: {} + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + + cssesc@3.0.0: {} + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + emoji-regex@8.0.0: {} + + env-paths@2.2.1: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@8.0.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.0.0: {} + + eslint@9.2.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 3.0.2 + '@eslint/js': 9.2.0 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.2.4 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.0.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@10.0.1: + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 4.0.0 + + espree@9.6.1: + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + + esquery@1.5.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.0.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + + flatted@3.3.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@14.0.0: {} + + globals@15.1.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + + globjoin@0.1.4: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + html-tags@3.3.1: {} + + husky@9.0.11: {} + + ignore@5.3.1: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-object@5.0.0: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + known-css-properties@0.30.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash.truncate@4.4.2: {} + + mathml-tag-names@2.1.3: {} + + mdn-data@2.0.30: {} + + meow@13.2.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.5: + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + ms@2.1.2: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + normalize-path@3.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-type@4.0.0: {} + + picocolors@1.0.0: {} + + picomatch@2.3.1: {} + + postcss-resolve-nested-selector@0.1.1: {} + + postcss-safe-parser@7.0.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-selector-parser@6.0.16: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + reusify@1.0.4: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + source-map-js@1.2.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-json-comments@3.1.1: {} + + style-search@0.1.0: {} + + stylelint@16.5.0: + dependencies: + '@csstools/css-parser-algorithms': 2.6.3(@csstools/css-tokenizer@2.3.1) + '@csstools/css-tokenizer': 2.3.1 + '@csstools/media-query-list-parser': 2.1.11(@csstools/css-parser-algorithms@2.6.3(@csstools/css-tokenizer@2.3.1))(@csstools/css-tokenizer@2.3.1) + '@csstools/selector-specificity': 3.0.3(postcss-selector-parser@6.0.16) + '@dual-bundle/import-meta-resolve': 4.0.0 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0 + css-functions-list: 3.2.2 + css-tree: 2.3.1 + debug: 4.3.4 + fast-glob: 3.3.2 + fastest-levenshtein: 1.0.16 + file-entry-cache: 8.0.0 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.30.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-resolve-nested-selector: 0.1.1 + postcss-safe-parser: 7.0.0(postcss@8.4.38) + postcss-selector-parser: 6.0.16 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + strip-ansi: 7.1.0 + supports-hyperlinks: 3.0.0 + svg-tags: 1.0.0 + table: 6.8.2 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.0.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + svg-tags@1.0.0: {} + + table@6.8.2: + dependencies: + ajv: 8.13.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + text-table@0.2.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + yocto-queue@0.1.0: {} diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 0000000..ec988c3 --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,197 @@ +/** @type {import('stylelint').Config} */ +export default { + plugins: [`@stylistic/stylelint-plugin`], + rules: { + "annotation-no-unknown": true, + "at-rule-empty-line-before": [ + `always`, + { + except: [ + `first-nested`, + `blockless-after-same-name-blockless`, + ], + ignore: [`after-comment`], + }, + ], + "at-rule-no-unknown": true, + "at-rule-no-vendor-prefix": true, + "block-no-empty": [ + true, + { + ignore: [`comments`], + }, + ], + "color-function-notation": `modern`, + "color-hex-alpha": `never`, + "color-hex-length": `long`, + "color-named": `never`, + "color-no-invalid-hex": true, + "comment-no-empty": true, + "custom-property-no-missing-var-function": true, + "declaration-block-no-duplicate-custom-properties": true, + "declaration-block-no-duplicate-properties": true, + "declaration-block-no-shorthand-property-overrides": true, + "declaration-block-single-line-max-declarations": 0, + "declaration-no-important": true, + "declaration-property-value-no-unknown": true, + "font-family-name-quotes": `always-where-required`, + "font-family-no-duplicate-names": [ + true, + { + ignoreFontFamilyNames: [`monospace`], + }, + ], + "font-family-no-missing-generic-family-keyword": true, + "function-calc-no-unspaced-operator": true, + "function-linear-gradient-no-nonstandard-direction": true, + "function-name-case": `lower`, + "function-no-unknown": true, + "function-url-quotes": `always`, + "import-notation": `string`, + "keyframe-block-no-duplicate-selectors": true, + "keyframe-declaration-no-important": true, + "keyframe-selector-notation": `percentage-unless-within-keyword-only-block`, + "length-zero-no-unit": [ + true, + { + ignore: [`custom-properties`], + ignoreFunctions: [ + `calc`, + `var`, + `/^--/`, + ], + }, + ], + "max-nesting-depth": [ + 2, + { + ignore: [ + `blockless-at-rules`, + `pseudo-classes`, + ], + }, + ], + "media-feature-name-no-unknown": true, + "media-feature-name-no-vendor-prefix": true, + "media-query-no-invalid": true, + "named-grid-areas-no-invalid": true, + "no-descending-specificity": true, + "no-duplicate-at-import-rules": true, + "no-duplicate-selectors": true, + "no-empty-source": true, + "no-invalid-double-slash-comments": true, + "no-invalid-position-at-import-rule": true, + "no-irregular-whitespace": true, + "number-max-precision": [ + 3, + { + insideFunctions: { + "/^(oklch|oklab)$/": 9, + }, + }, + ], + "property-no-unknown": [ + true, + { + ignoreAtRules: [`font-feature-values`], + }, + ], + "property-no-vendor-prefix": true, + "rule-empty-line-before": [ + `always`, + { + except: [`first-nested`], + ignore: [`after-comment`], + }, + ], + "selector-anb-no-unmatchable": true, + "selector-attribute-quotes": `always`, + "selector-max-compound-selectors": 2, + "selector-max-id": 0, + "selector-max-universal": 2, + "selector-not-notation": `complex`, + "selector-pseudo-class-no-unknown": true, + "selector-pseudo-element-colon-notation": `double`, + "selector-pseudo-element-no-unknown": true, + "selector-type-case": `lower`, + "selector-type-no-unknown": [ + true, + { + ignore: [`custom-elements`], + }, + ], + "string-no-newline": true, + "unit-no-unknown": true, + "value-keyword-case": `lower`, + "value-no-vendor-prefix": true, + + "@stylistic/at-rule-name-case": `lower`, + "@stylistic/at-rule-name-space-after": `always`, + "@stylistic/at-rule-semicolon-newline-after": `always`, + "@stylistic/block-closing-brace-empty-line-before": `never`, + "@stylistic/block-closing-brace-newline-after": `always`, + "@stylistic/block-closing-brace-newline-before": `always`, + "@stylistic/block-closing-brace-space-after": `always-single-line`, + "@stylistic/block-closing-brace-space-before": `always-single-line`, + "@stylistic/block-opening-brace-newline-after": `always`, + "@stylistic/block-opening-brace-space-after": `always-single-line`, + "@stylistic/block-opening-brace-space-before": `always`, + "@stylistic/color-hex-case": `lower`, + "@stylistic/declaration-bang-space-after": `never`, + "@stylistic/declaration-bang-space-before": `always`, + "@stylistic/declaration-block-semicolon-newline-after": `always`, + "@stylistic/declaration-block-semicolon-newline-before": `never-multi-line`, + "@stylistic/declaration-block-semicolon-space-after": `always-single-line`, + "@stylistic/declaration-block-semicolon-space-before": `never`, + "@stylistic/declaration-block-trailing-semicolon": `always`, + "@stylistic/declaration-colon-newline-after": `always-multi-line`, + "@stylistic/declaration-colon-space-after": `always-single-line`, + "@stylistic/declaration-colon-space-before": `never`, + "@stylistic/function-comma-newline-after": `always-multi-line`, + "@stylistic/function-comma-newline-before": `never-multi-line`, + "@stylistic/function-comma-space-after": `always-single-line`, + "@stylistic/function-comma-space-before": `never`, + "@stylistic/function-max-empty-lines": 0, + "@stylistic/function-parentheses-space-inside": `never-single-line`, + "@stylistic/function-whitespace-after": `always`, + "@stylistic/indentation": `tab`, + "@stylistic/max-empty-lines": 2, + "@stylistic/media-feature-colon-space-after": `always`, + "@stylistic/media-feature-colon-space-before": `never`, + "@stylistic/media-feature-name-case": `lower`, + "@stylistic/media-feature-parentheses-space-inside": `never`, + "@stylistic/media-feature-range-operator-space-after": `always`, + "@stylistic/media-feature-range-operator-space-before": `always`, + "@stylistic/media-query-list-comma-newline-after": `always-multi-line`, + "@stylistic/media-query-list-comma-newline-before": `never-multi-line`, + "@stylistic/media-query-list-comma-space-after": `always-single-line`, + "@stylistic/media-query-list-comma-space-before": `never-single-line`, + "@stylistic/no-eol-whitespace": true, + "@stylistic/no-extra-semicolons": true, + "@stylistic/no-missing-end-of-source-newline": true, + "@stylistic/number-leading-zero": `always`, + "@stylistic/number-no-trailing-zeros": true, + "@stylistic/property-case": `lower`, + "@stylistic/selector-attribute-brackets-space-inside": `never`, + "@stylistic/selector-attribute-operator-space-after": `never`, + "@stylistic/selector-attribute-operator-space-before": `never`, + "@stylistic/selector-combinator-space-after": `always`, + "@stylistic/selector-combinator-space-before": `always`, + "@stylistic/selector-descendant-combinator-no-non-space": true, + "@stylistic/selector-list-comma-newline-after": `always`, + "@stylistic/selector-list-comma-newline-before": `never-multi-line`, + "@stylistic/selector-list-comma-space-after": `always-single-line`, + "@stylistic/selector-list-comma-space-before": `never`, + "@stylistic/selector-max-empty-lines": 0, + "@stylistic/selector-pseudo-class-case": `lower`, + "@stylistic/selector-pseudo-class-parentheses-space-inside": `never`, + "@stylistic/selector-pseudo-element-case": `lower`, + "@stylistic/string-quotes": `double`, + "@stylistic/unit-case": `lower`, + "@stylistic/value-list-comma-newline-after": `always-multi-line`, + "@stylistic/value-list-comma-newline-before": `never-multi-line`, + "@stylistic/value-list-comma-space-after": `always-single-line`, + "@stylistic/value-list-comma-space-before": `never`, + "@stylistic/value-list-max-empty-lines": 0, + }, +} diff --git a/test/@stylistic/declaration-colon-newline-after.js b/test/@stylistic/declaration-colon-newline-after.js new file mode 100644 index 0000000..cd9364d --- /dev/null +++ b/test/@stylistic/declaration-colon-newline-after.js @@ -0,0 +1,57 @@ +import { testRule } from "../../utils/testRule.js" + +let plugin = { + name: `@stylistic/stylelint-plugin`, + prefix: `@stylistic/`, +} + +let rule = `${plugin.prefix}declaration-colon-newline-after` + +let code = ` +.valid { + background: + url("foo.png"), + url("bar.png"); + transform: + translate( + 1px, + 1px + ); +} + +.invalid { + background: url("foo.png"), + url("bar.png"); + transform: translate( + 1px, + 1px + ); +} +` + +testRule({ + description: `Multiline declarations should include a new line after a colon`, + rule, + plugin, + code, + expectedWarnings: [ + { + line: 14, + column: 12, + endLine: 14, + endColumn: 13, + rule, + severity: `error`, + text: `Expected newline after ":" with a multi-line declaration (${rule})`, + }, + { + line: 16, + column: 11, + endLine: 16, + endColumn: 12, + rule, + severity: `error`, + text: `Expected newline after ":" with a multi-line declaration (${rule})`, + }, + ], +}) diff --git a/test/annotation-no-unknown.js b/test/annotation-no-unknown.js new file mode 100644 index 0000000..abf8b2f --- /dev/null +++ b/test/annotation-no-unknown.js @@ -0,0 +1,31 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `annotation-no-unknown` + +let code = ` +.valid { + top: 0; + gap: 0 !important; +} + +.invalid { + gap: 0 !imprtant; +} +` + +testRule({ + description: `Annotations must be known`, + rule, + code, + expectedWarnings: [ + { + line: 8, + column: 9, + endLine: 8, + endColumn: 18, + rule, + severity: `error`, + text: `Unexpected unknown annotation "!imprtant" (${rule})`, + }, + ], +}) diff --git a/test/at-rule-empty-line-before.js b/test/at-rule-empty-line-before.js new file mode 100644 index 0000000..cdb21a9 --- /dev/null +++ b/test/at-rule-empty-line-before.js @@ -0,0 +1,110 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `at-rule-empty-line-before` + +let code = ` +@charset "UTF-8"; + +@import "a.css"; /* Shared-line comments do not affect. */ +@import "b.css"; +/* Ignore at-rules that follow a comment. */ +@import "c.css"; +@import "d.css"; + +@media (prefers-color-scheme: dark) { + :root { + --a: 0; + } +} + +.valid-1 { + top: 0; + + @layer layaut; +} + +@media all { + .valid-1 { + gap: 0; + } +} + +@layer foo { + @layer bar; /* Ignore first nested at-rules. */ + @layer baz; + + @layer bar { + .valid { + gap: 0; + } + } +} + +/* Invalid: */ + +@layer foo; +@custom-media --small-viewport (width > 600px); +@media (--small-viewport) { + + @layer bar; + @layer bar { + .invalid { + top: 0; + @layer bar; + } + } +} +` + +testRule({ + description: `There must be an empty line before the at-rule`, + rule, + code, + expectedWarnings: [ + { + line: 42, + column: 1, + endLine: 42, + endColumn: 48, + rule, + severity: `error`, + text: `Expected empty line before at-rule (${rule})`, + }, + { + line: 43, + column: 1, + endLine: 52, + endColumn: 2, + rule, + severity: `error`, + text: `Expected empty line before at-rule (${rule})`, + }, + { + line: 45, + column: 2, + endLine: 45, + endColumn: 13, + rule, + severity: `error`, + text: `Unexpected empty line before at-rule (${rule})`, + }, + { + line: 46, + column: 2, + endLine: 51, + endColumn: 3, + rule, + severity: `error`, + text: `Expected empty line before at-rule (${rule})`, + }, + { + line: 49, + column: 4, + endLine: 49, + endColumn: 15, + rule, + severity: `error`, + text: `Expected empty line before at-rule (${rule})`, + }, + ], +}) diff --git a/test/at-rule-no-unknown.js b/test/at-rule-no-unknown.js new file mode 100644 index 0000000..06f3c56 --- /dev/null +++ b/test/at-rule-no-unknown.js @@ -0,0 +1,123 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `at-rule-no-unknown` + +let code = ` +@charset "utf-8"; + +@import "a.css"; + +@font-face { + font-family: My Font; + src: url("./my-font.woff2") format("woff2"); +} + +@font-feature-values My Font { + @swash { + pretty: 1; + cool: 2; + } + + @annotation { + circled: 1; + black-boxed: 3; + } + + @ornaments { + fleurons: 1; + } + + @stylistic { + inscriptional-g: 1; + } + + @styleset { + geometric-m: 7; + } + + @character-variant { + beta-3: 2 3; + } +} + +@supports (gap: 0) { + a { + gap: 0; + } +} + +@page wide { + size: a4 landscape; + + @top-left-corner { + margin: 0; + } +} + +@keyframes slidein { + from { + transform: translateX(0%); + } + + to { + transform: translateX(100%); + } +} + +@counter-style thumbs { + system: cyclic; + symbols: "🐽"; + suffix: " "; +} + +@property --my-answer { + syntax: ""; + initial-value: 42; + inherits: false; +} + +@layer base; + +@layer base { + .valid { + gap: 0; + } +} + +@custom-media --small-viewport (width > 600px); + +@media (--small-viewport) { + .valid { + gap: 0; + } +} + +@container (width > 400px) { + .valid { + gap: 1em; + } +} + +@unknown-at-rule { + .invalid { + gap: 0; + } +} +` + +testRule({ + description: `At-rules must be known`, + rule, + code, + expectedWarnings: [ + { + line: 97, + column: 1, + endLine: 97, + endColumn: 17, + rule, + severity: `error`, + text: `Unexpected unknown at-rule "@unknown-at-rule" (${rule})`, + }, + ], +}) diff --git a/test/at-rule-no-vendor-prefix.js b/test/at-rule-no-vendor-prefix.js new file mode 100644 index 0000000..2fa6992 --- /dev/null +++ b/test/at-rule-no-vendor-prefix.js @@ -0,0 +1,51 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `at-rule-no-vendor-prefix` + +let code = ` +@keyframes valid { + 0% { + top: 0; + } +} + +@viewport { + orientation: landscape; +} + +@-webkit-keyframes invalid { + 0% { + top: 0; + } +} + +@-ms-viewport { + orientation: landscape; +} +` + +testRule({ + description: `At-rules must not contain vendor prefixes`, + rule, + code, + expectedWarnings: [ + { + line: 12, + column: 1, + endLine: 12, + endColumn: 19, + rule, + severity: `error`, + text: `Unexpected vendor-prefixed at-rule "@-webkit-keyframes" (${rule})`, + }, + { + line: 18, + column: 1, + endLine: 18, + endColumn: 14, + rule, + severity: `error`, + text: `Unexpected vendor-prefixed at-rule "@-ms-viewport" (${rule})`, + }, + ], +}) diff --git a/test/block-no-empty.js b/test/block-no-empty.js new file mode 100644 index 0000000..f1bea2e --- /dev/null +++ b/test/block-no-empty.js @@ -0,0 +1,101 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `block-no-empty` + +let code = ` +.valid { + gap: 0; +} + +@media print { + .valid { + gap: 0; + } +} + +.invalid-1 { +} + +.invalid-2 { + +} + +.invalid-3 { + /* foo */ +} + +@media print { + .invalid-1 { + } + + .invalid-2 { + + } + + .invalid-3 { + /* bar */ + } +} +` + +testRule({ + description: `The block must not be empty`, + rule, + code, + expectedWarnings: [ + { + line: 12, + column: 12, + endLine: 13, + endColumn: 2, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + { + line: 15, + column: 12, + endLine: 17, + endColumn: 2, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + { + line: 19, + column: 12, + endLine: 21, + endColumn: 2, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + { + line: 24, + column: 13, + endLine: 25, + endColumn: 3, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + { + line: 27, + column: 13, + endLine: 29, + endColumn: 3, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + { + line: 31, + column: 13, + endLine: 33, + endColumn: 3, + rule, + severity: `error`, + text: `Unexpected empty block (${rule})`, + }, + ], +}) diff --git a/test/declaration-block-single-line-max-declarations.js b/test/declaration-block-single-line-max-declarations.js new file mode 100644 index 0000000..050b377 --- /dev/null +++ b/test/declaration-block-single-line-max-declarations.js @@ -0,0 +1,28 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `declaration-block-single-line-max-declarations` + +let code = ` +.valid { + gap: 0; +} + +.invalid { gap: 0; } +` + +testRule({ + description: `The block must not be single-line`, + rule, + code, + expectedWarnings: [ + { + line: 6, + column: 10, + endLine: 6, + endColumn: 21, + rule, + severity: `error`, + text: `Expected no more than 0 declarations (${rule})`, + }, + ], +}) diff --git a/test/font-family-name-quotes.js b/test/font-family-name-quotes.js new file mode 100644 index 0000000..ad7e6ae --- /dev/null +++ b/test/font-family-name-quotes.js @@ -0,0 +1,52 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `font-family-name-quotes` + +let code = ` +/* The correct code */ +.valid-1 { + font: 1em arial, sans-serif; +} + +.valid-2 { + font-family: times new roman, times, serif; +} + +.valid-3 { + font-family: "Hawaii 5-0"; +} + +.invalid-1 { + font: 1em "Arial", sans-serif; +} + +.invalid-2 { + font-family: "Times New Roman", Times, serif; +} +` + +testRule({ + description: `The font name must be quoted unless it is just a valid CSS identifier`, + rule, + code, + expectedWarnings: [ + { + line: 16, + column: 12, + endLine: 16, + endColumn: 19, + rule, + severity: `error`, + text: `Unexpected quotes around "Arial" (${rule})`, + }, + { + line: 20, + column: 15, + endLine: 20, + endColumn: 32, + rule, + severity: `error`, + text: `Unexpected quotes around "Times New Roman" (${rule})`, + }, + ], +}) diff --git a/test/font-family-no-duplicate-names.js b/test/font-family-no-duplicate-names.js new file mode 100644 index 0000000..71ee212 --- /dev/null +++ b/test/font-family-no-duplicate-names.js @@ -0,0 +1,51 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `font-family-no-duplicate-names` + +let code = ` +.valid { + font-family: vollkorn, serif; +} + +.valid { + font-family: monospace, monospace; +} + +.valid { + font-family: fira-code, monospace, monospace; +} + +.invalid { + font-family: "Arial", Arial, sans-serif; +} + +.invalid { + font: 1em system-ui, sans-serif, sans-serif; +} +` + +testRule({ + description: `The font family should not be duplicated, except for the monospace keyword`, + rule, + code, + expectedWarnings: [ + { + line: 15, + column: 24, + endLine: 15, + endColumn: 29, + rule, + severity: `error`, + text: `Unexpected duplicate name Arial (${rule})`, + }, + { + line: 19, + column: 35, + endLine: 19, + endColumn: 45, + rule, + severity: `error`, + text: `Unexpected duplicate name sans-serif (${rule})`, + }, + ], +}) diff --git a/test/import-notation.js b/test/import-notation.js new file mode 100644 index 0000000..ecf96c2 --- /dev/null +++ b/test/import-notation.js @@ -0,0 +1,25 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `import-notation` + +let code = ` +@import "valid.scss"; +@import url("invalid.scss"); +` + +testRule({ + description: `The import notation must be a string`, + rule, + code, + expectedWarnings: [ + { + line: 3, + column: 9, + endLine: 3, + endColumn: 28, + rule, + severity: `error`, + text: `Expected "url("invalid.scss")" to be ""invalid.scss"" (${rule})`, + }, + ], +}) diff --git a/test/number-max-precision.js b/test/number-max-precision.js new file mode 100644 index 0000000..f572336 --- /dev/null +++ b/test/number-max-precision.js @@ -0,0 +1,82 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `number-max-precision` + +let code = ` +.valid { + gap: 0.123em; +} + +.valid { + color: oklch(69.83% 0.123456789 44.123456789); +} + +.valid { + color: oklab(0.74, 0.123456789, 0.123456789); +} + +.invalid { + gap: 0.1234em; +} + +.invalid { + color: oklch(69.83% 0.1234567891 44.1234567891); +} + +.invalid { + color: oklab(0.74, 0.1234567891, 0.1234567891); +} +` + +testRule({ + description: `Max precision must not exceed 9 within the oklch/oklab functions and 3 in all other cases.`, + rule, + code, + expectedWarnings: [ + { + line: 15, + column: 7, + endLine: 15, + endColumn: 13, + rule, + severity: `error`, + text: `Expected "0.1234" to be "0.123" (${rule})`, + }, + { + line: 19, + column: 22, + endLine: 19, + endColumn: 34, + rule, + severity: `error`, + text: `Expected "0.1234567891" to be "0.123456789" (${rule})`, + }, + { + line: 19, + column: 35, + endLine: 19, + endColumn: 48, + rule, + severity: `error`, + text: `Expected "44.1234567891" to be "44.123456789" (${rule})`, + }, + { + line: 23, + column: 21, + endLine: 23, + endColumn: 33, + rule, + severity: `error`, + text: `Expected "0.1234567891" to be "0.123456789" (${rule})`, + }, + { + line: 23, + column: 35, + endLine: 23, + endColumn: 47, + rule, + severity: `error`, + text: `Expected "0.1234567891" to be "0.123456789" (${rule})`, + }, + ], +}) diff --git a/test/property-no-unknown.js b/test/property-no-unknown.js new file mode 100644 index 0000000..290cd10 --- /dev/null +++ b/test/property-no-unknown.js @@ -0,0 +1,74 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `property-no-unknown` + +let code = ` +.valid { + --my-property: my-value; + color: #000000; +} + +@font-face { + font-family: my font; + src: url("./my-font.woff2") format("woff2"); +} + +@font-feature-values my font { + @swash { + pretty: 1; + cool: 2; + } + + @annotation { + circled: 1; + black-boxed: 3; + } + + @ornaments { + fleurons: 1; + } + + @stylistic { + inscriptional-g: 1; + } + + @styleset { + geometric-m: 7; + } + + @character-variant { + beta-3: 2 3; + } +} + +.invalid { + my-property: my-value; + colour: #000000; +} +` + +testRule({ + description: `Properties must be known`, + rule, + code, + expectedWarnings: [ + { + line: 41, + column: 2, + endLine: 41, + endColumn: 13, + rule, + severity: `error`, + text: `Unexpected unknown property "my-property" (${rule})`, + }, + { + line: 42, + column: 2, + endLine: 42, + endColumn: 8, + rule, + severity: `error`, + text: `Unexpected unknown property "colour" (${rule})`, + }, + ], +}) diff --git a/test/value-keyword-case.js b/test/value-keyword-case.js new file mode 100644 index 0000000..88c804c --- /dev/null +++ b/test/value-keyword-case.js @@ -0,0 +1,30 @@ +import { testRule } from "../utils/testRule.js" + +let rule = `value-keyword-case` + +let code = ` +.valid { + color: currentcolor; +} + +.invalid { + color: currentColor; +} +` + +testRule({ + description: `Keywords must be in lower case`, + rule, + code, + expectedWarnings: [ + { + line: 7, + column: 9, + endLine: 7, + endColumn: 10, + rule, + severity: `error`, + text: `Expected "currentColor" to be "currentcolor" (${rule})`, + }, + ], +}) diff --git a/utils/testRule.js b/utils/testRule.js new file mode 100644 index 0000000..9ac1673 --- /dev/null +++ b/utils/testRule.js @@ -0,0 +1,46 @@ +import { deepEqual } from "node:assert/strict" +import { describe, it } from "node:test" + +import stylelint from "stylelint" + +/** + * Test the specified rule with its configuration from the `stylelint.config.js` file. + * + * @param {Object} test - Test run parameters. + * @param {string} test.description - The description of the test. + * @param {string} [test.rule] - The name of the rule under test. If not specified, stylelint will use all the rules in the `stylelint.config.js` file. + * @param {Object} [test.plugin] - The plugin required for the rule under test. + * @param {string} test.code - The code for the rule under test. + * @param {Array} test.expectedWarnings - Full data of the warnings expected in the test. + * @returns {Promise} A promise that resolves when the test is complete. + */ +export async function testRule ({ description, rule, plugin, code, expectedWarnings }) { + let config = rule + ? { + plugins: plugin?.name, + rules: { + [rule]: await getRuleConfig(rule), + }, + } + : undefined + + describe(rule, () => { + it(description, async () => { + let warnings = await stylelint.lint({ code, config }).then((r) => r.results[0].warnings) + + deepEqual(warnings, expectedWarnings) + }) + }) +} + +/** + * Get the rule config from the `stylelint.config.js` file. + * + * @param {string} rule - The name of the rule. + * @returns {any} The config of the specified rule. + */ +async function getRuleConfig (rule) { + let configObject = await import(`../stylelint.config.js`).then((m) => m.default) + + return configObject.rules[rule] +}