├── .editorconfig ├── .github └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── index.test.mjs ├── package-lock.json ├── package.json ├── src ├── index.mjs └── lib │ ├── get-custom-media-from-imports.mjs │ ├── get-custom-media-from-root.mjs │ └── parse-media.mjs └── test └── import-custom-media.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,md,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | - 'master' 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 1 21 | - uses: actions/setup-node@v4 22 | with: 23 | node-version: lts/* 24 | 25 | - name: npm ci 26 | run: | 27 | npm ci --ignore-scripts 28 | 29 | - name: npm test 30 | run: | 31 | npm test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | !.editorconfig 4 | !.gitignore 5 | !.rollup.js 6 | !.tape.js 7 | !.travis.yml 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to Media Use Custom Media 2 | 3 | ### 4.0.0 (Dec 10, 2023) 4 | 5 | - Updated: peer `stylelint` to >=16 (breaking) 6 | - Removed `false` option, use `never` instead 7 | 8 | ### 3.0.0 (Dec 10, 2023) 9 | 10 | - Updated: peer `stylelint` to 10 - 15 (major) 11 | 12 | This package is in need of some larger maintenance and we can't guarantee that this package actually works with Stylelint 15. 13 | We however do not want a technical hurdle like a version constraint to get in the way. 14 | 15 | ### 2.0.1 (Nov 6, 2022) 16 | 17 | - Updated: peer `stylelint` to 10 - 14 (patch) 18 | 19 | ### 2.0.0 (May 12, 2020) 20 | 21 | - Updated: peer `stylelint` to 10 - 13 (major) 22 | - Updated: Node 10+ compatibility (major) 23 | 24 | ### 1.0.0 (September 25, 2018) 25 | 26 | - Initial version 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Media Use Custom Media 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope and avoid unrelated commits. 19 | 20 | 1. To begin; [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone git@github.com:YOUR_USER/stylelint-media-use-custom-media.git 24 | 25 | # Navigate to the newly cloned directory 26 | cd stylelint-media-use-custom-media 27 | 28 | # Assign the original repo to a remote called "upstream" 29 | git remote add upstream git@github.com:csstools/stylelint-media-use-custom-media.git 30 | 31 | # Install the tools necessary for testing 32 | npm install 33 | ``` 34 | 35 | 2. Create a branch for your feature or fix: 36 | ```bash 37 | # Move into a new branch for your feature 38 | git checkout -b feature/thing 39 | ``` 40 | ```bash 41 | # Move into a new branch for your fix 42 | git checkout -b fix/something 43 | ``` 44 | 45 | 3. If your code follows our practices, then push your feature branch: 46 | ```bash 47 | # Test current code 48 | npm test 49 | ``` 50 | ```bash 51 | # Push the branch for your new feature 52 | git push origin feature/thing 53 | ``` 54 | ```bash 55 | # Or, push the branch for your update 56 | git push origin update/something 57 | ``` 58 | 59 | That’s it! Now [open a pull request] with a clear title and description. 60 | 61 | [already been reported]: issues 62 | [fork this project]: fork 63 | [live example]: https://codepen.io/pen 64 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 65 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 66 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal 2 | 3 | ## Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an “owner”) of an original work of 8 | authorship and/or a database (each, a “Work”). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific works 12 | (“Commons”) that the public can reliably and without fear of later claims of 13 | infringement build upon, modify, incorporate in other works, reuse and 14 | redistribute as freely as possible in any form whatsoever and for any purposes, 15 | including without limitation commercial purposes. These owners may contribute 16 | to the Commons to promote the ideal of a free culture and the further 17 | production of creative, cultural and scientific works, or to gain reputation or 18 | greater distribution for their Work in part through the use and efforts of 19 | others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation of 22 | additional consideration or compensation, the person associating CC0 with a 23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and 25 | publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights (“Copyright and 31 | Related Rights”). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 1. the right to reproduce, adapt, distribute, perform, display, communicate, 34 | and translate a Work; 35 | 2. moral rights retained by the original author(s) and/or performer(s); 36 | 3. publicity and privacy rights pertaining to a person’s image or likeness 37 | depicted in a Work; 38 | 4. rights protecting against unfair competition in regards to a Work, 39 | subject to the limitations in paragraph 4(i), below; 40 | 5. rights protecting the extraction, dissemination, use and reuse of data in 41 | a Work; 42 | 6. database rights (such as those arising under Directive 96/9/EC of the 43 | European Parliament and of the Council of 11 March 1996 on the legal 44 | protection of databases, and under any national implementation thereof, 45 | including any amended or successor version of such directive); and 46 | 7. other similar, equivalent or corresponding rights throughout the world 47 | based on applicable law or treaty, and any national implementations 48 | thereof. 49 | 50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright 53 | and Related Rights and associated claims and causes of action, whether now 54 | known or unknown (including existing as well as future claims and causes of 55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 56 | duration provided by applicable law or treaty (including future time 57 | extensions), (iii) in any current or future medium and for any number of 58 | copies, and (iv) for any purpose whatsoever, including without limitation 59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer 60 | makes the Waiver for the benefit of each member of the public at large and 61 | to the detriment of Affirmer’s heirs and successors, fully intending that 62 | such Waiver shall not be subject to revocation, rescission, cancellation, 63 | termination, or any other legal or equitable action to disrupt the quiet 64 | enjoyment of the Work by the public as contemplated by Affirmer’s express 65 | Statement of Purpose. 66 | 67 | 3. Public License Fallback. Should any part of the Waiver for any reason be 68 | judged legally invalid or ineffective under applicable law, then the Waiver 69 | shall be preserved to the maximum extent permitted taking into account 70 | Affirmer’s express Statement of Purpose. In addition, to the extent the 71 | Waiver is so judged Affirmer hereby grants to each affected person a 72 | royalty-free, non transferable, non sublicensable, non exclusive, 73 | irrevocable and unconditional license to exercise Affirmer’s Copyright and 74 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 75 | maximum duration provided by applicable law or treaty (including future time 76 | extensions), (iii) in any current or future medium and for any number of 77 | copies, and (iv) for any purpose whatsoever, including without limitation 78 | commercial, advertising or promotional purposes (the “License”). The License 79 | shall be deemed effective as of the date CC0 was applied by Affirmer to the 80 | Work. Should any part of the License for any reason be judged legally 81 | invalid or ineffective under applicable law, such partial invalidity or 82 | ineffectiveness shall not invalidate the remainder of the License, and in 83 | such case Affirmer hereby affirms that he or she will not (i) exercise any 84 | of his or her remaining Copyright and Related Rights in the Work or (ii) 85 | assert any associated claims and causes of action with respect to the Work, 86 | in either case contrary to Affirmer’s express Statement of Purpose. 87 | 88 | 4. Limitations and Disclaimers. 89 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, 90 | surrendered, licensed or otherwise affected by this document. 91 | 2. Affirmer offers the Work as-is and makes no representations or warranties 92 | of any kind concerning the Work, express, implied, statutory or 93 | otherwise, including without limitation warranties of title, 94 | merchantability, fitness for a particular purpose, non infringement, or 95 | the absence of latent or other defects, accuracy, or the present or 96 | absence of errors, whether or not discoverable, all to the greatest 97 | extent permissible under applicable law. 98 | 3. Affirmer disclaims responsibility for clearing rights of other persons 99 | that may apply to the Work or any use thereof, including without 100 | limitation any person’s Copyright and Related Rights in the Work. 101 | Further, Affirmer disclaims responsibility for obtaining any necessary 102 | consents, permissions or other rights required for any use of the Work. 103 | 4. Affirmer understands and acknowledges that Creative Commons is not a 104 | party to this document and has no duty or obligation with respect to this 105 | CC0 or use of the Work. 106 | 107 | For more information, please see 108 | http://creativecommons.org/publicdomain/zero/1.0/. 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Media Use Custom Media [stylelint][stylelint] 2 | 3 | [![NPM Version][npm-img]][npm-url] 4 | [![test](https://github.com/csstools/stylelint-media-use-custom-media/actions/workflows/test.yml/badge.svg)](https://github.com/csstools/stylelint-media-use-custom-media/actions/workflows/test.yml) 5 | [![Support Chat][git-img]][git-url] 6 | 7 | [Media Use Custom Media] is a [stylelint] rule to enforce usage of custom media 8 | queries in CSS. 9 | 10 | ## Usage 11 | 12 | Add [stylelint] and [Media Use Custom Media] to your project. 13 | 14 | ```bash 15 | npm install stylelint stylelint-media-use-custom-media --save-dev 16 | ``` 17 | 18 | Add [Media Use Custom Media] to your [stylelint configuration]. 19 | 20 | ```js 21 | { 22 | "plugins": [ 23 | "stylelint-media-use-custom-media" 24 | ], 25 | "rules": { 26 | "csstools/media-use-custom-media": "always" || "always-known" || "known" || "never" || "ignore" 27 | } 28 | } 29 | ``` 30 | 31 | ## Options 32 | 33 | ### always 34 | 35 | If the first option is `"always"` or `true`, then [Media Use Custom Media] 36 | requires all `@media` queries to use Custom Media, and the following patterns 37 | are _not_ considered violations: 38 | 39 | ```pcss 40 | @media (--sm) {} 41 | 42 | @media (--sm), (--md) {} 43 | 44 | @media screen and (--sm) {} 45 | ``` 46 | 47 | While the following patterns are considered violations: 48 | 49 | ```pcss 50 | @media (max-width: 40rem) {} 51 | 52 | @media (max-width: 40rem), (--md) {} 53 | ``` 54 | 55 | ### never 56 | 57 | If the first option is `"never"`, then [Media Use Custom Media] 58 | requires all `@media` queries to not use Custom Media, and the following 59 | patterns are _not_ considered violations: 60 | 61 | ```pcss 62 | @media (max-width: 40rem) {} 63 | 64 | @media screen and (max-width: 40rem) {} 65 | ``` 66 | 67 | While the following patterns are considered violations: 68 | 69 | ```pcss 70 | @media (--md) {} 71 | 72 | @media screen and (--md) {} 73 | 74 | @media (--md), (max-width: 40rem) {} 75 | ``` 76 | 77 | ### known 78 | 79 | If the first option is `"known"`, then [Media Use Custom Media] requires all 80 | `@media` queries referencing Custom Media to be known from either 81 | `@custom-media` declarations in the file or imported using the second 82 | option. Then the following patterns are _not_ considered violations: 83 | 84 | ```pcss 85 | @custom-media --sm (min-width: 40rem); 86 | 87 | @media (--sm) {} 88 | 89 | @media (--sm), (min-width: 40rem) {} 90 | ``` 91 | 92 | While the following patterns are considered violations: 93 | 94 | ```pcss 95 | @media (--md) {} 96 | 97 | @media (--md), (min-width: 40rem) {} 98 | ``` 99 | 100 | ### always-known 101 | 102 | If the first option is `"always-known"`, then [Media Use Custom Media] requires all 103 | `@media` queries to use known Custom Media from either `@custom-media` 104 | declarations in the file or imported using the second option. Then the 105 | following patterns are _not_ considered violations: 106 | 107 | ```pcss 108 | @custom-media --sm (min-width: 40rem); 109 | 110 | @media (--sm) {} 111 | ``` 112 | 113 | While the following patterns are considered violations: 114 | 115 | ```pcss 116 | @custom-media --sm (min-width: 40rem); 117 | 118 | @media (--sm), (min-width: 40rem) {} 119 | 120 | @media (--md) {} 121 | ``` 122 | 123 | ### ignore 124 | 125 | If the first option is `"ignore"` or `null`, then [Media Use Custom Media] does 126 | nothing. 127 | 128 | --- 129 | 130 | ### importFrom 131 | 132 | When the first option is `"always-known"` or `"known"`, then the second option 133 | can specify sources where Custom Properties should be imported from by using an 134 | `importFrom` key. These imports might be CSS, JS, and JSON files, functions, 135 | and directly passed objects. 136 | 137 | ```js 138 | // .stylelintrc 139 | { 140 | "plugins": [ 141 | "stylelint-media-use-custom-media" 142 | ], 143 | "rules": { 144 | "csstools/media-use-custom-media": ["known", { 145 | "importFrom": [ 146 | "path/to/file.css", // => @custom-media --sm (min-width: 40rem); 147 | "path/to/file.json" // => { "custom-media": { "--sm": "(min-width: 40rem)" } } 148 | ] 149 | } 150 | } 151 | } 152 | ``` 153 | 154 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg 155 | [git-url]: https://gitter.im/stylelint/stylelint 156 | [npm-img]: https://img.shields.io/npm/v/stylelint-media-use-custom-media.svg 157 | [npm-url]: https://www.npmjs.com/package/stylelint-media-use-custom-media 158 | 159 | [stylelint]: https://github.com/stylelint/stylelint 160 | [stylelint configuration]: https://github.com/stylelint/stylelint/blob/master/docs/user-guide/configuration.md#readme 161 | [Media Use Custom Media]: https://github.com/csstools/stylelint-media-use-custom-media 162 | -------------------------------------------------------------------------------- /index.test.mjs: -------------------------------------------------------------------------------- 1 | import { testRule } from 'stylelint-test-rule-node'; 2 | import plugin from './src/index.mjs'; 3 | 4 | const rule = plugin.rule; 5 | const messages = plugin.rule.messages; 6 | 7 | let accept = [], reject = []; 8 | 9 | /* Test basic checks 10 | /* ========================================================================== */ 11 | 12 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'ignore', accept: [''] }); 13 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: null, accept: [''] }); 14 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'always', accept: [''] }); 15 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: true, accept: [''] }); 16 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'never', accept: [''] }); 17 | 18 | // /* Test "ignore" 19 | // /* ========================================================================== */ 20 | 21 | accept = [ 22 | { code: '@media (--sm) {}', description: 'valid custom media' }, 23 | { code: '@media (min-width: 40rem) {}', description: 'invalid custom media' } 24 | ]; 25 | 26 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'ignore', accept: accept }); 27 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: null, accept: accept }); 28 | 29 | // /* Test "always" 30 | // /* ========================================================================== */ 31 | 32 | accept = [ 33 | { code: '@media (--sm) {}', description: 'valid custom media' }, 34 | { code: '@media screen and (--sm) {}', description: 'valid custom media' }, 35 | { code: '@media not print and (--sm) {}', description: 'valid custom media' }, 36 | { code: '@media screen and (--sm), (--md) {}', description: 'valid custom media' }, 37 | { code: '@media not print and (--sm), (--md) {}', description: 'valid custom media' } 38 | ]; 39 | 40 | reject = [ 41 | { 42 | code: '@media (min-width: 40rem) {}', 43 | description: 'invalid custom media', 44 | message: messages.expected('(min-width: 40rem)'), 45 | }, 46 | { 47 | code: '@media screen and (min-width: 40rem) {}', 48 | description: 'invalid custom media', 49 | message: messages.expected('screen and (min-width: 40rem)'), 50 | }, 51 | { 52 | code: '@media not print and (min-width: 40rem) {}', 53 | description: 'invalid custom media', 54 | message: messages.expected('not print and (min-width: 40rem)'), 55 | }, 56 | { 57 | code: '@media screen and (min-width: 40rem), (--md) {}', 58 | description: 'invalid custom media', 59 | message: messages.expected('screen and (min-width: 40rem), (--md)'), 60 | }, 61 | { 62 | code: '@media not print and (min-width: 40rem), (--md) {}', 63 | description: 'invalid custom media', 64 | message: messages.expected('not print and (min-width: 40rem), (--md)'), 65 | }, 66 | { 67 | code: '@media (--md), screen and (min-width: 40rem) {}', 68 | description: 'invalid custom media', 69 | message: messages.expected('(--md), screen and (min-width: 40rem)'), 70 | }, 71 | { 72 | code: '@media (--md), not print and (min-width: 40rem) {}', 73 | description: 'invalid custom media', 74 | message: messages.expected('(--md), not print and (min-width: 40rem)'), 75 | } 76 | ]; 77 | 78 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'always', accept: accept, reject: reject }); 79 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: true, accept: accept, reject: reject }); 80 | 81 | // /* Test "never" 82 | // /* ========================================================================== */ 83 | 84 | accept = [ 85 | { code: '@media (min-width: 40rem) {}', description: 'valid custom media' } 86 | ]; 87 | 88 | reject = [ 89 | { code: '@media (--sm) {}', description: 'invalid custom media', message: messages.unexpected('(--sm)') } 90 | ]; 91 | 92 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'never', accept: accept, reject: reject }); 93 | 94 | // /* Test "known", [ "known", { importFrom } ] functionality 95 | // /* ========================================================================== */ 96 | 97 | accept = [ 98 | { code: '@custom-media --sm (min-width: 40rem); @media (--sm) {} @media (min-width: 40em) {}', description: 'known media' } 99 | ]; 100 | 101 | reject = [ 102 | { code: '@media (--md) {}', description: 'unknown media', message: messages.expected('(--md)') }, 103 | ]; 104 | 105 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'known', accept: accept, reject: reject }); 106 | 107 | accept = [ 108 | { code: '@media (--sm) {} @media (min-width: 40em) {}', description: 'known media' } 109 | ]; 110 | 111 | testRule({ 112 | plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: [ 113 | 'known', 114 | { importFrom: { customMedia: { '--sm': '(min-width: 40em)' } } } 115 | ], accept: accept, reject: reject }); 116 | 117 | // /* Test "always-known", [ "always-known", { importFrom } ] functionality 118 | // /* ========================================================================== */ 119 | 120 | accept = [ 121 | { code: '@custom-media --sm (min-width: 40rem); @media (--sm) {}', description: 'known custom media' } 122 | ]; 123 | 124 | reject = [ 125 | { code: '@media (--md) {}', description: 'unknown custom media', message: messages.expected('(--md)') }, 126 | { code: '@media (min-width: 40em) {}', description: 'not custom media', message: messages.expected('(min-width: 40em)') } 127 | ]; 128 | 129 | testRule({ plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: 'always-known', accept: accept, reject: reject }); 130 | 131 | accept = [ 132 | { code: '@media (--sm) {}', description: 'known custom media' } 133 | ]; 134 | 135 | testRule({ 136 | plugins: ['./src/index.mjs'], ruleName: rule.ruleName, config: [ 137 | 'always-known', 138 | { importFrom: { customMedia: { '--sm': '(min-width: 40em)' } } } 139 | ], accept: accept, reject: reject 140 | }); 141 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-media-use-custom-media", 3 | "version": "4.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "stylelint-media-use-custom-media", 9 | "version": "4.0.0", 10 | "license": "CC0-1.0", 11 | "devDependencies": { 12 | "stylelint": "^16.0.0", 13 | "stylelint-test-rule-node": "^0.2.0" 14 | }, 15 | "engines": { 16 | "node": ">=18.12.0" 17 | }, 18 | "peerDependencies": { 19 | "stylelint": ">=16" 20 | } 21 | }, 22 | "node_modules/@babel/code-frame": { 23 | "version": "7.26.2", 24 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", 25 | "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", 26 | "dev": true, 27 | "license": "MIT", 28 | "dependencies": { 29 | "@babel/helper-validator-identifier": "^7.25.9", 30 | "js-tokens": "^4.0.0", 31 | "picocolors": "^1.0.0" 32 | }, 33 | "engines": { 34 | "node": ">=6.9.0" 35 | } 36 | }, 37 | "node_modules/@babel/helper-validator-identifier": { 38 | "version": "7.25.9", 39 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", 40 | "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", 41 | "dev": true, 42 | "license": "MIT", 43 | "engines": { 44 | "node": ">=6.9.0" 45 | } 46 | }, 47 | "node_modules/@csstools/css-parser-algorithms": { 48 | "version": "3.0.4", 49 | "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", 50 | "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", 51 | "dev": true, 52 | "funding": [ 53 | { 54 | "type": "github", 55 | "url": "https://github.com/sponsors/csstools" 56 | }, 57 | { 58 | "type": "opencollective", 59 | "url": "https://opencollective.com/csstools" 60 | } 61 | ], 62 | "license": "MIT", 63 | "engines": { 64 | "node": ">=18" 65 | }, 66 | "peerDependencies": { 67 | "@csstools/css-tokenizer": "^3.0.3" 68 | } 69 | }, 70 | "node_modules/@csstools/css-tokenizer": { 71 | "version": "3.0.3", 72 | "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", 73 | "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", 74 | "dev": true, 75 | "funding": [ 76 | { 77 | "type": "github", 78 | "url": "https://github.com/sponsors/csstools" 79 | }, 80 | { 81 | "type": "opencollective", 82 | "url": "https://opencollective.com/csstools" 83 | } 84 | ], 85 | "license": "MIT", 86 | "engines": { 87 | "node": ">=18" 88 | } 89 | }, 90 | "node_modules/@csstools/media-query-list-parser": { 91 | "version": "4.0.2", 92 | "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", 93 | "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", 94 | "dev": true, 95 | "funding": [ 96 | { 97 | "type": "github", 98 | "url": "https://github.com/sponsors/csstools" 99 | }, 100 | { 101 | "type": "opencollective", 102 | "url": "https://opencollective.com/csstools" 103 | } 104 | ], 105 | "license": "MIT", 106 | "engines": { 107 | "node": ">=18" 108 | }, 109 | "peerDependencies": { 110 | "@csstools/css-parser-algorithms": "^3.0.4", 111 | "@csstools/css-tokenizer": "^3.0.3" 112 | } 113 | }, 114 | "node_modules/@csstools/selector-specificity": { 115 | "version": "5.0.0", 116 | "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", 117 | "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", 118 | "dev": true, 119 | "funding": [ 120 | { 121 | "type": "github", 122 | "url": "https://github.com/sponsors/csstools" 123 | }, 124 | { 125 | "type": "opencollective", 126 | "url": "https://opencollective.com/csstools" 127 | } 128 | ], 129 | "license": "MIT-0", 130 | "engines": { 131 | "node": ">=18" 132 | }, 133 | "peerDependencies": { 134 | "postcss-selector-parser": "^7.0.0" 135 | } 136 | }, 137 | "node_modules/@dual-bundle/import-meta-resolve": { 138 | "version": "4.1.0", 139 | "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", 140 | "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", 141 | "dev": true, 142 | "license": "MIT", 143 | "funding": { 144 | "type": "github", 145 | "url": "https://github.com/sponsors/wooorm" 146 | } 147 | }, 148 | "node_modules/@nodelib/fs.scandir": { 149 | "version": "2.1.5", 150 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 151 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 152 | "dev": true, 153 | "license": "MIT", 154 | "dependencies": { 155 | "@nodelib/fs.stat": "2.0.5", 156 | "run-parallel": "^1.1.9" 157 | }, 158 | "engines": { 159 | "node": ">= 8" 160 | } 161 | }, 162 | "node_modules/@nodelib/fs.stat": { 163 | "version": "2.0.5", 164 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 165 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 166 | "dev": true, 167 | "license": "MIT", 168 | "engines": { 169 | "node": ">= 8" 170 | } 171 | }, 172 | "node_modules/@nodelib/fs.walk": { 173 | "version": "1.2.8", 174 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 175 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 176 | "dev": true, 177 | "license": "MIT", 178 | "dependencies": { 179 | "@nodelib/fs.scandir": "2.1.5", 180 | "fastq": "^1.6.0" 181 | }, 182 | "engines": { 183 | "node": ">= 8" 184 | } 185 | }, 186 | "node_modules/ajv": { 187 | "version": "8.17.1", 188 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", 189 | "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", 190 | "dev": true, 191 | "license": "MIT", 192 | "dependencies": { 193 | "fast-deep-equal": "^3.1.3", 194 | "fast-uri": "^3.0.1", 195 | "json-schema-traverse": "^1.0.0", 196 | "require-from-string": "^2.0.2" 197 | }, 198 | "funding": { 199 | "type": "github", 200 | "url": "https://github.com/sponsors/epoberezkin" 201 | } 202 | }, 203 | "node_modules/ansi-regex": { 204 | "version": "5.0.1", 205 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 206 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 207 | "dev": true, 208 | "license": "MIT", 209 | "engines": { 210 | "node": ">=8" 211 | } 212 | }, 213 | "node_modules/ansi-styles": { 214 | "version": "4.3.0", 215 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 216 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 217 | "dev": true, 218 | "license": "MIT", 219 | "dependencies": { 220 | "color-convert": "^2.0.1" 221 | }, 222 | "engines": { 223 | "node": ">=8" 224 | }, 225 | "funding": { 226 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 227 | } 228 | }, 229 | "node_modules/argparse": { 230 | "version": "2.0.1", 231 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 232 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 233 | "dev": true, 234 | "license": "Python-2.0" 235 | }, 236 | "node_modules/array-union": { 237 | "version": "2.1.0", 238 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 239 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 240 | "dev": true, 241 | "license": "MIT", 242 | "engines": { 243 | "node": ">=8" 244 | } 245 | }, 246 | "node_modules/astral-regex": { 247 | "version": "2.0.0", 248 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 249 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 250 | "dev": true, 251 | "license": "MIT", 252 | "engines": { 253 | "node": ">=8" 254 | } 255 | }, 256 | "node_modules/balanced-match": { 257 | "version": "2.0.0", 258 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", 259 | "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", 260 | "dev": true, 261 | "license": "MIT" 262 | }, 263 | "node_modules/braces": { 264 | "version": "3.0.3", 265 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 266 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 267 | "dev": true, 268 | "license": "MIT", 269 | "dependencies": { 270 | "fill-range": "^7.1.1" 271 | }, 272 | "engines": { 273 | "node": ">=8" 274 | } 275 | }, 276 | "node_modules/callsites": { 277 | "version": "3.1.0", 278 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 279 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 280 | "dev": true, 281 | "license": "MIT", 282 | "engines": { 283 | "node": ">=6" 284 | } 285 | }, 286 | "node_modules/color-convert": { 287 | "version": "2.0.1", 288 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 289 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 290 | "dev": true, 291 | "license": "MIT", 292 | "dependencies": { 293 | "color-name": "~1.1.4" 294 | }, 295 | "engines": { 296 | "node": ">=7.0.0" 297 | } 298 | }, 299 | "node_modules/color-name": { 300 | "version": "1.1.4", 301 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 302 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 303 | "dev": true, 304 | "license": "MIT" 305 | }, 306 | "node_modules/colord": { 307 | "version": "2.9.3", 308 | "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", 309 | "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", 310 | "dev": true, 311 | "license": "MIT" 312 | }, 313 | "node_modules/cosmiconfig": { 314 | "version": "9.0.0", 315 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", 316 | "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", 317 | "dev": true, 318 | "license": "MIT", 319 | "dependencies": { 320 | "env-paths": "^2.2.1", 321 | "import-fresh": "^3.3.0", 322 | "js-yaml": "^4.1.0", 323 | "parse-json": "^5.2.0" 324 | }, 325 | "engines": { 326 | "node": ">=14" 327 | }, 328 | "funding": { 329 | "url": "https://github.com/sponsors/d-fischer" 330 | }, 331 | "peerDependencies": { 332 | "typescript": ">=4.9.5" 333 | }, 334 | "peerDependenciesMeta": { 335 | "typescript": { 336 | "optional": true 337 | } 338 | } 339 | }, 340 | "node_modules/css-functions-list": { 341 | "version": "3.2.3", 342 | "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", 343 | "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", 344 | "dev": true, 345 | "license": "MIT", 346 | "engines": { 347 | "node": ">=12 || >=16" 348 | } 349 | }, 350 | "node_modules/css-tree": { 351 | "version": "3.1.0", 352 | "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", 353 | "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", 354 | "dev": true, 355 | "license": "MIT", 356 | "dependencies": { 357 | "mdn-data": "2.12.2", 358 | "source-map-js": "^1.0.1" 359 | }, 360 | "engines": { 361 | "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" 362 | } 363 | }, 364 | "node_modules/cssesc": { 365 | "version": "3.0.0", 366 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 367 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 368 | "dev": true, 369 | "license": "MIT", 370 | "bin": { 371 | "cssesc": "bin/cssesc" 372 | }, 373 | "engines": { 374 | "node": ">=4" 375 | } 376 | }, 377 | "node_modules/debug": { 378 | "version": "4.4.0", 379 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 380 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 381 | "dev": true, 382 | "license": "MIT", 383 | "dependencies": { 384 | "ms": "^2.1.3" 385 | }, 386 | "engines": { 387 | "node": ">=6.0" 388 | }, 389 | "peerDependenciesMeta": { 390 | "supports-color": { 391 | "optional": true 392 | } 393 | } 394 | }, 395 | "node_modules/dir-glob": { 396 | "version": "3.0.1", 397 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 398 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 399 | "dev": true, 400 | "license": "MIT", 401 | "dependencies": { 402 | "path-type": "^4.0.0" 403 | }, 404 | "engines": { 405 | "node": ">=8" 406 | } 407 | }, 408 | "node_modules/emoji-regex": { 409 | "version": "8.0.0", 410 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 411 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 412 | "dev": true, 413 | "license": "MIT" 414 | }, 415 | "node_modules/env-paths": { 416 | "version": "2.2.1", 417 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", 418 | "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", 419 | "dev": true, 420 | "license": "MIT", 421 | "engines": { 422 | "node": ">=6" 423 | } 424 | }, 425 | "node_modules/error-ex": { 426 | "version": "1.3.2", 427 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 428 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 429 | "dev": true, 430 | "license": "MIT", 431 | "dependencies": { 432 | "is-arrayish": "^0.2.1" 433 | } 434 | }, 435 | "node_modules/fast-deep-equal": { 436 | "version": "3.1.3", 437 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 438 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 439 | "dev": true, 440 | "license": "MIT" 441 | }, 442 | "node_modules/fast-glob": { 443 | "version": "3.3.2", 444 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 445 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 446 | "dev": true, 447 | "license": "MIT", 448 | "dependencies": { 449 | "@nodelib/fs.stat": "^2.0.2", 450 | "@nodelib/fs.walk": "^1.2.3", 451 | "glob-parent": "^5.1.2", 452 | "merge2": "^1.3.0", 453 | "micromatch": "^4.0.4" 454 | }, 455 | "engines": { 456 | "node": ">=8.6.0" 457 | } 458 | }, 459 | "node_modules/fast-uri": { 460 | "version": "3.0.3", 461 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", 462 | "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", 463 | "dev": true, 464 | "license": "BSD-3-Clause" 465 | }, 466 | "node_modules/fastest-levenshtein": { 467 | "version": "1.0.16", 468 | "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", 469 | "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", 470 | "dev": true, 471 | "license": "MIT", 472 | "engines": { 473 | "node": ">= 4.9.1" 474 | } 475 | }, 476 | "node_modules/fastq": { 477 | "version": "1.18.0", 478 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", 479 | "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", 480 | "dev": true, 481 | "license": "ISC", 482 | "dependencies": { 483 | "reusify": "^1.0.4" 484 | } 485 | }, 486 | "node_modules/file-entry-cache": { 487 | "version": "9.1.0", 488 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", 489 | "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", 490 | "dev": true, 491 | "license": "MIT", 492 | "dependencies": { 493 | "flat-cache": "^5.0.0" 494 | }, 495 | "engines": { 496 | "node": ">=18" 497 | } 498 | }, 499 | "node_modules/fill-range": { 500 | "version": "7.1.1", 501 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 502 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 503 | "dev": true, 504 | "license": "MIT", 505 | "dependencies": { 506 | "to-regex-range": "^5.0.1" 507 | }, 508 | "engines": { 509 | "node": ">=8" 510 | } 511 | }, 512 | "node_modules/flat-cache": { 513 | "version": "5.0.0", 514 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", 515 | "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", 516 | "dev": true, 517 | "license": "MIT", 518 | "dependencies": { 519 | "flatted": "^3.3.1", 520 | "keyv": "^4.5.4" 521 | }, 522 | "engines": { 523 | "node": ">=18" 524 | } 525 | }, 526 | "node_modules/flatted": { 527 | "version": "3.3.2", 528 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", 529 | "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", 530 | "dev": true, 531 | "license": "ISC" 532 | }, 533 | "node_modules/glob-parent": { 534 | "version": "5.1.2", 535 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 536 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 537 | "dev": true, 538 | "license": "ISC", 539 | "dependencies": { 540 | "is-glob": "^4.0.1" 541 | }, 542 | "engines": { 543 | "node": ">= 6" 544 | } 545 | }, 546 | "node_modules/global-modules": { 547 | "version": "2.0.0", 548 | "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", 549 | "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", 550 | "dev": true, 551 | "license": "MIT", 552 | "dependencies": { 553 | "global-prefix": "^3.0.0" 554 | }, 555 | "engines": { 556 | "node": ">=6" 557 | } 558 | }, 559 | "node_modules/global-prefix": { 560 | "version": "3.0.0", 561 | "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", 562 | "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", 563 | "dev": true, 564 | "license": "MIT", 565 | "dependencies": { 566 | "ini": "^1.3.5", 567 | "kind-of": "^6.0.2", 568 | "which": "^1.3.1" 569 | }, 570 | "engines": { 571 | "node": ">=6" 572 | } 573 | }, 574 | "node_modules/globby": { 575 | "version": "11.1.0", 576 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 577 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 578 | "dev": true, 579 | "license": "MIT", 580 | "dependencies": { 581 | "array-union": "^2.1.0", 582 | "dir-glob": "^3.0.1", 583 | "fast-glob": "^3.2.9", 584 | "ignore": "^5.2.0", 585 | "merge2": "^1.4.1", 586 | "slash": "^3.0.0" 587 | }, 588 | "engines": { 589 | "node": ">=10" 590 | }, 591 | "funding": { 592 | "url": "https://github.com/sponsors/sindresorhus" 593 | } 594 | }, 595 | "node_modules/globby/node_modules/ignore": { 596 | "version": "5.3.2", 597 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 598 | "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 599 | "dev": true, 600 | "license": "MIT", 601 | "engines": { 602 | "node": ">= 4" 603 | } 604 | }, 605 | "node_modules/globjoin": { 606 | "version": "0.1.4", 607 | "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", 608 | "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", 609 | "dev": true, 610 | "license": "MIT" 611 | }, 612 | "node_modules/has-flag": { 613 | "version": "4.0.0", 614 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 615 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 616 | "dev": true, 617 | "license": "MIT", 618 | "engines": { 619 | "node": ">=8" 620 | } 621 | }, 622 | "node_modules/html-tags": { 623 | "version": "3.3.1", 624 | "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", 625 | "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", 626 | "dev": true, 627 | "license": "MIT", 628 | "engines": { 629 | "node": ">=8" 630 | }, 631 | "funding": { 632 | "url": "https://github.com/sponsors/sindresorhus" 633 | } 634 | }, 635 | "node_modules/ignore": { 636 | "version": "6.0.2", 637 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", 638 | "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", 639 | "dev": true, 640 | "license": "MIT", 641 | "engines": { 642 | "node": ">= 4" 643 | } 644 | }, 645 | "node_modules/import-fresh": { 646 | "version": "3.3.0", 647 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 648 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 649 | "dev": true, 650 | "license": "MIT", 651 | "dependencies": { 652 | "parent-module": "^1.0.0", 653 | "resolve-from": "^4.0.0" 654 | }, 655 | "engines": { 656 | "node": ">=6" 657 | }, 658 | "funding": { 659 | "url": "https://github.com/sponsors/sindresorhus" 660 | } 661 | }, 662 | "node_modules/import-fresh/node_modules/resolve-from": { 663 | "version": "4.0.0", 664 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 665 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 666 | "dev": true, 667 | "license": "MIT", 668 | "engines": { 669 | "node": ">=4" 670 | } 671 | }, 672 | "node_modules/imurmurhash": { 673 | "version": "0.1.4", 674 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 675 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 676 | "dev": true, 677 | "license": "MIT", 678 | "engines": { 679 | "node": ">=0.8.19" 680 | } 681 | }, 682 | "node_modules/ini": { 683 | "version": "1.3.8", 684 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 685 | "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 686 | "dev": true, 687 | "license": "ISC" 688 | }, 689 | "node_modules/is-arrayish": { 690 | "version": "0.2.1", 691 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 692 | "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", 693 | "dev": true, 694 | "license": "MIT" 695 | }, 696 | "node_modules/is-extglob": { 697 | "version": "2.1.1", 698 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 699 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 700 | "dev": true, 701 | "license": "MIT", 702 | "engines": { 703 | "node": ">=0.10.0" 704 | } 705 | }, 706 | "node_modules/is-fullwidth-code-point": { 707 | "version": "3.0.0", 708 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 709 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 710 | "dev": true, 711 | "license": "MIT", 712 | "engines": { 713 | "node": ">=8" 714 | } 715 | }, 716 | "node_modules/is-glob": { 717 | "version": "4.0.3", 718 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 719 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 720 | "dev": true, 721 | "license": "MIT", 722 | "dependencies": { 723 | "is-extglob": "^2.1.1" 724 | }, 725 | "engines": { 726 | "node": ">=0.10.0" 727 | } 728 | }, 729 | "node_modules/is-number": { 730 | "version": "7.0.0", 731 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 732 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 733 | "dev": true, 734 | "license": "MIT", 735 | "engines": { 736 | "node": ">=0.12.0" 737 | } 738 | }, 739 | "node_modules/is-plain-object": { 740 | "version": "5.0.0", 741 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 742 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", 743 | "dev": true, 744 | "license": "MIT", 745 | "engines": { 746 | "node": ">=0.10.0" 747 | } 748 | }, 749 | "node_modules/isexe": { 750 | "version": "2.0.0", 751 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 752 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 753 | "dev": true, 754 | "license": "ISC" 755 | }, 756 | "node_modules/js-tokens": { 757 | "version": "4.0.0", 758 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 759 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 760 | "dev": true, 761 | "license": "MIT" 762 | }, 763 | "node_modules/js-yaml": { 764 | "version": "4.1.0", 765 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 766 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 767 | "dev": true, 768 | "license": "MIT", 769 | "dependencies": { 770 | "argparse": "^2.0.1" 771 | }, 772 | "bin": { 773 | "js-yaml": "bin/js-yaml.js" 774 | } 775 | }, 776 | "node_modules/json-buffer": { 777 | "version": "3.0.1", 778 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 779 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 780 | "dev": true, 781 | "license": "MIT" 782 | }, 783 | "node_modules/json-parse-even-better-errors": { 784 | "version": "2.3.1", 785 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 786 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", 787 | "dev": true, 788 | "license": "MIT" 789 | }, 790 | "node_modules/json-schema-traverse": { 791 | "version": "1.0.0", 792 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 793 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 794 | "dev": true, 795 | "license": "MIT" 796 | }, 797 | "node_modules/keyv": { 798 | "version": "4.5.4", 799 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 800 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 801 | "dev": true, 802 | "license": "MIT", 803 | "dependencies": { 804 | "json-buffer": "3.0.1" 805 | } 806 | }, 807 | "node_modules/kind-of": { 808 | "version": "6.0.3", 809 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 810 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 811 | "dev": true, 812 | "license": "MIT", 813 | "engines": { 814 | "node": ">=0.10.0" 815 | } 816 | }, 817 | "node_modules/known-css-properties": { 818 | "version": "0.35.0", 819 | "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", 820 | "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", 821 | "dev": true, 822 | "license": "MIT" 823 | }, 824 | "node_modules/lines-and-columns": { 825 | "version": "1.2.4", 826 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", 827 | "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", 828 | "dev": true, 829 | "license": "MIT" 830 | }, 831 | "node_modules/lodash.truncate": { 832 | "version": "4.4.2", 833 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 834 | "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", 835 | "dev": true, 836 | "license": "MIT" 837 | }, 838 | "node_modules/mathml-tag-names": { 839 | "version": "2.1.3", 840 | "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", 841 | "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", 842 | "dev": true, 843 | "license": "MIT", 844 | "funding": { 845 | "type": "github", 846 | "url": "https://github.com/sponsors/wooorm" 847 | } 848 | }, 849 | "node_modules/mdn-data": { 850 | "version": "2.12.2", 851 | "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", 852 | "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", 853 | "dev": true, 854 | "license": "CC0-1.0" 855 | }, 856 | "node_modules/meow": { 857 | "version": "13.2.0", 858 | "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", 859 | "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", 860 | "dev": true, 861 | "license": "MIT", 862 | "engines": { 863 | "node": ">=18" 864 | }, 865 | "funding": { 866 | "url": "https://github.com/sponsors/sindresorhus" 867 | } 868 | }, 869 | "node_modules/merge2": { 870 | "version": "1.4.1", 871 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 872 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 873 | "dev": true, 874 | "license": "MIT", 875 | "engines": { 876 | "node": ">= 8" 877 | } 878 | }, 879 | "node_modules/micromatch": { 880 | "version": "4.0.8", 881 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", 882 | "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 883 | "dev": true, 884 | "license": "MIT", 885 | "dependencies": { 886 | "braces": "^3.0.3", 887 | "picomatch": "^2.3.1" 888 | }, 889 | "engines": { 890 | "node": ">=8.6" 891 | } 892 | }, 893 | "node_modules/ms": { 894 | "version": "2.1.3", 895 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 896 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 897 | "dev": true, 898 | "license": "MIT" 899 | }, 900 | "node_modules/nanoid": { 901 | "version": "3.3.8", 902 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", 903 | "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", 904 | "dev": true, 905 | "funding": [ 906 | { 907 | "type": "github", 908 | "url": "https://github.com/sponsors/ai" 909 | } 910 | ], 911 | "license": "MIT", 912 | "bin": { 913 | "nanoid": "bin/nanoid.cjs" 914 | }, 915 | "engines": { 916 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 917 | } 918 | }, 919 | "node_modules/normalize-path": { 920 | "version": "3.0.0", 921 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 922 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 923 | "dev": true, 924 | "license": "MIT", 925 | "engines": { 926 | "node": ">=0.10.0" 927 | } 928 | }, 929 | "node_modules/parent-module": { 930 | "version": "1.0.1", 931 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 932 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 933 | "dev": true, 934 | "license": "MIT", 935 | "dependencies": { 936 | "callsites": "^3.0.0" 937 | }, 938 | "engines": { 939 | "node": ">=6" 940 | } 941 | }, 942 | "node_modules/parse-json": { 943 | "version": "5.2.0", 944 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", 945 | "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", 946 | "dev": true, 947 | "license": "MIT", 948 | "dependencies": { 949 | "@babel/code-frame": "^7.0.0", 950 | "error-ex": "^1.3.1", 951 | "json-parse-even-better-errors": "^2.3.0", 952 | "lines-and-columns": "^1.1.6" 953 | }, 954 | "engines": { 955 | "node": ">=8" 956 | }, 957 | "funding": { 958 | "url": "https://github.com/sponsors/sindresorhus" 959 | } 960 | }, 961 | "node_modules/path-type": { 962 | "version": "4.0.0", 963 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 964 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 965 | "dev": true, 966 | "license": "MIT", 967 | "engines": { 968 | "node": ">=8" 969 | } 970 | }, 971 | "node_modules/picocolors": { 972 | "version": "1.1.1", 973 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 974 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 975 | "dev": true, 976 | "license": "ISC" 977 | }, 978 | "node_modules/picomatch": { 979 | "version": "2.3.1", 980 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 981 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 982 | "dev": true, 983 | "license": "MIT", 984 | "engines": { 985 | "node": ">=8.6" 986 | }, 987 | "funding": { 988 | "url": "https://github.com/sponsors/jonschlinkert" 989 | } 990 | }, 991 | "node_modules/postcss": { 992 | "version": "8.4.49", 993 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", 994 | "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", 995 | "dev": true, 996 | "funding": [ 997 | { 998 | "type": "opencollective", 999 | "url": "https://opencollective.com/postcss/" 1000 | }, 1001 | { 1002 | "type": "tidelift", 1003 | "url": "https://tidelift.com/funding/github/npm/postcss" 1004 | }, 1005 | { 1006 | "type": "github", 1007 | "url": "https://github.com/sponsors/ai" 1008 | } 1009 | ], 1010 | "license": "MIT", 1011 | "dependencies": { 1012 | "nanoid": "^3.3.7", 1013 | "picocolors": "^1.1.1", 1014 | "source-map-js": "^1.2.1" 1015 | }, 1016 | "engines": { 1017 | "node": "^10 || ^12 || >=14" 1018 | } 1019 | }, 1020 | "node_modules/postcss-resolve-nested-selector": { 1021 | "version": "0.1.6", 1022 | "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", 1023 | "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", 1024 | "dev": true, 1025 | "license": "MIT" 1026 | }, 1027 | "node_modules/postcss-safe-parser": { 1028 | "version": "7.0.1", 1029 | "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", 1030 | "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", 1031 | "dev": true, 1032 | "funding": [ 1033 | { 1034 | "type": "opencollective", 1035 | "url": "https://opencollective.com/postcss/" 1036 | }, 1037 | { 1038 | "type": "tidelift", 1039 | "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" 1040 | }, 1041 | { 1042 | "type": "github", 1043 | "url": "https://github.com/sponsors/ai" 1044 | } 1045 | ], 1046 | "license": "MIT", 1047 | "engines": { 1048 | "node": ">=18.0" 1049 | }, 1050 | "peerDependencies": { 1051 | "postcss": "^8.4.31" 1052 | } 1053 | }, 1054 | "node_modules/postcss-selector-parser": { 1055 | "version": "7.0.0", 1056 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", 1057 | "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", 1058 | "dev": true, 1059 | "license": "MIT", 1060 | "dependencies": { 1061 | "cssesc": "^3.0.0", 1062 | "util-deprecate": "^1.0.2" 1063 | }, 1064 | "engines": { 1065 | "node": ">=4" 1066 | } 1067 | }, 1068 | "node_modules/postcss-value-parser": { 1069 | "version": "4.2.0", 1070 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 1071 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 1072 | "dev": true, 1073 | "license": "MIT" 1074 | }, 1075 | "node_modules/queue-microtask": { 1076 | "version": "1.2.3", 1077 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1078 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1079 | "dev": true, 1080 | "funding": [ 1081 | { 1082 | "type": "github", 1083 | "url": "https://github.com/sponsors/feross" 1084 | }, 1085 | { 1086 | "type": "patreon", 1087 | "url": "https://www.patreon.com/feross" 1088 | }, 1089 | { 1090 | "type": "consulting", 1091 | "url": "https://feross.org/support" 1092 | } 1093 | ], 1094 | "license": "MIT" 1095 | }, 1096 | "node_modules/require-from-string": { 1097 | "version": "2.0.2", 1098 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1099 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1100 | "dev": true, 1101 | "license": "MIT", 1102 | "engines": { 1103 | "node": ">=0.10.0" 1104 | } 1105 | }, 1106 | "node_modules/resolve-from": { 1107 | "version": "5.0.0", 1108 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 1109 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 1110 | "dev": true, 1111 | "license": "MIT", 1112 | "engines": { 1113 | "node": ">=8" 1114 | } 1115 | }, 1116 | "node_modules/reusify": { 1117 | "version": "1.0.4", 1118 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1119 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1120 | "dev": true, 1121 | "license": "MIT", 1122 | "engines": { 1123 | "iojs": ">=1.0.0", 1124 | "node": ">=0.10.0" 1125 | } 1126 | }, 1127 | "node_modules/run-parallel": { 1128 | "version": "1.2.0", 1129 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1130 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1131 | "dev": true, 1132 | "funding": [ 1133 | { 1134 | "type": "github", 1135 | "url": "https://github.com/sponsors/feross" 1136 | }, 1137 | { 1138 | "type": "patreon", 1139 | "url": "https://www.patreon.com/feross" 1140 | }, 1141 | { 1142 | "type": "consulting", 1143 | "url": "https://feross.org/support" 1144 | } 1145 | ], 1146 | "license": "MIT", 1147 | "dependencies": { 1148 | "queue-microtask": "^1.2.2" 1149 | } 1150 | }, 1151 | "node_modules/signal-exit": { 1152 | "version": "4.1.0", 1153 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 1154 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 1155 | "dev": true, 1156 | "license": "ISC", 1157 | "engines": { 1158 | "node": ">=14" 1159 | }, 1160 | "funding": { 1161 | "url": "https://github.com/sponsors/isaacs" 1162 | } 1163 | }, 1164 | "node_modules/slash": { 1165 | "version": "3.0.0", 1166 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1167 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1168 | "dev": true, 1169 | "license": "MIT", 1170 | "engines": { 1171 | "node": ">=8" 1172 | } 1173 | }, 1174 | "node_modules/slice-ansi": { 1175 | "version": "4.0.0", 1176 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 1177 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 1178 | "dev": true, 1179 | "license": "MIT", 1180 | "dependencies": { 1181 | "ansi-styles": "^4.0.0", 1182 | "astral-regex": "^2.0.0", 1183 | "is-fullwidth-code-point": "^3.0.0" 1184 | }, 1185 | "engines": { 1186 | "node": ">=10" 1187 | }, 1188 | "funding": { 1189 | "url": "https://github.com/chalk/slice-ansi?sponsor=1" 1190 | } 1191 | }, 1192 | "node_modules/source-map-js": { 1193 | "version": "1.2.1", 1194 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 1195 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 1196 | "dev": true, 1197 | "license": "BSD-3-Clause", 1198 | "engines": { 1199 | "node": ">=0.10.0" 1200 | } 1201 | }, 1202 | "node_modules/string-width": { 1203 | "version": "4.2.3", 1204 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1205 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1206 | "dev": true, 1207 | "license": "MIT", 1208 | "dependencies": { 1209 | "emoji-regex": "^8.0.0", 1210 | "is-fullwidth-code-point": "^3.0.0", 1211 | "strip-ansi": "^6.0.1" 1212 | }, 1213 | "engines": { 1214 | "node": ">=8" 1215 | } 1216 | }, 1217 | "node_modules/strip-ansi": { 1218 | "version": "6.0.1", 1219 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1220 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1221 | "dev": true, 1222 | "license": "MIT", 1223 | "dependencies": { 1224 | "ansi-regex": "^5.0.1" 1225 | }, 1226 | "engines": { 1227 | "node": ">=8" 1228 | } 1229 | }, 1230 | "node_modules/stylelint": { 1231 | "version": "16.12.0", 1232 | "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz", 1233 | "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==", 1234 | "dev": true, 1235 | "funding": [ 1236 | { 1237 | "type": "opencollective", 1238 | "url": "https://opencollective.com/stylelint" 1239 | }, 1240 | { 1241 | "type": "github", 1242 | "url": "https://github.com/sponsors/stylelint" 1243 | } 1244 | ], 1245 | "license": "MIT", 1246 | "dependencies": { 1247 | "@csstools/css-parser-algorithms": "^3.0.4", 1248 | "@csstools/css-tokenizer": "^3.0.3", 1249 | "@csstools/media-query-list-parser": "^4.0.2", 1250 | "@csstools/selector-specificity": "^5.0.0", 1251 | "@dual-bundle/import-meta-resolve": "^4.1.0", 1252 | "balanced-match": "^2.0.0", 1253 | "colord": "^2.9.3", 1254 | "cosmiconfig": "^9.0.0", 1255 | "css-functions-list": "^3.2.3", 1256 | "css-tree": "^3.0.1", 1257 | "debug": "^4.3.7", 1258 | "fast-glob": "^3.3.2", 1259 | "fastest-levenshtein": "^1.0.16", 1260 | "file-entry-cache": "^9.1.0", 1261 | "global-modules": "^2.0.0", 1262 | "globby": "^11.1.0", 1263 | "globjoin": "^0.1.4", 1264 | "html-tags": "^3.3.1", 1265 | "ignore": "^6.0.2", 1266 | "imurmurhash": "^0.1.4", 1267 | "is-plain-object": "^5.0.0", 1268 | "known-css-properties": "^0.35.0", 1269 | "mathml-tag-names": "^2.1.3", 1270 | "meow": "^13.2.0", 1271 | "micromatch": "^4.0.8", 1272 | "normalize-path": "^3.0.0", 1273 | "picocolors": "^1.1.1", 1274 | "postcss": "^8.4.49", 1275 | "postcss-resolve-nested-selector": "^0.1.6", 1276 | "postcss-safe-parser": "^7.0.1", 1277 | "postcss-selector-parser": "^7.0.0", 1278 | "postcss-value-parser": "^4.2.0", 1279 | "resolve-from": "^5.0.0", 1280 | "string-width": "^4.2.3", 1281 | "supports-hyperlinks": "^3.1.0", 1282 | "svg-tags": "^1.0.0", 1283 | "table": "^6.9.0", 1284 | "write-file-atomic": "^5.0.1" 1285 | }, 1286 | "bin": { 1287 | "stylelint": "bin/stylelint.mjs" 1288 | }, 1289 | "engines": { 1290 | "node": ">=18.12.0" 1291 | } 1292 | }, 1293 | "node_modules/stylelint-test-rule-node": { 1294 | "version": "0.2.2", 1295 | "resolved": "https://registry.npmjs.org/stylelint-test-rule-node/-/stylelint-test-rule-node-0.2.2.tgz", 1296 | "integrity": "sha512-muMgHkW0rpGwIHuL+dgxiWgojcPNByNiBHm5+NqZT/vUAWo0QZYY1jCDVbw2G404LFpzHDd0BZ4JEMKOIw/Dlw==", 1297 | "dev": true, 1298 | "funding": [ 1299 | { 1300 | "type": "opencollective", 1301 | "url": "https://opencollective.com/stylelint" 1302 | }, 1303 | { 1304 | "type": "github", 1305 | "url": "https://github.com/sponsors/stylelint" 1306 | } 1307 | ], 1308 | "license": "MIT", 1309 | "engines": { 1310 | "node": ">=18.12.0" 1311 | }, 1312 | "peerDependencies": { 1313 | "stylelint": "^16.0.1" 1314 | } 1315 | }, 1316 | "node_modules/supports-color": { 1317 | "version": "7.2.0", 1318 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1319 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1320 | "dev": true, 1321 | "license": "MIT", 1322 | "dependencies": { 1323 | "has-flag": "^4.0.0" 1324 | }, 1325 | "engines": { 1326 | "node": ">=8" 1327 | } 1328 | }, 1329 | "node_modules/supports-hyperlinks": { 1330 | "version": "3.1.0", 1331 | "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", 1332 | "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", 1333 | "dev": true, 1334 | "license": "MIT", 1335 | "dependencies": { 1336 | "has-flag": "^4.0.0", 1337 | "supports-color": "^7.0.0" 1338 | }, 1339 | "engines": { 1340 | "node": ">=14.18" 1341 | }, 1342 | "funding": { 1343 | "url": "https://github.com/sponsors/sindresorhus" 1344 | } 1345 | }, 1346 | "node_modules/svg-tags": { 1347 | "version": "1.0.0", 1348 | "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", 1349 | "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", 1350 | "dev": true 1351 | }, 1352 | "node_modules/table": { 1353 | "version": "6.9.0", 1354 | "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", 1355 | "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", 1356 | "dev": true, 1357 | "license": "BSD-3-Clause", 1358 | "dependencies": { 1359 | "ajv": "^8.0.1", 1360 | "lodash.truncate": "^4.4.2", 1361 | "slice-ansi": "^4.0.0", 1362 | "string-width": "^4.2.3", 1363 | "strip-ansi": "^6.0.1" 1364 | }, 1365 | "engines": { 1366 | "node": ">=10.0.0" 1367 | } 1368 | }, 1369 | "node_modules/to-regex-range": { 1370 | "version": "5.0.1", 1371 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1372 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1373 | "dev": true, 1374 | "license": "MIT", 1375 | "dependencies": { 1376 | "is-number": "^7.0.0" 1377 | }, 1378 | "engines": { 1379 | "node": ">=8.0" 1380 | } 1381 | }, 1382 | "node_modules/util-deprecate": { 1383 | "version": "1.0.2", 1384 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1385 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1386 | "dev": true, 1387 | "license": "MIT" 1388 | }, 1389 | "node_modules/which": { 1390 | "version": "1.3.1", 1391 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1392 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1393 | "dev": true, 1394 | "license": "ISC", 1395 | "dependencies": { 1396 | "isexe": "^2.0.0" 1397 | }, 1398 | "bin": { 1399 | "which": "bin/which" 1400 | } 1401 | }, 1402 | "node_modules/write-file-atomic": { 1403 | "version": "5.0.1", 1404 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", 1405 | "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", 1406 | "dev": true, 1407 | "license": "ISC", 1408 | "dependencies": { 1409 | "imurmurhash": "^0.1.4", 1410 | "signal-exit": "^4.0.1" 1411 | }, 1412 | "engines": { 1413 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0" 1414 | } 1415 | } 1416 | } 1417 | } 1418 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-media-use-custom-media", 3 | "version": "4.0.0", 4 | "description": "Enforce usage of custom media queries in CSS", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "csstools/stylelint-media-use-custom-media", 8 | "homepage": "https://github.com/csstools/stylelint-media-use-custom-media#readme", 9 | "bugs": "https://github.com/csstools/stylelint-media-use-custom-media/issues", 10 | "type": "module", 11 | "main": "src/index.mjs", 12 | "files": [ 13 | "src" 14 | ], 15 | "scripts": { 16 | "test": "node --test" 17 | }, 18 | "engines": { 19 | "node": ">=18.12.0" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "stylelint": "^16.0.0", 24 | "stylelint-test-rule-node": "^0.2.0" 25 | }, 26 | "peerDependencies": { 27 | "stylelint": ">=16" 28 | }, 29 | "keywords": [ 30 | "stylelint", 31 | "stylelint-plugin", 32 | "css", 33 | "use", 34 | "custom", 35 | "media", 36 | "custom-media", 37 | "queries", 38 | "query", 39 | "variables", 40 | "variable", 41 | "vars", 42 | "var" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/index.mjs: -------------------------------------------------------------------------------- 1 | import stylelint from "stylelint"; 2 | import parseMedia from './lib/parse-media.mjs'; 3 | import getCustomMediaFromImports from './lib/get-custom-media-from-imports.mjs'; 4 | import getCustomMediaFromRoot from './lib/get-custom-media-from-root.mjs'; 5 | 6 | const ruleName = 'csstools/media-use-custom-media'; 7 | 8 | const messages = stylelint.utils.ruleMessages(ruleName, { 9 | expected(expression) { 10 | return `Expected a custom media query instead of "${expression}".`; 11 | }, 12 | unexpected(expression) { 13 | return `Expected no custom media query instead of "${expression}".`; 14 | } 15 | }); 16 | 17 | const ruleFunction = (method, opts) => { 18 | // sources to import custom selectors from 19 | const importFrom = [].concat(Object(opts).importFrom || []); 20 | 21 | // conditionally promise any custom selectors are imported 22 | const customMediaPromise = isMethodAlwaysKnown(method) || isMethodKnown(method) 23 | ? getCustomMediaFromImports(importFrom) 24 | : {}; 25 | 26 | return async (root, result) => { 27 | // valid methods are: "always" || "always-known" || "never" || "known" || true || null 28 | const validOptions = stylelint.utils.validateOptions(result, ruleName, { 29 | actual: method, 30 | possible() { 31 | return isMethodAlways(method) || isMethodAlwaysKnown(method) || isMethodIndifferent(method) || isMethodKnown(method) || isMethodNever(method); 32 | } 33 | }); 34 | 35 | // conditionally enforce the use of custom media 36 | if (validOptions && !isMethodIndifferent(method)) { 37 | // all custom properties from the file and imports 38 | const customMedia = isMethodAlwaysKnown(method) || isMethodKnown(method) 39 | ? Object.assign(await customMediaPromise, getCustomMediaFromRoot(root)) 40 | : {}; 41 | 42 | // check every @media at-rule 43 | root.walkAtRules(mediaAtRuleNameRegExp, atrule => { 44 | const mediaAST = parseMedia(atrule.params); 45 | let word = `@${atrule.name}`; 46 | 47 | // check whether media queries are using custom media references 48 | const isCorrectlyUsingMedia = mediaAST.nodes.every( 49 | node => node.nodes.every( 50 | child => { 51 | // whether the expression is like @media (--foo) 52 | const isCustomExpression = checkCustomExpression(child); 53 | 54 | const returnValue = isCustomExpression 55 | ? isMethodKnown(method) || isMethodAlwaysKnown(method) 56 | // @media (--foo) && ("always-known" || "known") && @custom-media --foo bar; 57 | ? child.value.slice(1, -1) in customMedia 58 | // @media (--foo) && "always" 59 | : isMethodAlways(method) 60 | // !@media (--foo) && ("known" || "never") 61 | : isMethodKnown(method) || isMethodNever(method); 62 | 63 | if (!returnValue) { 64 | word = String(child); 65 | } 66 | 67 | return returnValue; 68 | } 69 | ) 70 | ); 71 | 72 | // conditionally report media queries not using custom media references 73 | if (!isCorrectlyUsingMedia) { 74 | stylelint.utils.report({ 75 | message: isMethodNever(method) 76 | ? messages.unexpected(atrule.params) 77 | : messages.expected(atrule.params), 78 | node: atrule, 79 | result, 80 | ruleName, 81 | word 82 | }); 83 | } 84 | }); 85 | } 86 | }; 87 | }; 88 | 89 | ruleFunction.ruleName = ruleName; 90 | ruleFunction.messages = messages; 91 | 92 | export default stylelint.createPlugin(ruleName, ruleFunction); 93 | 94 | const mediaAtRuleNameRegExp = /^media$/i; 95 | const customMediaExpressionRegExp = /\(--[\w-]+\)/i; 96 | 97 | const checkCustomExpression = node => node.nodeType === 'expression' && customMediaExpressionRegExp.test(node.value) 98 | 99 | const isMethodIndifferent = method => method === 'ignore' || method === null; 100 | const isMethodAlways = method => method === 'always' || method === true; 101 | const isMethodAlwaysKnown = method => method === 'always-known'; 102 | const isMethodKnown = method => method === 'known'; 103 | const isMethodNever = method => method === 'never'; 104 | -------------------------------------------------------------------------------- /src/lib/get-custom-media-from-imports.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import postcss from 'postcss'; 4 | import getCustomMediaFromRoot from './get-custom-media-from-root.mjs'; 5 | 6 | /* Get Custom Media from CSS File 7 | /* ========================================================================== */ 8 | 9 | async function getCustomMediaFromCSSFile(from) { 10 | const css = await readFile(from); 11 | const root = postcss.parse(css, { from }); 12 | 13 | return getCustomMediaFromRoot(root); 14 | } 15 | 16 | /* Get Custom Media from Object 17 | /* ========================================================================== */ 18 | 19 | function getCustomMediaFromObject(object) { 20 | const customMedia = Object.assign( 21 | {}, 22 | Object(object).customMedia, 23 | Object(object)['custom-media'] 24 | ); 25 | 26 | return customMedia; 27 | } 28 | 29 | /* Get Custom Media from JSON file 30 | /* ========================================================================== */ 31 | 32 | async function getCustomMediaFromJSONFile(from) { 33 | const object = await readJSON(from); 34 | 35 | return getCustomMediaFromObject(object); 36 | } 37 | 38 | /* Get Custom Media from JS file 39 | /* ========================================================================== */ 40 | 41 | async function getCustomMediaFromJSFile(from) { 42 | const object = await import(from); 43 | 44 | return getCustomMediaFromObject(object); 45 | } 46 | 47 | /* Get Custom Media from Imports 48 | /* ========================================================================== */ 49 | 50 | export default function getCustomMediaFromImports(sources) { 51 | return sources.map(source => { 52 | if (source instanceof Promise) { 53 | return source; 54 | } else if (source instanceof Function) { 55 | return source(); 56 | } 57 | 58 | // read the source as an object 59 | const opts = source === Object(source) ? source : { from: String(source) }; 60 | 61 | // skip objects with Custom Media 62 | if (opts.customMedia || opts['custom-media']) { 63 | return opts 64 | } 65 | 66 | // source pathname 67 | const from = path.resolve(String(opts.from || '')); 68 | 69 | // type of file being read from 70 | const type = (opts.type || path.extname(from).slice(1)).toLowerCase(); 71 | 72 | return { type, from }; 73 | }).reduce(async (customMedia, source) => { 74 | const { type, from } = await source; 75 | 76 | if (type === 'css') { 77 | return Object.assign(await customMedia, await getCustomMediaFromCSSFile(from)); 78 | } 79 | 80 | if (type === 'js') { 81 | return Object.assign(await customMedia, await getCustomMediaFromJSFile(from)); 82 | } 83 | 84 | if (type === 'json') { 85 | return Object.assign(await customMedia, await getCustomMediaFromJSONFile(from)); 86 | } 87 | 88 | return Object.assign(await customMedia, await getCustomMediaFromObject(await source)); 89 | }, {}); 90 | } 91 | 92 | /* Promise-ified utilities 93 | /* ========================================================================== */ 94 | 95 | const readFile = from => new Promise((resolve, reject) => { 96 | fs.readFile(from, 'utf8', (error, result) => { 97 | if (error) { 98 | reject(error); 99 | } else { 100 | resolve(result); 101 | } 102 | }); 103 | }); 104 | 105 | const readJSON = async from => JSON.parse(await readFile(from)); 106 | -------------------------------------------------------------------------------- /src/lib/get-custom-media-from-root.mjs: -------------------------------------------------------------------------------- 1 | // return custom selectors from the css root 2 | export default root => { 3 | // initialize custom selectors 4 | const customMedias = {}; 5 | 6 | // for each @custom-media at-rule 7 | root.nodes.forEach(node => { 8 | if (isCustomMedia(node)) { 9 | // extract the name and selectors from the params of the custom selector 10 | const [, name, selectors] = node.params.match(customMediaParamsRegExp); 11 | 12 | // write the parsed selectors to the custom selector 13 | customMedias[name] = selectors; 14 | } 15 | }); 16 | 17 | // return all custom medias 18 | return customMedias; 19 | }; 20 | 21 | // match the custom selector name 22 | const customMediaNameRegExp = /^custom-media$/i; 23 | 24 | // match the custom selector params 25 | const customMediaParamsRegExp = /^(--[A-z][\w-]*)\s+([\W\w]+)\s*$/; 26 | 27 | // whether the atrule is a custom selector 28 | const isCustomMedia = node => node.type === 'atrule' && customMediaNameRegExp.test(node.name) && customMediaParamsRegExp.test(node.params); 29 | -------------------------------------------------------------------------------- /src/lib/parse-media.mjs: -------------------------------------------------------------------------------- 1 | function parse(string, splitByAnd) { 2 | const array = []; 3 | let buffer = ''; 4 | let split = false; 5 | let func = 0; 6 | let i = -1; 7 | 8 | while (++i < string.length) { 9 | const char = string[i]; 10 | 11 | if (char === '(') { 12 | func += 1; 13 | } else if (char === ')') { 14 | if (func > 0) { 15 | func -= 1; 16 | } 17 | } else if (func === 0) { 18 | if (splitByAnd && andRegExp.test(buffer + char)) { 19 | split = true; 20 | } else if (!splitByAnd && char === ',') { 21 | split = true; 22 | } 23 | } 24 | 25 | if (split) { 26 | array.push(splitByAnd ? new MediaExpression(buffer + char) : new MediaQuery(buffer)); 27 | 28 | buffer = ''; 29 | split = false; 30 | } else { 31 | buffer += char 32 | } 33 | } 34 | 35 | if (buffer !== '') { 36 | array.push(splitByAnd ? new MediaExpression(buffer) : new MediaQuery(buffer)); 37 | } 38 | 39 | return array; 40 | } 41 | 42 | class MediaQueryList { 43 | constructor(string) { 44 | this.nodes = parse(string); 45 | this.nodeType = 'list'; 46 | } 47 | 48 | invert() { 49 | this.nodes.forEach(node => { 50 | node.invert(); 51 | }) 52 | 53 | return this; 54 | } 55 | 56 | clone() { 57 | return new MediaQueryList(String(this)); 58 | } 59 | 60 | toString() { 61 | return this.nodes.join(','); 62 | } 63 | } 64 | 65 | class MediaQuery { 66 | constructor(string) { 67 | const [, before, media, after ] = string.match(spaceWrapRegExp); 68 | const [, modifier = '', afterModifier = ' ', type = '', beforeAnd = '', and = '', beforeExpression = '', expression1 = '', expression2 = ''] = media.match(mediaRegExp) || []; 69 | const raws = { before, after, afterModifier, originalModifier: modifier || '', beforeAnd, and, beforeExpression }; 70 | const nodes = parse(expression1 || expression2, true); 71 | 72 | Object.assign(this, { 73 | modifier, 74 | type, 75 | raws, 76 | nodes, 77 | nodeType: 'query' 78 | }); 79 | } 80 | 81 | clone(overrides) { 82 | const instance = new MediaQuery(String(this)); 83 | 84 | Object.assign(instance, overrides); 85 | 86 | return instance; 87 | } 88 | 89 | invert() { 90 | this.modifier = this.modifier ? '' : this.raws.originalModifier; 91 | 92 | return this; 93 | } 94 | 95 | toString() { 96 | const { raws } = this; 97 | 98 | return `${raws.before}${this.modifier}${this.modifier ? `${raws.afterModifier}` : ''}${this.type}${raws.beforeAnd}${raws.and}${raws.beforeExpression}${this.nodes.join('')}${this.raws.after}`; 99 | } 100 | } 101 | 102 | class MediaExpression { 103 | constructor(string) { 104 | const [, value, after = '', and = '', afterAnd = '' ] = string.match(andRegExp) || [null, string]; 105 | const raws = { after, and, afterAnd }; 106 | 107 | Object.assign(this, { value, raws, nodeType: 'expression' }); 108 | } 109 | 110 | clone(overrides) { 111 | const instance = new MediaExpression(String(this)); 112 | 113 | Object.assign(instance, overrides); 114 | 115 | return instance; 116 | } 117 | 118 | toString() { 119 | const { raws } = this; 120 | 121 | return `${this.value}${raws.after}${raws.and}${raws.afterAnd}`; 122 | } 123 | } 124 | 125 | const modifierRE = '(not|only)'; 126 | const typeRE = '(all|print|screen|speech)'; 127 | const noExpressionRE = '([\\W\\w]*)'; 128 | const expressionRE = '([\\W\\w]+)'; 129 | const noSpaceRE = '(\\s*)'; 130 | const spaceRE = '(\\s+)'; 131 | const andRE = '(?:(\\s+)(and))'; 132 | const andRegExp = new RegExp(`^${expressionRE}(?:${andRE}${spaceRE})$`, 'i'); 133 | const spaceWrapRegExp = new RegExp(`^${noSpaceRE}${noExpressionRE}${noSpaceRE}$`); 134 | const mediaRegExp = new RegExp(`^(?:${modifierRE}${spaceRE})?(?:${typeRE}(?:${andRE}${spaceRE}${expressionRE})?|${expressionRE})$`, 'i'); 135 | 136 | export default string => new MediaQueryList(string); 137 | -------------------------------------------------------------------------------- /test/import-custom-media.css: -------------------------------------------------------------------------------- 1 | @custom-media --md (min-width: 60rem); 2 | --------------------------------------------------------------------------------