From 10282491c9c9c39db8481342404ead189eb9d7ee Mon Sep 17 00:00:00 2001 From: francoismassart Date: Mon, 9 Dec 2024 13:59:07 +0100 Subject: [PATCH] chore: start from scratch --- CODE_OF_CONDUCT.md | 128 - CONTRIBUTING.md | 24 - LICENSE | 21 - README.md | 300 - docs/rules/classnames-order.md | 77 - .../enforces-negative-arbitrary-values.md | 91 - docs/rules/enforces-shorthand.md | 87 - docs/rules/migration-from-tailwind-2.md | 128 - docs/rules/no-arbitrary-value.md | 77 - docs/rules/no-contradicting-classname.md | 77 - docs/rules/no-custom-classname.md | 118 - docs/rules/no-unnecessary-arbitrary-value.md | 110 - lib/.prettierrc.json | 6 - lib/config/flat-recommended.js | 30 - lib/config/groups.js | 1633 ------ lib/config/recommended.js | 18 - lib/config/rules.js | 15 - lib/index.js | 28 - lib/rules/classnames-order.js | 274 - .../enforces-negative-arbitrary-values.js | 218 - lib/rules/enforces-shorthand.js | 540 -- lib/rules/migration-from-tailwind-2.js | 329 -- lib/rules/no-arbitrary-value.js | 218 - lib/rules/no-contradicting-classname.js | 265 - lib/rules/no-custom-classname.js | 221 - lib/rules/no-unnecessary-arbitrary-value.js | 362 -- lib/util/ast.js | 389 -- lib/util/cssFiles.js | 43 - lib/util/customConfig.js | 90 - lib/util/docsUrl.js | 8 - lib/util/generated.js | 10 - lib/util/groupMethods.js | 613 --- lib/util/parser.js | 30 - lib/util/prettier/order.js | 101 - lib/util/regex.js | 16 - lib/util/removeDuplicatesFromArray.js | 7 - ...eDuplicatesFromClassnamesAndWhitespaces.js | 18 - lib/util/settings.js | 50 - lib/util/types/angle.js | 11 - lib/util/types/color.js | 191 - lib/util/types/length.js | 46 - package-lock.json | 4866 ----------------- package.json | 56 - sponsors/daily.dev.jpg | Bin 42898 -> 0 bytes tests/.prettierrc.json | 6 - tests/integrations/flat-config.js | 40 - tests/integrations/flat-config/.npmrc | 1 - tests/integrations/flat-config/a.vue | 6 - .../integrations/flat-config/eslint.config.js | 13 - tests/integrations/flat-config/package.json | 13 - tests/integrations/legacy-config.js | 40 - tests/integrations/legacy-config/.eslintrc | 12 - tests/integrations/legacy-config/.npmrc | 1 - tests/integrations/legacy-config/a.vue | 6 - tests/integrations/legacy-config/package.json | 13 - tests/lib/index.js | 33 - tests/lib/rules/another.css | 45 - tests/lib/rules/arbitrary-values.js | 690 --- tests/lib/rules/classnames-order.js | 958 ---- tests/lib/rules/dummy.css | 45 - .../enforces-negative-arbitrary-values.js | 153 - tests/lib/rules/enforces-shorthand.js | 814 --- tests/lib/rules/migration-from-tailwind-2.js | 404 -- tests/lib/rules/no-arbitrary-value.js | 176 - tests/lib/rules/no-contradicting-classname.js | 775 --- tests/lib/rules/no-custom-classname.js | 1494 ----- .../rules/no-unnecessary-arbitrary-value.js | 272 - tests/lib/util/groupMethods.js | 297 - 68 files changed, 18247 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 docs/rules/classnames-order.md delete mode 100644 docs/rules/enforces-negative-arbitrary-values.md delete mode 100644 docs/rules/enforces-shorthand.md delete mode 100644 docs/rules/migration-from-tailwind-2.md delete mode 100644 docs/rules/no-arbitrary-value.md delete mode 100644 docs/rules/no-contradicting-classname.md delete mode 100644 docs/rules/no-custom-classname.md delete mode 100644 docs/rules/no-unnecessary-arbitrary-value.md delete mode 100644 lib/.prettierrc.json delete mode 100644 lib/config/flat-recommended.js delete mode 100644 lib/config/groups.js delete mode 100644 lib/config/recommended.js delete mode 100644 lib/config/rules.js delete mode 100644 lib/index.js delete mode 100644 lib/rules/classnames-order.js delete mode 100644 lib/rules/enforces-negative-arbitrary-values.js delete mode 100644 lib/rules/enforces-shorthand.js delete mode 100644 lib/rules/migration-from-tailwind-2.js delete mode 100644 lib/rules/no-arbitrary-value.js delete mode 100644 lib/rules/no-contradicting-classname.js delete mode 100644 lib/rules/no-custom-classname.js delete mode 100644 lib/rules/no-unnecessary-arbitrary-value.js delete mode 100644 lib/util/ast.js delete mode 100644 lib/util/cssFiles.js delete mode 100644 lib/util/customConfig.js delete mode 100644 lib/util/docsUrl.js delete mode 100644 lib/util/generated.js delete mode 100644 lib/util/groupMethods.js delete mode 100644 lib/util/parser.js delete mode 100644 lib/util/prettier/order.js delete mode 100644 lib/util/regex.js delete mode 100644 lib/util/removeDuplicatesFromArray.js delete mode 100644 lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js delete mode 100644 lib/util/settings.js delete mode 100644 lib/util/types/angle.js delete mode 100644 lib/util/types/color.js delete mode 100644 lib/util/types/length.js delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 sponsors/daily.dev.jpg delete mode 100644 tests/.prettierrc.json delete mode 100644 tests/integrations/flat-config.js delete mode 100644 tests/integrations/flat-config/.npmrc delete mode 100644 tests/integrations/flat-config/a.vue delete mode 100644 tests/integrations/flat-config/eslint.config.js delete mode 100644 tests/integrations/flat-config/package.json delete mode 100644 tests/integrations/legacy-config.js delete mode 100644 tests/integrations/legacy-config/.eslintrc delete mode 100644 tests/integrations/legacy-config/.npmrc delete mode 100644 tests/integrations/legacy-config/a.vue delete mode 100644 tests/integrations/legacy-config/package.json delete mode 100644 tests/lib/index.js delete mode 100644 tests/lib/rules/another.css delete mode 100644 tests/lib/rules/arbitrary-values.js delete mode 100644 tests/lib/rules/classnames-order.js delete mode 100644 tests/lib/rules/dummy.css delete mode 100644 tests/lib/rules/enforces-negative-arbitrary-values.js delete mode 100644 tests/lib/rules/enforces-shorthand.js delete mode 100644 tests/lib/rules/migration-from-tailwind-2.js delete mode 100644 tests/lib/rules/no-arbitrary-value.js delete mode 100644 tests/lib/rules/no-contradicting-classname.js delete mode 100644 tests/lib/rules/no-custom-classname.js delete mode 100644 tests/lib/rules/no-unnecessary-arbitrary-value.js delete mode 100644 tests/lib/util/groupMethods.js diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 6084b7c..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email - address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -francois.massart@gmail.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 9cbf0de..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,24 +0,0 @@ -# Contributing to `esLint-plugin-tailwindcss` - -## Contributing - -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. - -## Development - -You can use [Corepack](https://nodejs.org/api/corepack.html) to ensure you're using the same package -manager. Run `corepack enabled` before running `npm install`. - -## Pull Request Process - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a - build. -2. Update the README.md with details of changes to the interface, this includes new environment - variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 09fd7d5..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) Francois Massart - -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 deleted file mode 100644 index 74cf550..0000000 --- a/README.md +++ /dev/null @@ -1,300 +0,0 @@ -# eslint-plugin-tailwindcss - -![npm latest version](https://img.shields.io/npm/v/eslint-plugin-tailwindcss?style=for-the-badge) ![license](https://img.shields.io/npm/l/eslint-plugin-tailwindcss?style=for-the-badge) ![downloads](https://img.shields.io/npm/dt/eslint-plugin-tailwindcss?style=for-the-badge) - -![eslint-plugin-tailwindcss logo](.github/logo.png) - -Rules enforcing best practices and consistency using [Tailwind CSS](https://tailwindcss.com/). - -While you can use the official plugin [`prettier-plugin-tailwindcss`](https://www.npmjs.com/package/prettier-plugin-tailwindcss) for ordering your classnames... - -**`eslint-plugin-tailwindcss` offers more than 5 other rules, that you can benefit from on top of `prettier-plugin-tailwindcss`. Sounds good ? Keep reading πŸ‘‡** - -## Supported Rules - -Learn more about each supported rules by reading their documentation: - -- [`classnames-order`](docs/rules/classnames-order.md): order classnames for consistency and it makes merge conflict a bit easier to resolve -- [`enforces-negative-arbitrary-values`](docs/rules/enforces-negative-arbitrary-values.md): make sure to use negative arbitrary values classname without the negative classname e.g. `-top-[5px]` should become `top-[-5px]` -- [`enforces-shorthand`](docs/rules/enforces-shorthand.md): merge multiple classnames into shorthand if possible e.g. `mx-5 my-5` should become `m-5` -- [`migration-from-tailwind-2`](docs/rules/migration-from-tailwind-2.md) for easy upgrade from Tailwind CSS `v2` to `v3`. - Warning: at the moment you should [temporary turn off the `no-custom-classname`](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/88) rule if you want to see the warning from `migration-from-tailwind-2` -- [`no-arbitrary-value`](docs/rules/no-arbitrary-value.md): forbid using arbitrary values in classnames (turned off by default) -- [`no-custom-classname`](docs/rules/no-custom-classname.md): only allow classnames from Tailwind CSS and the values from the `whitelist` option -- [`no-contradicting-classname`](docs/rules/no-contradicting-classname.md): e.g. avoid `p-2 p-3`, different Tailwind CSS classnames (`pt-2` & `pt-3`) but targeting the same property several times for the same variant. -- [`no-unnecessary-arbitrary-value`](docs/rules/no-unnecessary-arbitrary-value.md): e.g. replacing `m-[1.25rem]` by its configuration based classname `m-5` - -Using ESLint extension for Visual Studio Code, you will get these messages -![detected-errors](.github/output.png) - -You can can the same information on your favorite command line software as well. - -## 🀝 Support `eslint-plugin-tailwindcss` - -| πŸ₯° How you can support us? | πŸ’ͺ They did it! | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Premium Sponsors**
Support us by becoming a sponsor.
[Become a recurring sponsor](https://github.com/sponsors/francoismassart?frequency=recurring) | | -| **Current Sponsors**
Any amount is appreciated. | @jonz94 @theMosaad @acewf @charkour @dailydotdev @codecov @sourcegraph | -| **Past sponsors**
Even if this is just a one-time thing.
[Become a backer](https://github.com/sponsors/francoismassart?frequency=one-time) | @aniravi24 @mongolyy @t3dotgg | -| **Contributors**
This project can evolve thanks to all the people who contribute.
You are welcome to [contribute](CONTRIBUTING.md) to this project by reporting issues, feature requests or even opening Pull Requests. | | -| **Supporters**
Talk about the plugin on your social networks | eslint-plugin-tailwindcss on Twitter | - -## Latest changelog - -- fix: [`no-arbitrary-value` rule is too broad](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/318) -- fix: [support `tag.div` and `tag(Component)`](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/302) (by [nihalgonsalves](https://github.com/nihalgonsalves) πŸ™) -- feat: [**support flat config and ESLint 9**](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/330) (by [kazupon](https://github.com/kazupon) πŸ™) -- feat: new rule [**`no-unnecessary-arbitrary-value`**](docs/rules/no-unnecessary-arbitrary-value.md) πŸŽ‰ -- fix: retro compatibility for older Tailwind CSS (before typescript config) -- fix: [composable touch action classnames](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/293) -- fix: [`shadow-md` + `shadow-[#color]`can be used together 🀝](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/298) -- fix: [`tabular-nums` and `slashed-zero` can be used together 🀝](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/316) -- fix: [`size-*` based `size`, `spacing`, `width` and `height` πŸ€“](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/315) -- fix: [there is no `size-screen` πŸ˜…](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/307) -- fix: [edge cases with whitespace in `enforces-shorthand`](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/308)(by [kachkaev](https://github.com/kachkaev) πŸ™) -- fix: [parsing spreads in function call returns](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/285)(by [egorpavlikhin](https://github.com/egorpavlikhin) πŸ™) -- feat: [support for Tailwind CSS 3.4.0](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/297) -- ci: [add github actions workflow](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/303) (by [nihalgonsalves](https://github.com/nihalgonsalves) πŸ™) -- fix: [bg-center mark as conflicting with bg-[image:xxx]](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/260) -- feat: [support enforcing truncate shorthand](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/255) (by [bezbac](https://github.com/bezbac) πŸ™) -- fix: [parsing spreads in object expressions](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/251) (by [bezbac](https://github.com/bezbac) πŸ™) -- fix: [do not handle non-ASCII whitespace as separator](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/246) (by [uhyo](https://github.com/uhyo) πŸ™) -- fix: [prefix support for named group/peer syntax](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/244) (by [bezbac](https://github.com/bezbac) πŸ™) -- feat: [support tailwind config in typescript](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/242) (by [quesabe](https://github.com/quesabe) πŸ™) - **You may have to upgrade your Tailwind CSS version to `3.3.2`** - -[View all releases on github](https://github.com/francoismassart/eslint-plugin-tailwindcss/releases) - -## Screencasts on our YouTube Channel - -| YouTube Channel | [ESLint plugin Tailwind CSS](https://www.youtube.com/@eslint-plugin-tailwind-css)
youtube.com/@eslint-plugin-tailwindcss | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | - -## Installation - -### 1. Install `eslint` and `eslint-plugin-tailwindcss` - -You'll first need to install [ESLint](http://eslint.org): - -``` -$ npm i -D eslint eslint-plugin-tailwindcss - -``` - -### 2. Create Configuration file - -#### `.eslintrc` - -Use .eslintrc.\* file to configure rules in ESLint < v9. See also: https://eslint.org/docs/latest/use/configure/. - -```js -module.exports = { - root: true, - extends: ["plugin:tailwindcss/recommended"], -}; -``` - -If you would like to know about configuration, Learn more in [ESLint docs](https://eslint.org/docs/latest/use/configure/configuration-files) - -#### `eslint.config.js` - -Use `eslint.config.js` file to configure rules. This is the default in ESLint v9, but can be used starting from ESLint v8.57.0. See also: https://eslint.org/docs/latest/use/configure/configuration-files-new. - -```js -import tailwind from "eslint-plugin-tailwindcss"; - -export default [...tailwind.configs["flat/recommended"]]; -``` - -If you would like to know about configuration, Learn more in [ESLint docs](https://eslint.org/docs/latest/use/configure/configuration-files-new) - -### 3. Configure ESLint parsers - -Depending on the languages you are using in your project you must tell which parser will analyze your source files. - -Our recommendations: - -#### For `.eslintrc` - -- For `js[x]`, `react`, `ts[x]`: - - Install the parser: `npm i -D @typescript-eslint/parser` - - Assign it to your files in `eslintrc`: - ```json5 - overrides: [ - { - files: ['*.ts', '*.tsx', '*.js'], - parser: '@typescript-eslint/parser', - }, - ], - ``` -- For `vue.js`: - - Install the parser: `npm i -D vue-eslint-parser` - - Assign it to your files in `eslintrc`: - ```json5 - overrides: [ - { - files: ['*.vue'], - parser: 'vue-eslint-parser', - }, - ], - ``` -- For `HTML` and similar: - - Install the parser: `npm i -D @angular-eslint/template-parser` - - Assign it to your files in `eslintrc`: - ```json5 - overrides: [ - { - files: ['*.html', '*.blade.php'], - parser: '@angular-eslint/template-parser', - }, - ], - ``` - -> We removed the default parsers which were added to `v3.8.2` because it created negative impact on dependencies resolution, bundle size increase and possible conflicts with existing configurations. - -#### For `eslint.config.js` - -- For `js[x]`, `ts[x]`: - - - Install the parser: `npm i -D @eslint/js typescript-eslint` - - Assign it to your files in `eslint.config.js`: - - ```js - import js from "@eslint/js"; - import ts from "typescript-eslint"; - import tailwind from "eslint-plugin-tailwindcss"; - - export default [ - // add eslint built-in - js.configs.recommended, - // add `typescript-eslint` flat config simply - // if you would like use more another configuration, - // see the section: https://typescript-eslint.io/getting-started#details - ...ts.configs.recommended, - ...tailwind.configs["flat/recommended"], - ]; - ``` - -- For `vue.js`: - - - Install the parser: `npm i -D eslint-plugin-vue` - - Assign it to your files in `eslint.config.js`: - - ```js - import vue from "eslint-plugin-vue"; - import tailwind from "eslint-plugin-tailwindcss"; - - export default [ - // add `eslint-plugin-vue` flat config simply - // if you would like use more another configuration, - // see the section: https://eslint.vuejs.org/user-guide/#bundle-configurations-eslint-config-js - ...vue.configs["flat/recommended"], - ...tailwind.configs["flat/recommended"], - ]; - ``` - -### 4. Add a npm script - -In your `package.json` add one or more script(s) to run eslint targeting your source files: - -```json5 -"scripts": { - "lint": "eslint ./src", - "lint:debug": "eslint ./src --debug", - "lint:fix": "eslint ./src --fix" -}, -``` - -### 5. Run the linting task - -`npm run lint` can do the job on demand but you can also get live feedback using [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), **just make sure you restart VS Code** as it can be required for the plugin to work as expected. - -## More settings - -The rules accept settings meant to fit your own choices, make sure to read the [documentation of each rule](https://github.com/francoismassart/eslint-plugin-tailwindcss/tree/master/docs/rules). - -### Optional shared settings - -Most rules share the same settings, instead of duplicating the options all over the place... - -You should define the [shared settings](https://eslint.org/docs/user-guide/configuring#adding-shared-settings) that will be shared across all the plugin rules. - -All these settings already have nice default values that are explained in the documentation. - -#### For `.eslintrc` - -FYI, here are the `default` values: - -```json5 -{ - settings: { - tailwindcss: { - // These are the default values but feel free to customize - callees: ["classnames", "clsx", "ctl"], - config: "tailwind.config.js", // returned from `loadConfig()` utility if not provided - cssFiles: [ - "**/*.css", - "!**/node_modules", - "!**/.*", - "!**/dist", - "!**/build", - ], - cssFilesRefreshRate: 5_000, - removeDuplicates: true, - skipClassAttribute: false, - whitelist: [], - tags: [], // can be set to e.g. ['tw'] for use in tw`bg-blue` - classRegex: "^class(Name)?$", // can be modified to support custom attributes. E.g. "^tw$" for `twin.macro` - }, - }, -} -``` - -#### For `eslint.config.js` - -```js -import tailwind from "eslint-plugin-tailwindcss"; - -export default [ - ...tailwind.configs["flat/recommended"], - { - settings: { - tailwindcss: { - // These are the default values but feel free to customize - callees: ["classnames", "clsx", "ctl"], - config: "tailwind.config.js", // returned from `loadConfig()` utility if not provided - cssFiles: [ - "**/*.css", - "!**/node_modules", - "!**/.*", - "!**/dist", - "!**/build", - ], - cssFilesRefreshRate: 5_000, - removeDuplicates: true, - skipClassAttribute: false, - whitelist: [], - tags: [], // can be set to e.g. ['tw'] for use in tw`bg-blue` - classRegex: "^class(Name)?$", // can be modified to support custom attributes. E.g. "^tw$" for `twin.macro` - }, - }, - }, -]; -``` - -The plugin will look for each setting in this order and stops searching as soon as it finds the settings: - -1. In the rule option argument (rule level) -2. In the shared settings (plugin level) -3. Default value of the requested setting (plugin level)... - -## Upcoming Rules - -- `validate-modifiers`: I don't know if possible, but I'd like to make sure all the modifiers prefixes of a classname are valid e.g. `yolo:bg-red` should throw an error... - -- `no-redundant-variant`: e.g. avoid `mx-5 sm:mx-5`, no need to redefine `mx` in `sm:` variant as it uses the same value (`5`) - -- `only-valid-arbitrary-values`: - - - e.g. avoid `top-[42]`, only `0` value can be unitless. - - e.g. avoid `text-[rgba(10%,20%,30,50%)]`, can't mix `%` and `0-255`. diff --git a/docs/rules/classnames-order.md b/docs/rules/classnames-order.md deleted file mode 100644 index 5fb6f92..0000000 --- a/docs/rules/classnames-order.md +++ /dev/null @@ -1,77 +0,0 @@ -# Use a consistent orders for the Tailwind CSS classnames, based on the official order (tailwindcss/classnames-order) - -Enforces a consistent order of the Tailwind CSS classnames and its variants. - -> **Note**: Since version `3.6.0`, the ordering is solely done using the [order process from the official `prettier-plugin-tailwindcss`](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) - -## Rule Details - -Examples of **incorrect** code for this rule: - -```html -
-``` - -Examples of **correct** code for this rule: - -```html -
-``` - -### Options - -```js -... -"tailwindcss/classnames-order": [, { - "callees": Array, - "config": |, - "removeDuplicates": , - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `removeDuplicates` (default: `true`) - -Duplicate classnames are automatically removed but you can always disable this behavior by setting `removeDuplicates` to `false`. - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes diff --git a/docs/rules/enforces-negative-arbitrary-values.md b/docs/rules/enforces-negative-arbitrary-values.md deleted file mode 100644 index 48f4632..0000000 --- a/docs/rules/enforces-negative-arbitrary-values.md +++ /dev/null @@ -1,91 +0,0 @@ -# Warns about `-` prefixed classnames using arbitrary values (enforces-negative-arbitrary-values) - -There are 2 ways to declare **negative arbitrary values**: - -a) Dash prefixed classname with absolute arbitrary value like `-top-[1px]` ❌ - -b) Unprefixed classname (no dash) with negative value inside the square brackets like `top-[-1px]` βœ… - -I believe, **we should always prefer the (b) approach "Unprefixed classname"** for few reasons: - -- In Tailwind CSS **v2.x.x** the (a) **was not supported** (example: https://play.tailwindcss.com/fsS91hkyKx) -- You can get nasty using (a) like `-top-[-1px]` πŸ₯΄ -- Using `var()` you simply don't know if you are dealing with a negative or positive value -- [Adam recommends the unprefixed approach πŸŽ‰](https://twitter.com/adamwathan/status/1487895306847105038) - -## Rule Details - -Examples of **incorrect** code for this rule: - -```html -
- Negative arbitrary values -
-``` - -`-right-[var(--my-var)*-1]` will generate this non sense: `right: calc(var(--my-var) * -1 * -1);` - -Examples of **correct** code for this rule: - -```html -
- Negative arbitrary values -
-``` - -### Options - -```js -... -"tailwindcss/enforces-negative-arbitrary-values": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes diff --git a/docs/rules/enforces-shorthand.md b/docs/rules/enforces-shorthand.md deleted file mode 100644 index 6b694fb..0000000 --- a/docs/rules/enforces-shorthand.md +++ /dev/null @@ -1,87 +0,0 @@ -# Replaces multiple Tailwind CSS classnames by their shorthand (enforces-shorthand) - -This rule will help you reduce the number of [Tailwind CSS](https://tailwindcss.com/) classnames by using shorthands. - -## Rule Details - -Examples of **incorrect** code for this rule: - -```html -
border shorthand
-``` - -Examples of **correct** code for this rule: - -```html -
border shorthand
-``` - -#### Limitations - -At the moment, the rule will not merge mixed classnames (e.g. using regular values AND arbitrary values). - -```html -
- won't be converted to border-0 shorthand -
-``` - -Also, unless you are using the `classnames-order` rule, the order of your classnames may be affected by the fix. -If indeed, you are using the `classnames-order` rule, then it'll be automatically re-ordered during the next lint process (depending on your autofix preferences) and you won't notice any order issue. - -### Options - -```js -... -"tailwindcss/enforces-shorthand": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes - -## Further Reading - -This rule will fix the issue for you. diff --git a/docs/rules/migration-from-tailwind-2.md b/docs/rules/migration-from-tailwind-2.md deleted file mode 100644 index c84cb64..0000000 --- a/docs/rules/migration-from-tailwind-2.md +++ /dev/null @@ -1,128 +0,0 @@ -# Detect obsolete classnames when upgrading to Tailwind CSS v3 (migration-from-tailwind-2) - -This rule helps you upgrade your codebase from [Tailwind CSS](https://tailwindcss.com/) v2 to v3, you can read more about [upgrading your Tailwind CSS projects from v2 to v3](https://tailwindcss.com/docs/upgrade-guide). - -## Rule Details - -### Obsolete classnames - -Examples of **incorrect** code for this rule: - -```html -
- Automatic transforms and filters -
-``` - -Examples of **correct** code for this rule: - -```html -
- Automatic transforms and filters -
-``` - -The classnames `backdrop-filter`, `filter` & `transform` are [not needed when you are using Tailwind CSS v3](https://tailwindcss.com/docs/upgrade-guide#automatic-transforms-and-filters) because it is always running in JIT mode. - -The rule can fix your code by removing these obsolete classnames. - -### Renamed classnames - -Examples of **incorrect** code for this rule: - -```html -
overflow-clip/ellipsis
-``` - -Examples of **correct** code for this rule: - -```html -
overflow-clip/ellipsis
-``` - -The classnames `overflow-clip` & `overflow-ellipsis` [were renamed in v3](https://tailwindcss.com/docs/upgrade-guide#overflow-clip-ellipsis) to avoid confusion with new CSS properties. As said in the official docs: - -> The old class names will always work but you’re encouraged to update to the new ones. - -This rule can replace the old classnames by the updated classnames, it will work on the following classnames: - -- `overflow-clip` => `text-clip` -- `overflow-ellipsis` => `text-ellipsis` -- `flex-grow` => `grow` -- `flex-shrink` => `shrink` -- `decoration-clone` => `box-decoration-clone` -- `decoration-slice` => `box-decoration-slice` -- `placeholder-color` => `placeholder:text-color` - -### Removed `bg-opacity` - -Examples of **incorrect** code for this rule: - -```html -
bg-opacity
-``` - -Examples of **correct** code for this rule: - -```html -
bg-opacity
-``` - -While still being supported, the `bg-opacity` utility classname was removed from the docs because you should use a suffix modifier on a `bg-color` to provide an alpha value instead. - -This rule will report the issue but **it will not fix it for you**... - -### Options - -```js -... -"tailwindcss/migration-from-tailwind-2": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes diff --git a/docs/rules/no-arbitrary-value.md b/docs/rules/no-arbitrary-value.md deleted file mode 100644 index 84f1e86..0000000 --- a/docs/rules/no-arbitrary-value.md +++ /dev/null @@ -1,77 +0,0 @@ -# Forbid using arbitrary values in classnames (no-arbitrary-value) - -Tailwind CSS 3 is Just In Time, all the time. It brings flexibility, great compilation perfs and arbitrary values. -Arbitrary values are great but can be problematic too if you wish to restrict developer to stick with the values defined in the Tailwind CSS config file. - -**By default this rule is turned `off`, if you want to use it set it to `warn` or `error`.** - -## Rule Details - -Examples of **incorrect** code for this rule: - -```html -
border width
-``` - -Examples of **correct** code for this rule: - -```html -
border width
-``` - -### Options - -```js -... -"tailwindcss/no-arbitrary-value": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes - -## Further Reading - -This rule will not fix the issue for you because it cannot guess the correct class candidate. diff --git a/docs/rules/no-contradicting-classname.md b/docs/rules/no-contradicting-classname.md deleted file mode 100644 index b96ad32..0000000 --- a/docs/rules/no-contradicting-classname.md +++ /dev/null @@ -1,77 +0,0 @@ -# Avoid contradicting Tailwind CSS classnames (e.g. "w-3 w-5") (no-contradicting-classname) - -The majority of the Tailwind CSS classes only affect a single CSS property. -Using two or more classnames which affect the same property for the same variant means trouble and confusion. - -## Rule Details - -The rule aims to warn you about contradictions in the classnames you are attaching to an element. - -Examples of **incorrect** code for this rule: - -```html -
which is the correct width ?
-``` - -Examples of **correct** code for this rule: - -```html -
padding is defined once per variant max.
-``` - -### Options - -```js -... -"tailwindcss/no-contradicting-classname": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes - -## Further Reading - -This rule will not fix the issue but will let you know about the issue. diff --git a/docs/rules/no-custom-classname.md b/docs/rules/no-custom-classname.md deleted file mode 100644 index ef63432..0000000 --- a/docs/rules/no-custom-classname.md +++ /dev/null @@ -1,118 +0,0 @@ -# Detect classnames which do not belong to Tailwind CSS (no-custom-classname) - -Enable this rule if you do not want to accept using classnames that are not defined in [Tailwind CSS](https://tailwindcss.com/). - -## Rule Details - -Examples of **incorrect** code for this rule: - -```html -
my-custom is not defined in Tailwind CSS!
-``` - -Examples of **correct** code for this rule: - -```html -
- Only Tailwind CSS classnames -
-``` - -### Options - -```js -... -"tailwindcss/no-custom-classname": [, { - "callees": Array, - "config": |, - "cssFiles": Array, - "cssFilesRefreshRate": , - "skipClassAttribute": , - "tags": Array, - "whitelist": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `cssFiles` (default: `["**/*.css", "!**/node_modules", "!**/.*", "!**/dist", "!**/build"]`) - -By default the plugin will now look for any `css` files while ignoring files in special folders (`node_modules/`, `dist/`, `build/` folders as well as hidden folders prefixed by a dot e.g. `.git/`). - -Each `css` files will be processed in order to extract the declared classnames in order to accept them. - -> If you are experiencing performance issues with this plugin, make sure to provide this setting and restrict its value to only parse the correct subset of CSS files. Read more about such cases in [PR#92](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/92) and [PR#93](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/93). - -### `cssFilesRefreshRate` (default: `5_000`) - -The plugin read and parses CSS files which can be a time consuming process depending on your files. - -By default, it runs the process if files were updated for at least 5 seconds (`5_000` ms) but you can increase this setting to enhance performances while reducing the update interval. - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `whitelist` (default: `[]`) - -The `whitelist` is empty by default but you can add custom regular expressions to this array to avoid getting warnings or errors while using your custom classes. - -For example, imagine we are using the following custom classnames: `skin-summer`, `skin-xmas`, `custom-1`, `custom-2`, `custom-3`. - -The `whitelist` options should be set to: - -- `['skin\\-(summer|xmas)', 'custom\\-[1-3]']` -- or if you don't like regular expressions (but you should): - `['skin\\-summer', 'skin\\-xmas', 'custom\\-1', 'custom\\-2', 'custom\\-3']` - -### Using negative lookahead expression - -If you want to **allow the usage of custom classname while checking the existence of specific Tailwind CSS classnames**, you can use negative lookahead expression in the whitelisted regex. - -e.g. I want to allow custom classnames while checking the validity of the `text-` and `bg-` classnames: - -```js -[ - // white list any classname which does NOT start with `bg-` and `text-` - "(?!(bg|text)\\-).*", -]; -``` - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes - -## Further Reading - -This rule will not fix the issue but will let you know about the issue. diff --git a/docs/rules/no-unnecessary-arbitrary-value.md b/docs/rules/no-unnecessary-arbitrary-value.md deleted file mode 100644 index 953a3a1..0000000 --- a/docs/rules/no-unnecessary-arbitrary-value.md +++ /dev/null @@ -1,110 +0,0 @@ -# Avoid unjustified arbitrary classnames (no-unnecessary-arbitrary-value) - -Arbitrary values are handy but you should stick to regular classnames defined in the Tailwind CSS config file as much as you can. - -## Rule Details - -Given the default configuration in which `h-auto` exists... There is no need to use an arbitrary classname. - -Examples of **incorrect** code for this rule: - -```html -
height
-``` - -Examples of **correct** code for this rule: - -```html -
height
-``` - -### The rule can handle `0` values with or without their units - -Given the default configuration in which `h-0` exists... There is no need to use an arbitrary classname. - -Examples of **incorrect** code with `0` based value: - -```html -
Use `h-0` (`0px`) instead
-``` - -Examples of **correct** code with `0` based value: - -```html - -``` - -### The rule can handle negative & double negative - -Given the default configuration... There is no need to use an arbitrary classname. - -Examples of **incorrect** code for negative arbitrary values: - -```html -
[Double] negative values
-``` - -Examples of **correct** code for negative arbitrary values: - -```html -
[Double] negative values
-``` - -### Options - -```js -... -"tailwindcss/no-unnecessary-arbitrary-value": [, { - "callees": Array, - "config": |, - "skipClassAttribute": , - "tags": Array, -}] -... -``` - -### `callees` (default: `["classnames", "clsx", "ctl", "cva", "tv"]`) - -If you use some utility library like [@netlify/classnames-template-literals](https://github.com/netlify/classnames-template-literals), you can add its name to the list to make sure it gets parsed by this rule. - -For best results, gather the declarative classnames together, avoid mixing conditional classnames in between, move them at the end. - -### `ignoredKeys` (default: `["compoundVariants", "defaultVariants"]`) - -Using libraries like `cva`, some of its object keys are not meant to contain classnames in its value(s). -You can specify which key(s) won't be parsed by the plugin using this setting. -For example, `cva` has `compoundVariants` and `defaultVariants`. -NB: As `compoundVariants` can have classnames inside its `class` property, you can also use a callee to make sure this inner part gets parsed while its parent is ignored. - -### `config` (default: generated by `tailwindcss/lib/lib/load-config`) - -By default the plugin will try to load the file returned by the official `loadConfig()` utility. - -This allows the plugin to use your customized `colors`, `spacing`, `screens`... - -You can provide another path or filename for your Tailwind CSS config file like `"config/tailwind.js"`. - -If the external file cannot be loaded (e.g. incorrect path or deleted file), an empty object `{}` will be used instead. - -It is also possible to directly inject a configuration as plain `object` like `{ prefix: "tw-", theme: { ... } }`. - -Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). - -### `skipClassAttribute` (default: `false`) - -Set `skipClassAttribute` to `true` if you only want to lint the classnames inside one of the `callees`. -While, this will avoid linting the `class` and `className` attributes, it will still lint matching `callees` inside of these attributes. - -### `tags` (default: `[]`) - -Optional, if you are using tagged templates, you should provide the tags in this array. - -### `classRegex` (default: `"^class(Name)?$"`) - -Optional, can be used to support custom attributes - -## Further Reading - -If there is exactly one equivalent regular classname, this rule will fix the issue for you by replacing the arbitrary classnames by their unique substitutes. - -But if there are several possible substitutes for an arbitrary classname, then you can manually perform the replacement. diff --git a/lib/.prettierrc.json b/lib/.prettierrc.json deleted file mode 100644 index 2a46d49..0000000 --- a/lib/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "printWidth": 120, - "semi": true, - "singleQuote": true, - "trailingComma": "es5" -} diff --git a/lib/config/flat-recommended.js b/lib/config/flat-recommended.js deleted file mode 100644 index d6d103c..0000000 --- a/lib/config/flat-recommended.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @fileoverview Recommended coniguration for flat style - * @see https://eslint.org/docs/latest/use/configure/configuration-files-new - * @author François Massart - */ -'use strict'; - -const rules = require('./rules'); - -module.exports = [ - { - name: 'tailwindcss:base', - plugins: { - get tailwindcss() { - return require('../index'); - }, - }, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - }, - { - name: 'tailwindcss:rules', - rules, - }, -]; diff --git a/lib/config/groups.js b/lib/config/groups.js deleted file mode 100644 index 8ab91d5..0000000 --- a/lib/config/groups.js +++ /dev/null @@ -1,1633 +0,0 @@ -/** - * @fileoverview Default groups for Tailwind CSS classnames - * @description The hierarchy of `members` can be useful to detect redundant and/or contradicting classnames - * @version v3.1.3 - * @see https://tailwindcss.com/docs - * @author François Massart - */ -module.exports.groups = [ - { - type: 'Core Concepts', - members: [ - { - type: 'Hover, Focus, & Other States', - members: [ - { - type: 'group', - members: 'group', - }, - { - type: 'peer', - members: 'peer', - }, - ], - }, - { - type: 'Dark Mode', - members: '${dark}', - }, - { - type: 'Arbitrary properties', - members: '${arbitraryProperties}', - }, - ], - }, - { - type: 'Layout', - members: [ - { - type: 'Aspect Ratio', - members: 'aspect\\-(?${aspectRatio})', - configKey: 'aspectRatio', - }, - { - type: 'Container', - members: 'container', - }, - { - type: 'Columns', - members: 'columns\\-(?${columns})', - configKey: 'columns', - }, - { - type: 'Break After', - members: 'break\\-after\\-(?auto|avoid|all|avoid\\-page|page|left|right|column)', - }, - { - type: 'Break Before', - members: 'break\\-before\\-(?auto|avoid|all|avoid\\-page|page|left|right|column)', - }, - { - type: 'Break Inside', - members: 'break\\-inside\\-(?auto|avoid|avoid\\-page|avoid\\-column)', - }, - { - type: 'Box Decoration Break', - members: 'box\\-decoration\\-(?clone|slice)', - }, - { - type: 'Deprecated Box Decoration Break', - members: 'decoration\\-(?clone|slice)', - deprecated: true, - }, - { - type: 'Box Sizing', - members: 'box\\-(?border|content)', - }, - { - type: 'Display', - members: - 'block|flex|grid|flow\\-root|contents|hidden|inline(\\-(block|flex|table|grid))?|table\\-(column|footer|header|row)\\-group|table(\\-(caption|row|cell|column))?|list\\-item', - }, - { - type: 'Floats', - members: 'float\\-(?right|left|none)', - }, - { - type: 'Clear', - members: 'clear\\-(?left|right|both|none)', - }, - { - type: 'Isolation', - members: '(isolate|isolation\\-auto)', - }, - { - type: 'Object Fit', - members: 'object\\-(?contain|cover|fill|none|scale\\-down)', - }, - { - type: 'Object Position', - members: 'object\\-(?${objectPosition})', - configKey: 'objectPosition', - }, - { - type: 'Overflow', - members: [ - { - type: 'overflow', - members: 'overflow\\-(?auto|hidden|clip|visible|scroll)', - shorthand: 'all', - body: 'overflow', - }, - { - type: 'overflow-x', - members: 'overflow\\-x\\-(?auto|hidden|clip|visible|scroll)', - shorthand: 'x', - body: 'overflow-x', - }, - { - type: 'overflow-y', - members: 'overflow\\-y\\-(?auto|hidden|clip|visible|scroll)', - shorthand: 'y', - body: 'overflow-y', - }, - ], - }, - { - type: 'Overscroll Behavior', - members: [ - { - type: 'overscroll', - members: 'overscroll\\-(?auto|contain|none)', - shorthand: 'all', - body: 'overscroll', - }, - { - type: 'overscroll-x', - members: 'overscroll\\-x\\-(?auto|contain|none)', - shorthand: 'x', - body: 'overscroll-x', - }, - { - type: 'overscroll-y', - members: 'overscroll\\-y\\-(?auto|contain|none)', - shorthand: 'y', - body: 'overscroll-y', - }, - ], - }, - { - type: 'Position', - members: 'static|fixed|absolute|relative|sticky', - }, - { - type: 'Top / Right / Bottom / Left', - members: [ - { - type: 'inset', - members: '(inset\\-(?${inset})|\\-inset\\-(?${-inset}))', - shorthand: 'all', - body: 'inset', - configKey: 'inset', - }, - { - type: 'inset-y', - members: '(inset\\-y\\-(?${inset})|\\-inset\\-y\\-(?${-inset}))', - shorthand: 'y', - body: 'inset-y', - configKey: 'inset', - }, - { - type: 'inset-x', - members: '(inset\\-x\\-(?${inset})|\\-inset\\-x\\-(?${-inset}))', - shorthand: 'x', - body: 'inset-x', - configKey: 'inset', - }, - { - type: 'top', - members: '(top\\-(?${inset})|\\-top\\-(?${-inset}))', - shorthand: 't', - body: 'top', - configKey: 'inset', - }, - { - type: 'right', - members: '(right\\-(?${inset})|\\-right\\-(?${-inset}))', - shorthand: 'r', - body: 'right', - configKey: 'inset', - }, - { - type: 'bottom', - members: '(bottom\\-(?${inset})|\\-bottom\\-(?${-inset}))', - shorthand: 'b', - body: 'bottom', - configKey: 'inset', - }, - { - type: 'left', - members: '(left\\-(?${inset})|\\-left\\-(?${-inset}))', - shorthand: 'l', - body: 'left', - configKey: 'inset', - }, - ], - }, - { - type: 'Visibility', - members: '(in)?visible|collapse', - }, - { - type: 'Z-Index', - members: '(z\\-(?${zIndex})|\\-z\\-(?${-zIndex}))', - configKey: 'zIndex', - }, - ], - }, - { - type: 'Flexbox & Grid', - members: [ - { - type: 'Flex Basis', - members: 'basis\\-(?${flexBasis})', - configKey: 'flexBasis', - }, - { - type: 'Flex Direction', - members: 'flex\\-(row|col)(\\-reverse)?', - }, - { - type: 'Flex Wrap', - members: 'flex\\-(wrap(\\-reverse)?|nowrap)', - }, - { - type: 'Flex', - members: 'flex\\-(?${flex})', - configKey: 'flex', - }, - { - type: 'Flex Grow', - members: 'grow(\\-(?${flexGrow}))?', - configKey: 'flexGrow', - }, - { - type: 'Deprecated Flex Grow', - members: 'flex\\-grow(\\-(?${flexGrow}))?', - deprecated: true, - configKey: 'flexGrow', - }, - { - type: 'Flex Shrink', - members: 'shrink(\\-(?${flexShrink}))?', - configKey: 'flexShrink', - }, - { - type: 'Deprecated Flex Shrink', - members: 'flex\\-shrink(\\-(?${flexShrink}))?', - deprecated: true, - configKey: 'flexShrink', - }, - { - type: 'Order', - members: '(order\\-(?${order})|\\-order\\-(?${-order}))', - configKey: 'order', - }, - { - type: 'Grid Template Columns', - members: 'grid\\-cols\\-(?${gridTemplateColumns})', - configKey: 'gridTemplateColumns', - }, - { - type: 'Grid Column Start / End', - members: [ - { - type: 'grid-column', - members: 'col\\-(?${gridColumn})', - configKey: 'gridColumn', - }, - { - type: 'grid-column-start', - members: 'col\\-start\\-(?${gridColumnStart})', - configKey: 'gridColumnStart', - }, - { - type: 'grid-column-end', - members: 'col\\-end\\-(?${gridColumnEnd})', - configKey: 'gridColumnEnd', - }, - ], - }, - { - type: 'Grid Template Rows', - members: 'grid\\-rows\\-(?${gridTemplateRows})', - configKey: 'gridTemplateRows', - }, - { - type: 'Grid Row Start / End', - members: [ - { - type: 'grid-row', - members: 'row\\-(?${gridRow})', - configKey: 'gridRow', - }, - { - type: 'grid-row-start', - members: 'row\\-start\\-(?${gridRowStart})', - configKey: 'gridRowStart', - }, - { - type: 'grid-row-end', - members: 'row\\-end\\-(?${gridRowEnd})', - configKey: 'gridRowEnd', - }, - ], - }, - { - type: 'Grid Auto Flow', - members: 'grid\\-flow\\-(dense|(row|col)(\\-dense)?)', - }, - { - type: 'Grid Auto Columns', - members: 'auto\\-cols\\-(?${gridAutoColumns})', - configKey: 'gridAutoColumns', - }, - { - type: 'Grid Auto Rows', - members: 'auto\\-rows\\-(?${gridAutoRows})', - configKey: 'gridAutoRows', - }, - { - type: 'Gap', - members: [ - { - type: 'gap', - members: 'gap\\-(?${gap})', - shorthand: 'all', - body: 'gap', - configKey: 'gap', - }, - { - type: 'column-gap', - members: 'gap\\-x\\-(?${gap})', - shorthand: 'x', - body: 'gap-x', - configKey: 'gap', - }, - { - type: 'row-gap', - members: 'gap\\-y\\-(?${gap})', - shorthand: 'y', - body: 'gap-y', - configKey: 'gap', - }, - ], - }, - { - type: 'Justify Content', - members: 'justify\\-(start|end|center|between|around|evenly)', - }, - { - type: 'Justify Items', - members: 'justify\\-items\\-(start|end|center|stretch)', - }, - { - type: 'Justify Self', - members: 'justify\\-self\\-(auto|start|end|center|stretch)', - }, - { - type: 'Align Content', - members: 'content\\-(center|start|end|between|around|evenly|baseline)', - }, - { - type: 'Align Items', - members: 'items\\-(start|end|center|baseline|stretch)', - }, - { - type: 'Align Self', - members: 'self\\-(auto|start|end|center|stretch|baseline)', - }, - { - type: 'Place Content', - members: 'place\\-content\\-(center|start|end|between|around|evenly|stretch|baseline)', - }, - { - type: 'Place Items', - members: 'place\\-items\\-(start|end|center|stretch|baseline)', - }, - { - type: 'Place Self', - members: 'place\\-self\\-(auto|start|end|center|stretch)', - }, - ], - }, - { - type: 'Spacing', - members: [ - { - type: 'Padding', - members: [ - { - type: 'p', - members: 'p\\-(?${padding})', - shorthand: 'all', - body: 'p', - configKey: 'padding', - }, - { - type: 'py', - members: 'py\\-(?${padding})', - shorthand: 'y', - body: 'py', - configKey: 'padding', - }, - { - type: 'px', - members: 'px\\-(?${padding})', - shorthand: 'x', - body: 'px', - configKey: 'padding', - }, - { - type: 'pt', - members: 'pt\\-(?${padding})', - shorthand: 't', - body: 'pt', - configKey: 'padding', - }, - { - type: 'pr', - members: 'pr\\-(?${padding})', - shorthand: 'r', - body: 'pr', - configKey: 'padding', - }, - { - type: 'pb', - members: 'pb\\-(?${padding})', - shorthand: 'b', - body: 'pb', - configKey: 'padding', - }, - { - type: 'pl', - members: 'pl\\-(?${padding})', - shorthand: 'l', - body: 'pl', - configKey: 'padding', - }, - ], - }, - { - type: 'Margin', - members: [ - { - type: 'm', - members: '(m\\-(?${margin})|\\-m\\-(?${-margin}))', - shorthand: 'all', - body: 'm', - configKey: 'margin', - }, - { - type: 'my', - members: '(my\\-(?${margin})|\\-my\\-(?${-margin}))', - shorthand: 'y', - body: 'my', - configKey: 'margin', - }, - { - type: 'mx', - members: '(mx\\-(?${margin})|\\-mx\\-(?${-margin}))', - shorthand: 'x', - body: 'mx', - configKey: 'margin', - }, - { - type: 'mt', - members: '(mt\\-(?${margin})|\\-mt\\-(?${-margin}))', - shorthand: 't', - body: 'mt', - configKey: 'margin', - }, - { - type: 'mr', - members: '(mr\\-(?${margin})|\\-mr\\-(?${-margin}))', - shorthand: 'r', - body: 'mr', - configKey: 'margin', - }, - { - type: 'mb', - members: '(mb\\-(?${margin})|\\-mb\\-(?${-margin}))', - shorthand: 'b', - body: 'mb', - configKey: 'margin', - }, - { - type: 'ml', - members: '(ml\\-(?${margin})|\\-ml\\-(?${-margin}))', - shorthand: 'l', - body: 'ml', - configKey: 'margin', - }, - ], - }, - { - type: 'Space Between', - members: [ - { - type: 'space-y', - members: '(space\\-y\\-(?${space})|\\-space\\-y\\-(?${-space}))', - configKey: 'space', - }, - { - type: 'space-x', - members: '(space\\-x\\-(?${space})|\\-space\\-x\\-(?${-space}))', - configKey: 'space', - }, - { - type: 'space-y-reverse', - members: 'space\\-y\\-reverse', - }, - { - type: 'space-x-reverse', - members: 'space\\-x\\-reverse', - }, - ], - }, - ], - }, - { - type: 'Sizing', - members: [ - { - type: 'Width', - members: 'w\\-(?${width})', - configKey: 'width', - }, - { - type: 'Min-Width', - members: 'min\\-w\\-(?${minWidth})', - configKey: 'minWidth', - }, - { - type: 'Max-Width', - members: 'max\\-w\\-(?${maxWidth})', - configKey: 'maxWidth', - }, - { - type: 'Height', - members: 'h\\-(?${height})', - configKey: 'height', - }, - { - type: 'Min-Height', - members: 'min\\-h\\-(?${minHeight})', - configKey: 'minHeight', - }, - { - type: 'Max-Height', - members: 'max\\-h\\-(?${maxHeight})', - configKey: 'maxHeight', - }, - { - type: 'Size', - members: 'size\\-(?${size})', - configKey: 'size', - }, - ], - }, - { - type: 'Typography', - members: [ - { - type: 'Font Family', - members: 'font\\-(?${fontFamily})', - configKey: 'fontFamily', - }, - { - type: 'Font Size', - members: 'text\\-(?${fontSize})', - configKey: 'fontSize', - }, - { - type: 'Font Smoothing', - members: '(subpixel\\-)?antialiased', - }, - { - type: 'Font Style', - members: '(not\\-)?italic', - }, - { - type: 'Font Weight', - members: 'font\\-(?${fontWeight})', - configKey: 'fontWeight', - }, - { - type: 'Font Variant Numeric', - members: [ - { - type: 'Normal Nums', - members: 'normal\\-nums', - }, - { - type: 'Ordinal', - members: 'ordinal', - }, - { - type: 'Slashed Zero', - members: 'slashed-zero', - }, - { - type: 'Style Nums', - members: '(lining|oldstyle)\\-nums', - }, - { - type: 'Proportinal or Tabular', - members: '(proportional|tabular)\\-nums', - }, - { - type: 'Fractions', - members: '(diagonal|stacked)\\-fractions', - }, - ], - }, - { - type: 'Letter Spacing', - members: '(tracking\\-(?${letterSpacing})|\\-tracking\\-(?${-letterSpacing}))', - configKey: 'letterSpacing', - }, - // { - // type: 'Line Clamp', - // members: 'line\\-clamp\\-(?${lineClamp})', - // configKey: 'lineClamp', - // }, - { - type: 'Line Height', - members: 'leading\\-(?${lineHeight})', - configKey: 'lineHeight', - }, - // { - // type: 'List Style Image', - // members: 'list\\-image\\-(?${listStyleImage})', - // configKey: 'listStyleImage', - // }, - { - type: 'List Style Type', - members: 'list\\-(?${listStyleType})', - configKey: 'listStyleType', - }, - { - type: 'List Style Position', - members: 'list\\-(in|out)side', - }, - { - type: 'Text Alignment', - members: 'text\\-(left|center|right|justify|start|end)', - }, - { - type: 'Text Color', - members: 'text\\-(?${textColor})', - configKey: 'colors', - }, - { - type: 'Text Decoration', - members: '(no\\-)?underline|overline|line\\-through', - }, - { - type: 'Text Decoration Color', - members: 'decoration\\-(?${colors})', - configKey: 'colors', - }, - { - type: 'Text Decoration Style', - members: 'decoration\\-(solid|double|dotted|dashed|wavy)', - }, - { - type: 'Text Decoration Thickness', - members: 'decoration\\-(?${textDecorationThickness})', - configKey: 'textDecorationThickness', - }, - { - type: 'Text Underline Offset', - members: 'underline\\-offset\\-(?${textUnderlineOffset})', - configKey: 'textUnderlineOffset', - }, - { - type: 'Text Transform', - members: '(upper|lower|normal\\-)case|capitalize', - }, - { - type: 'Text Overflow', - members: 'truncate|text\\-(ellipsis|clip)', - }, - { - type: 'Deprecated Text Overflow', - members: 'overflow\\-(ellipsis|clip)', - deprecated: true, - }, - { - type: 'Text Wrap', - members: 'text\\-(wrap|nowrap|balance|pretty)', - }, - { - type: 'Text Indent', - members: '(indent\\-(?${textIndent})|\\-indent\\-(?${-textIndent}))', - configKey: 'textIndent', - }, - { - type: 'Vertical Alignment', - members: 'align\\-(baseline|top|middle|bottom|text\\-(top|bottom)|sub|super)', - }, - { - type: 'Whitespace', - members: 'whitespace\\-(normal|nowrap|pre(\\-(line|wrap))?)', - }, - { - type: 'Word Break', - members: 'break\\-(normal|words|all|keep)', - }, - { - type: 'Content', - members: 'content\\-(?${content})', - configKey: 'content', - }, - ], - }, - { - type: 'Backgrounds', - members: [ - { - type: 'Background Image URL', - members: 'bg\\-\\[(image\\:|url\\()(?${backgroundImageUrl})\\)\\]', - }, - { - type: 'Background Attachment', - members: 'bg\\-(fixed|local|scroll)', - }, - { - type: 'Background Clip', - members: 'bg\\-clip\\-(border|padding|content|text)', - }, - { - type: 'Background Color', - members: 'bg\\-(?${colors})', - configKey: 'colors', - }, - { - type: 'Deprecated Background Opacity', - members: 'bg\\-opacity\\-(?${backgroundOpacity})', - deprecated: true, - }, - { - type: 'Background Origin', - members: 'bg\\-origin\\-(border|padding|content)', - }, - { - type: 'Background Position', - members: 'bg\\-(?${backgroundPosition})', - configKey: 'backgroundPosition', - }, - { - type: 'Background Repeat', - members: 'bg\\-(no\\-repeat|repeat(\\-(x|y|round|space))?)', - }, - { - type: 'Background Size', - members: 'bg\\-(?${backgroundSize})', - configKey: 'backgroundSize', - }, - { - type: 'Background Image', - members: 'bg\\-(?${backgroundImage})', - configKey: 'backgroundImage', - }, - { - type: 'Gradient Color Stops', - members: [ - { - type: 'from', - members: 'from\\-(?${gradientColorStopPositions})', - configKey: 'gradientColorStopPositions', - }, - { - type: 'via', - members: 'via\\-(?${gradientColorStopPositions})', - configKey: 'gradientColorStopPositions', - }, - { - type: 'to', - members: 'to\\-(?${gradientColorStopPositions})', - configKey: 'gradientColorStopPositions', - }, - ], - }, - ], - }, - { - type: 'Borders', - members: [ - { - type: 'Border Radius', - members: [ - { - type: 'border-radius', - members: 'rounded(\\-(?${borderRadius}))?', - shorthand: 'all', - body: 'rounded', - configKey: 'borderRadius', - }, - { - type: 'border-radius-top', - members: 'rounded\\-t(\\-(?${borderRadius}))?', - shorthand: 't', - body: 'rounded-t', - configKey: 'borderRadius', - }, - { - type: 'border-radius-right', - members: 'rounded\\-r(\\-(?${borderRadius}))?', - shorthand: 'r', - body: 'rounded-r', - configKey: 'borderRadius', - }, - { - type: 'border-radius-bottom', - members: 'rounded\\-b(\\-(?${borderRadius}))?', - shorthand: 'b', - body: 'rounded-b', - configKey: 'borderRadius', - }, - { - type: 'border-radius-left', - members: 'rounded\\-l(\\-(?${borderRadius}))?', - shorthand: 'l', - body: 'rounded-l', - configKey: 'borderRadius', - }, - { - type: 'border-radius-top-left', - members: 'rounded\\-tl(\\-(?${borderRadius}))?', - shorthand: 'tl', - body: 'rounded-tl', - configKey: 'borderRadius', - }, - { - type: 'border-radius-top-right', - members: 'rounded\\-tr(\\-(?${borderRadius}))?', - shorthand: 'tr', - body: 'rounded-tr', - configKey: 'borderRadius', - }, - { - type: 'border-radius-bottom-right', - members: 'rounded\\-br(\\-(?${borderRadius}))?', - shorthand: 'br', - body: 'rounded-br', - configKey: 'borderRadius', - }, - { - type: 'border-radius-bottom-left', - members: 'rounded\\-bl(\\-(?${borderRadius}))?', - shorthand: 'bl', - body: 'rounded-bl', - configKey: 'borderRadius', - }, - ], - }, - { - type: 'Border Width', - members: [ - { - type: 'border-width', - members: 'border(\\-(?${borderWidth}))?', - shorthand: 'all', - body: 'border', - configKey: 'borderWidth', - }, - { - type: 'border-y-width', - members: 'border\\-y(\\-(?${borderWidth}))?', - shorthand: 'y', - body: 'border-y', - configKey: 'borderWidth', - }, - { - type: 'border-x-width', - members: 'border\\-x(\\-(?${borderWidth}))?', - shorthand: 'x', - body: 'border-x', - configKey: 'borderWidth', - }, - { - type: 'border-top-width', - members: 'border\\-t(\\-(?${borderWidth}))?', - shorthand: 't', - body: 'border-t', - configKey: 'borderWidth', - }, - { - type: 'border-right-width', - members: 'border\\-r(\\-(?${borderWidth}))?', - shorthand: 'r', - body: 'border-r', - configKey: 'borderWidth', - }, - { - type: 'border-bottom-width', - members: 'border\\-b(\\-(?${borderWidth}))?', - shorthand: 'b', - body: 'border-b', - configKey: 'borderWidth', - }, - { - type: 'border-left-width', - members: 'border\\-l(\\-(?${borderWidth}))?', - shorthand: 'l', - body: 'border-l', - configKey: 'borderWidth', - }, - ], - }, - { - type: 'Border Color', - members: [ - { - type: 'border-color', - members: 'border\\-(?${borderColor})', - shorthand: 'all', - body: 'border', - configKey: 'borderColor', - }, - { - type: 'border-y-color', - members: 'border\\-y\\-(?${borderColor})', - shorthand: 'y', - body: 'border-y', - configKey: 'borderColor', - }, - { - type: 'border-x-color', - members: 'border\\-x\\-(?${borderColor})', - shorthand: 'x', - body: 'border-x', - configKey: 'borderColor', - }, - { - type: 'border-top-color', - members: 'border\\-t\\-(?${borderColor})', - shorthand: 't', - body: 'border-t', - configKey: 'borderColor', - }, - { - type: 'border-right-color', - members: 'border\\-r\\-(?${borderColor})', - shorthand: 'r', - body: 'border-r', - configKey: 'borderColor', - }, - { - type: 'border-bottom-color', - members: 'border\\-b\\-(?${borderColor})', - shorthand: 'b', - body: 'border-b', - configKey: 'borderColor', - }, - { - type: 'border-left-color', - members: 'border\\-l\\-(?${borderColor})', - shorthand: 'l', - body: 'border-l', - configKey: 'borderColor', - }, - ], - }, - { - type: 'Deprecated Border Opacity', - members: 'border\\-opacity\\-(?${borderOpacity})', - deprecated: true, - configKey: 'borderOpacity', - }, - { - type: 'Border Style', - members: 'border\\-(solid|dashed|dotted|double|hidden|none)', - }, - { - type: 'Divide Width', - members: [ - { - type: 'divide-y', - members: 'divide\\-y(\\-(?${divideWidth}))?', - configKey: 'divideWidth', - }, - { - type: 'divide-x', - members: 'divide\\-x(\\-(?${divideWidth}))?', - configKey: 'divideWidth', - }, - { - type: 'divide-y-reverse', - members: 'divide\\-y\\-reverse', - }, - { - type: 'divide-x-reverse', - members: 'divide\\-x\\-reverse', - }, - ], - }, - { - type: 'Divide Color', - members: 'divide\\-(?${divideColor})', - configKey: 'divideColor', - }, - { - type: 'Divide Style', - members: 'divide\\-(solid|dashed|dotted|double|none)', - }, - { - type: 'Outline Width', - members: 'outline\\-(?${outlineWidth})', - configKey: 'outlineWidth', - }, - { - type: 'Outline Color', - members: 'outline\\-(?${outlineColor})', - configKey: 'outlineColor', - }, - { - type: 'Outline Style', - members: 'outline(\\-(none|dashed|dotted|double|hidden))?', - }, - { - type: 'Outline Offset', - members: - '(outline\\-offset\\-(?${outlineOffset})|\\-outline\\-offset\\-(?${-outlineOffset}))', - configKey: 'outlineOffset', - }, - { - type: 'Ring Width', - members: [ - { - type: 'ring', - members: 'ring(\\-(?${ringWidth}))?', - configKey: 'ringWidth', - }, - ], - }, - { - type: 'Ring Inset', - members: [ - { - type: 'ring-inset', - members: 'ring\\-inset', - }, - ], - }, - { - type: 'Ring Color', - members: 'ring\\-(?${colors})', - configKey: 'colors', - }, - { - type: 'Deprecated Ring Opacity', - members: 'ring\\-opacity\\-(?${ringOpacity})', - deprecated: true, - configKey: 'ringOpacity', - }, - { - type: 'Ring Offset Width', - members: 'ring\\-offset\\-(?${ringOffsetWidth})', - configKey: 'ringOffsetWidth', - }, - { - type: 'Ring Offset Color', - members: 'ring\\-offset\\-(?${colors})', - configKey: 'colors', - }, - ], - }, - { - type: 'Effects', - members: [ - { - type: 'Box Shadow', - members: 'shadow(\\-(?${boxShadow}))?', - configKey: 'boxShadow', - }, - { - type: 'Box Shadow Color', - members: 'shadow(\\-(?${boxShadowColor}))?', - configKey: 'boxShadowColor', - }, - { - type: 'Opacity', - members: 'opacity\\-(?${opacity})', - configKey: 'opacity', - }, - { - type: 'Mix Blend Mode', - members: - 'mix\\-blend\\-(normal|multiply|screen|overlay|darken|lighten|color\\-(burn|dodge)|(hard|soft)\\-light|difference|exclusion|hue|saturation|color|luminosity|plus\\-lighter)', - }, - { - type: 'Background Blend Mode', - members: - 'bg\\-blend\\-(normal|multiply|screen|overlay|darken|lighten|color\\-(dodge|burn)|(hard|soft)\\-light|difference|exclusion|hue|saturation|color|luminosity)', - }, - ], - }, - { - type: 'Filters', - members: [ - { - type: 'Deprecated Filter', - members: 'filter', - deprecated: true, - }, - { - type: 'Blur', - members: 'blur(\\-(?${blur}))?', - configKey: 'blur', - }, - { - type: 'Brightness', - members: 'brightness\\-(?${brightness})', - configKey: 'brightness', - }, - { - type: 'Contrast', - members: 'contrast\\-(?${contrast})', - configKey: 'contrast', - }, - { - type: 'Drop Shadow', - members: 'drop\\-shadow(\\-(?${dropShadow}))?', - configKey: 'dropShadow', - }, - { - type: 'Grayscale', - members: 'grayscale(\\-(?${grayscale}))?', - configKey: 'grayscale', - }, - { - type: 'Hue Rotate', - members: 'hue\\-rotate\\-(?${hueRotate})|\\-hue\\-rotate\\-(?${-hueRotate})', - configKey: 'hueRotate', - }, - { - type: 'Invert', - members: 'invert(\\-(?${invert}))?', - configKey: 'invert', - }, - { - type: 'Saturate', - members: 'saturate\\-(?${saturate})', - configKey: 'saturate', - }, - { - type: 'Sepia', - members: 'sepia(\\-(?${sepia}))?', - configKey: 'sepia', - }, - { - type: 'Backdrop Blur', - members: 'backdrop\\-blur(\\-(?${backdropBlur}))?', - configKey: 'backdropBlur', - }, - { - type: 'Backdrop Brightness', - members: 'backdrop\\-brightness\\-(?${backdropBrightness})', - configKey: 'backdropBrightness', - }, - { - type: 'Backdrop Contrast', - members: 'backdrop\\-contrast\\-(?${backdropContrast})', - configKey: 'backdropContrast', - }, - { - type: 'Backdrop Grayscale', - members: 'backdrop\\-grayscale(\\-(?${backdropGrayscale}))?', - configKey: 'backdropGrayscale', - }, - { - type: 'Backdrop Hue Rotate', - members: - 'backdrop\\-hue\\-rotate\\-(?${backdropHueRotate})|\\-backdrop\\-hue\\-rotate\\-(?${-backdropHueRotate})', - configKey: 'backdropHueRotate', - }, - { - type: 'Backdrop Invert', - members: 'backdrop\\-invert(\\-(?${backdropInvert}))?', - configKey: 'backdropInvert', - }, - { - type: 'Backdrop Opacity', - members: 'backdrop\\-opacity\\-(?${backdropOpacity})', - configKey: 'backdropOpacity', - }, - { - type: 'Backdrop Saturate', - members: 'backdrop\\-saturate\\-(?${backdropSaturate})', - configKey: 'backdropSaturate', - }, - { - type: 'Backdrop Sepia', - members: 'backdrop\\-sepia(\\-(?${backdropSepia}))?', - configKey: 'backdropSepia', - }, - ], - }, - { - type: 'Tables', - members: [ - { - type: 'Border Collapse', - members: 'border\\-(collapse|separate)', - }, - { - type: 'Border Spacing', - members: [ - { - type: 'border-spacing', - members: 'border\\-spacing\\-(?${borderSpacing})', - shorthand: 'all', - body: 'border-spacing', - configKey: 'borderSpacing', - }, - { - type: 'border-spacing-x', - members: 'border\\-spacing\\-x\\-(?${borderSpacing})', - shorthand: 'x', - body: 'border-spacing-x', - configKey: 'borderSpacing', - }, - { - type: 'border-spacing-y', - members: 'border\\-spacing\\-y\\-(?${borderSpacing})', - shorthand: 'y', - body: 'border-spacing-y', - configKey: 'borderSpacing', - }, - ], - }, - { - type: 'Table Layout', - members: 'table\\-(auto|fixed)', - }, - ], - }, - { - type: 'Transitions & Animation', - members: [ - { - type: 'Transition Property', - members: 'transition(\\-(?${transitionProperty}))?', - configKey: 'transitionProperty', - }, - { - type: 'Transition Duration', - members: 'duration(\\-(?${transitionDuration}))?', - configKey: 'transitionDuration', - }, - { - type: 'Transition Timing Function', - members: 'ease(\\-(?${transitionTimingFunction}))?', - configKey: 'transitionTimingFunction', - }, - { - type: 'Transition Delay', - members: 'delay\\-(?${transitionDelay})', - configKey: 'transitionDelay', - }, - { - type: 'Animation', - members: 'animate\\-(?${animation})', - configKey: 'animation', - }, - ], - }, - { - type: 'Transforms', - members: [ - { - type: 'Transform GPU', - members: [ - { - type: 'transform-gpu', - members: 'transform\\-gpu', - }, - ], - }, - { - type: 'Transform None', - members: [ - { - type: 'transform-none', - members: 'transform\\-none', - }, - ], - }, - { - type: 'Deprecated Transform', - members: [ - { - type: 'transform', - members: 'transform', - deprecated: true, - }, - ], - }, - { - type: 'Scale', - members: [ - { - type: 'scale', - members: 'scale\\-(?${scale})|\\-scale\\-(?${-scale})', - shorthand: 'all', - body: 'scale', - configKey: 'scale', - }, - { - type: 'scale-y', - members: 'scale\\-y\\-(?${scale})|\\-scale\\-y\\-(?${-scale})', - shorthand: 'y', - body: 'scale-y', - configKey: 'scale', - }, - { - type: 'scale-x', - members: 'scale\\-x\\-(?${scale})|\\-scale\\-x\\-(?${-scale})', - shorthand: 'x', - body: 'scale-x', - configKey: 'scale', - }, - ], - }, - { - type: 'Rotate', - members: '(rotate\\-(?${rotate})|\\-rotate\\-(?${-rotate}))', - configKey: 'rotate', - }, - { - type: 'Translate', - members: [ - { - type: 'translate-x', - members: '(translate\\-x\\-(?${translate})|\\-translate\\-x\\-(?${-translate}))', - configKey: 'translate', - }, - { - type: 'translate-y', - members: '(translate\\-y\\-(?${translate})|\\-translate\\-y\\-(?${-translate}))', - configKey: 'translate', - }, - ], - }, - { - type: 'Skew', - members: [ - { - type: 'skew-x', - members: '(skew\\-x\\-(?${skew})|\\-skew\\-x\\-(?${-skew}))', - configKey: 'skew', - }, - { - type: 'skew-y', - members: '(skew\\-y\\-(?${skew})|\\-skew\\-y\\-(?${-skew}))', - configKey: 'skew', - }, - ], - }, - { - type: 'Transform Origin', - members: 'origin\\-(?${transformOrigin})', - configKey: 'transformOrigin', - }, - ], - }, - { - type: 'Interactivity', - members: [ - { - type: 'Accent Color', - members: 'accent\\-(?${accentColor})', - configKey: 'accentColor', - }, - { - type: 'Appearance', - members: 'appearance\\-none', - }, - { - type: 'Cursor', - members: 'cursor\\-(?${cursor})', - configKey: 'cursor', - }, - { - type: 'Caret Color', - members: 'caret\\-(?${colors})', - configKey: 'colors', - }, - { - type: 'Pointer Events', - members: 'pointer\\-events\\-(none|auto)', - }, - { - type: 'Resize', - members: 'resize(\\-(none|x|y))?', - }, - { - type: 'Scroll Behavior', - members: 'scroll\\-(auto|smooth)', - }, - { - type: 'Scroll Margin', - members: 'scroll\\-(?${scrollMargin})', - configKey: 'scrollMargin', - members: [ - { - type: 'scroll-m', - members: 'scroll-m\\-(?${scrollMargin})|\\-scroll-m\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-my', - members: 'scroll-my\\-(?${scrollMargin})|\\-scroll-my\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-mx', - members: 'scroll-mx\\-(?${scrollMargin})|\\-scroll-mx\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-mt', - members: 'scroll-mt\\-(?${scrollMargin})|\\-scroll-mt\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-mr', - members: 'scroll-mr\\-(?${scrollMargin})|\\-scroll-mr\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-mb', - members: 'scroll-mb\\-(?${scrollMargin})|\\-scroll-mb\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - { - type: 'scroll-ml', - members: 'scroll-ml\\-(?${scrollMargin})|\\-scroll-ml\\-(?${-scrollMargin})', - configKey: 'scrollMargin', - }, - ], - }, - { - type: 'Scroll Padding', - members: 'scroll\\-(?${scrollPadding})', - configKey: 'scrollPadding', - members: [ - { - type: 'scroll-p', - members: 'scroll-p\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-py', - members: 'scroll-py\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-px', - members: 'scroll-px\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-pt', - members: 'scroll-pt\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-pr', - members: 'scroll-pr\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-pb', - members: 'scroll-pb\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - { - type: 'scroll-pl', - members: 'scroll-pl\\-(?${scrollPadding})', - configKey: 'scrollPadding', - }, - ], - }, - { - type: 'Scroll Snap Align', - members: 'snap\\-(start|end|center|align-none)', - }, - { - type: 'Scroll Snap Stop', - members: 'snap\\-(normal|always)', - }, - { - type: 'Scroll Snap Type', - members: 'snap\\-(none|x|y|both)', - }, - { - type: 'Scroll Snap Type Strictness', - members: 'snap\\-(mandatory|proximity)', - }, - { - type: 'Touch Action', - members: [ - { - type: 'Touch Action Mode', - members: 'touch\\-(auto|none|manipulation)', - }, - { - type: 'Touch Action X', - members: 'touch\\-(pan\\-(x|left|right))', - }, - { - type: 'Touch Action Y', - members: 'touch\\-(pan\\-(y|up|down))', - }, - { - type: 'Touch Action Pinch Zoom', - members: 'touch\\-pinch\\-zoom', - }, - ], - }, - { - type: 'User Select', - members: 'select\\-(none|text|all|auto)', - }, - { - type: 'Will Change', - members: 'will\\-change\\-(?${willChange})', - configKey: 'willChange', - }, - ], - }, - { - type: 'SVG', - members: [ - { - type: 'Fill', - members: 'fill\\-(?${fill})', - configKey: 'fill', - }, - { - type: 'Stroke', - members: 'stroke\\-(?${stroke})', - configKey: 'stroke', - }, - { - type: 'Stroke Width', - members: 'stroke\\-(?${strokeWidth})', - configKey: 'strokeWidth', - }, - ], - }, - { - type: 'Accessibility', - members: [ - { - type: 'Screen Readers', - members: '(not\\-)?sr\\-only', - }, - { - type: 'Forced Color Adjust', - members: 'forced\\-color\\-adjust\\-(auto|none)', - }, - ], - }, - { - type: 'Official Plugins', - members: [ - { - // TODO: - // Support for custom prose classname like on - // https://tailwindcss.com/docs/typography-plugin#changing-the-default-class-name - // Adding custom color themes - // https://tailwindcss.com/docs/typography-plugin#adding-custom-color-themes - type: 'Typography', - members: [ - { - type: 'prose', - members: '(not\\-)?prose', - }, - { - type: 'Prose Gray Scale', - members: 'prose\\-(gray|slate|zinc|neutral|stone)', - }, - { - type: 'Prose Type Scale', - members: 'prose\\-(sm|base|lg|2?xl)', - }, - { - type: 'Prose Dark Mode', - members: 'prose\\-invert', - }, - // These are modifiers and not the last part of the classname - // { - // type: 'Prose Element modifiers', - // members: - // 'prose\\-(headings|lead|h1|h2|h3|h4|p|a|blockquote|figure|figcaption|strong|em|code|pre|ol|ul|li|table|thead|tr|th|td|img|video|hr)', - // }, - ], - }, - // ('Forms' plugin has no related classnames, only selectors like `input[type='password']`) - { - type: 'Aspect Ratio', - members: [ - { - type: 'aspect-w', - members: 'aspect\\-(none|w\\-(?${aspectRatio}))', - }, - { - type: 'aspect-h', - members: 'aspect\\-h\\-(?${aspectRatio})', - }, - ], - }, - { - type: 'Line Clamp', - members: 'line\\-clamp\\-(none|(?${lineClamp}))', - }, - ], - }, -]; diff --git a/lib/config/recommended.js b/lib/config/recommended.js deleted file mode 100644 index be94036..0000000 --- a/lib/config/recommended.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @fileoverview Recommended coniguration for legacy style - * @see https://eslint.org/docs/latest/use/configure/configuration-files - * @author François Massart - */ -'use strict'; - -const rules = require('./rules'); - -module.exports = { - plugins: ['tailwindcss'], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - rules, -}; diff --git a/lib/config/rules.js b/lib/config/rules.js deleted file mode 100644 index 2399d01..0000000 --- a/lib/config/rules.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @fileoverview Default rules configuration - * @author François Massart - */ - -module.exports = { - 'tailwindcss/classnames-order': 'warn', - 'tailwindcss/enforces-negative-arbitrary-values': 'warn', - 'tailwindcss/enforces-shorthand': 'warn', - 'tailwindcss/migration-from-tailwind-2': 'warn', - 'tailwindcss/no-arbitrary-value': 'off', - 'tailwindcss/no-custom-classname': 'warn', - 'tailwindcss/no-contradicting-classname': 'error', - 'tailwindcss/no-unnecessary-arbitrary-value': 'warn', -}; diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 53824e7..0000000 --- a/lib/index.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @fileoverview Rules enforcing best practices while using Tailwind CSS - * @author François Massart - */ -'use strict'; - -//------------------------------------------------------------------------------ -// Plugin Definition -//------------------------------------------------------------------------------ - -// import all rules in lib/rules -var base = __dirname + '/rules/'; -module.exports = { - rules: { - 'classnames-order': require(base + 'classnames-order'), - 'enforces-negative-arbitrary-values': require(base + 'enforces-negative-arbitrary-values'), - 'enforces-shorthand': require(base + 'enforces-shorthand'), - 'migration-from-tailwind-2': require(base + 'migration-from-tailwind-2'), - 'no-arbitrary-value': require(base + 'no-arbitrary-value'), - 'no-contradicting-classname': require(base + 'no-contradicting-classname'), - 'no-custom-classname': require(base + 'no-custom-classname'), - 'no-unnecessary-arbitrary-value': require(base + 'no-unnecessary-arbitrary-value'), - }, - configs: { - recommended: require('./config/recommended'), - 'flat/recommended': require('./config/flat-recommended'), - }, -}; diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js deleted file mode 100644 index e7e3573..0000000 --- a/lib/rules/classnames-order.js +++ /dev/null @@ -1,274 +0,0 @@ -/** - * @fileoverview Use a consistent orders for the Tailwind CSS classnames, based on property then on variants - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const removeDuplicatesFromClassnamesAndWhitespaces = require('../util/removeDuplicatesFromClassnamesAndWhitespaces'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); -const order = require('../util/prettier/order'); -const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').createContext; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const INVALID_CLASSNAMES_ORDER_MSG = 'Invalid Tailwind CSS classnames order'; - -const contextFallbackCache = new WeakMap(); - -module.exports = { - meta: { - docs: { - description: 'Enforce a consistent and logical order of the Tailwind CSS classnames', - category: 'Stylistic Issues', - recommended: false, - url: docsUrl('classnames-order'), - }, - messages: { - invalidOrder: INVALID_CLASSNAMES_ORDER_MSG, - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - removeDuplicates: { - // default: true, - type: 'boolean', - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - const removeDuplicates = getOption(context, 'removeDuplicates'); - - const mergedConfig = customConfig.resolve(twConfig); - const contextFallback = // Set the created contextFallback in the cache if it does not exist yet. - ( - contextFallbackCache.has(mergedConfig) - ? contextFallbackCache - : contextFallbackCache.set(mergedConfig, createContextFallback(mergedConfig)) - ).get(mergedConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - /** - * Recursive function crawling into child nodes - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const sortNodeArgumentValue = (node, arg = null) => { - let originalClassNamesValue = null; - let start = null; - let end = null; - let prefix = ''; - let suffix = ''; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - const range = astUtil.extractRangeFromNode(node); - if (node.type === 'TextAttribute') { - start = range[0]; - end = range[1]; - } else { - start = range[0] + 1; - end = range[1] - 1; - } - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - arg.expressions.forEach((exp) => { - sortNodeArgumentValue(node, exp); - }); - arg.quasis.forEach((quasis) => { - sortNodeArgumentValue(node, quasis); - }); - return; - case 'ConditionalExpression': - sortNodeArgumentValue(node, arg.consequent); - sortNodeArgumentValue(node, arg.alternate); - return; - case 'LogicalExpression': - sortNodeArgumentValue(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - sortNodeArgumentValue(node, el); - }); - return; - case 'ObjectExpression': - const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'; - const isVue = node.key && node.key.type === 'VDirectiveKey'; - arg.properties.forEach((prop) => { - const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value; - sortNodeArgumentValue(node, propVal); - }); - return; - case 'Property': - sortNodeArgumentValue(node, arg.key); - break; - case 'Literal': - originalClassNamesValue = arg.value; - start = arg.range[0] + 1; - end = arg.range[1] - 1; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - start = arg.range[0]; - end = arg.range[1]; - // https://github.com/eslint/eslint/issues/13360 - // The problem is that range computation includes the backticks (`test`) - // but value.raw does not include them, so there is a mismatch. - // start/end does not include the backticks, therefore it matches value.raw. - const txt = context.getSourceCode().getText(arg); - prefix = astUtil.getTemplateElementPrefix(txt, originalClassNamesValue); - suffix = astUtil.getTemplateElementSuffix(txt, originalClassNamesValue); - originalClassNamesValue = astUtil.getTemplateElementBody(txt, prefix, suffix); - break; - } - } - - let { classNames, whitespaces, headSpace, tailSpace } = - astUtil.extractClassnamesFromValue(originalClassNamesValue); - - if (classNames.length <= 1) { - // Don't run sorting for a single or empty className - return; - } - - let orderedClassNames = order(classNames, contextFallback).split(' '); - - if (removeDuplicates) { - removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace); - } - - // Generates the validated/sorted attribute value - let validatedClassNamesValue = ''; - for (let i = 0; i < orderedClassNames.length; i++) { - const w = whitespaces[i] ?? ''; - const cls = orderedClassNames[i]; - validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`; - if (headSpace && tailSpace && i === orderedClassNames.length - 1) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''; - } - } - - if (originalClassNamesValue !== validatedClassNamesValue) { - validatedClassNamesValue = prefix + validatedClassNamesValue + suffix; - context.report({ - node: node, - messageId: 'invalidOrder', - fix: function (fixer) { - return fixer.replaceTextRange([start, end], validatedClassNamesValue); - }, - }); - } - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - sortNodeArgumentValue(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - sortNodeArgumentValue(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - - node.arguments.forEach((arg) => { - sortNodeArgumentValue(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - - sortNodeArgumentValue(node, node.quasi); - }, - }; - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - sortNodeArgumentValue(node, null); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - sortNodeArgumentValue(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - sortNodeArgumentValue(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/enforces-negative-arbitrary-values.js b/lib/rules/enforces-negative-arbitrary-values.js deleted file mode 100644 index 1218993..0000000 --- a/lib/rules/enforces-negative-arbitrary-values.js +++ /dev/null @@ -1,218 +0,0 @@ -/** - * @fileoverview Warns about `-` prefixed classnames using arbitrary values - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const NEGATIVE_ARBITRARY_VALUE = `Arbitrary value classname '{{classname}}' should not start with a dash (-)`; - -module.exports = { - meta: { - docs: { - description: 'Warns about dash prefixed classnames using arbitrary values', - category: 'Best Practices', - recommended: true, - url: docsUrl('enforces-negative-arbitrary-values'), - }, - messages: { - negativeArbitraryValue: NEGATIVE_ARBITRARY_VALUE, - }, - fixable: null, - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Recursive function crawling into child nodes - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const parseForNegativeArbitraryClassNames = (node, arg = null) => { - let originalClassNamesValue = null; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - arg.expressions.forEach((exp) => { - parseForNegativeArbitraryClassNames(node, exp); - }); - arg.quasis.forEach((quasis) => { - parseForNegativeArbitraryClassNames(node, quasis); - }); - return; - case 'ConditionalExpression': - parseForNegativeArbitraryClassNames(node, arg.consequent); - parseForNegativeArbitraryClassNames(node, arg.alternate); - return; - case 'LogicalExpression': - parseForNegativeArbitraryClassNames(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - parseForNegativeArbitraryClassNames(node, el); - }); - return; - case 'ObjectExpression': - const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'; - const isVue = node.key && node.key.type === 'VDirectiveKey'; - arg.properties.forEach((prop) => { - const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value; - parseForNegativeArbitraryClassNames(node, propVal); - }); - return; - case 'Property': - parseForNegativeArbitraryClassNames(node, arg.key); - return; - case 'Literal': - originalClassNamesValue = arg.value; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - break; - } - } - - let { classNames } = astUtil.extractClassnamesFromValue(originalClassNamesValue); - - const detected = classNames.filter((cls) => { - const suffix = groupUtil.getSuffix(cls, mergedConfig.separator); - const negArbitraryValRegEx = - /^\-((inset|scale)(\-(y|x))?|top|right|bottom|left|top|z|order|(scroll\-)?m(y|x|t|r|l|b)?|(skew|space|translate)\-(y|x)|rotate|tracking|indent|(backdrop\-)?hue\-rotate)\-\[.*\]$/i; - return negArbitraryValRegEx.test(suffix); - }); - - detected.forEach((className) => { - context.report({ - node, - messageId: 'negativeArbitraryValue', - data: { - classname: className, - }, - }); - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - parseForNegativeArbitraryClassNames(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - parseForNegativeArbitraryClassNames(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - node.arguments.forEach((arg) => { - parseForNegativeArbitraryClassNames(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - parseForNegativeArbitraryClassNames(node, node.quasi); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - parseForNegativeArbitraryClassNames(node); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - parseForNegativeArbitraryClassNames(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - parseForNegativeArbitraryClassNames(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/enforces-shorthand.js b/lib/rules/enforces-shorthand.js deleted file mode 100644 index b147bfa..0000000 --- a/lib/rules/enforces-shorthand.js +++ /dev/null @@ -1,540 +0,0 @@ -/** - * @fileoverview Avoid using multiple Tailwind CSS classnames when not required (e.g. "mx-3 my-3" could be replaced by "m-3") - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const defaultGroups = require('../config/groups').groups; -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const SHORTHAND_CANDIDATE_CLASSNAMES_DETECTED_MSG = `Classnames '{{classnames}}' could be replaced by the '{{shorthand}}' shorthand!`; - -module.exports = { - meta: { - docs: { - description: 'Enforces the usage of shorthand Tailwind CSS classnames', - category: 'Best Practices', - recommended: true, - url: docsUrl('enforces-shorthand'), - }, - messages: { - shorthandCandidateDetected: SHORTHAND_CANDIDATE_CLASSNAMES_DETECTED_MSG, - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - // These are shorthand candidates that do not share the same parent type - const complexEquivalences = [ - { - needles: ['overflow-hidden', 'text-ellipsis', 'whitespace-nowrap'], - shorthand: 'truncate', - mode: 'exact', - }, - { - needles: ['w-', 'h-'], - shorthand: 'size-', - mode: 'value', - }, - ]; - - // Init assets - const targetProperties = { - Layout: ['Overflow', 'Overscroll Behavior', 'Top / Right / Bottom / Left'], - 'Flexbox & Grid': ['Gap'], - Spacing: ['Padding', 'Margin'], - Sizing: ['Width', 'Height'], - Borders: ['Border Radius', 'Border Width', 'Border Color'], - Tables: ['Border Spacing'], - Transforms: ['Scale'], - Typography: ['Text Overflow', 'Whitespace'], - }; - - // We don't want to affect other rules by object reference - const cloned = JSON.parse(JSON.stringify(defaultGroups)); - const targetGroups = cloned.filter((g) => Object.keys(targetProperties).includes(g.type)); - targetGroups.forEach((g) => { - // Without using the clone, other rules would be affected by this `filter()` - g.members = g.members.filter((sub) => targetProperties[g.type].includes(sub.type)); - }); - - /** - * Retrieve the main part of a classname base on its shorthand scope - * @param {Object} targetGroups A specific subset of the groups - * @param {String} parentType The name of the parent e.g. 'Border Radius' - * @param {String} shorthand The searched shorthand e.g. 'all', 'y', 't', 'tr' - * @returns - */ - const getBodyByShorthand = (targetGroups, parentType, shorthand) => { - const findByMemberType = (obj) => obj.members.find((m) => m.type === parentType); - const mainGroup = targetGroups.find(findByMemberType); - if (!mainGroup) { - return ''; - } - const typeGroup = mainGroup.members.find((m) => m.type === parentType); - // const typeGroup = mainGroup.find(findByMemberType); - if (!typeGroup) { - return ''; - } - const type = typeGroup.members.find((m) => m.shorthand === shorthand); - return !type ? '' : type.body; - }; - - /** - * Parse the classnames and report found shorthand candidates - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const parseForShorthandCandidates = (node, arg = null) => { - let originalClassNamesValue = null; - let start = null; - let end = null; - let prefix = ''; - let suffix = ''; - const troubles = []; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - const range = astUtil.extractRangeFromNode(node); - if (node.type === 'TextAttribute') { - start = range[0]; - end = range[1]; - } else { - start = range[0] + 1; - end = range[1] - 1; - } - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - arg.expressions.forEach((exp) => { - parseForShorthandCandidates(node, exp); - }); - arg.quasis.forEach((quasis) => { - parseForShorthandCandidates(node, quasis); - }); - return; - case 'ConditionalExpression': - parseForShorthandCandidates(node, arg.consequent); - parseForShorthandCandidates(node, arg.alternate); - return; - case 'LogicalExpression': - parseForShorthandCandidates(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - parseForShorthandCandidates(node, el); - }); - return; - case 'ObjectExpression': - const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'; - const isVue = node.key && node.key.type === 'VDirectiveKey'; - arg.properties.forEach((prop) => { - const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value; - parseForShorthandCandidates(node, propVal); - }); - return; - case 'Property': - parseForShorthandCandidates(node, arg.key); - return; - - case 'Literal': - originalClassNamesValue = arg.value; - start = arg.range[0] + 1; - end = arg.range[1] - 1; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - start = arg.range[0]; - end = arg.range[1]; - // https://github.com/eslint/eslint/issues/13360 - // The problem is that range computation includes the backticks (`test`) - // but value.raw does not include them, so there is a mismatch. - // start/end does not include the backticks, therefore it matches value.raw. - const txt = context.getSourceCode().getText(arg); - prefix = astUtil.getTemplateElementPrefix(txt, originalClassNamesValue); - suffix = astUtil.getTemplateElementSuffix(txt, originalClassNamesValue); - originalClassNamesValue = astUtil.getTemplateElementBody(txt, prefix, suffix); - break; - } - } - - let { classNames, whitespaces, headSpace, tailSpace } = - astUtil.extractClassnamesFromValue(originalClassNamesValue); - - if (classNames.length <= 1) { - // Don't run sorting for a single or empty className - return; - } - - const parsed = []; - - classNames.forEach((className, index) => { - parsed.push(groupUtil.parseClassname(className, targetGroups, mergedConfig, index)); - }); - - const validated = []; - - // Handle sets of classnames with different parent types - let remaining = parsed; - for (const { needles: inputSet, shorthand: outputClassname, mode } of complexEquivalences) { - if (remaining.length < inputSet.length) { - continue; - } - - // Matching classes - const parsedElementsInInputSet = remaining.filter((remainingClass) => { - if (mode === 'exact') { - // Test if the name contains the target class, eg. 'text-ellipsis' inside 'md:text-ellipsis'... - return inputSet.some((inputClass) => remainingClass.name.includes(inputClass)); - } - // Test if the body of the class matches, eg. 'h-' inside 'h-10' - if (mode === 'value') { - const bodyMatch = inputSet.some((inputClassPattern) => inputClassPattern === remainingClass.body); - if ([undefined, null].includes(mergedConfig.theme.size)) { - return false; - } - // w-screen + h-screen ≠ size-screen (Issue #307) - const sizeKeys = Object.keys(mergedConfig.theme.size); - const isSize = ['w-', 'h-'].includes(remainingClass.body); - const isValidSize = sizeKeys.includes(remainingClass.value); - const wValue = mergedConfig.theme.width[remainingClass.value]; - const hValue = mergedConfig.theme.height[remainingClass.value]; - const sizeValue = mergedConfig.theme.size[remainingClass.value]; - const fullMatch = wValue === hValue && wValue === sizeValue; - return bodyMatch && !(isSize && !isValidSize && !fullMatch); - } - }); - - const variantGroups = new Map(); - parsedElementsInInputSet.forEach((o) => { - const val = mode === 'value' ? o.value : ''; - const v = `${o.variants}${o.important ? '!' : ''}${val}`; - if (!variantGroups.has(v)) { - variantGroups.set( - v, - parsedElementsInInputSet.filter( - (c) => c.variants === o.variants && c.important === o.important && (val === '' || c.value === val) - ) - ); - } - }); - const validKeys = new Set(); - variantGroups.forEach((classes, key) => { - let skip = false; - // Make sure all required classes for the shorthand are present - if (classes.length < inputSet.length) { - skip = true; - } - // Make sure the classes share all the single/shared/same value - if (mode === 'value' && new Set(classes.map((p) => p.value)).size !== 1) { - skip = true; - } - if (!skip) { - validKeys.add(key); - } - }); - validKeys.forEach((k) => { - const candidates = variantGroups.get(k); - const index = candidates[0].index; - const variants = candidates[0].variants; - const important = candidates[0].important ? '!' : ''; - const classValue = mode === 'value' ? candidates[0].value : ''; - - const patchedClassname = `${variants}${important}${mergedConfig.prefix}${outputClassname}${classValue}`; - troubles.push([candidates.map((c) => `${c.name}`), patchedClassname]); - - const validatedClassname = groupUtil.parseClassname(patchedClassname, targetGroups, mergedConfig, index); - validated.push(validatedClassname); - - remaining = remaining.filter((p) => !candidates.includes(p)); - }); - } - - // Handle sets of classnames with the same parent type - // Each group parentType - const checkedGroups = []; - remaining.forEach((classname, idx, arr) => { - // Valid candidate - if (classname.parentType === '') { - validated.push(classname); - } else if (!checkedGroups.includes(classname.parentType)) { - checkedGroups.push(classname.parentType); - const sameType = remaining.filter((cls) => cls.parentType === classname.parentType); - // Comparing same parentType classnames - const checkedVariantsValue = []; - sameType.forEach((cls) => { - const key = cls.variants + (cls.important ? '!' : '') + cls.value; - if (!checkedVariantsValue.includes(key)) { - checkedVariantsValue.push(key); - const sameVariantAndValue = sameType.filter((v) => { - return !(v.variants !== cls.variants || v.value !== cls.value || v.important !== cls.important); - }); - if (sameVariantAndValue.length === 1) { - validated.push(cls); - } else if (sameVariantAndValue.length) { - const supportCorners = ['Border Radius'].includes(classname.parentType); - const hasTL = - supportCorners && sameVariantAndValue.some((c) => ['tl', 't', 'all'].includes(c.shorthand)); - const hasTR = - supportCorners && sameVariantAndValue.some((c) => ['tr', 't', 'all'].includes(c.shorthand)); - const hasBR = - supportCorners && sameVariantAndValue.some((c) => ['br', 'b', 'all'].includes(c.shorthand)); - const hasBL = - supportCorners && sameVariantAndValue.some((c) => ['bl', 'b', 'all'].includes(c.shorthand)); - const hasT = sameVariantAndValue.some((c) => c.shorthand === 't') || (hasTL && hasTR); - const hasR = sameVariantAndValue.some((c) => c.shorthand === 'r') || (hasTR && hasBR); - const hasB = sameVariantAndValue.some((c) => c.shorthand === 'b') || (hasBL && hasBR); - const hasL = sameVariantAndValue.some((c) => c.shorthand === 'l') || (hasTL && hasBL); - const hasX = sameVariantAndValue.some((c) => c.shorthand === 'x') || (hasL && hasR); - const hasY = sameVariantAndValue.some((c) => c.shorthand === 'y') || (hasT && hasB); - const hasAllProp = sameVariantAndValue.some((c) => c.shorthand === 'all'); - const hasAllPropNoCorner = hasY && hasX; - const hasAllPropWithCorners = (hasL && hasR) || (hasT && hasB); - const hasAllEquivalent = !supportCorners ? hasAllPropNoCorner : hasAllPropWithCorners; - const hasAll = hasAllProp || hasAllEquivalent; - const important = cls.important ? '!' : ''; - const isNegative = ('' + cls.value).substring(0, 1) === '-'; - const minus = isNegative ? '-' : ''; - const absoluteVal = isNegative ? ('' + cls.value).substring(1) : cls.value; - - if (hasAll) { - const all = getBodyByShorthand(targetGroups, classname.parentType, 'all'); - const val = absoluteVal.length ? '-' + absoluteVal : ''; - const patchedName = `${cls.variants}${important}${minus}${mergedConfig.prefix}${all}${val}`; - troubles.push([sameVariantAndValue.map((c) => c.name), patchedName]); - cls.name = patchedName; - cls.shorthand = 'all'; - validated.push(cls); - } else if (hasY || hasX) { - const xOrY = hasX ? 'x' : 'y'; - const xOrYType = getBodyByShorthand(targetGroups, classname.parentType, xOrY); - const patchedName = `${cls.variants}${important}${minus}${mergedConfig.prefix}${xOrYType}${absoluteVal.length ? '-' + absoluteVal : '' - }`; - const toBeReplaced = sameVariantAndValue - .filter((c) => { - const candidates = hasX ? ['l', 'r'] : ['t', 'b']; - return candidates.includes(c.shorthand); - }) - .map((c) => c.name); - const toBeKept = sameVariantAndValue.filter((c) => { - const candidates = hasY ? ['l', 'r'] : ['t', 'b']; - return candidates.includes(c.shorthand); - }); - - troubles.push([toBeReplaced, patchedName]); - let replaced = false; - sameVariantAndValue.forEach((ref, i) => { - if (toBeKept.find((k) => k.name === ref.name)) { - validated.push(ref); - } else if (!replaced) { - replaced = true; - const cloned = JSON.parse(JSON.stringify(ref)); - cloned.name = patchedName; - cloned.shorthand = xOrY; - validated.push(cloned); - } - }); - } else if (supportCorners && (hasT || hasR || hasB || hasL)) { - const side = hasT ? 't' : hasR ? 'r' : hasB ? 'b' : 'l'; - const sideBody = getBodyByShorthand(targetGroups, classname.parentType, side); - const val = absoluteVal.length ? '-' + absoluteVal : ''; - const patchedName = `${cls.variants}${important}${minus}${mergedConfig.prefix}${sideBody}${val}`; - const toBeReplaced = sameVariantAndValue - .filter((c) => { - const candidates = hasT ? ['tl', 'tr'] : hasR ? ['tr', 'br'] : hasB ? ['bl', 'br'] : ['tl', 'bl']; - return candidates.includes(c.shorthand); - }) - .map((c) => c.name); - const toBeKept = sameVariantAndValue.filter((c) => { - const candidates = hasT ? ['bl', 'br'] : hasR ? ['tl', 'bl'] : hasB ? ['tl', 'tr'] : ['tr', 'br']; - return candidates.includes(c.shorthand); - }); - - troubles.push([toBeReplaced, patchedName]); - let replaced = false; - sameVariantAndValue.forEach((ref, i) => { - if (toBeKept.find((k) => k.name === ref.name)) { - validated.push(ref); - } else if (!replaced) { - replaced = true; - const cloned = JSON.parse(JSON.stringify(ref)); - cloned.name = patchedName; - cloned.shorthand = side; - validated.push(cloned); - } - }); - } else { - validated.push(...sameVariantAndValue); - } - } - } - }); - } - }); - - // Try to keep the original order - validated.sort((a, b) => (a.index < b.index ? -1 : +1)); - - // Generates the validated attribute value - const union = validated.map((val) => val.leading + val.name + val.trailing); - - let validatedClassNamesValue = ''; - - // Generates the validated attribute value - if (union.length === 1) { - validatedClassNamesValue += headSpace ? whitespaces[0] : ''; - validatedClassNamesValue += union[0]; - validatedClassNamesValue += tailSpace ? whitespaces[whitespaces.length - 1] : ''; - } else { - for (let i = 0; i < union.length; i++) { - const isLast = i === union.length - 1; - const w = whitespaces[i] ?? ''; - const cls = union[i]; - validatedClassNamesValue += headSpace ? `${w}${cls}` : isLast ? `${cls}` : `${cls}${w}`; - if (tailSpace && isLast) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''; - } - } - } - - troubles.forEach((issue) => { - if (originalClassNamesValue !== validatedClassNamesValue) { - validatedClassNamesValue = prefix + validatedClassNamesValue + suffix; - context.report({ - node: node, - messageId: 'shorthandCandidateDetected', - data: { - classnames: issue[0].join(', '), - shorthand: issue[1], - }, - fix: function (fixer) { - return fixer.replaceTextRange([start, end], validatedClassNamesValue); - }, - }); - } - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - parseForShorthandCandidates(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - parseForShorthandCandidates(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - - node.arguments.forEach((arg) => { - parseForShorthandCandidates(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - - parseForShorthandCandidates(node, node.quasi); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - parseForShorthandCandidates(node); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - parseForShorthandCandidates(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - parseForShorthandCandidates(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/migration-from-tailwind-2.js b/lib/rules/migration-from-tailwind-2.js deleted file mode 100644 index 5de0f0f..0000000 --- a/lib/rules/migration-from-tailwind-2.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * @fileoverview Detect obsolete classnames when upgrading to Tailwind CSS v3 - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const CLASSNAME_NOT_NEEDED_MSG = `Classname '{{classnames}}' is not needed in Tailwind CSS v3!`; -const CLASSNAMES_NOT_NEEDED_MSG = `Classnames '{{classnames}}' are not needed in Tailwind CSS v3!`; -const CLASSNAME_CHANGED_MSG = `Classname '{{deprecated}}' should be updated to '{{updated}}' in Tailwind CSS v3!`; -const OPACITY_CLASS_DEPRECATED_MSG = `Classname '{{classname}}' should be replaced by an opacity suffix (eg. '/{{value}}')`; - -module.exports = { - meta: { - docs: { - description: 'Detect obsolete classnames when upgrading to Tailwind CSS v3', - category: 'Possible Errors', - recommended: true, - url: docsUrl('migration-from-tailwind-2'), - }, - messages: { - classnameNotNeeded: CLASSNAME_NOT_NEEDED_MSG, - classnamesNotNeeded: CLASSNAMES_NOT_NEEDED_MSG, - classnameChanged: CLASSNAME_CHANGED_MSG, - classnameOpacityDeprecated: OPACITY_CLASS_DEPRECATED_MSG, - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Recursive function crawling into child nodes - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const parseForObsoleteClassNames = (node, arg = null) => { - let originalClassNamesValue = null; - let start = null; - let end = null; - let prefix = ''; - let suffix = ''; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - const range = astUtil.extractRangeFromNode(node); - if (node.type === 'TextAttribute') { - start = range[0]; - end = range[1]; - } else { - start = range[0] + 1; - end = range[1] - 1; - } - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - arg.expressions.forEach((exp) => { - parseForObsoleteClassNames(node, exp); - }); - arg.quasis.forEach((quasis) => { - parseForObsoleteClassNames(node, quasis); - }); - return; - case 'ConditionalExpression': - parseForObsoleteClassNames(node, arg.consequent); - parseForObsoleteClassNames(node, arg.alternate); - return; - case 'LogicalExpression': - parseForObsoleteClassNames(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - parseForObsoleteClassNames(node, el); - }); - return; - case 'ObjectExpression': - arg.properties.forEach((prop) => { - parseForObsoleteClassNames(node, prop.key); - }); - return; - case 'Property': - parseForObsoleteClassNames(node, arg.key); - return; - case 'Literal': - originalClassNamesValue = arg.value; - start = arg.range[0] + 1; - end = arg.range[1] - 1; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - start = arg.range[0]; - end = arg.range[1]; - // https://github.com/eslint/eslint/issues/13360 - // The problem is that range computation includes the backticks (`test`) - // but value.raw does not include them, so there is a mismatch. - // start/end does not include the backticks, therefore it matches value.raw. - const txt = context.getSourceCode().getText(arg); - prefix = astUtil.getTemplateElementPrefix(txt, originalClassNamesValue); - suffix = astUtil.getTemplateElementSuffix(txt, originalClassNamesValue); - originalClassNamesValue = astUtil.getTemplateElementBody(txt, prefix, suffix); - break; - } - } - - let { classNames, whitespaces, headSpace, tailSpace } = - astUtil.extractClassnamesFromValue(originalClassNamesValue); - - const notNeeded = []; - const outdated = []; - const deprecatedBgOpacity = []; - const filtered = classNames.filter((cls) => { - const suffix = groupUtil.getSuffix(cls, mergedConfig.separator); - if (/^((backdrop\-)?(filter|transform))$/i.test(suffix)) { - notNeeded.push(cls); - return false; - } - let overflowRes = /^overflow\-(?clip|ellipsis)$/i.exec(suffix); - if (overflowRes && overflowRes.groups && overflowRes.groups.value) { - outdated.push([cls, cls.replace(/overflow\-(clip|ellipsis)$/i, `text-${overflowRes.groups.value}`)]); - } - let growShrinkRes = /flex\-(?grow|shrink)(\-(?${flexVal}))?/i.exec(suffix); - if (growShrinkRes && growShrinkRes.groups && growShrinkRes.groups.prop) { - const prop = growShrinkRes.groups.prop; - const flexVal = growShrinkRes.groups.flexVal; - const optionalVal = flexVal ? `\-${flexVal}` : ''; - const fixRegex = new RegExp(`flex\-${prop}${optionalVal}`); - outdated.push([cls, cls.replace(fixRegex, `${prop}${flexVal ? '-' + flexVal : ''}`)]); - } - let boxRes = /^decoration\-(?clone|slice)$/i.exec(suffix); - if (boxRes && boxRes.groups && boxRes.groups.value) { - const boxVal = boxRes.groups.value; - const fixRegex = new RegExp(`decoration\-${boxVal}`); - outdated.push([cls, cls.replace(fixRegex, `box-decoration\-${boxVal}`)]); - } - let bgOpacityRes = /^(bg|border|ring)\-opacity\-(?\d{1,})$/i.exec(suffix); - if (bgOpacityRes && bgOpacityRes.groups && bgOpacityRes.groups.value) { - const opacityVal = bgOpacityRes.groups.value; - deprecatedBgOpacity.push([cls, opacityVal]); - } - let placeholderRes = /^placeholder\-(?.{1,})$/i.exec(suffix); - if (placeholderRes && placeholderRes.groups && placeholderRes.groups.value) { - const placeholderVal = placeholderRes.groups.value; - const fixPlaceholderRegex = new RegExp(`placeholder\-${placeholderVal}$`); - outdated.push([cls, cls.replace(fixPlaceholderRegex, `placeholder:text\-${placeholderVal}`)]); - } - return true; - }); - - if (notNeeded.length) { - let validatedClassNamesValue = ''; - for (let i = 0; i < filtered.length; i++) { - const isLast = i === filtered.length - 1; - const w = whitespaces[i] ?? ''; - const cls = filtered[i]; - validatedClassNamesValue += headSpace ? `${w}${cls}` : isLast ? `${cls}` : `${cls}${w}`; - if (headSpace && tailSpace && isLast) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''; - } - } - validatedClassNamesValue = prefix + validatedClassNamesValue + suffix; - context.report({ - node, - messageId: notNeeded.length === 1 ? 'classnameNotNeeded' : 'classnamesNotNeeded', - data: { - classnames: notNeeded.join(', '), - }, - fix: function (fixer) { - return fixer.replaceTextRange([start, end], validatedClassNamesValue); - }, - }); - } - - outdated.forEach((outdatedClass) => { - let validatedClassNamesValue = ''; - for (let i = 0; i < filtered.length; i++) { - const w = whitespaces[i] ?? ''; - const cls = filtered[i]; - validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`; - if (headSpace && tailSpace && i === filtered.length - 1) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''; - } - } - validatedClassNamesValue = - prefix + validatedClassNamesValue.replace(outdatedClass[0], outdatedClass[1]) + suffix; - context.report({ - node, - messageId: 'classnameChanged', - data: { - deprecated: outdatedClass[0], - updated: outdatedClass[1], - }, - fix: function (fixer) { - return fixer.replaceTextRange([start, end], validatedClassNamesValue); - }, - }); - }); - deprecatedBgOpacity.forEach((bgClass) => { - context.report({ - node, - messageId: 'classnameOpacityDeprecated', - data: { - classname: bgClass[0], - value: bgClass[1], - }, - }); - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - parseForObsoleteClassNames(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - parseForObsoleteClassNames(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - node.arguments.forEach((arg) => { - parseForObsoleteClassNames(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - parseForObsoleteClassNames(node, node.quasi); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - parseForObsoleteClassNames(node); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - parseForObsoleteClassNames(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - parseForObsoleteClassNames(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/no-arbitrary-value.js b/lib/rules/no-arbitrary-value.js deleted file mode 100644 index 71cf459..0000000 --- a/lib/rules/no-arbitrary-value.js +++ /dev/null @@ -1,218 +0,0 @@ -/** - * @fileoverview Forbid using arbitrary values in classnames - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const ARBITRARY_VALUE_DETECTED_MSG = `Arbitrary value detected in '{{classname}}'`; - -module.exports = { - meta: { - docs: { - description: 'Forbid using arbitrary values in classnames', - category: 'Best Practices', - recommended: false, - url: docsUrl('no-arbitrary-value'), - }, - messages: { - arbitraryValueDetected: ARBITRARY_VALUE_DETECTED_MSG, - }, - fixable: null, - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Recursive function crawling into child nodes - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const parseForArbitraryValues = (node, arg = null) => { - let originalClassNamesValue = null; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - arg.expressions.forEach((exp) => { - parseForArbitraryValues(node, exp); - }); - arg.quasis.forEach((quasis) => { - parseForArbitraryValues(node, quasis); - }); - return; - case 'ConditionalExpression': - parseForArbitraryValues(node, arg.consequent); - parseForArbitraryValues(node, arg.alternate); - return; - case 'LogicalExpression': - parseForArbitraryValues(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - parseForArbitraryValues(node, el); - }); - return; - case 'ObjectExpression': - const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'; - const isVue = node.key && node.key.type === 'VDirectiveKey'; - arg.properties.forEach((prop) => { - const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value; - parseForArbitraryValues(node, propVal); - }); - return; - case 'Property': - parseForArbitraryValues(node, arg.key); - return; - case 'Literal': - originalClassNamesValue = arg.value; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - break; - } - } - - let { classNames } = astUtil.extractClassnamesFromValue(originalClassNamesValue); - const forbidden = []; - classNames.forEach((cls, idx) => { - const parsed = groupUtil.parseClassname(cls, [], mergedConfig, idx); - if (/\[.*\]/i.test(parsed.body)) { - forbidden.push(parsed.name); - } - }); - - forbidden.forEach((forbiddenClass) => { - context.report({ - node, - messageId: 'arbitraryValueDetected', - data: { - classname: forbiddenClass, - }, - }); - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - parseForArbitraryValues(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - parseForArbitraryValues(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - node.arguments.forEach((arg) => { - parseForArbitraryValues(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - parseForArbitraryValues(node, node.quasi); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - parseForArbitraryValues(node, null); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - parseForArbitraryValues(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - parseForArbitraryValues(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/no-contradicting-classname.js b/lib/rules/no-contradicting-classname.js deleted file mode 100644 index c7ce96c..0000000 --- a/lib/rules/no-contradicting-classname.js +++ /dev/null @@ -1,265 +0,0 @@ -/** - * @fileoverview Avoid contradicting Tailwind CSS classnames (e.g. "w-3 w-5") - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const defaultGroups = require('../config/groups').groups; -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const CONFLICTING_CLASSNAMES_DETECTED_MSG = `Classnames {{classnames}} are conflicting!`; - -module.exports = { - meta: { - docs: { - description: 'Avoid contradicting Tailwind CSS classnames (e.g. "w-3 w-5")', - category: 'Possible Errors', - recommended: true, - url: docsUrl('no-contradicting-classname'), - }, - messages: { - conflictingClassnames: CONFLICTING_CLASSNAMES_DETECTED_MSG, - }, - fixable: null, - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const ignoredKeys = getOption(context, 'ignoredKeys'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - // Init assets before sorting - const groups = groupUtil.getGroups(defaultGroups, mergedConfig); - - /** - * Parse the classnames and report found conflicts - * @param {Array} classNames - * @param {ASTNode} node - */ - const parseForContradictingClassNames = (classNames, node) => { - // Init assets before sorting - const sorted = groupUtil.initGroupSlots(groups); - - // Move each classname inside its dedicated group - classNames.forEach((className) => { - const idx = groupUtil.getGroupIndex(className, groups, mergedConfig.separator); - if (idx > -1) { - sorted[idx].push(className); - } - }); - - // Only multiple classNames - const sortedGroups = sorted.filter((slot) => slot.length > 1); - const arbitraryPropsGroupIndex = sortedGroups.findIndex((slot) => { - const suffix = groupUtil.getSuffix(slot[0], mergedConfig.separator); - return groupUtil.getArbitraryProperty(suffix, mergedConfig.separator) !== ''; - }); - - // Sorts each groups' classnames - const ambiguousArbitraryValuesOrClasses = String.raw`(\[(.*${mergedConfig.separator}))|(^((?!:).)*$)`; - - sortedGroups.forEach((group, groupIndex) => { - const variants = []; - group.forEach((cls) => { - const prefix = groupUtil.getPrefix(cls, mergedConfig.separator); - const name = cls.substr(prefix.length); - if (groupIndex === arbitraryPropsGroupIndex) { - // Arbitrary Props - const arbitraryProp = groupUtil.getArbitraryProperty(name, mergedConfig.separator); - const identifier = prefix + arbitraryProp; - const idx = variants.findIndex((v) => identifier === v.prefix); - if (idx === -1) { - variants.push({ - prefix: identifier, - name: [name], - }); - } else { - variants[idx].name.push(name); - } - } else { - // "Regular classNames" - const rePrefix = prefix === '' ? ambiguousArbitraryValuesOrClasses : '^' + prefix; - const idx = variants.findIndex((v) => v.prefix === rePrefix); - if (idx === -1) { - variants.push({ - prefix: rePrefix, - name: [name], - }); - } else { - variants[idx].name.push(name); - } - } - }); - - // Several classNames with the same prefix - const potentialTroubles = variants.filter((v) => v.name.length > 1); - if (potentialTroubles.length) { - potentialTroubles.forEach((variantGroup) => { - const re = new RegExp(variantGroup.prefix); - const conflicting = group.filter((c) => re.test(c)); - context.report({ - node: node, - messageId: 'conflictingClassnames', - data: { - classnames: conflicting.join(', '), - }, - }); - }); - } - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - astUtil.parseNodeRecursive(node, null, parseForContradictingClassNames, true, false, ignoredKeys); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - astUtil.parseNodeRecursive( - node, - node.value.expression, - parseForContradictingClassNames, - true, - false, - ignoredKeys - ); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - const allClassnamesForNode = []; - const pushClasses = (classNames, targetNode) => { - if (targetNode === null) { - // Classnames should be parsed in isolation (e.g. conditional expressions) - parseForContradictingClassNames(classNames, node); - } else { - // Gather the classes prior to validation - allClassnamesForNode.push(...classNames); - } - }; - node.arguments.forEach((arg) => { - astUtil.parseNodeRecursive(node, arg, pushClasses, true, false, ignoredKeys); - }); - parseForContradictingClassNames(allClassnamesForNode, node); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - - const allClassnamesForNode = []; - const pushClasses = (classNames, targetNode) => { - if (targetNode === null) { - // Classnames should be parsed in isolation (e.g. conditional expressions) - parseForContradictingClassNames(classNames, node); - } else { - // Gather the classes prior to validation - allClassnamesForNode.push(...classNames); - } - }; - astUtil.parseNodeRecursive(node, node.quasi, pushClasses, true, false, ignoredKeys); - parseForContradictingClassNames(allClassnamesForNode, node); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - astUtil.parseNodeRecursive(node, null, parseForContradictingClassNames, true, false, ignoredKeys); - break; - case astUtil.isArrayExpression(node): - const allClassnamesForNode = []; - const pushClasses = (classNames, targetNode) => { - if (targetNode === null) { - // Classnames should be parsed in isolation (e.g. conditional expressions) - parseForContradictingClassNames(classNames, node); - } else { - // Gather the classes prior to validation - allClassnamesForNode.push(...classNames); - } - }; - node.value.expression.elements.forEach((el) => { - astUtil.parseNodeRecursive(node, el, pushClasses, true, false, ignoredKeys); - }); - parseForContradictingClassNames(allClassnamesForNode, node); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - astUtil.parseNodeRecursive(node, prop, parseForContradictingClassNames, false, false, ignoredKeys); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/no-custom-classname.js b/lib/rules/no-custom-classname.js deleted file mode 100644 index 97216be..0000000 --- a/lib/rules/no-custom-classname.js +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @fileoverview Detect classnames which do not belong to Tailwind CSS - * @author no-custom-classname - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const defaultGroups = require('../config/groups').groups; -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); -const getClassnamesFromCSS = require('../util/cssFiles'); -const createContextFallback = require('tailwindcss/lib/lib/setupContextUtils').createContext; -const generated = require('../util/generated'); -const escapeRegex = require('../util/regex').escapeRegex; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const CUSTOM_CLASSNAME_DETECTED_MSG = `Classname '{{classname}}' is not a Tailwind CSS class!`; - -// Group/peer names can be arbitrarily named and are not -// generated by generateRules. Using a custom regexp to -// validate these avoids false reports. -const getGroupNameRegex = (prefix = '') => - new RegExp(`^${escapeRegex(prefix)}(group|peer)\/[\\w\\$\\#\\@\\%\\^\\&\\*\\_\\-]+$`, 'i'); - -const contextFallbackCache = new WeakMap(); - -module.exports = { - meta: { - docs: { - description: 'Detect classnames which do not belong to Tailwind CSS', - category: 'Best Practices', - recommended: false, - url: docsUrl('no-custom-classname'), - }, - messages: { - customClassnameDetected: CUSTOM_CLASSNAME_DETECTED_MSG, - }, - fixable: null, - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - cssFiles: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - cssFilesRefreshRate: { - type: 'number', - // default: 5_000, - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - whitelist: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const ignoredKeys = getOption(context, 'ignoredKeys'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const cssFiles = getOption(context, 'cssFiles'); - const cssFilesRefreshRate = getOption(context, 'cssFilesRefreshRate'); - const whitelist = getOption(context, 'whitelist'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - const contextFallback = // Set the created contextFallback in the cache if it does not exist yet. - ( - contextFallbackCache.has(mergedConfig) - ? contextFallbackCache - : contextFallbackCache.set(mergedConfig, createContextFallback(mergedConfig)) - ).get(mergedConfig); - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - // Init assets before sorting - const groups = groupUtil.getGroups(defaultGroups, mergedConfig); - const classnamesFromFiles = getClassnamesFromCSS(cssFiles, cssFilesRefreshRate); - const groupNameRegex = getGroupNameRegex(mergedConfig.prefix); - - /** - * Parse the classnames and report found conflicts - * @param {Array} classNames - * @param {ASTNode} node - */ - const parseForCustomClassNames = (classNames, node) => { - classNames.forEach((className) => { - const gen = generated(className, contextFallback); - if (gen.length) { - return; // Lazier is faster... processing next className! - } - const idx = groupUtil.getGroupIndex(className, groups, mergedConfig.separator); - if (idx >= 0) { - return; // Lazier is faster... processing next className! - } - const whitelistIdx = groupUtil.getGroupIndex(className, whitelist, mergedConfig.separator); - if (whitelistIdx >= 0) { - return; // Lazier is faster... processing next className! - } - const fromFilesIdx = groupUtil.getGroupIndex(className, classnamesFromFiles, mergedConfig.separator); - if (fromFilesIdx >= 0) { - return; // Lazier is faster... processing next className! - } - if (groupNameRegex.test(className)) { - return; // Lazier is faster... processing next className! - } - - // No match found - context.report({ - node, - messageId: 'customClassnameDetected', - data: { - classname: className, - }, - }); - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - astUtil.parseNodeRecursive(node, null, parseForCustomClassNames, false, false, ignoredKeys); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - astUtil.parseNodeRecursive(node, node.value.expression, parseForCustomClassNames, false, false, ignoredKeys); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - node.arguments.forEach((arg) => { - astUtil.parseNodeRecursive(node, arg, parseForCustomClassNames, false, false, ignoredKeys); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { - return; - } - astUtil.parseNodeRecursive(node, node.quasi, parseForCustomClassNames, false, false, ignoredKeys); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - astUtil.parseNodeRecursive(node, null, parseForCustomClassNames, false, false, ignoredKeys); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - astUtil.parseNodeRecursive(node, arg, parseForCustomClassNames, false, false, ignoredKeys); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - astUtil.parseNodeRecursive(node, prop, parseForCustomClassNames, false, false, ignoredKeys); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/rules/no-unnecessary-arbitrary-value.js b/lib/rules/no-unnecessary-arbitrary-value.js deleted file mode 100644 index 58904f8..0000000 --- a/lib/rules/no-unnecessary-arbitrary-value.js +++ /dev/null @@ -1,362 +0,0 @@ -/** - * @fileoverview Detect arbitrary classnames which have an existing equivalent preset in the configuration - * @author François Massart - */ -'use strict'; - -const docsUrl = require('../util/docsUrl'); -const customConfig = require('../util/customConfig'); -const astUtil = require('../util/ast'); -const groupUtil = require('../util/groupMethods'); -const getOption = require('../util/settings'); -const parserUtil = require('../util/parser'); -const { validZeroRegEx } = require('../util/types/length'); -const defaultGroups = require('../config/groups').groups; - -// TODO get the correct value of start and end -// TODO make rule fixable when only 1 match -// TODO propose several fixes when multiple matches + priority to exact match - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -// Predefine message for use in context.report conditional. -// messageId will still be usable in tests. -const UNNECESSARY_ARBITRARY_VALUE_DETECTED_MSG = `The arbitrary class '{{classname}}' could be replaced by '{{presets}}'`; - -module.exports = { - meta: { - docs: { - description: 'Forbid using arbitrary values in classnames when an equivalent preset exists', - category: 'Best Practices', - recommended: true, - url: docsUrl('no-unnecessary-arbitrary-value'), - }, - messages: { - unnecessaryArbitraryValueDetected: UNNECESSARY_ARBITRARY_VALUE_DETECTED_MSG, - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - callees: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - ignoredKeys: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - config: { - // returned from `loadConfig()` utility - type: ['string', 'object'], - }, - tags: { - type: 'array', - items: { type: 'string', minLength: 0 }, - uniqueItems: true, - }, - }, - }, - ], - }, - - create: function (context) { - const callees = getOption(context, 'callees'); - const skipClassAttribute = getOption(context, 'skipClassAttribute'); - const tags = getOption(context, 'tags'); - const twConfig = getOption(context, 'config'); - const classRegex = getOption(context, 'classRegex'); - - const mergedConfig = customConfig.resolve(twConfig); - const groups = groupUtil.getGroups(defaultGroups, mergedConfig); - const configKeys = groupUtil.getGroupConfigKeys(defaultGroups); - let parentTemplateLiteral = null; - - //---------------------------------------------------------------------- - // Helpers - //---------------------------------------------------------------------- - - /** - * Recursive function crawling into child nodes - * @param {ASTNode} node The root node of the current parsing - * @param {ASTNode} arg The child node of node - * @returns {void} - */ - const parseForArbitraryValues = (node, arg = null) => { - let start = null; - let end = null; - let originalClassNamesValue = null; - if (arg === null) { - originalClassNamesValue = astUtil.extractValueFromNode(node); - const range = astUtil.extractRangeFromNode(node); - if (node.type === 'TextAttribute') { - start = range[0]; - end = range[1]; - } else { - start = range[0] + 1; - end = range[1] - 1; - } - } else { - switch (arg.type) { - case 'Identifier': - return; - case 'TemplateLiteral': - parentTemplateLiteral = arg; - arg.expressions.forEach((exp) => { - parseForArbitraryValues(node, exp); - }); - arg.quasis.forEach((quasis) => { - parseForArbitraryValues(node, quasis); - }); - parentTemplateLiteral = null; - return; - case 'ConditionalExpression': - parseForArbitraryValues(node, arg.consequent); - parseForArbitraryValues(node, arg.alternate); - return; - case 'LogicalExpression': - parseForArbitraryValues(node, arg.right); - return; - case 'ArrayExpression': - arg.elements.forEach((el) => { - parseForArbitraryValues(node, el); - }); - return; - case 'ObjectExpression': - const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames'; - const isVue = node.key && node.key.type === 'VDirectiveKey'; - arg.properties.forEach((prop) => { - const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value; - parseForArbitraryValues(node, propVal); - }); - return; - case 'Property': - parseForArbitraryValues(node, arg.key); - start = arg.range[0] + 1; - end = arg.range[1] - 1; - return; - case 'Literal': - originalClassNamesValue = arg.value; - start = arg.range[0] + 1; - end = arg.range[1] - 1; - break; - case 'TemplateElement': - originalClassNamesValue = arg.value.raw; - if (originalClassNamesValue === '') { - return; - } - start = arg.range[0]; - end = arg.range[1]; - if (parentTemplateLiteral) { - if (parentTemplateLiteral.range[0] === start) { - start += 1; // Skip opening backtick - } else { - start += 1; // Skip closing } - } - if (parentTemplateLiteral.range[1] === end) { - end -= 1; // Skip closing backtick - } else { - end -= 2; // Skip opening ${ - } - } - break; - } - } - - const arbitraryRegEx = /^(?.*)\[(?.*)\]$/i; - const { classNames } = astUtil.extractClassnamesFromValue(originalClassNamesValue); - const arbitraryClassnames = classNames.filter((c) => arbitraryRegEx.test(c)); - - if (arbitraryClassnames.length === 0) { - return; - } - - const unnecessaryArbitraryClasses = []; - const existingSubstitutes = []; - - arbitraryClassnames.forEach((arbitraryClass, idx) => { - const parsed = groupUtil.parseClassname(arbitraryClass, [], mergedConfig, idx); - const res = arbitraryRegEx.exec(parsed.name); - if (res && res.groups && res.groups.backBone && res.groups.arbitraryValue) { - const backBone = res.groups.backBone; - const arbitraryValue = res.groups.arbitraryValue; - const groupIdx = groupUtil.getGroupIndex(arbitraryClass, groups, mergedConfig.separator); - if ([groups[groupIdx], parsed.body, arbitraryValue].includes(undefined)) { - return false; - } - const canBeNegative = groups[groupIdx].indexOf('?') !== -1; - const isNegativeClass = parsed.body.indexOf('-') === 0; - const isNegativeValue = arbitraryValue.indexOf('-') === 0; - const configurationKey = configKeys[groupIdx]; - const configuration = mergedConfig.theme[configurationKey]; - if ([undefined, null].includes(configuration)) { - return false; - } - const configurationKeys = Object.keys(configuration); - const zeroValueWithOrWithoutUnitsPattern = new RegExp(validZeroRegEx, 'i'); - const isZeroArbitraryValue = zeroValueWithOrWithoutUnitsPattern.test(arbitraryValue); - const negativeSubstitutes = []; - const matchingConfigurationKeys = configurationKeys.filter((key) => { - const configValue = configuration[key]; - if (isZeroArbitraryValue && zeroValueWithOrWithoutUnitsPattern.test(configValue)) { - // Both config and tested values are 0 based (with or without units) - negativeSubstitutes.push(false); - return true; - } - // Negative possibilities - if (canBeNegative) { - const absoluteValue = isNegativeValue ? arbitraryValue.substring(1) : arbitraryValue; - const computedAsNegative = isNegativeClass !== isNegativeValue; - if (`${configValue}` === `${absoluteValue}`) { - negativeSubstitutes.push(computedAsNegative); - return true; - } - return false; - } - // Default - if (`${configValue}` === `${arbitraryValue}`) { - negativeSubstitutes.push(false); - return true; - } - return false; - }); - if (matchingConfigurationKeys.length) { - unnecessaryArbitraryClasses.push(parsed.name); - existingSubstitutes.push( - matchingConfigurationKeys.map((key, idx) => { - let patchedBody = backBone.substring(parsed.variants.length); - patchedBody = patchedBody.charAt(0) === '-' ? patchedBody.substring(1) : patchedBody; - const noneOrMinus = negativeSubstitutes[idx] ? '-' : ''; - if (key === 'DEFAULT') { - return parsed.variants + noneOrMinus + patchedBody.substring(0, patchedBody.length - 1); - } - return parsed.variants + noneOrMinus + patchedBody + key; - }) - ); - } - } - }); - - // TODO Group by range and bundle the fix - const fixables = {}; - unnecessaryArbitraryClasses.forEach((forbiddenClass, idx) => { - if (existingSubstitutes[idx].length === 1) { - const rangeKey = `s${start}e${end}`; - if (!fixables[rangeKey]) { - fixables[rangeKey] = []; - } - const fixer = { - unjustified: forbiddenClass, - substitute: existingSubstitutes[idx][0], - }; - fixables[rangeKey].push(fixer); - } else { - context.report({ - node, - messageId: 'unnecessaryArbitraryValueDetected', - data: { - classname: forbiddenClass, - presets: existingSubstitutes[idx].join("' or '"), - }, - }); - } - }); - Object.keys(fixables).forEach((rangeKey) => { - const batchFixes = fixables[rangeKey]; - let patched = originalClassNamesValue; - const forbiddenClasses = []; - const substitutes = []; - for (let idx = 0; idx < batchFixes.length; idx++) { - // BUG replace could affect same class with distinct variants... eg. h-0 might affect min-h-0 - const unjustified = batchFixes[idx].unjustified; - forbiddenClasses.push(unjustified); - const substitute = batchFixes[idx].substitute; - substitutes.push(substitute); - patched = patched.replace(unjustified, substitute); - } - context.report({ - node, - messageId: 'unnecessaryArbitraryValueDetected', - data: { - classname: forbiddenClasses.join(', '), - presets: substitutes.join(', '), - }, - fix: function (fixer) { - return fixer.replaceTextRange([start, end], patched); - }, - }); - }); - }; - - //---------------------------------------------------------------------- - // Public - //---------------------------------------------------------------------- - - const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { - return; - } - if (astUtil.isLiteralAttributeValue(node)) { - parseForArbitraryValues(node); - } else if (node.value && node.value.type === 'JSXExpressionContainer') { - parseForArbitraryValues(node, node.value.expression); - } - }; - - const callExpressionVisitor = function (node) { - const calleeStr = astUtil.calleeToString(node.callee); - if (callees.findIndex((name) => calleeStr === name) === -1) { - return; - } - node.arguments.forEach((arg) => { - parseForArbitraryValues(node, arg); - }); - }; - - const scriptVisitor = { - JSXAttribute: attributeVisitor, - TextAttribute: attributeVisitor, - CallExpression: callExpressionVisitor, - TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { - return; - } - parseForArbitraryValues(node, node.quasi); - }, - }; - - const templateVisitor = { - CallExpression: callExpressionVisitor, - /* - Tagged templates inside data bindings - https://github.com/vuejs/vue/issues/9721 - */ - VAttribute: function (node) { - switch (true) { - case !astUtil.isValidVueAttribute(node, classRegex): - return; - case astUtil.isVLiteralValue(node): - parseForArbitraryValues(node, null); - break; - case astUtil.isArrayExpression(node): - node.value.expression.elements.forEach((arg) => { - parseForArbitraryValues(node, arg); - }); - break; - case astUtil.isObjectExpression(node): - node.value.expression.properties.forEach((prop) => { - parseForArbitraryValues(node, prop); - }); - break; - } - }, - }; - - return parserUtil.defineTemplateBodyVisitor(context, templateVisitor, scriptVisitor); - }, -}; diff --git a/lib/util/ast.js b/lib/util/ast.js deleted file mode 100644 index ca01cb3..0000000 --- a/lib/util/ast.js +++ /dev/null @@ -1,389 +0,0 @@ -/** - * @fileoverview Utility functions for AST - */ - -'use strict'; - -const { separatorRegEx } = require('./regex'); -// context.parserPath -// /.../eslint-plugin-tailwindcss/node_modules/espree/espree.js -// /.../eslint-plugin-tailwindcss/node_modules/@angular-eslint/template-parser/dist/index.js - -const removeDuplicatesFromArray = require('./removeDuplicatesFromArray'); - -function calleeToString(calleeNode) { - if (calleeNode.type === 'Identifier') { - return calleeNode.name; - } - if (calleeNode.type === 'MemberExpression') { - return `${calleeNode.object.name}.${calleeNode.property.name}`; - } -} - -/** - * Find out if node is `class` or `className` - * - * @param {ASTNode} node The AST node being checked - * @param {String} classRegex Regex to test the attribute that is being checked against - * @returns {Boolean} - */ -function isClassAttribute(node, classRegex) { - if (!node.name) { - return false; - } - let name = ''; - switch (node.type) { - case 'TextAttribute': - name = node.name; - break; - case 'JSXAttribute': - if (node.name.type === 'JSXNamespacedName') { - const ns = node.name.namespace.name || ''; - name = (ns.length ? ns + ':' : '') + node.name.name.name; - } else { - name = node.name.name; - } - break; - default: - name = node.name.name; - } - return new RegExp(classRegex).test(name); -} - -/** - * Find out if node is `class` - * - * @param {ASTNode} node The AST node being checked - * @param {String} classRegex Regex to test the attribute that is being checked against - * @returns {Boolean} - */ -function isVueClassAttribute(node, classRegex) { - const re = new RegExp(classRegex); - let name = ''; - switch (true) { - case node.key && node.key.name && re.test(node.key.name): - // class="vue-classes-as-litteral" - return true; - case node.key && - node.key.name && - node.key.name.name && - node.key.argument && - node.key.argument.name && - /^bind$/.test(node.key.name.name) && - re.test(node.key.argument.name): - // v-bind:class="vue-classes-as-bind" - // :class="vue-classes-as-bind" - return true; - default: - return false; - } -} - -/** - * Find out if node's value attribute is just simple text - * - * @param {ASTNode} node The AST node being checked - * @returns {Boolean} - */ -function isVLiteralValue(node) { - return node.value && node.value.type === 'VLiteral'; -} - -/** - * Find out if node's value attribute is an ArrayExpression - * - * @param {ASTNode} node The AST node being checked - * @returns {Boolean} - */ -function isArrayExpression(node) { - return node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ArrayExpression'; -} - -/** - * Find out if node's value attribute is an ObjectExpression - * - * @param {ASTNode} node The AST node being checked - * @returns {Boolean} - */ -function isObjectExpression(node) { - return node.value && node.value.type === 'VExpressionContainer' && node.value.expression.type === 'ObjectExpression'; -} - -/** - * Find out if node's value attribute is just simple text - * - * @param {ASTNode} node The AST node being checked - * @returns {Boolean} - */ -function isVueValidAttributeValue(node) { - switch (true) { - case isVLiteralValue(node): // Simple string - case isArrayExpression(node): // ['tw-unknown-class'] - case isObjectExpression(node): // {'tw-unknown-class': true} - return true; - break; - default: - return false; - } -} - -/** - * Find out if node's value attribute is just simple text - * - * @param {ASTNode} node The AST node being checked - * @returns {Boolean} - */ -function isLiteralAttributeValue(node) { - if (node.type === 'TextAttribute' && node.name === 'class' && typeof node.value === 'string') { - return true; - } - if (node.value) { - switch (node.value.type) { - case 'Literal': - // No support for dynamic or conditional... - return !/\{|\?|\}/.test(node.value.value); - case 'JSXExpressionContainer': - // className={"..."} - return node.value.expression.type === 'Literal'; - } - } - return false; -} - -/** - * Find out if the node is a valid candidate for our rules - * - * @param {ASTNode} node The AST node being checked - * @param {String} classRegex Regex to test the attribute that is being checked against - * @returns {Boolean} - */ -function isValidJSXAttribute(node, classRegex) { - if (!isClassAttribute(node, classRegex)) { - // Only run for class[Name] attributes - return false; - } - if (!isLiteralAttributeValue(node)) { - // No support for dynamic or conditional classnames - return false; - } - return true; -} - -/** - * Find out if the node is a valid candidate for our rules - * - * @param {ASTNode} node The AST node being checked - * @param {String} classRegex Regex to test the attribute that is being checked against - * @returns {Boolean} - */ -function isValidVueAttribute(node, classRegex) { - if (!isVueClassAttribute(node, classRegex)) { - // Only run for class attributes - return false; - } - if (!isVueValidAttributeValue(node)) { - // No support for dynamic or conditional classnames - return false; - } - return true; -} - -function extractRangeFromNode(node) { - if (node.type === 'TextAttribute' && node.name === 'class') { - return [node.valueSpan.fullStart.offset, node.valueSpan.end.offset]; - } - if (node.value === undefined) { - return [0, 0]; - } - switch (node.value.type) { - case 'JSXExpressionContainer': - return node.value.expression.range; - default: - return node.value.range; - } -} - -function extractValueFromNode(node) { - if (node.type === 'TextAttribute' && node.name === 'class') { - return node.value; - } - if (node.value === undefined) { - return node.value; - } - - switch (node.value.type) { - case 'JSXExpressionContainer': - return node.value.expression.value; - case 'VExpressionContainer': - switch (node.value.expression.type) { - case 'ArrayExpression': - return node.value.expression.elements; - break; - case 'ObjectExpression': - return node.value.expression.properties; - break; - } - return node.value.expression.value; - default: - return node.value.value; - } -} - -function extractClassnamesFromValue(classStr) { - if (typeof classStr !== 'string') { - return { classNames: [], whitespaces: [], headSpace: false, tailSpace: false }; - } - let parts = classStr.split(separatorRegEx); - if (parts[0] === '') { - parts.shift(); - } - if (parts[parts.length - 1] === '') { - parts.pop(); - } - let headSpace = separatorRegEx.test(parts[0]); - let tailSpace = separatorRegEx.test(parts[parts.length - 1]); - const isClass = (_, i) => (headSpace ? i % 2 !== 0 : i % 2 === 0); - const isNotClass = (_, i) => (headSpace ? i % 2 === 0 : i % 2 !== 0); - let classNames = parts.filter(isClass); - let whitespaces = parts.filter(isNotClass); - return { classNames: classNames, whitespaces: whitespaces, headSpace: headSpace, tailSpace: tailSpace }; -} - -/** - * Inspect and parse an abstract syntax node and run a callback function - * - * @param {ASTNode} rootNode The current root node being parsed by eslint - * @param {ASTNode} childNode The AST node child argument being checked - * @param {Function} cb The callback function - * @param {Boolean} skipConditional Optional, indicate distinct parsing for conditional nodes - * @param {Boolean} isolate Optional, set internally to isolate parsing and validation on conditional children - * @param {Array} ignoredKeys Optional, set object keys which should not be parsed e.g. for `cva` - * @returns {void} - */ -function parseNodeRecursive(rootNode, childNode, cb, skipConditional = false, isolate = false, ignoredKeys = []) { - // TODO allow vue non litteral - let originalClassNamesValue; - let classNames; - if (childNode === null) { - originalClassNamesValue = extractValueFromNode(rootNode); - ({ classNames } = extractClassnamesFromValue(originalClassNamesValue)); - classNames = removeDuplicatesFromArray(classNames); - if (classNames.length === 0) { - // Don't run for empty className - return; - } - cb(classNames, rootNode); - } else if (childNode === undefined) { - // Ignore invalid child candidates (probably inside complex TemplateLiteral) - return; - } else { - const forceIsolation = skipConditional ? true : isolate; - let trim = false; - switch (childNode.type) { - case 'TemplateLiteral': - childNode.expressions.forEach((exp) => { - parseNodeRecursive(rootNode, exp, cb, skipConditional, forceIsolation, ignoredKeys); - }); - childNode.quasis.forEach((quasis) => { - parseNodeRecursive(rootNode, quasis, cb, skipConditional, isolate, ignoredKeys); - }); - return; - case 'ConditionalExpression': - parseNodeRecursive(rootNode, childNode.consequent, cb, skipConditional, forceIsolation, ignoredKeys); - parseNodeRecursive(rootNode, childNode.alternate, cb, skipConditional, forceIsolation, ignoredKeys); - return; - case 'LogicalExpression': - parseNodeRecursive(rootNode, childNode.right, cb, skipConditional, forceIsolation, ignoredKeys); - return; - case 'ArrayExpression': - childNode.elements.forEach((el) => { - parseNodeRecursive(rootNode, el, cb, skipConditional, forceIsolation, ignoredKeys); - }); - return; - case 'ObjectExpression': - childNode.properties.forEach((prop) => { - const isUsedByClassNamesPlugin = rootNode.callee && rootNode.callee.name === 'classnames'; - - if (prop.type === 'SpreadElement') { - // Ignore spread elements - return; - } - - if (prop.key.type === 'Identifier' && ignoredKeys.includes(prop.key.name)) { - // Ignore specific keys defined in settings - return; - } - - parseNodeRecursive( - rootNode, - isUsedByClassNamesPlugin ? prop.key : prop.value, - cb, - skipConditional, - forceIsolation, - ignoredKeys - ); - }); - return; - case 'Property': - parseNodeRecursive(rootNode, childNode.key, cb, skipConditional, forceIsolation, ignoredKeys); - return; - case 'Literal': - trim = true; - originalClassNamesValue = childNode.value; - break; - case 'TemplateElement': - originalClassNamesValue = childNode.value.raw; - break; - } - ({ classNames } = extractClassnamesFromValue(originalClassNamesValue)); - classNames = removeDuplicatesFromArray(classNames); - if (classNames.length === 0) { - // Don't run for empty className - return; - } - const targetNode = isolate ? null : rootNode; - cb(classNames, targetNode); - } -} - -function getTemplateElementPrefix(text, raw) { - const idx = text.indexOf(raw); - if (idx === 0) { - return ''; - } - return text.split(raw).shift(); -} - -function getTemplateElementSuffix(text, raw) { - if (text.indexOf(raw) === -1) { - return ''; - } - return text.split(raw).pop(); -} - -function getTemplateElementBody(text, prefix, suffix) { - let arr = text.split(prefix); - arr.shift(); - let body = arr.join(prefix); - arr = body.split(suffix); - arr.pop(); - return arr.join(suffix); -} - -module.exports = { - calleeToString, - extractRangeFromNode, - extractValueFromNode, - extractClassnamesFromValue, - isClassAttribute, - isLiteralAttributeValue, - isArrayExpression, - isObjectExpression, - isValidJSXAttribute, - isValidVueAttribute, - isVLiteralValue, - parseNodeRecursive, - getTemplateElementPrefix, - getTemplateElementSuffix, - getTemplateElementBody, -}; diff --git a/lib/util/cssFiles.js b/lib/util/cssFiles.js deleted file mode 100644 index 4a49a33..0000000 --- a/lib/util/cssFiles.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -const fg = require('fast-glob'); -const fs = require('fs'); -const postcss = require('postcss'); -const removeDuplicatesFromArray = require('./removeDuplicatesFromArray'); - -let previousGlobsResults = []; -let lastUpdate = null; -let classnamesFromFiles = []; - -/** - * Read CSS files and extract classnames - * @param {Array} patterns Glob patterns to locate files - * @param {Number} refreshRate Interval - * @returns {Array} List of classnames - */ -const generateClassnamesListSync = (patterns, refreshRate = 5_000) => { - const now = new Date().getTime(); - const files = fg.sync(patterns, { suppressErrors: true }); - const newGlobs = previousGlobsResults.flat().join(',') != files.flat().join(','); - const expired = lastUpdate === null || now - lastUpdate > refreshRate; - if (newGlobs || expired) { - previousGlobsResults = files; - lastUpdate = now; - let detectedClassnames = []; - for (const file of files) { - const data = fs.readFileSync(file, 'utf-8'); - const root = postcss.parse(data); - root.walkRules((rule) => { - const regexp = /\.([^\.\,\s\n\:\(\)\[\]\'~\+\>\*\\]*)/gim; - const matches = [...rule.selector.matchAll(regexp)]; - const classnames = matches.map((arr) => arr[1]); - detectedClassnames.push(...classnames); - }); - detectedClassnames = removeDuplicatesFromArray(detectedClassnames); - } - classnamesFromFiles = detectedClassnames; - } - return classnamesFromFiles; -}; - -module.exports = generateClassnamesListSync; diff --git a/lib/util/customConfig.js b/lib/util/customConfig.js deleted file mode 100644 index 9bc6a40..0000000 --- a/lib/util/customConfig.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const resolveConfig = require('tailwindcss/resolveConfig'); -let twLoadConfig; - -try { - twLoadConfig = require('tailwindcss/lib/lib/load-config'); -} catch (err) { - twLoadConfig = null; -} - -const CHECK_REFRESH_RATE = 1_000; -let previousConfig = null; -let lastCheck = null; -let mergedConfig = null; -let lastModifiedDate = null; - -/** - * @see https://stackoverflow.com/questions/9210542/node-js-require-cache-possible-to-invalidate - * @param {string} module The path to the module - * @returns the module's export - */ -function requireUncached(module) { - delete require.cache[require.resolve(module)]; - if (twLoadConfig === null) { - return require(module); - } else { - return twLoadConfig.loadConfig(module); - } -} - -function loadConfig(config) { - let loadedConfig = null; - if (typeof config === 'string') { - const resolvedPath = path.isAbsolute(config) ? config : path.join(path.resolve(), config); - try { - const stats = fs.statSync(resolvedPath); - if (stats === null) { - loadedConfig = {}; - } else if (lastModifiedDate !== stats.mtime) { - lastModifiedDate = stats.mtime; - loadedConfig = requireUncached(resolvedPath); - } else { - loadedConfig = null; - } - } catch (err) { - loadedConfig = {}; - } finally { - return loadedConfig; - } - } else { - if (typeof config === 'object' && config !== null) { - return config; - } - return {}; - } -} - -function convertConfigToString(config) { - switch (typeof config) { - case 'string': - return config; - case 'object': - return JSON.stringify(config); - default: - return config.toString(); - } -} - -function resolve(twConfig) { - const now = new Date().getTime(); - const newConfig = convertConfigToString(twConfig) !== convertConfigToString(previousConfig); - const expired = now - lastCheck > CHECK_REFRESH_RATE; - if (newConfig || expired) { - previousConfig = twConfig; - lastCheck = now; - const userConfig = loadConfig(twConfig); - // userConfig is null when config file was not modified - if (userConfig !== null) { - mergedConfig = resolveConfig(userConfig); - } - } - return mergedConfig; -} - -module.exports = { - resolve, -}; diff --git a/lib/util/docsUrl.js b/lib/util/docsUrl.js deleted file mode 100644 index 496e42c..0000000 --- a/lib/util/docsUrl.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -// Copied from https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/util/docsUrl.js -function docsUrl(ruleName) { - return `https://github.com/francoismassart/eslint-plugin-tailwindcss/tree/master/docs/rules/${ruleName}.md`; -} - -module.exports = docsUrl; diff --git a/lib/util/generated.js b/lib/util/generated.js deleted file mode 100644 index 708f14e..0000000 --- a/lib/util/generated.js +++ /dev/null @@ -1,10 +0,0 @@ -var generateRulesFallback = require('tailwindcss/lib/lib/generateRules').generateRules; - -function generate(className, context) { - // const order = generateRulesFallback(new Set([className]), context).sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null; - const gen = generateRulesFallback(new Set([className]), context); - // console.debug(gen); - return gen; -} - -module.exports = generate; diff --git a/lib/util/groupMethods.js b/lib/util/groupMethods.js deleted file mode 100644 index ac1bed8..0000000 --- a/lib/util/groupMethods.js +++ /dev/null @@ -1,613 +0,0 @@ -/** - * @fileoverview Utilities used for grouping classnames - */ - -'use strict'; - -// Ambiguous values -// ================ -// Supported hints: length, color, angle, list -// ------------------------------------------- -// -// border-[color/width] -// text-[color/size] -// ring-[color/width] -// ring-offset-[color/width] -// stroke-[current/width] -// bg-[color/(position/size)] -// -// font-[family/weight] - -const angle = require('./types/angle'); -const color = require('./types/color'); -const length = require('./types/length'); - -/** - * Escape special chars for regular expressions - * - * @param {String} str Regular expression to be escaped - * @returns {String} Escaped version - */ -function escapeSpecialChars(str) { - return str.replace(/\W/g, '\\$&'); -} - -/** - * Generates the opacity suffix based on config - * - * @param {Object} config Tailwind CSS Config - * @returns {String} The suffix or an empty string - */ -function generateOptionalOpacitySuffix(config) { - const opacityKeys = !config.theme['opacity'] ? [] : Object.keys(config.theme['opacity']); - opacityKeys.push('\\[(\\d*\\.?\\d*)%?\\]'); - return `(\\/(${opacityKeys.join('|')}))?`; -} - -/** - * Generate the possible options for the RegEx - * - * @param {String} propName The name of the prop e.g. textColor - * @param {Array} keys Keys to be injected in the options - * @param {Object} config Tailwind CSS Config - * @param {Boolean} isNegative If the value is negative - * @returns {String} Generated part of regex exposing the possible values - */ -function generateOptions(propName, keys, config, isNegative = false) { - const opacitySuffixes = generateOptionalOpacitySuffix(config); - const genericArbitraryOption = '\\[(.*)\\]'; - const defaultKeyIndex = keys.findIndex((v) => v === 'DEFAULT'); - if (defaultKeyIndex > -1) { - keys.splice(defaultKeyIndex, 1); - } - const escapedKeys = keys.map((k) => escapeSpecialChars(k)); - switch (propName) { - case 'dark': - // Optional `dark` class - if (config.darkMode === 'class') { - return 'dark'; - } else if (Array.isArray(config.darkMode) && config.darkMode.length === 2 && config.darkMode[0] === 'class') { - // https://tailwindcss.com/docs/dark-mode#customizing-the-class-name - // For the sake of simplicity we only support a single class name - let value = ''; - const res = /^\.(?[A-Z0-9\:\-\_\[\d\]]*)$/gi.exec(config.darkMode[1]); - if (res && res.groups) { - if (res.groups.classnameValue) { - value = res.groups.classnameValue; - } - } - return value; - } else { - return ''; - } - case 'arbitraryProperties': - escapedKeys.push(genericArbitraryOption); - return '(' + escapedKeys.join('|') + ')'; - case 'colors': - case 'accentColor': - case 'borderColor': - case 'boxShadowColor': - case 'divideColor': - case 'fill': - case 'outlineColor': - case 'textColor': - case 'stroke': - case 'gradientColorStopPositions': - // Colors can use segments like 'indigo' and 'indigo-light' - // https://tailwindcss.com/docs/customizing-colors#color-object-syntax - const options = []; - keys.forEach((k) => { - const color = config.theme[propName][k] || config.theme.colors[k]; - if (typeof color === 'string') { - options.push(escapeSpecialChars(k) + opacitySuffixes); - } else { - const variants = Object.keys(color).map((colorKey) => escapeSpecialChars(colorKey)); - const defaultIndex = variants.findIndex((v) => v === 'DEFAULT'); - const hasDefault = defaultIndex > -1; - if (hasDefault) { - variants.splice(defaultIndex, 1); - } - options.push(k + '(\\-(' + variants.join('|') + '))' + (hasDefault ? '?' : '') + opacitySuffixes); - } - }); - const arbitraryColors = [...color.mergedColorValues]; - switch (propName) { - case 'fill': - // Forbidden prefixes - arbitraryColors.push(`(?!(angle|length|list)\:).{1,}`); - break; - case 'gradientColorStopPositions': - arbitraryColors.push(color.RGBAPercentages); // RGBA % 0.5[%] - arbitraryColors.push(color.optionalColorPrefixedVar); - arbitraryColors.push(color.notHSLAPlusWildcard); - break; - case 'textColor': - arbitraryColors.push(color.RGBAPercentages); // RGBA % 0.5[%] - arbitraryColors.push(color.mandatoryColorPrefixed); - break; - default: - arbitraryColors.push(color.mandatoryColorPrefixed); - } - options.push(`\\[(${arbitraryColors.join('|')})\\]`); - return '(' + options.join('|') + ')'; - case 'borderSpacing': - case 'borderWidth': - case 'divideWidth': - case 'fontSize': - case 'outlineWidth': - case 'outlineOffset': - case 'ringWidth': - case 'ringOffsetWidth': - case 'textUnderlineOffset': - escapedKeys.push(length.selectedUnitsRegEx); - escapedKeys.push(length.anyCalcRegEx); - // Mandatory `length:` prefix + wildcard - escapedKeys.push(`\\[length\\:.{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'strokeWidth': - escapedKeys.push(length.selectedUnitsRegEx); - escapedKeys.push(length.anyCalcRegEx); - // Mandatory `length:` prefix + calc + wildcard - escapedKeys.push(`\\[length\\:calc\\(.{1,}\\)\\]`); - // Mandatory `length:` prefix + wildcard + optional units - escapedKeys.push(`\\[length\\:(.{1,})(${length.selectedUnits.join('|')})?\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'gap': - case 'height': - case 'lineHeight': - case 'maxHeight': - case 'size': - case 'maxWidth': - case 'minHeight': - case 'minWidth': - case 'padding': - case 'width': - case 'blur': - case 'brightness': - case 'contrast': - case 'grayscale': - case 'invert': - case 'saturate': - case 'sepia': - case 'backdropBlur': - case 'backdropBrightness': - case 'backdropContrast': - case 'backdropGrayscale': - case 'backdropInvert': - case 'backdropOpacity': - case 'backdropSaturate': - case 'backdropSepia': - case 'transitionDuration': - case 'transitionTimingFunction': - case 'transitionDelay': - case 'animation': - case 'transformOrigin': - case 'scale': - case 'cursor': - // All units - escapedKeys.push(length.mergedUnitsRegEx); - // Forbidden prefixes - escapedKeys.push(`\\[(?!(angle|color|length|list)\:).{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'backdropHueRotate': - case 'hueRotate': - case 'inset': - case 'letterSpacing': - case 'margin': - case 'scrollMargin': - case 'skew': - case 'space': - case 'textIndent': - case 'translate': - // All units - escapedKeys.push(length.mergedUnitsRegEx); - // Forbidden prefixes - escapedKeys.push(`\\[(?!(angle|color|length|list)\:).{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'backgroundOpacity': - case 'borderOpacity': - case 'opacity': - case 'ringOpacity': - // 0 ... .5 ... 1 - escapedKeys.push(`\\[(0(\\.\\d{1,})?|\\.\\d{1,}|1)\\]`); - escapedKeys.push(length.anyCalcRegEx); - // Unprefixed var() - escapedKeys.push(`\\[var\\(\\-\\-[A-Za-z\\-]{1,}\\)\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'rotate': - escapedKeys.push(`\\[(${angle.mergedAngleValues.join('|')})\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'gridTemplateColumns': - case 'gridColumn': - case 'gridColumnStart': - case 'gridColumnEnd': - case 'gridTemplateRows': - case 'gridRow': - case 'gridRowStart': - case 'gridRowEnd': - case 'gridAutoColumns': - case 'gridAutoRows': - // Forbidden prefixes - escapedKeys.push(`\\[(?!(angle|color|length)\:).{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'listStyleType': - // Forbidden prefixes - escapedKeys.push(`\\[(?!(angle|color|length|list)\:).{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'objectPosition': - // Forbidden prefixes - escapedKeys.push(`\\[(?!(angle|color|length)\:).{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'backgroundPosition': - case 'boxShadow': - case 'dropShadow': - case 'transitionProperty': - // Forbidden prefixes - escapedKeys.push(`\\[(?!((angle|color|length|list)\:)|#|var\\().{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'backgroundSize': - // Forbidden prefixes - escapedKeys.push(`\\[length\:.{1,}\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'backgroundImageUrl': - // Forbidden prefixes - escapedKeys.push(`.{1,}`); - return '(' + escapedKeys.join('|') + ')'; - case 'backgroundImage': - // Forbidden prefixes - escapedKeys.push(`\\[url\\(.{1,}\\)\\]`); - return '(' + escapedKeys.join('|') + ')'; - case 'order': - case 'zIndex': - escapedKeys.push(genericArbitraryOption); - return '(' + escapedKeys.join('|') + ')'; - case 'fontWeight': - case 'typography': - case 'lineClamp': - // Cannot be arbitrary? - return '(' + escapedKeys.join('|') + ')'; - case 'aspectRatio': - case 'flexGrow': - case 'flexShrink': - case 'fontFamily': - case 'flex': - case 'borderRadius': - default: - escapedKeys.push(genericArbitraryOption); - return '(' + escapedKeys.join('|') + ')'; - } -} - -const cachedRegexes = new WeakMap(); - -/** - * Customize the regex based on config - * - * @param {String} re Regular expression - * @param {Object} config The merged Tailwind CSS config - * @returns {String} Patched version with config values and additional parameters - */ -function patchRegex(re, config) { - if (!cachedRegexes.has(config)) { - cachedRegexes.set(config, {}); - } - const cache = cachedRegexes.get(config); - if (re in cache) { - return cache[re]; - } - let patched = '\\!?'; - // Prefix - if (config.prefix.length) { - patched += escapeSpecialChars(config.prefix); - } - // Props - let replaced = re; - const propsRe = /\$\{(\-?[a-z]*)\}/gi; - const res = replaced.matchAll(propsRe); - const resArray = [...res]; - const props = resArray.map((arr) => arr[1]); - if (props.length === 0) { - return (cache[re] = `${patched}(${replaced})`); - } - // e.g. backgroundColor, letterSpacing, -margin... - props.forEach((prop) => { - const token = new RegExp('\\$\\{' + prop + '\\}'); - const isNegative = prop.substr(0, 1) === '-'; - const absoluteProp = isNegative ? prop.substr(1) : prop; - if (prop === 'dark') { - // Special case, not a default property from the theme - replaced = replaced.replace(token, generateOptions(absoluteProp, [], config, isNegative)); - return `${patched}(${replaced})`; - } else if (prop === 'arbitraryProperties') { - // Special case - replaced = replaced.replace( - new RegExp('\\$\\{' + absoluteProp + '\\}'), - generateOptions(absoluteProp, [], config, isNegative) - ); - return `${patched}(${replaced})`; - } else if (prop === 'backgroundImageUrl') { - // Special case - replaced = replaced.replace(new RegExp('\\$\\{' + prop + '\\}'), generateOptions(prop, [], config, false)); - return `${patched}(${replaced})`; - } else if (!config.theme || !config.theme[absoluteProp]) { - // prop not found in config - return; - } - // Normal scenario - const keys = Object.keys(config.theme[absoluteProp]) - .filter((key) => { - if (isNegative) { - // Negative prop cannot support NaN values and inherits positive values - const val = config.theme[absoluteProp][key]; - const isCalc = typeof val === 'string' && val.indexOf('calc') === 0; - const num = parseFloat(val); - if (isCalc) { - return true; - } - if (isNaN(num)) { - return false; - } - } else if (key[0] === '-') { - // Positive prop cannot use key starting with '-' - return false; - } - return true; - }) - .map((key) => { - if (isNegative && key[0] === '-') { - return key.substring(1); - } - return key; - }); - if (keys.length === 0 || replaced.match(token) === null) { - // empty array - return; - } - const opts = generateOptions(absoluteProp, keys, config, isNegative); - replaced = replaced.replace(token, opts); - }); - return (cache[re] = `${patched}(${replaced})`); -} - -/** - * Generates a flatten array from the groups config - * - * @param {Array} groupsConfig The array of objects containing the regex - * @param {Object} twConfig The merged config of Tailwind CSS - * @returns {Array} Flatten array - */ -function getGroups(groupsConfig, twConfig = null) { - const groups = []; - groupsConfig.forEach((group) => { - // e.g. SIZING or SPACING - group.members.forEach((prop) => { - // e.g. Width or Padding - if (typeof prop.members === 'string') { - // Unique property, like `width` limited to one value - groups.push(prop.members); - } else { - // Multiple properties, like `padding`, `padding-top`... - prop.members.forEach((subprop) => { - groups.push(subprop.members); - }); - } - }); - }); - if (twConfig === null) { - return groups; - } - return groups.map((re) => patchRegex(re, twConfig)); -} - -/** - * Generates an array of empty arrays prior to grouping - * - * @param {Array} groups The array of objects containing the regex - * @returns {Array} Array of empty arrays - */ -function initGroupSlots(groups) { - const slots = []; - groups.forEach((g) => slots.push([])); - return slots; -} - -/** - * Searches for a match between classname and Tailwind CSS group - * - * @param {Array} name The target classname - * @param {Array} arr The flatten array containing the regex - * @param {String} separator The delimiter to be used between variants - * @returns {Array} Array of empty arrays - */ -function getGroupIndex(name, arr, separator = ':') { - const classSuffix = getSuffix(name, separator); - let idx = arr.findIndex((pattern) => { - const classRe = new RegExp(`^(${pattern})$`); - return classRe.test(classSuffix); - }, classSuffix); - return idx; -} - -/** - * Generates a flatten array from the groups configKeys - * - * @param {Array} groupsConfig The array of objects containing the regex - * @param {Object} twConfig The merged config of Tailwind CSS - * @returns {Array} Flatten array - */ -function getGroupConfigKeys(groupsConfig) { - const groups = []; - groupsConfig.forEach((group) => { - // e.g. SIZING or SPACING - group.members.forEach((prop) => { - // e.g. Width or Padding - if (typeof prop.members === 'string') { - // Unique property, like `width` limited to one value - groups.push(prop.configKey ? prop.configKey : null); - } else { - // Multiple properties, like `padding`, `padding-top`... - prop.members.forEach((subprop) => { - groups.push(subprop.configKey ? subprop.configKey : null); - }); - } - }); - }); - return groups; -} - -/** - * Returns the prefix (variants) of a className including the separator or an empty string if none - * - * @param {String} name Classname to be parsed - * @param {String} separator The separator character as in config - * @returns {String} The prefix - */ -function getPrefix(name, separator) { - const rootSeparator = String.raw`(? "mask-type" - * - * @see https://tailwindcss.com/docs/adding-custom-styles#arbitrary-properties - * @param {String} name Classname suffix (without it variants) to be parsed - * @param {String} separator The separator character as in config - * @returns {String} The arbitrary property - */ -function getArbitraryProperty(name, separator) { - const arbitraryPropPattern = String.raw`^\[([a-z\-]*)${separator}\.*`; - const arbitraryPropRegExp = new RegExp(arbitraryPropPattern); - const results = arbitraryPropRegExp.exec(name); - return results === null ? '' : results[1]; -} - -/** - * Get the last part of the full classname - * e.g. "lg:w-[100px]" => "w-[100px]" - * - * @param {String} className The target classname - * @param {String} separator The delimiter to be used between variants - * @returns {String} The classname without its variants - */ -function getSuffix(className, separator = ':') { - const prefix = getPrefix(className, separator); - return className.substring(prefix.length); -} - -/** - * Find the group of a classname - * - * @param {String} name Classname to be find using patterns (without modifiers) - * @param {Array} group The group being tested - * @param {Object} config Tailwind CSS config - * @param {String} parentType The name of the parent group - * @returns {Object} The infos - */ -function findInGroup(name, group, config, parentType = null) { - if (typeof group.members === 'string') { - const pattern = patchRegex(group.members, config); - const classRe = new RegExp(`^(${pattern})$`); - if (classRe.test(name)) { - const res = classRe.exec(name); - let value = ''; - if (res && res.groups) { - if (res.groups.value) { - value = res.groups.value; - } - if (res.groups.negativeValue) { - value = '-' + res.groups.negativeValue; - } - } - return { - group: parentType, - ...group, - value: value, - }; - } else { - return null; - } - } else { - const innerGroup = group.members.find((v) => findInGroup(name, v, config, group.type)); - if (!innerGroup) { - return null; - } else { - return findInGroup(name, innerGroup, config, group.type); - } - } -} - -/** - * Returns an object with parsed properties - * - * @param {String} name Classname to be parsed - * @param {Array} arr The flatten array containing the regex - * @param {Object} config The Tailwind CSS config - * @param {Number} index The index - * @returns {Object} Parsed infos - */ -function parseClassname(name, arr, config, index = null) { - const leadingRe = new RegExp('^(?\\s*)'); - const trailingRe = new RegExp('(?\\s*)$'); - let leading = ''; - let core = ''; - let trailing = ''; - const leadingRes = leadingRe.exec(name); - if (leadingRes && leadingRes.groups) { - leading = leadingRes.groups.leading || ''; - } - const trailingRes = trailingRe.exec(name); - if (trailingRes && trailingRes.groups) { - trailing = trailingRes.groups.trailing || ''; - } - core = name.substring(leading.length, name.length - trailing.length); - const variants = getPrefix(core, config.separator); - const classSuffix = getSuffix(core, config.separator); - let slot = null; - arr.forEach((group) => { - if (slot === null) { - const found = findInGroup(classSuffix, group, config); - if (found) { - slot = found; - } - } - }); - const value = slot ? slot.value : ''; - const isNegative = value[0] === '-'; - const off = isNegative ? 1 : 0; - const body = core.substr(0, core.length - value.length + off).substr(variants.length + off); - return { - index: index, - name: core, - variants: variants, - parentType: slot ? slot.group : '', - body: body, - value: value, - shorthand: slot && slot.shorthand ? slot.shorthand : '', - leading: leading, - trailing: trailing, - important: body.substr(0, 1) === '!', - }; -} - -module.exports = { - initGroupSlots, - getArbitraryProperty, - getGroups, - getGroupIndex, - getGroupConfigKeys, - getPrefix, - getSuffix, - parseClassname, -}; diff --git a/lib/util/parser.js b/lib/util/parser.js deleted file mode 100644 index 82d13df..0000000 --- a/lib/util/parser.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @see parserServices https://eslint.org/docs/developer-guide/working-with-rules#the-context-object - * @param {Object} context - * @param {Function} templateBodyVisitor - * @param {Function} scriptVisitor - * @returns - */ -function defineTemplateBodyVisitor(context, templateBodyVisitor, scriptVisitor) { - const parserServices = getParserServices(context); - if (parserServices == null || parserServices.defineTemplateBodyVisitor == null) { - // Default parser - return scriptVisitor; - } - - // Using "vue-eslint-parser" requires this setup - // @see https://eslint.org/docs/developer-guide/working-with-rules#the-context-object - return parserServices.defineTemplateBodyVisitor(templateBodyVisitor, scriptVisitor); -} - -/** - * This function is API compatible with eslint v8.x and eslint v9 or later. - * @see https://eslint.org/blog/2023/09/preparing-custom-rules-eslint-v9/#from-context-to-sourcecode - */ -function getParserServices(context) { - return context.sourceCode ? context.sourceCode.parserServices : context.parserServices; -} - -module.exports = { - defineTemplateBodyVisitor, -}; diff --git a/lib/util/prettier/order.js b/lib/util/prettier/order.js deleted file mode 100644 index 04d76a7..0000000 --- a/lib/util/prettier/order.js +++ /dev/null @@ -1,101 +0,0 @@ -const { separatorRegEx } = require('../regex'); - -function bigSign(bigIntValue) { - return (bigIntValue > 0n) - (bigIntValue < 0n); -} - -function prefixCandidate(context, selector) { - let prefix = context.tailwindConfig.prefix; - return typeof prefix === 'function' ? prefix(selector) : prefix + selector; -} - -// Polyfill for older Tailwind CSS versions -function getClassOrderPolyfill(classes, { env }) { - // A list of utilities that are used by certain Tailwind CSS utilities but - // that don't exist on their own. This will result in them "not existing" and - // sorting could be weird since you still require them in order to make the - // host utitlies work properly. (Thanks Biology) - let parasiteUtilities = new Set([prefixCandidate(env.context, 'group'), prefixCandidate(env.context, 'peer')]); - - let classNamesWithOrder = []; - - for (let className of classes) { - let order = env.generateRules(new Set([className]), env.context).sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null; - - if (order === null && parasiteUtilities.has(className)) { - // This will make sure that it is at the very beginning of the - // `components` layer which technically means 'before any - // components'. - order = env.context.layerOrder.components; - } - - classNamesWithOrder.push([className, order]); - } - - return classNamesWithOrder; -} - -function sortClasses(classStr, { env, ignoreFirst = false, ignoreLast = false }) { - if (typeof classStr !== 'string' || classStr === '') { - return classStr; - } - - // Ignore class attributes containing `{{`, to match Prettier behaviour: - // https://github.com/prettier/prettier/blob/main/src/language-html/embed.js#L83-L88 - if (classStr.includes('{{')) { - return classStr; - } - - let result = ''; - let parts = classStr.split(separatorRegEx); - let classes = parts.filter((_, i) => i % 2 === 0); - let whitespace = parts.filter((_, i) => i % 2 !== 0); - - if (classes[classes.length - 1] === '') { - classes.pop(); - } - - let prefix = ''; - if (ignoreFirst) { - prefix = `${classes.shift() ?? ''}${whitespace.shift() ?? ''}`; - } - - let suffix = ''; - if (ignoreLast) { - suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}`; - } - - let classNamesWithOrder = env.context.getClassOrder - ? env.context.getClassOrder(classes) - : getClassOrderPolyfill(classes, { env }); - - classes = classNamesWithOrder - .sort(([, a], [, z]) => { - if (a === z) return 0; - // if (a === null) return options.unknownClassPosition === 'start' ? -1 : 1 - // if (z === null) return options.unknownClassPosition === 'start' ? 1 : -1 - if (a === null) return -1; - if (z === null) return 1; - return bigSign(a - z); - }) - .map(([className]) => className); - - for (let i = 0; i < classes.length; i++) { - result += `${classes[i]}${whitespace[i] ?? ''}`; - } - - return prefix + result + suffix; -} - -/** - * - * @param {Array} unordered the unordered classnames - * @param context - * @returns {Array} the ordered classnames - */ -function order(unordered, context) { - const sorted = sortClasses(unordered.join(' '), { env: { context: context } }); - return sorted; -} - -module.exports = order; diff --git a/lib/util/regex.js b/lib/util/regex.js deleted file mode 100644 index 78189ac..0000000 --- a/lib/util/regex.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Escapes a string to be used in a regular expression - * Copied from https://stackoverflow.com/a/3561711. - * @param {string} input - * @returns {string} - */ -function escapeRegex(input) { - return input.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); -} - -const separatorRegEx = /([\t\n\f\r ]+)/; - -module.exports = { - escapeRegex, - separatorRegEx, -}; diff --git a/lib/util/removeDuplicatesFromArray.js b/lib/util/removeDuplicatesFromArray.js deleted file mode 100644 index 6301fe4..0000000 --- a/lib/util/removeDuplicatesFromArray.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -function removeDuplicatesFromArray(arr) { - return [...new Set(arr)]; -} - -module.exports = removeDuplicatesFromArray; diff --git a/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js b/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js deleted file mode 100644 index f660273..0000000 --- a/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -function removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace) { - let previous = orderedClassNames[0]; - const offset = (!headSpace && !tailSpace) || tailSpace ? -1 : 0; - for (let i = 1; i < orderedClassNames.length; i++) { - const cls = orderedClassNames[i]; - // This function assumes that the list of classNames is ordered, so just comparing to the previous className is enough - if (cls === previous) { - orderedClassNames.splice(i, 1); - whitespaces.splice(i + offset, 1); - i--; - } - previous = cls; - } -} - -module.exports = removeDuplicatesFromClassnamesAndWhitespaces; diff --git a/lib/util/settings.js b/lib/util/settings.js deleted file mode 100644 index 9c24567..0000000 --- a/lib/util/settings.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; -let resolveDefaultConfigPathAlias; - -try { - const { resolveDefaultConfigPath } = require('tailwindcss/lib/util/resolveConfigPath'); - resolveDefaultConfigPathAlias = resolveDefaultConfigPath; -} catch (err) { - resolveDefaultConfigPathAlias = null; -} - -function getOption(context, name) { - // Options (defined at rule level) - const options = context.options[0] || {}; - if (options[name] != undefined) { - return options[name]; - } - // Settings (defined at plugin level, shared accross rules) - if (context.settings && context.settings.tailwindcss && context.settings.tailwindcss[name] != undefined) { - return context.settings.tailwindcss[name]; - } - // Fallback to defaults - switch (name) { - case 'callees': - return ['classnames', 'clsx', 'ctl', 'cva', 'tv']; - case 'ignoredKeys': - return ['compoundVariants', 'defaultVariants']; - case 'classRegex': - return '^class(Name)?$'; - case 'config': - if (resolveDefaultConfigPathAlias === null) { - return 'tailwind.config.js'; - } else { - return resolveDefaultConfigPathAlias(); - } - case 'cssFiles': - return ['**/*.css', '!**/node_modules', '!**/.*', '!**/dist', '!**/build']; - case 'cssFilesRefreshRate': - return 5_000; - case 'removeDuplicates': - return true; - case 'skipClassAttribute': - return false; - case 'tags': - return []; - case 'whitelist': - return []; - } -} - -module.exports = getOption; diff --git a/lib/util/types/angle.js b/lib/util/types/angle.js deleted file mode 100644 index 3073b83..0000000 --- a/lib/util/types/angle.js +++ /dev/null @@ -1,11 +0,0 @@ -const units = ['deg', 'grad', 'rad', 'turn']; - -const mergedAngleValues = [ - `\\-?(\\d{1,}(\\.\\d{1,})?|\\.\\d{1,})(${units.join('|')})`, - `calc\\(.{1,}\\)`, - `var\\(\\-\\-[A-Za-z\\-]{1,}\\)`, -]; - -module.exports = { - mergedAngleValues, -}; diff --git a/lib/util/types/color.js b/lib/util/types/color.js deleted file mode 100644 index a6e1452..0000000 --- a/lib/util/types/color.js +++ /dev/null @@ -1,191 +0,0 @@ -const cssNamedColors = [ - 'indianred', - 'lightcoral', - 'salmon', - 'darksalmon', - 'lightsalmon', - 'crimson', - 'red', - 'firebrick', - 'darkred', - 'pink', - 'lightpink', - 'hotpink', - 'deeppink', - 'mediumvioletred', - 'palevioletred', - 'coral', - 'tomato', - 'orangered', - 'darkorange', - 'orange', - 'gold', - 'yellow', - 'lightyellow', - 'lemonchiffon', - 'lightgoldenrodyellow', - 'papayawhip', - 'moccasin', - 'peachpuff', - 'palegoldenrod', - 'khaki', - 'darkkhaki', - 'lavender', - 'thistle', - 'plum', - 'violet', - 'orchid', - 'fuchsia', - 'magenta', - 'mediumorchid', - 'mediumpurple', - 'blueviolet', - 'darkviolet', - 'darkorchid', - 'darkmagenta', - 'purple', - 'rebeccapurple', - 'indigo', - 'mediumslateblue', - 'slateblue', - 'darkslateblue', - 'greenyellow', - 'chartreuse', - 'lawngreen', - 'lime', - 'limegreen', - 'palegreen', - 'lightgreen', - 'mediumspringgreen', - 'springgreen', - 'mediumseagreen', - 'seagreen', - 'forestgreen', - 'green', - 'darkgreen', - 'yellowgreen', - 'olivedrab', - 'olive', - 'darkolivegreen', - 'mediumaquamarine', - 'darkseagreen', - 'lightseagreen', - 'darkcyan', - 'teal', - 'aqua', - 'cyan', - 'lightcyan', - 'paleturquoise', - 'aquamarine', - 'turquoise', - 'mediumturquoise', - 'darkturquoise', - 'cadetblue', - 'steelblue', - 'lightsteelblue', - 'powderblue', - 'lightblue', - 'skyblue', - 'lightskyblue', - 'deepskyblue', - 'dodgerblue', - 'cornflowerblue', - 'royalblue', - 'blue', - 'mediumblue', - 'darkblue', - 'navy', - 'midnightblue', - 'cornsilk', - 'blanchedalmond', - 'bisque', - 'navajowhite', - 'wheat', - 'burlywood', - 'tan', - 'rosybrown', - 'sandybrown', - 'goldenrod', - 'darkgoldenrod', - 'peru', - 'chocolate', - 'saddlebrown', - 'sienna', - 'brown', - 'maroon', - 'white', - 'snow', - 'honeydew', - 'mintcream', - 'azure', - 'aliceblue', - 'ghostwhite', - 'whitesmoke', - 'seashell', - 'beige', - 'oldlace', - 'floralwhite', - 'ivory', - 'antiquewhite', - 'linen', - 'lavenderblush', - 'mistyrose', - 'gainsboro', - 'lightgray', - 'lightgrey', - 'silver', - 'darkgray', - 'darkgrey', - 'gray', - 'grey', - 'dimgray', - 'dimgrey', - 'lightslategray', - 'lightslategrey', - 'slategray', - 'slategrey', - 'darkslategray', - 'darkslategrey', - 'black', - 'transparent', - 'currentColor', -]; - -// RGB[A] hexa: #123456AA, #B4DA55, #000A, #123 -const hexRGBA = '\\#(([0-9A-Fa-f]{8})|([0-9A-Fa-f]{6})|([0-9A-Fa-f]{4})|([0-9A-Fa-f]{3}))'; - -// RGB 0-255: rgb(10,20,30) -const RGBIntegers = 'rgb\\(\\d{1,3}\\,\\d{1,3}\\,\\d{1,3}\\)'; - -// RGB %: rgb(25%,50%,75%) -const RGBPercentages = 'rgb\\(\\d{1,3}%\\,\\d{1,3}%\\,\\d{1,3}%\\)'; - -// RGBA: rgba(50,100,255,.5), rgba(50,100,255,50%) -const supportedRGBA = 'rgba\\(\\d{1,3}\\,\\d{1,3}\\,\\d{1,3}\\,\\d*(\\.\\d*)?%?\\)'; - -const RGBAPercentages = 'rgba\\(\\d{1,3}%\\,\\d{1,3}%\\,\\d{1,3}%\\,\\d*(\\.\\d*)?%?\\)'; - -const optionalColorPrefixedVar = '(color\\:)?var\\(\\-\\-[A-Za-z\\-]{1,}\\)'; - -const mandatoryColorPrefixed = 'color\\:(?!(hsla\\()).{1,}'; - -const notHSLAPlusWildcard = '(?!(hsla\\()).{1,}'; - -// HSL -const supportedHSL = 'hsl\\(\\d{1,3}%?\\,\\d{1,3}%?\\,\\d{1,3}%?\\)'; - -// 'hsla\\(\\d{1,3}%?\\,\\d{1,3}%?\\,\\d{1,3}%?\\,\\d*(\\.\\d*)?%?\\)', - -const colorValues = [hexRGBA, RGBIntegers, RGBPercentages, supportedRGBA, supportedHSL]; - -const mergedColorValues = [...cssNamedColors, ...colorValues]; - -module.exports = { - cssNamedColors, - colorValues, - mergedColorValues, - RGBAPercentages, - optionalColorPrefixedVar, - mandatoryColorPrefixed, - notHSLAPlusWildcard, -}; diff --git a/lib/util/types/length.js b/lib/util/types/length.js deleted file mode 100644 index f71511e..0000000 --- a/lib/util/types/length.js +++ /dev/null @@ -1,46 +0,0 @@ -const removeDuplicatesFromArray = require('../removeDuplicatesFromArray'); - -// Units -const fontUnits = ['cap', 'ch', 'em', 'ex', 'ic', 'lh', 'rem', 'rlh']; -const viewportUnits = ['vb', 'vh', 'vi', 'vw', 'vmin', 'vmax']; -const absoluteUnits = ['px', 'mm', 'cm', 'in', 'pt', 'pc']; -const perInchUnits = ['lin', 'pt', 'mm']; -const otherUnits = ['%']; -const mergedUnits = removeDuplicatesFromArray([ - ...fontUnits, - ...viewportUnits, - ...absoluteUnits, - ...perInchUnits, - ...otherUnits, -]); -const selectedUnits = mergedUnits.filter((el) => { - // All units minus this blacklist - return !['cap', 'ic', 'vb', 'vi'].includes(el); -}); - -const absoluteValues = ['0', 'xx\\-small', 'x\\-small', 'small', 'medium', 'large', 'x\\-large', 'xx\\-large']; -const relativeValues = ['larger', 'smaller']; -const globalValues = ['inherit', 'initial', 'unset']; -const mergedValues = [...absoluteValues, ...relativeValues, ...globalValues]; - -const mergedLengthValues = [`\\-?\\d*\\.?\\d*(${mergedUnits.join('|')})`, ...mergedValues]; -mergedLengthValues.push('length\\:var\\(\\-\\-[a-z\\-]{1,}\\)'); - -const mergedUnitsRegEx = `\\[(\\d{1,}(\\.\\d{1,})?|(\\.\\d{1,})?)(${mergedUnits.join('|')})\\]`; - -const selectedUnitsRegEx = `\\[(\\d{1,}(\\.\\d{1,})?|(\\.\\d{1,})?)(${selectedUnits.join('|')})\\]`; - -const anyCalcRegEx = `\\[calc\\(.{1,}\\)\\]`; - -const validZeroRegEx = `^(0(\\.0{1,})?|\\.0{1,})(${mergedUnits.join('|')})?$`; - -module.exports = { - mergedUnits, - selectedUnits, - mergedUnitsRegEx, - selectedUnitsRegEx, - anyCalcRegEx, - mergedValues, - mergedLengthValues, - validZeroRegEx, -}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f04c866..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4866 +0,0 @@ -{ - "name": "eslint-plugin-tailwindcss", - "version": "3.17.2", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "eslint-plugin-tailwindcss", - "version": "3.17.2", - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.5", - "postcss": "^8.4.4" - }, - "devDependencies": { - "@angular-eslint/template-parser": "^15.2.0", - "@tailwindcss/aspect-ratio": "^0.4.2", - "@tailwindcss/forms": "^0.5.3", - "@tailwindcss/line-clamp": "^0.4.2", - "@tailwindcss/typography": "^0.5.8", - "@typescript-eslint/parser": "^5.50.0", - "autoprefixer": "^10.4.0", - "daisyui": "^2.6.4", - "eslint": "^8.57.0", - "mocha": "^10.2.0", - "semver": "^7.6.0", - "tailwindcss": "^3.4.0", - "typescript": "4.3.5", - "vue-eslint-parser": "^9.4.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "tailwindcss": "^3.4.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.0.tgz", - "integrity": "sha512-a0bfXxYyGoWJHrVQ4QER0HdRgselcTtJeyqiFPAxID2ZxF0IBGKLNTtugUTXekEmiLev8yGLX9TqAtthN57fEg==", - "dev": true - }, - "node_modules/@angular-eslint/template-parser": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-15.2.0.tgz", - "integrity": "sha512-xnnxPfV/G0Ll3B0HGrF1ucsc/DHmNE6UhhmWxYPTERq0McbZGRiATa66hCoOZ/Rdylun4ogBfsRKAG8XxEvlvw==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "15.2.0", - "eslint-scope": "^7.0.0" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@tailwindcss/aspect-ratio": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", - "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", - "dev": true, - "peerDependencies": { - "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" - } - }, - "node_modules/@tailwindcss/forms": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", - "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", - "dev": true, - "dependencies": { - "mini-svg-data-uri": "^1.2.3" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" - } - }, - "node_modules/@tailwindcss/line-clamp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz", - "integrity": "sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==", - "dev": true, - "peerDependencies": { - "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" - } - }, - "node_modules/@tailwindcss/typography": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", - "integrity": "sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw==", - "dev": true, - "dependencies": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "postcss-selector-parser": "6.0.10" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz", - "integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.50.0", - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/typescript-estree": "5.50.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz", - "integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/visitor-keys": "5.50.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz", - "integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz", - "integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/visitor-keys": "5.50.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz", - "integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.50.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", - "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", - "dev": true, - "dependencies": { - "browserslist": "^4.17.5", - "caniuse-lite": "^1.0.30001272", - "fraction.js": "^4.1.1", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.1.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.1.tgz", - "integrity": "sha512-MFJr0uY4RvTQUKvPq7dh9grVOTYSFeXja2mBXioCGjnjJoXrAp9jJ1NQTDR73c9nwBSAQiNKloKl5zq9WB9UPw==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/color-string": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", - "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", - "dev": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", - "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/daisyui": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.6.4.tgz", - "integrity": "sha512-3l7WHgpOTdyUBVu2TD6uZH4+IbwaBnPXdoWjjvmzT6hxR5+WUHMl4vvPP+7/VB+CcoxSB296qaYWXQcoWcbISw==", - "dev": true, - "dependencies": { - "color": "^4.2", - "css-selector-tokenizer": "^0.8.0", - "postcss-js": "^4.0.0", - "tailwindcss": "^3.0" - }, - "peerDependencies": { - "autoprefixer": "^10.0.2", - "postcss": "^8.1.6" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.15.tgz", - "integrity": "sha512-WDw2IUL3k4QpbzInV3JZK+Zd1NjWJPDZ28oUSchWb/kf6AVj7/niaAlgcJlvojFa1d7pJSyQ/KSZsEtq5W7aGQ==", - "dev": true - }, - "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 - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "dev": true, - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz", - "integrity": "sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA==", - "dev": true, - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", - "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", - "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/vue-eslint-parser": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", - "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true - }, - "@angular-eslint/bundled-angular-compiler": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.0.tgz", - "integrity": "sha512-a0bfXxYyGoWJHrVQ4QER0HdRgselcTtJeyqiFPAxID2ZxF0IBGKLNTtugUTXekEmiLev8yGLX9TqAtthN57fEg==", - "dev": true - }, - "@angular-eslint/template-parser": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-15.2.0.tgz", - "integrity": "sha512-xnnxPfV/G0Ll3B0HGrF1ucsc/DHmNE6UhhmWxYPTERq0McbZGRiATa66hCoOZ/Rdylun4ogBfsRKAG8XxEvlvw==", - "dev": true, - "requires": { - "@angular-eslint/bundled-angular-compiler": "15.2.0", - "eslint-scope": "^7.0.0" - } - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@tailwindcss/aspect-ratio": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", - "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", - "dev": true, - "requires": {} - }, - "@tailwindcss/forms": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", - "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", - "dev": true, - "requires": { - "mini-svg-data-uri": "^1.2.3" - } - }, - "@tailwindcss/line-clamp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz", - "integrity": "sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==", - "dev": true, - "requires": {} - }, - "@tailwindcss/typography": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", - "integrity": "sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw==", - "dev": true, - "requires": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "postcss-selector-parser": "6.0.10" - } - }, - "@typescript-eslint/parser": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz", - "integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.50.0", - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/typescript-estree": "5.50.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz", - "integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/visitor-keys": "5.50.0" - } - }, - "@typescript-eslint/types": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz", - "integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz", - "integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.50.0", - "@typescript-eslint/visitor-keys": "5.50.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz", - "integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.50.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", - "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", - "dev": true, - "requires": { - "browserslist": "^4.17.5", - "caniuse-lite": "^1.0.30001272", - "fraction.js": "^4.1.1", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.1.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001286", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", - "dev": true - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.1.tgz", - "integrity": "sha512-MFJr0uY4RvTQUKvPq7dh9grVOTYSFeXja2mBXioCGjnjJoXrAp9jJ1NQTDR73c9nwBSAQiNKloKl5zq9WB9UPw==", - "dev": true, - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "dependencies": { - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", - "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", - "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "daisyui": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.6.4.tgz", - "integrity": "sha512-3l7WHgpOTdyUBVu2TD6uZH4+IbwaBnPXdoWjjvmzT6hxR5+WUHMl4vvPP+7/VB+CcoxSB296qaYWXQcoWcbISw==", - "dev": true, - "requires": { - "color": "^4.2", - "css-selector-tokenizer": "^0.8.0", - "postcss-js": "^4.0.0", - "tailwindcss": "^3.0" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "electron-to-chromium": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.15.tgz", - "integrity": "sha512-WDw2IUL3k4QpbzInV3JZK+Zd1NjWJPDZ28oUSchWb/kf6AVj7/niaAlgcJlvojFa1d7pJSyQ/KSZsEtq5W7aGQ==", - "dev": true - }, - "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 - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "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 - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mini-svg-data-uri": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz", - "integrity": "sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "requires": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - } - }, - "postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.11" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", - "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - } - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "requires": { - "pify": "^2.3.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", - "dev": true, - "requires": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "postcss-selector-parser": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", - "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "vue-eslint-parser": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", - "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 77b7308..0000000 --- a/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "eslint-plugin-tailwindcss", - "version": "3.17.2", - "description": "Rules enforcing best practices while using Tailwind CSS", - "keywords": [ - "eslint", - "eslintplugin", - "eslint-plugin", - "tailwind", - "tailwindcss" - ], - "author": "François Massart", - "repository": { - "type": "git", - "url": "https://github.com/francoismassart/eslint-plugin-tailwindcss" - }, - "homepage": "https://github.com/francoismassart/eslint-plugin-tailwindcss", - "bugs": "https://github.com/francoismassart/eslint-plugin-tailwindcss/issues", - "main": "lib/index.js", - "scripts": { - "test": "npm run test:base && npm run test:integration", - "test:base": "mocha \"tests/lib/**/*.js\"", - "test:integration": "mocha \"tests/integrations/*.js\" --timeout 60000" - }, - "files": [ - "lib" - ], - "peerDependencies": { - "tailwindcss": "^3.4.0" - }, - "dependencies": { - "fast-glob": "^3.2.5", - "postcss": "^8.4.4" - }, - "devDependencies": { - "@angular-eslint/template-parser": "^15.2.0", - "@tailwindcss/aspect-ratio": "^0.4.2", - "@tailwindcss/forms": "^0.5.3", - "@tailwindcss/line-clamp": "^0.4.2", - "@tailwindcss/typography": "^0.5.8", - "@typescript-eslint/parser": "^5.50.0", - "autoprefixer": "^10.4.0", - "daisyui": "^2.6.4", - "eslint": "^8.57.0", - "mocha": "^10.2.0", - "semver": "^7.6.0", - "tailwindcss": "^3.4.0", - "typescript": "4.3.5", - "vue-eslint-parser": "^9.4.2" - }, - "packageManager": "npm@10.2.5+sha256.8002e3e7305d2abd4016e1368af48d49b066c269079eeb10a56e7d6598acfdaa", - "engines": { - "node": ">=18.12.0" - }, - "license": "MIT" -} diff --git a/sponsors/daily.dev.jpg b/sponsors/daily.dev.jpg deleted file mode 100644 index e43c30bb454ba026f0f2e048b763d064402e1166..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42898 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 QeG_{9FRM&4eVzaR03F1%jQ{`u diff --git a/tests/.prettierrc.json b/tests/.prettierrc.json deleted file mode 100644 index c2ba36e..0000000 --- a/tests/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "printWidth": 120, - "semi": true, - "singleQuote": false, - "trailingComma": "es5" -} diff --git a/tests/integrations/flat-config.js b/tests/integrations/flat-config.js deleted file mode 100644 index 391c69f..0000000 --- a/tests/integrations/flat-config.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -const { strict: assert } = require("assert"); -const cp = require("child_process"); -const path = require("path"); -const semver = require("semver"); - -const ESLINT = `.${path.sep}node_modules${path.sep}.bin${path.sep}eslint`; - -describe("Integration with flat config", () => { - let originalCwd; - - before(() => { - originalCwd = process.cwd(); - process.chdir(path.join(__dirname, "flat-config")); - cp.execSync("npm i -f", { stdio: "inherit" }); - }); - after(() => { - process.chdir(originalCwd); - }); - - it("should work with flat config", () => { - if ( - !semver.satisfies( - process.version, - require(path.join(__dirname, "flat-config/node_modules/eslint/package.json")).engines.node - ) - ) { - return; - } - - const result = JSON.parse( - cp.execSync(`${ESLINT} a.vue --format=json`, { - encoding: "utf8", - }) - ); - assert.strictEqual(result.length, 1); - assert.deepStrictEqual(result[0].messages[0].messageId, "invalidOrder"); - }); -}); diff --git a/tests/integrations/flat-config/.npmrc b/tests/integrations/flat-config/.npmrc deleted file mode 100644 index 43c97e7..0000000 --- a/tests/integrations/flat-config/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/tests/integrations/flat-config/a.vue b/tests/integrations/flat-config/a.vue deleted file mode 100644 index 4a4fb2a..0000000 --- a/tests/integrations/flat-config/a.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/tests/integrations/flat-config/eslint.config.js b/tests/integrations/flat-config/eslint.config.js deleted file mode 100644 index effca20..0000000 --- a/tests/integrations/flat-config/eslint.config.js +++ /dev/null @@ -1,13 +0,0 @@ -import vue from "eslint-plugin-vue"; -import tailwind from "eslint-plugin-tailwindcss"; - -export default [ - ...vue.configs["flat/recommended"], - ...tailwind.configs["flat/recommended"], - { - rules: { - "vue/multi-word-component-names": "off", - "tailwindcss/classnames-order": "warn", - }, - }, -]; diff --git a/tests/integrations/flat-config/package.json b/tests/integrations/flat-config/package.json deleted file mode 100644 index 39fbfc6..0000000 --- a/tests/integrations/flat-config/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "private": true, - "name": "integration-test-for-flat-config", - "version": "1.0.0", - "type": "module", - "description": "Integration test for flat config", - "dependencies": { - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.24.0", - "eslint-plugin-tailwindcss": "file:../../..", - "vue-eslint-parser": "^9.4.2" - } -} diff --git a/tests/integrations/legacy-config.js b/tests/integrations/legacy-config.js deleted file mode 100644 index 243b10d..0000000 --- a/tests/integrations/legacy-config.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -const { strict: assert } = require("assert"); -const cp = require("child_process"); -const path = require("path"); -const semver = require("semver"); - -const ESLINT = `.${path.sep}node_modules${path.sep}.bin${path.sep}eslint`; - -describe("Integration with legacy config", () => { - let originalCwd; - - before(() => { - originalCwd = process.cwd(); - process.chdir(path.join(__dirname, "legacy-config")); - cp.execSync("npm i -f", { stdio: "inherit" }); - }); - after(() => { - process.chdir(originalCwd); - }); - - it("should work with legacy config", () => { - if ( - !semver.satisfies( - process.version, - require(path.join(__dirname, "legacy-config/node_modules/eslint/package.json")).engines.node - ) - ) { - return; - } - - const result = JSON.parse( - cp.execSync(`${ESLINT} a.vue --format=json`, { - encoding: "utf8", - }) - ); - assert.strictEqual(result.length, 1); - assert.deepStrictEqual(result[0].messages[0].messageId, "invalidOrder"); - }); -}); diff --git a/tests/integrations/legacy-config/.eslintrc b/tests/integrations/legacy-config/.eslintrc deleted file mode 100644 index 7595199..0000000 --- a/tests/integrations/legacy-config/.eslintrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "root": true, - "parser": "vue-eslint-parser", - "parserOptions": { - "sourceType": "module" - }, - "extends": ["plugin:vue/vue3-recommended", "plugin:tailwindcss/recommended"], - "rules": { - "vue/multi-word-component-names": "off", - "tailwindcss/classnames-order": "warn" - } -} diff --git a/tests/integrations/legacy-config/.npmrc b/tests/integrations/legacy-config/.npmrc deleted file mode 100644 index 43c97e7..0000000 --- a/tests/integrations/legacy-config/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/tests/integrations/legacy-config/a.vue b/tests/integrations/legacy-config/a.vue deleted file mode 100644 index 4a4fb2a..0000000 --- a/tests/integrations/legacy-config/a.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/tests/integrations/legacy-config/package.json b/tests/integrations/legacy-config/package.json deleted file mode 100644 index 39fbfc6..0000000 --- a/tests/integrations/legacy-config/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "private": true, - "name": "integration-test-for-flat-config", - "version": "1.0.0", - "type": "module", - "description": "Integration test for flat config", - "dependencies": { - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.24.0", - "eslint-plugin-tailwindcss": "file:../../..", - "vue-eslint-parser": "^9.4.2" - } -} diff --git a/tests/lib/index.js b/tests/lib/index.js deleted file mode 100644 index 605c237..0000000 --- a/tests/lib/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @fileoverview Use a consistent orders for the Tailwind CSS classnames, based on property then on variants - * @author François Massart - */ -"use strict"; - -var plugin = require("../../lib/index"); - -var assert = require("assert"); -var fs = require("fs"); -var path = require("path"); - -var rules = fs.readdirSync(path.resolve(__dirname, "../../lib/rules/")).map(function (f) { - return path.basename(f, ".js"); -}); - -describe("all rule files should be exported by the plugin", function () { - rules.forEach(function (ruleName) { - it(`should export ${ruleName}`, function () { - assert.equal(plugin.rules[ruleName], require(path.join("../../lib/rules", ruleName))); - }); - }); -}); - -describe("configurations", function () { - it(`should export a "recommended" configuration`, function () { - assert(plugin.configs.recommended); - }); - - it(`should export a "flat/recommended" configuration`, function () { - assert(plugin.configs["flat/recommended"]); - }); -}); diff --git a/tests/lib/rules/another.css b/tests/lib/rules/another.css deleted file mode 100644 index b51d1f7..0000000 --- a/tests/lib/rules/another.css +++ /dev/null @@ -1,45 +0,0 @@ -/* override input always getting focus styling from package "focus-visible" */ -.focus-outline[data-focus-visible-added].focus-visible.focus, -.focus-outline[data-focus-visible-added].focus-visible.focus:focus, -input[type="text"].focus\:outline-none[data-focus-visible-added]:focus, -input[type="email"].focus\:outline-none[data-focus-visible-added]:focus { - box-shadow: none; - outline-width: 0; -} -/* .custom-carousel li:first-child { - padding-left: 20vw; -} */ - -:global(.slide:not(.selected)) { - opacity: 0.2; -} - -.gallery-snackbar { - @apply w-full; -} - -.custom-slide { - height: 70vh; -} - -@media (min-width: 768px) { - .custom-carousel li:not(:first-child) { - @apply pl-16; - } - - .custom-carousel li:not(:last-child) { - @apply pr-16; - } - - .custom-carousel :global(.carousel-root) { - padding: 0 calc(10vw + 16px); - } - - .gallery-snackbar { - width: calc(80vw - 32px); - } - - .indicator { - margin-left: calc(10vw + 16px); - } -} diff --git a/tests/lib/rules/arbitrary-values.js b/tests/lib/rules/arbitrary-values.js deleted file mode 100644 index 2416317..0000000 --- a/tests/lib/rules/arbitrary-values.js +++ /dev/null @@ -1,690 +0,0 @@ -/** - * @fileoverview Detect ambiguous / invalid arbitrary values - * @description By valid we mean accepted by the JIT compiler even if the injected value is invalid on its CSS usage - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/no-custom-classname"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -var config = [ - { - config: { - darkMode: "class", - }, - }, -]; - -var generateErrors = (classnames) => { - const errors = []; - classnames.split(" ").map((classname) => { - errors.push({ - messageId: "customClassnameDetected", - data: { - classname: classname, - }, - }); - }); - return errors; -}; - -var ruleTester = new RuleTester({ parserOptions }); - -ruleTester.run("arbitrary-values", rule, { - valid: [ - { - code: ` -
- - Tailwind CSS V3: dark is valid when - config.darkMode === 'class' - -
- `, - options: config, - }, - { - code: ` -
- Aspect Ratio accepts arbitrary values -
- `, - }, - { - code: ` -
- Valid object-position -
- `, - options: config, - }, - { - code: ` -
- Should accept the arbitrary values -
`, - }, - { - code: ` -
- Arbitrary values for zIndex -
- `, - options: config, - }, - { - code: ` -
- Arbitrary values for flex -
- `, - options: config, - }, - { - code: ` -
- Arbitrary values for positive integers -
- `, - options: config, - }, - { - code: ` -
- Arbitrary values for order -
- `, - options: config, - }, - { - code: ` -
- placeholderColor using #RGB -
- `, - options: config, - }, - { - code: ` -
- placeholderColor using rgb[a]() -
- `, - options: config, - }, - { - code: ` -
- placeholderColor using hsl -
- `, - options: config, - }, - { - code: ` -
- Battle testing valid textColor -
- `, - options: config, - }, - { - code: ` -
- Color opacity shorthands -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for border-width -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for divide-x-width divide-y-width -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for fontSize -
- `, - options: config, - }, - { - code: ` -
-
1
-
2
-
3
-
4
-
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for height -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for line-height -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for max-height -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for max-width -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for min-height -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for min-width -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for padding -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for ringWidth -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for width -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for inset -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for letterSpacing -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for margin -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for space -
- `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for opacity -
- `, - options: config, - }, - { - code: ` -
rotate
- `, - options: config, - }, - { - code: ` -
- Invalid grids, cols -
- `, - options: config, - }, - { - code: ` -
    -
  • one
  • -
  • two
  • -
  • three
  • -
- `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` -
- Arbitrary values for border-radius -
- `, - }, - { - code: ` -
- Testing arbitrary values for: opacity, order, zIndex -
`, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` -
origin
- `, - options: config, - }, - { - code: ` -
scale
- `, - options: config, - }, - { - code: ` -
scale
- `, - options: config, - }, - { - code: ` -
translate
- `, - options: config, - }, - { - code: ` -
skew
- `, - options: config, - }, - { - code: ` -
Cursor
- `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` - - `, - options: config, - }, - { - code: ` -
- Valid arbitrary values for stroke-width -
- `, - options: config, - }, - { - code: ` -
- support named group/peer syntax -
- `, - options: config, - }, - ], - - invalid: [ - { - code: ` -
- - Tailwind CSS V3: dark is not valid unless - config.darkMode === 'class' - -
- `, - errors: generateErrors("dark"), - }, - { - code: ` -
- Aspect Ratio -
- `, - errors: generateErrors("aspect-ko"), - }, - { - code: ` -
- Invalid object-position -
- `, - options: config, - errors: generateErrors("object-[angle:0%,10%]"), - }, - { - code: ` -
- Unsupported placeholderColor using hsla -
- `, - options: config, - errors: generateErrors("placeholder:text-[hsla(240,100%,50%,0.7]"), - }, - { - code: ` -
- Invalid arbitrary values for rotate -
- `, - options: config, - errors: generateErrors("rotate-[angle:5deg]"), - }, - { - code: ` -
    -
  • invalid
  • -
  • nope
  • -
  • no
  • -
- `, - options: config, - errors: generateErrors("list-[angle:var(--some)] list-[list:var(--some)]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("ease-[list:1px]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("delay-[list:var(--some)]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("animate-[list:1s]"), - }, - { - code: ` -
origin
- `, - options: config, - errors: generateErrors("origin-[list:top,right] origin-[list:10%]"), - }, - { - code: ` -
translate
- `, - options: config, - errors: generateErrors("translate-y-[list:-10px]"), - }, - { - code: ` -
skew
- `, - options: config, - errors: generateErrors("skew-y-[list:-10px]"), - }, - { - code: ` -
Cursor
- `, - options: config, - errors: generateErrors("cursor-[list:'yolo','ftw']"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("outline-[list:2px,solid,red] outline-[list:var(--some)]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("outline-[list:2px,solid,red] outline-[list:var(--some)]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("fill-[list:var(--some)] fill-[angle:var(--some)]"), - }, - { - code: ` - - `, - options: config, - errors: generateErrors("stroke-[angle:var(--some)]"), - }, - ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ - code: `${tag}\`stroke-[var(--some)] stroke-['yolo'] stroke-[angle:var(--some)]\``, - errors: generateErrors("stroke-[angle:var(--some)]"), - options: [ - { - tags: ["myTag"], - }, - ], - }))), - ], -}); diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js deleted file mode 100644 index 032c1f4..0000000 --- a/tests/lib/rules/classnames-order.js +++ /dev/null @@ -1,958 +0,0 @@ -/** - * @fileoverview Use a consistent orders for the Tailwind CSS classnames, based on property then on variants - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/classnames-order"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -var ruleTester = new RuleTester({ parserOptions }); - -const generateErrors = (count) => { - const errors = []; - - for (let i = 0; i < count; i++) { - errors.push({ - messageId: "invalidOrder", - }); - } - - return errors; -}; - -const errors = generateErrors(1); - -const sharedOptions = [ - { - config: { - theme: { - extend: { - fontSize: { large: "20rem" }, - colors: { - "deque-blue": "#243c5a", - }, - }, - }, - plugins: [ - require("@tailwindcss/typography"), - require("@tailwindcss/forms"), - require("@tailwindcss/aspect-ratio"), - require("@tailwindcss/line-clamp"), - ], - }, - }, -]; - -const skipClassAttributeOptions = [ - { - skipClassAttribute: true, - config: { - theme: {}, - plugins: [], - }, - }, -]; - -ruleTester.run("classnames-order", rule, { - valid: [ - { - code: `
Simple, basic
`, - }, - { - code: `
Simple, using 'tw' prop
`, - options: [ - { - classRegex: "^tw$", - }, - ], - }, - { - code: "
ctl + exp
", - }, - { - code: "
ctl + var
", - }, - { - code: "
Space trim issue
", - }, - { - code: ` - ctl(\` - flex items-center justify-center - \${variant === SpinnerVariant.OVERLAY && \`z-60 rounded border-2 bg-gray-400 px-4 \${widthClass} \${heightClass}\`} - \${ - variant === SpinnerVariant.FULLSCREEN && - \`z-60 fixed bottom-0 left-0 right-0 top-0 bg-white bg-opacity-60 px-4 dark:bg-purple-900 dark:bg-opacity-60\` - } - \`)`, - }, - { - code: `
Simple quotes
`, - }, - { - code: `
Extra space at the end
`, - }, - { - code: `
Extra space at the end, but with 'tw' prop
`, - options: [ - { - classRegex: "^tw$", - }, - ], - }, - { - code: `
'p', then 'py' then 'px'
`, - }, - { - code: ``, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: `ctl(\` - container - flex - w-12 - sm:w-6 - lg:w-4 - \`)`, - }, - { - code: `
Custom prefix and separator
`, - options: [ - { - config: { prefix: "lorem-", separator: "_" }, - }, - ], - }, - { - code: `
Allowed arbitrary value
`, - }, - { - code: `
Stackable variants
`, - }, - { - code: `
clsx
`, - options: [ - { - callees: ["clsx"], - }, - ], - }, - { - code: `
Options override shared settings
`, - options: [ - { - config: { prefix: "opts-" }, - }, - ], - settings: { - tailwindcss: { - config: { prefix: "sttgs-" }, - }, - }, - }, - { - code: `
Use settings
`, - settings: { - tailwindcss: { - config: { prefix: "sttgs-", separator: "_" }, - }, - }, - }, - ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ - code: `${tag}\` - container - flex - w-12 - sm:w-6 - lg:w-4 - \``, - options: [ - { - tags: ["myTag"], - }, - ], - }))), - { - code: `
Number values
`, - settings: { - tailwindcss: { - config: { theme: { zIndex: { dialog: 10000 } } }, - }, - }, - }, - { - code: `
Extra spaces
`, - }, - { - code: `
Valid using mode official
`, - }, - { - code: `
Valid using mode official
`, - options: [ - { - config: { prefix: "lorem-", separator: "_" }, - }, - ], - }, - { - // No plugin - code: ` - - `, - }, - { - code: ` -
https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/109#issuecomment-1044625260 no config, so bg-deque-blue text-large goes at first position because custom
- `, - }, - { - code: ` -
https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/109#issuecomment-1044625260
- `, - options: sharedOptions, - }, - { - // Kitchensink - code: ` - - `, - options: sharedOptions, - }, - { - code: ` -
Issue #131
- `, - }, - { - code: ` -
-

Issue #142

-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
- `, - }, - { - code: `
No errors while typing
`, - }, - { - code: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - }, - { - code: `
Do not treat full width space as class separator
`, - }, - { - code: ` - const func = () => ({ a: 12 }); -
Spread of a function return inside clsx
- `, - }, - ], - invalid: [ - { - code: ` - export interface FakePropsInterface { - readonly name?: string; - } - function Fake({ - name = 'yolo' - }: FakeProps) { - return ( - <> -

Welcome {name}

-

Bye {name}

- - ); - } - export default Fake; - `, - output: ` - export interface FakePropsInterface { - readonly name?: string; - } - function Fake({ - name = 'yolo' - }: FakeProps) { - return ( - <> -

Welcome {name}

-

Bye {name}

- - ); - } - export default Fake; - `, - parser: require.resolve("@typescript-eslint/parser"), - errors: errors, - }, - { - code: `
Classnames will be ordered
`, - output: `
Classnames will be ordered
`, - errors: errors, - }, - { - code: `
Enhancing readability
`, - output: `
Enhancing readability
`, - errors: errors, - }, - { - code: `
Enhancing readability with 'tw' prop
`, - output: `
Enhancing readability with 'tw' prop
`, - options: [ - { - classRegex: "^tw$", - }, - ], - errors: errors, - }, - { - code: `
:)...
`, - output: `
:)...
`, - errors: errors, - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: "ctl(`p-10 w-full ${some}`)", - output: "ctl(`w-full p-10 ${some}`)", - errors: errors, - }, - { - code: "
Space trim issue with fix
", - output: "
Space trim issue with fix
", - errors: errors, - }, - { - code: `
`, - output: `
`, - options: [ - { - config: { - plugins: [require("@tailwindcss/typography")], - }, - }, - ], - errors: errors, - }, - { - code: `
`, - output: `
`, - options: [ - { - config: { - plugins: [require("@tailwindcss/line-clamp")], - }, - }, - ], - errors: errors, - }, - { - code: `
Simple quotes
`, - output: `
Simple quotes
`, - errors: errors, - }, - { - options: [ - { - removeDuplicates: false, - }, - ], - code: `
removeDuplicates: false
`, - output: `
removeDuplicates: false
`, - errors: errors, - }, - { - code: `
Single line dups + no head/tail spaces
`, - output: `
Single line dups + no head/tail spaces
`, - errors: errors, - }, - { - code: `
Single dups line + head spaces
`, - output: `
Single dups line + head spaces
`, - errors: errors, - }, - { - code: `
Single line dups + tail spaces
`, - output: `
Single line dups + tail spaces
`, - errors: errors, - }, - { - // Multiline + both head/tail spaces - code: ` - ctl(\` - invalid - sm:w-6 - container - invalid - flex - container - w-12 - flex - container - lg:w-4 - lg:w-4 - \`);`, - output: ` - ctl(\` - invalid - container - flex - w-12 - sm:w-6 - lg:w-4 - \`);`, - errors: errors, - }, - { - code: ` - ctl(\` - invalid - sm:w-6 - container - w-12 - flex - lg:w-4 - \`);`, - output: ` - ctl(\` - invalid - container - flex - w-12 - sm:w-6 - lg:w-4 - \`);`, - errors: errors, - }, - { - code: ` - const buttonClasses = ctl(\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - lg:w-9 - flex - \${hasError && "bg-red"} - \`);`, - output: ` - const buttonClasses = ctl(\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - flex - lg:w-9 - \${hasError && "bg-red"} - \`);`, - errors: errors, - }, - { - code: ` - const buttonClasses = ctl(\` - \${fullWidth ? "w-12" : "w-6"} - flex - container - \${fullWidth ? "sm:w-7" : "sm:w-4"} - lg:py-4 - sm:py-6 - \${hasError && "bg-red"} - \`);`, - output: ` - const buttonClasses = ctl(\` - \${fullWidth ? "w-12" : "w-6"} - container - flex - \${fullWidth ? "sm:w-7" : "sm:w-4"} - sm:py-6 - lg:py-4 - \${hasError && "bg-red"} - \`);`, - errors: generateErrors(2), - }, - { - code: `
Allowed arbitrary value but incorrect order
`, - output: `
Allowed arbitrary value but incorrect order
`, - errors: errors, - }, - { - code: `clsx(\`absolute bottom-0 w-full h-[70px] flex flex-col\`);`, - output: `clsx(\`absolute bottom-0 flex h-[70px] w-full flex-col\`);`, - options: [ - { - callees: ["clsx"], - }, - ], - errors: errors, - }, - { - code: `cva({ - primary: ["absolute bottom-0 w-full h-[70px] flex flex-col"], - })`, - output: `cva({ - primary: ["absolute bottom-0 flex h-[70px] w-full flex-col"], - })`, - options: [ - { - callees: ["cva"], - }, - ], - errors: errors, - }, - { - code: `
clsx
`, - output: `
clsx
`, - options: [ - { - callees: ["clsx"], - }, - ], - errors: errors, - }, - { - code: ` - ctl(\` - px-2 - flex - \${ - !isDisabled && - \` - top-0 - flex - border-0 - \` - } - \${ - isDisabled && - \` - border-0 - mx-0 - \` - } - \`) - `, - output: ` - ctl(\` - flex - px-2 - \${ - !isDisabled && - \` - top-0 - flex - border-0 - \` - } - \${ - isDisabled && - \` - mx-0 - border-0 - \` - } - \`) - `, - errors: generateErrors(2), - }, - { - code: `
...
`, - output: `
...
`, - errors: errors, - }, - { - code: `ctl(\`\${enabled && "px-2 flex"}\`)`, - output: `ctl(\`\${enabled && "flex px-2"}\`)`, - errors: errors, - }, - { - code: `ctl(\`px-2 flex\`)`, - output: `ctl(\`flex px-2\`)`, - errors: errors, - }, - { - code: ` - ctl(\` - px-2 - flex - \`) - `, - output: ` - ctl(\` - flex - px-2 - \`) - `, - errors: errors, - }, - { - code: ` -
- #19 -
- `, - output: ` -
- #19 -
- `, - errors: errors, - }, - { - code: ` -
- `, - output: ` -
- `, - errors: generateErrors(2), - }, - ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].flatMap(tag => ([ - { - code: ` - ${tag}\` - invalid - sm:w-6 - container - w-12 - flex - lg:w-4 - \`;`, - output: ` - ${tag}\` - invalid - container - flex - w-12 - sm:w-6 - lg:w-4 - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: errors, - }, - { - code: ` - const buttonClasses = ${tag}\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - lg:w-9 - flex - \${hasError && "bg-red"} - \`;`, - output: ` - const buttonClasses = ${tag}\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - flex - lg:w-9 - \${hasError && "bg-red"} - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: errors, - }, - { - code: ` - const buttonClasses = ${tag}\` - \${fullWidth ? "w-12" : "w-6"} - flex - container - \${fullWidth ? "sm:w-7" : "sm:w-4"} - lg:py-4 - sm:py-6 - \${hasError && "bg-red"} - \`;`, - output: ` - const buttonClasses = ${tag}\` - \${fullWidth ? "w-12" : "w-6"} - container - flex - \${fullWidth ? "sm:w-7" : "sm:w-4"} - sm:py-6 - lg:py-4 - \${hasError && "bg-red"} - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: generateErrors(2), - }, - ]))), - { - code: ` - classnames([ - 'invalid lg:w-4 sm:w-6', - ['w-12 flex'], - ])`, - output: ` - classnames([ - 'invalid sm:w-6 lg:w-4', - ['flex w-12'], - ])`, - errors: generateErrors(2), - }, - { - code: ` - classnames({ - invalid, - flex: myFlag, - 'lg:w-4 sm:w-6': resize - })`, - output: ` - classnames({ - invalid, - flex: myFlag, - 'sm:w-6 lg:w-4': resize - })`, - errors: errors, - }, - { - code: ` -
-
`, - output: ` -
-
`, - errors: [...errors, ...errors], - parser: require.resolve("@angular-eslint/template-parser"), - }, - { - code: `
:)
`, - output: `
:)
`, - errors: errors, - parser: require.resolve("@angular-eslint/template-parser"), - }, - { - code: ` -
- :) -
`, - output: ` -
- :) -
`, - errors: errors, - parser: require.resolve("@angular-eslint/template-parser"), - }, - { - code: `
Using official sorting
`, - output: `
Using official sorting
`, - errors: errors, - }, - { - code: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex-col flex" : ""}\`)`, - output: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex flex-col" : ""}\`)`, - errors: errors, - }, - { - code: `ctl(\`p-3 border-gray-300 m-4 h-24 lg:p-4 flex border-2 lg:m-4\`)`, - output: `ctl(\`m-4 flex h-24 border-2 border-gray-300 p-3 lg:m-4 lg:p-4\`)`, - errors: errors, - }, - { - // https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/131 - code: "", - output: "", - errors: errors, - }, - { - // https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/131 - code: "", - output: "", - errors: errors, - }, - { - // https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/131 - code: ``, - output: ``, - errors: errors, - }, - { - // https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/131 - code: ``, - output: ``, - errors: errors, - }, - { - code: ` -
skipClassAttribute
- `, - output: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - errors: errors, - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ``, - output: ``, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - `, - output: ` - `, - errors: errors, - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: `
support named group/peer syntax
`, - output: `
support named group/peer syntax
`, - errors: errors, - }, - ], -}); diff --git a/tests/lib/rules/dummy.css b/tests/lib/rules/dummy.css deleted file mode 100644 index 68c1587..0000000 --- a/tests/lib/rules/dummy.css +++ /dev/null @@ -1,45 +0,0 @@ -/* Only used for running tests */ -.some { - color: black; -} -.white-listed { - color: yellow; -} -.classnames { - color: red; -} -.one, -.two { - color: red; -} - -@media screen and (min-width: 480px) { - body { - background-color: lightgreen; - } -} - -#main { - border: 1px solid black; -} -@layer base { - ul li { - padding: 5px; - } - .base { - display: block; - } -} - -@tailwind utilities; - -.btn { - @apply border-red; -} - -.parent { - .child, - .btn { - background: none; - } -} diff --git a/tests/lib/rules/enforces-negative-arbitrary-values.js b/tests/lib/rules/enforces-negative-arbitrary-values.js deleted file mode 100644 index 1fd9df2..0000000 --- a/tests/lib/rules/enforces-negative-arbitrary-values.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @fileoverview Warns about `-` prefixed classnames using arbitrary values - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/enforces-negative-arbitrary-values"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -const skipClassAttributeOptions = [ - { - skipClassAttribute: true, - config: { - theme: {}, - plugins: [], - }, - }, -]; - -var generateErrors = (classes) => { - const errors = []; - classes.split(" ").forEach((cls) => - errors.push({ - messageId: "negativeArbitraryValue", - data: { - classname: cls, - }, - }) - ); - return errors; -}; - -var ruleTester = new RuleTester({ parserOptions }); - -ruleTester.run("enforces-negative-arbitrary-values", rule, { - valid: [ - { - code: `
top-[-50px]
`, - }, - { - code: `
top-[-50px]
`, - }, - { - code: `
top-[-50px]
`, - }, - { - code: `
No errors while typing
`, - }, - { - code: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - }, - ], - invalid: [ - { - code: `
all
`, - errors: generateErrors( - "-inset-[1px] -inset-y-[1px] -inset-x-[1px] -top-[1px] -right-[1px] -bottom-[1px] -left-[1px] -top-[1px] -z-[2] -order-[2] -m-[1px] -my-[1px] -mx-[1px] -mt-[1px] -mr-[1px] -mb-[1px] -ml-[1px] -mt-[1px] -space-y-[1px] -space-x-[1px] -tracking-[1px] -indent-[1px] -hue-rotate-[50%] -backdrop-hue-rotate-[50%] -scale-[50%] -scale-y-[50%] -scale-x-[50%] -rotate-[45deg] -translate-x-[1px] -translate-y-[1px] -skew-x-[45deg] -skew-y-[45deg] -scroll-m-[1px] -scroll-my-[1px] -scroll-mx-[1px] -scroll-mt-[1px] -scroll-mr-[1px] -scroll-mb-[1px] -scroll-ml-[1px] -scroll-mt-[1px]" - ), - }, - { - code: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - errors: generateErrors("-inset-y-[1px]"), - }, - { - code: `cva({ - primary: ["-inset-[1px] -inset-y-[1px] -inset-x-[1px] -top-[1px] -right-[1px] -bottom-[1px] -left-[1px] -top-[1px] -z-[2] -order-[2] -m-[1px] -my-[1px] -mx-[1px] -mt-[1px] -mr-[1px] -mb-[1px] -ml-[1px] -mt-[1px] -space-y-[1px] -space-x-[1px] -tracking-[1px] -indent-[1px] -hue-rotate-[50%] -backdrop-hue-rotate-[50%] -scale-[50%] -scale-y-[50%] -scale-x-[50%] -rotate-[45deg] -translate-x-[1px] -translate-y-[1px] -skew-x-[45deg] -skew-y-[45deg] -scroll-m-[1px] -scroll-my-[1px] -scroll-mx-[1px] -scroll-mt-[1px] -scroll-mr-[1px] -scroll-mb-[1px] -scroll-ml-[1px] -scroll-mt-[1px]"] - });`, - options: [ - { - callees: ["cva"], - }, - ], - errors: generateErrors( - "-inset-[1px] -inset-y-[1px] -inset-x-[1px] -top-[1px] -right-[1px] -bottom-[1px] -left-[1px] -top-[1px] -z-[2] -order-[2] -m-[1px] -my-[1px] -mx-[1px] -mt-[1px] -mr-[1px] -mb-[1px] -ml-[1px] -mt-[1px] -space-y-[1px] -space-x-[1px] -tracking-[1px] -indent-[1px] -hue-rotate-[50%] -backdrop-hue-rotate-[50%] -scale-[50%] -scale-y-[50%] -scale-x-[50%] -rotate-[45deg] -translate-x-[1px] -translate-y-[1px] -skew-x-[45deg] -skew-y-[45deg] -scroll-m-[1px] -scroll-my-[1px] -scroll-mx-[1px] -scroll-mt-[1px] -scroll-mr-[1px] -scroll-mb-[1px] -scroll-ml-[1px] -scroll-mt-[1px]" - ), - }, - { - code: ` - - `, - errors: generateErrors("-inset-[1px] -inset-y-[1px]"), - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - errors: generateErrors("-top-[1px] -bottom-[1px]"), - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - errors: generateErrors("-left-[1px] -top-[1px]"), - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - errors: generateErrors("-my-[1px] -mx-[1px]"), - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: `
support named group/peer syntax
`, - errors: generateErrors("group/edit:-inset-[1px]"), - }, - ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ - code: `${tag}\`-my-[1px] -mx-[1px]\``, - errors: generateErrors("-my-[1px] -mx-[1px]"), - options: [ - { - tags: ["myTag"], - }, - ], - }))), - ], -}); diff --git a/tests/lib/rules/enforces-shorthand.js b/tests/lib/rules/enforces-shorthand.js deleted file mode 100644 index 30ecb43..0000000 --- a/tests/lib/rules/enforces-shorthand.js +++ /dev/null @@ -1,814 +0,0 @@ -/** - * @fileoverview Detect classname candidates for shorthand replacement - * @description E.g. `mx-4 my-4` can be replaced by `m-4` - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/enforces-shorthand"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -const skipClassAttributeOptions = [ - { - skipClassAttribute: true, - config: { - theme: {}, - plugins: [], - }, - }, -]; - -const incompleteCustomWidthHeightOptions = [ - { - config: { - theme: { - extend: { - width: { custom: "100px" }, - height: { custom: "100px" }, - }, - }, - plugins: [], - }, - }, -]; - -const customSpacingOnlyOptions = [ - { - config: { - theme: { - extend: { - spacing: { custom: "100px" }, - }, - }, - plugins: [], - }, - }, -]; - -const customSizeOnlyOptions = [ - { - config: { - theme: { - extend: { - size: { size: "100px" }, - }, - }, - plugins: [], - }, - }, -]; - -const ambiguousOptions = [ - { - config: { - theme: { - extend: { - width: { ambiguous: "75px" }, - height: { ambiguous: "120px" }, - size: { ambiguous: "100px" }, - }, - }, - plugins: [], - }, - }, -]; - -var generateError = (classnames, shorthand) => { - return { - messageId: "shorthandCandidateDetected", - data: { - classnames: classnames.join(", "), - shorthand: shorthand, - }, - }; -}; - -var ruleTester = new RuleTester({ parserOptions }); - -ruleTester.run("shorthands", rule, { - valid: [ - { - code: ` -
- No shorthand possible for overflow -
- `, - }, - { - code: ` -
- No shorthand possible for overscroll -
- `, - }, - { - code: ` -
- No shorthand possible for margin -
- `, - }, - { - code: ` -
- No shorthand possible for inset -
`, - }, - { - code: ` -
- Cannot merge mixed values (arbitrary + regular) -
`, - }, - { - code: ` -
- No shorthand possible for gap -
`, - }, - { - code: ``, - }, - { - code: `
`, - }, - { - code: `
issue #91
`, - }, - { - code: `
No errors while typing
`, - }, - { - code: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - }, - { - code: ` -
- support named group/peer syntax -
- `, - }, - { - code: ` -
- Possible shorthand available for truncate, but some of the classes have modifiers -
- `, - }, - { - code: ` -
- Possible shorthand available for truncate, but some of the classes have important -
- `, - }, - { - code: "
issue #312
", - }, - { - code: "
issue #307
", - }, - { - code: `
Incomplete config should not use size-*
`, - options: incompleteCustomWidthHeightOptions, - }, - { - code: `
Ambiguous cannot size-*
`, - options: ambiguousOptions, - }, - { - code: `
h-custom & w-custom don't exist... no size-*
`, - options: customSizeOnlyOptions, - }, - ], - - invalid: [ - { - code: ` -
- Possible shorthand for overflow -
- `, - output: ` -
- Possible shorthand for overflow -
- `, - errors: [generateError(["overflow-x-auto", "overflow-y-auto"], "overflow-auto")], - }, - { - code: ` -
- Possible shorthand for overscroll -
- `, - output: ` -
- Possible shorthand for overscroll -
- `, - errors: [generateError(["overscroll-x-contain", "overscroll-y-contain"], "overscroll-contain")], - }, - { - code: ` -
- Possible shorthand for margin -
- `, - output: ` -
- Possible shorthand for margin -
- `, - errors: [generateError(["mt-0", "mb-0"], "my-0")], - }, - { - code: ` -
- Possible shorthand for negative margin -
- `, - output: ` -
- Possible shorthand for negative margin -
- `, - errors: [generateError(["-mt-1", "-mb-1"], "-my-1")], - }, - { - code: ` -
- Possible shorthand for margin -
- `, - output: ` -
- Possible shorthand for margin -
- `, - errors: [ - generateError(["mt-0", "mb-0"], "my-0"), - generateError(["md:mx-2", "md:my-2"], "md:m-2"), - generateError(["py-0", "px-0"], "p-0"), - ], - }, - { - code: ` -
- Possible shorthand for border-radius -
- `, - output: ` -
- Possible shorthand for border-radius -
- `, - errors: [ - generateError(["rounded-tl-sm", "rounded-tr-sm"], "rounded-t-sm"), - generateError(["md:rounded-t-md", "md:rounded-b-md"], "md:rounded-md"), - ], - }, - { - code: ` -
- Possible shorthand for border-radius -
- `, - output: ` -
- Possible shorthand for border-radius -
- `, - errors: [generateError(["rounded-tl", "rounded-tr", "rounded-b"], "rounded")], - }, - { - code: ` -
- Possible shorthand for border-radius -
- `, - output: ` -
- Possible shorthand for border-radius -
- `, - errors: [generateError(["rounded-tl-sm", "rounded-tr-sm", "rounded-b-sm"], "rounded-sm")], - }, - { - code: ` -
- Possible shorthand for border-radius -
- `, - output: ` -
- Possible shorthand for border-radius -
- `, - errors: [generateError(["rounded-tl-sm", "rounded-tr-sm"], "rounded-t-sm")], - }, - { - code: ` -
- Possible shorthand for border-radius -
- `, - output: ` -
- Possible shorthand for border-radius -
- `, - errors: [ - generateError(["rounded-tl-sm", "rounded-tr-sm"], "rounded-t-sm"), - generateError(["md:rounded-t-md", "md:rounded-b-md"], "md:rounded-md"), - ], - }, - { - code: ` -
- Possible shorthand for border-width -
- `, - output: ` -
- Possible shorthand for border-width -
- `, - errors: [ - generateError(["border-t-4", "border-b-4"], "border-y-4"), - generateError(["md:border-t-0", "md:border-b-0"], "md:border-y-0"), - generateError(["lg:border-y", "lg:border-l", "lg:border-r"], "lg:border"), - ], - }, - { - code: ` -
- Possible shorthand for border-width -
- `, - output: ` -
- Possible shorthand for border-width -
- `, - errors: [generateError(["border-t-4", "border-b-4"], "border-y-4")], - }, - { - code: ` -
- No shorthand possible -
`, - output: ` -
- No shorthand possible -
`, - errors: [ - generateError(["top-[0]", "bottom-[0]"], "inset-y-[0]"), - generateError(["right-[var(--some-value)]", "left-[var(--some-value)]"], "inset-x-[var(--some-value)]"), - ], - }, - { - code: ` -
- Randomized classnames order -
- `, - output: ` -
- Randomized classnames order -
- `, - errors: [ - generateError(["rounded-tr-sm", "rounded-tl-sm"], "rounded-t-sm"), - generateError(["md:rounded-b-md", "md:rounded-t-md"], "md:rounded-md"), - generateError(["border-t-4", "border-b-4"], "border-y-4"), - ], - }, - { - code: ` -
- Possible shorthand for gap -
`, - output: ` -
- Possible shorthand for gap -
`, - errors: [generateError(["gap-x-4", "gap-y-4"], "gap-4")], - }, - { - code: ` -
- Possible shorthand for border-color -
`, - output: ` -
- Possible shorthand for border-color -
`, - errors: [ - generateError( - ["border-t-indigo-200/50", "border-x-indigo-200/50", "border-b-indigo-200/50"], - "border-indigo-200/50" - ), - ], - }, - { - code: ``, - output: ``, - errors: [generateError(["scale-x-75", "scale-y-75"], "scale-75")], - }, - { - code: ``, - output: ``, - errors: [generateError(["scale-x-75", "scale-y-75"], "scale-75")], - }, - { - code: ``, - output: ``, - errors: [generateError(["-scale-x-50", "-scale-y-50"], "-scale-50")], - }, - { - code: ` -
- Multilines -
`, - output: ` -
- Multilines -
`, - errors: [generateError(["w-48", "h-48"], "size-48"), generateError(["py-8", "px-8"], "p-8")], - }, - { - code: `classnames(['py-8 px-8 w-48 h-48 text-white'])`, - output: `classnames(['p-8 size-48 text-white'])`, - errors: [generateError(["w-48", "h-48"], "size-48"), generateError(["py-8", "px-8"], "p-8")], - }, - { - code: `classnames({'py-8 px-8 text-white': true})`, - output: `classnames({'p-8 text-white': true})`, - errors: [generateError(["py-8", "px-8"], "p-8")], - }, - { - code: `classnames({'!py-8 !px-8 text-white': true})`, - output: `classnames({'!p-8 text-white': true})`, - errors: [generateError(["!py-8", "!px-8"], "!p-8")], - }, - { - code: `classnames({'!pt-8 !pb-8 pr-8 !pl-8': true})`, - output: `classnames({'!py-8 pr-8 !pl-8': true})`, - errors: [generateError(["!pt-8", "!pb-8"], "!py-8")], - }, - { - code: `classnames({'!pt-8 !pb-8 !pr-8 !pl-8': true})`, - output: `classnames({'!p-8': true})`, - errors: [generateError(["!pt-8", "!pb-8", "!pr-8", "!pl-8"], "!p-8")], - }, - { - code: `classnames({'!pt-8 pb-8 pr-8 pl-8': true})`, - output: `classnames({'!pt-8 pb-8 px-8': true})`, - errors: [generateError(["pr-8", "pl-8"], "px-8")], - }, - { - code: `classnames({'md:!rounded-tr block md:rounded-tl md:rounded-br md:rounded-bl': true})`, - output: `classnames({'md:!rounded-tr block md:rounded-tl md:rounded-b': true})`, - errors: [generateError(["md:rounded-br", "md:rounded-bl"], "md:rounded-b")], - }, - { - code: ` -
- Issue #120 -
- `, - output: ` -
- Issue #120 -
- `, - errors: [generateError(["rounded-r-full", "rounded-l-full"], "rounded-full")], - }, - { - code: `classnames('sfc-border-l-0 sfc-border-r-0')`, - output: `classnames('sfc-border-x-0')`, - options: [ - { - config: { prefix: "sfc-" }, - }, - ], - errors: [generateError(["sfc-border-l-0", "sfc-border-r-0"], "sfc-border-x-0")], - }, - { - code: `classnames('md_sfc-border-l-0 md_sfc-border-r-0')`, - output: `classnames('md_sfc-border-x-0')`, - options: [ - { - config: { prefix: "sfc-", separator: "_" }, - }, - ], - errors: [generateError(["md_sfc-border-l-0", "md_sfc-border-r-0"], "md_sfc-border-x-0")], - }, - { - code: ` -
- Issue #148 -
- `, - output: ` -
- Issue #148 -
- `, - errors: [generateError(["border-spacing-x-px", "border-spacing-y-px"], "border-spacing-px")], - }, - { - code: ` -
skipClassAttribute
- `, - output: ` -
skipClassAttribute
- `, - options: skipClassAttributeOptions, - errors: [generateError(["mt-0", "mb-0"], "my-0")], - }, - { - code: ` -
- Issue #182 -
- `, - output: ` -
- Issue #182 -
- `, - errors: [generateError(["p-2", "pl-2", "pr-2"], "p-2")], - }, - { - code: `cva({ - primary: ["border-l-0 border-r-0"], - });`, - output: `cva({ - primary: ["border-x-0"], - });`, - options: [ - { - callees: ["cva"], - }, - ], - errors: [generateError(["border-l-0", "border-r-0"], "border-x-0")], - }, - { - code: ` - - `, - output: ` - - `, - errors: [generateError(["overflow-x-auto", "overflow-y-auto"], "overflow-auto")], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - errors: [generateError(["overscroll-x-contain", "overscroll-y-contain"], "overscroll-contain")], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - errors: [generateError(["mt-0", "mb-0"], "my-0")], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - errors: [generateError(["-mt-1", "-mb-1"], "-my-1")], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` -
- support named group/peer syntax -
- `, - output: ` -
- support named group/peer syntax -
- `, - errors: [generateError(["group/name:rounded-r-full", "group/name:rounded-l-full"], "group/name:rounded-full")], - }, - { - code: ` -
- Possible shorthand when using truncate -
- `, - output: ` -
- Possible shorthand when using truncate -
- `, - errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")], - }, - { - code: ` -
- Possible shorthand when using truncate with breakpoint -
- `, - output: ` -
- Possible shorthand when using truncate with breakpoint -
- `, - errors: [generateError(["md:overflow-hidden", "md:text-ellipsis", "md:whitespace-nowrap"], "md:truncate")], - }, - { - code: ` -
- Possible shorthand when using truncate with hover -
- `, - output: ` -
- Possible shorthand when using truncate with hover -
- `, - errors: [ - generateError(["hover:overflow-hidden", "hover:text-ellipsis", "hover:whitespace-nowrap"], "hover:truncate"), - ], - }, - { - code: ` -
- Possible shorthand when using truncate with hover, breakpoint, important and prefix -
- `, - output: ` -
- Possible shorthand when using truncate with hover, breakpoint, important and prefix -
- `, - errors: [ - generateError( - ["hover:sm:!tw-overflow-hidden", "hover:sm:!tw-text-ellipsis", "hover:sm:!tw-whitespace-nowrap"], - "hover:sm:!tw-truncate" - ), - ], - options: [ - { - config: { prefix: "tw-" }, - }, - ], - }, - { - code: ` -
- Possible shorthand when using truncate, tested with additional classnames -
- `, - output: ` -
- Possible shorthand when using truncate, tested with additional classnames -
- `, - errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")], - }, - { - code: "
Leading space trim issue with fix
", - output: "
Leading space trim issue with fix
", - errors: [generateError(["px-10", "py-10"], "p-10")], - }, - { - code: "
Leading space trim issue with fix (2)
", - output: "
Leading space trim issue with fix (2)
", - errors: [generateError(["px-10", "py-10"], "p-10")], - }, - { - code: "
Trailing space trim issue with fix
", - output: "
Trailing space trim issue with fix
", - errors: [generateError(["px-10", "py-10"], "p-10")], - }, - { - code: "
Trailing space trim issue with fix (2)
", - output: "
Trailing space trim issue with fix (2)
", - errors: [generateError(["px-10", "py-10"], "p-10")], - }, - { - code: `
New size-* utilities
`, - output: `
New size-* utilities
`, - errors: [generateError(["h-10", "w-10"], "size-10")], - }, - { - code: `
New size-* utilities
`, - output: `
New size-* utilities
`, - errors: [generateError(["md:h-5", "md:w-5"], "md:size-5")], - }, - { - code: `
size-*
`, - output: `
size-*
`, - errors: [generateError(["h-custom", "w-custom"], "size-custom")], - options: customSpacingOnlyOptions, - }, - ...["myTag", "myTag.subTag", "myTag(SomeComponent)"].map((tag) => ({ - code: `${tag}\`overflow-hidden text-ellipsis whitespace-nowrap text-white text-xl\``, - output: `${tag}\`truncate text-white text-xl\``, - errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")], - options: [ - { - tags: ["myTag"], - }, - ], - })), - ], -}); diff --git a/tests/lib/rules/migration-from-tailwind-2.js b/tests/lib/rules/migration-from-tailwind-2.js deleted file mode 100644 index 0ada3e7..0000000 --- a/tests/lib/rules/migration-from-tailwind-2.js +++ /dev/null @@ -1,404 +0,0 @@ -/** - * @fileoverview Detect obsolete classnames when upgrading to Tailwind CSS v3 - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/migration-from-tailwind-2"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -const skipClassAttributeOptions = [ - { - skipClassAttribute: true, - config: { - theme: {}, - plugins: [], - }, - }, -]; - -var ruleTester = new RuleTester({ parserOptions }); - -ruleTester.run("migration-from-tailwind-2", rule, { - valid: [ - { - code: `
Automatic transforms and filters
`, - }, - { - code: `
transition-transform
`, - }, - { - code: `
transition-[var(--transform)]
`, - }, - { - code: `
No errors while typing
`, - }, - ], - invalid: [ - { - code: `
Automatic transform
`, - output: `
Automatic transform
`, - errors: [ - { - messageId: "classnameNotNeeded", - data: { - classnames: "transform", - }, - }, - ], - }, - { - code: `
Automatic transform
`, - output: `
Automatic transform
`, - errors: [ - { - messageId: "classnameNotNeeded", - data: { - classnames: "backdrop-filter", - }, - }, - ], - }, - { - code: `
Automatic transforms and filters
`, - output: `
Automatic transforms and filters
`, - errors: [ - { - messageId: "classnamesNotNeeded", - data: { - classnames: "transform, filter, backdrop-filter", - }, - }, - ], - }, - { - code: `
overflow-clip/ellipsis
`, - output: `
overflow-clip/ellipsis
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "overflow-clip", - updated: "text-clip", - }, - }, - ], - }, - { - code: `
overflow-clip/ellipsis
`, - output: `
overflow-clip/ellipsis
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "overflow-ellipsis", - updated: "text-ellipsis", - }, - }, - ], - }, - { - code: `
support named group/peer syntax
`, - output: `
support named group/peer syntax
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "group/name:overflow-ellipsis", - updated: "group/name:text-ellipsis", - }, - }, - ], - }, - { - code: `
flex-grow/shrink
`, - output: `
flex-grow/shrink
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-grow-0", - updated: "grow-0", - }, - }, - ], - }, - { - code: `
flex-grow/shrink
`, - output: `
flex-grow/shrink
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-shrink", - updated: "shrink", - }, - }, - ], - }, - { - code: `
decoration-clone/slice
`, - output: `
decoration-clone/slice
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "decoration-clone", - updated: "box-decoration-clone", - }, - }, - ], - }, - { - code: `
decoration-clone/slice
`, - output: `
decoration-clone/slice
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "decoration-slice", - updated: "box-decoration-slice", - }, - }, - ], - }, - { - code: `
bg-opacity
`, - errors: [ - { - messageId: "classnameOpacityDeprecated", - data: { - classname: "bg-opacity-50", - value: "50", - }, - }, - ], - }, - { - code: `
ring-opacity
`, - errors: [ - { - messageId: "classnameOpacityDeprecated", - data: { - classname: "ring-opacity-50", - value: "50", - }, - }, - { - messageId: "classnameOpacityDeprecated", - data: { - classname: "border-opacity-50", - value: "50", - }, - }, - ], - }, - { - code: `
`, - output: `
`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "placeholder-red-900", - updated: "placeholder:text-red-900", - }, - }, - ], - }, - { - code: `classnames(["placeholder-red-900"])`, - output: `classnames(["placeholder:text-red-900"])`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "placeholder-red-900", - updated: "placeholder:text-red-900", - }, - }, - ], - }, - { - code: `classnames({"placeholder-red-900": true})`, - output: `classnames({"placeholder:text-red-900": true})`, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "placeholder-red-900", - updated: "placeholder:text-red-900", - }, - }, - ], - }, - { - code: ` - @if (session()->has('status')) -

- {{ session('status') }} -

- @endif`, - output: ` - @if (session()->has('status')) -

- {{ session('status') }} -

- @endif`, - parser: require.resolve("@angular-eslint/template-parser"), - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-shrink", - updated: "shrink", - }, - }, - ], - }, - { - code: `
`, - output: `
`, - options: skipClassAttributeOptions, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "placeholder-red-900", - updated: "placeholder:text-red-900", - }, - }, - ], - }, - { - code: ` - - `, - output: ` - - `, - options: skipClassAttributeOptions, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "placeholder-red-900", - updated: "placeholder:text-red-900", - }, - }, - ], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - options: skipClassAttributeOptions, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-shrink", - updated: "shrink", - }, - }, - ], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - options: skipClassAttributeOptions, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "decoration-slice", - updated: "box-decoration-slice", - }, - }, - ], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - { - code: ` - - `, - output: ` - - `, - options: skipClassAttributeOptions, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-grow-0", - updated: "grow-0", - }, - }, - ], - filename: "test.vue", - parser: require.resolve("vue-eslint-parser"), - }, - ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ - code: `${tag}\`flex-grow-0\``, - output: `${tag}\`grow-0\``, - errors: [ - { - messageId: "classnameChanged", - data: { - deprecated: "flex-grow-0", - updated: "grow-0", - }, - }, - ], - options: [ - { - tags: ["myTag"], - }, - ], - }))), - ], -}); diff --git a/tests/lib/rules/no-arbitrary-value.js b/tests/lib/rules/no-arbitrary-value.js deleted file mode 100644 index 0a438e0..0000000 --- a/tests/lib/rules/no-arbitrary-value.js +++ /dev/null @@ -1,176 +0,0 @@ -/** - * @fileoverview Forbid using arbitrary values in classnames - * @author François Massart - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -var rule = require("../../../lib/rules/no-arbitrary-value"); -var RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -var parserOptions = { - ecmaVersion: 2019, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, -}; - -const skipClassAttributeOptions = [ - { - skipClassAttribute: true, - config: { - theme: {}, - plugins: [], - }, - }, -]; - -var generateErrors = (classnames) => { - const errors = []; - if (typeof classnames === "string") { - classnames = classnames.split(" "); - } - classnames.map((classname) => { - errors.push({ - messageId: "arbitraryValueDetected", - data: { - classname: classname, - }, - }); - }); - return errors; -}; - -var ruleTester = new RuleTester({ parserOptions }); - -ruleTester.run("no-arbitrary-value", rule, { - valid: [ - { - code: `
No arbitrary value
`, - }, - { - code: `
No errors while typing
`, - }, - { - code: `
Skip class attribute
`, - options: skipClassAttributeOptions, - }, - { - code: `
Issue #318
`, - }, - ], - - invalid: [ - { - code: `
Arbitrary width!
`, - errors: generateErrors("w-[10px]"), - }, - { - code: `
Arbitrary width in named group!
`, - errors: generateErrors("group/name:w-[10px]"), - }, - { - code: `
Arbitrary width!
`, - errors: generateErrors("w-[10px]"), - }, - { - code: `
Arbitrary values!
`, - errors: generateErrors("bg-[rgba(10,20,30,0.5)] [mask-type:luminance]"), - }, - { - code: `ctl(\` - [mask-type:luminance] - container - flex - bg-[rgba(10,20,30,0.5)] - w-12 - sm:w-6 - lg:w-4 - \`)`, - errors: generateErrors("[mask-type:luminance] bg-[rgba(10,20,30,0.5)]"), - }, - { - code: ` -