├── .gitignore ├── LICENSE ├── README.md ├── build.js ├── package-lock.json ├── package.json ├── postinstall.js ├── scripts ├── copy-licenses.js └── install-fixture-deps.js ├── src └── index.js └── tests ├── fixtures ├── basic │ ├── index.html │ ├── prettier.config.js │ └── tailwind.config.js ├── cjs │ ├── index.html │ ├── prettier.config.js │ └── tailwind.config.cjs ├── custom-js │ ├── index.js │ ├── prettier.config.js │ └── tailwind.config.js ├── custom-jsx │ ├── index.jsx │ ├── prettier.config.js │ └── tailwind.config.js ├── esm-explicit │ ├── config.mjs │ ├── index.html │ └── prettier.config.js ├── esm │ ├── index.html │ ├── prettier.config.js │ └── tailwind.config.mjs ├── no-prettier-config │ ├── index.html │ └── tailwind.config.js ├── plugins │ ├── index.html │ ├── prettier.config.js │ └── tailwind.config.js ├── ts-explicit │ ├── config.ts │ ├── index.html │ └── prettier.config.js ├── ts │ ├── index.html │ ├── prettier.config.js │ └── tailwind.config.ts └── v3-2 │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── prettier.config.js │ └── tailwind.config.js ├── plugins.test.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Tailwind Labs Inc. 4 | Copyright (c) _nderscore 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation notice 2 | 3 | ## ⚠️ This package is no longer actively maintained ⚠️ 4 | 5 | [`prettier-plugin-tailwindcss`](https://www.npmjs.com/package/prettier-plugin-tailwindcss) 6 | now supports sorting Tailwind classes in custom locations, making this fork obsolete. 7 | 8 | --- 9 | 10 | --- 11 | 12 | # prettier-plugin-nativewind 13 | 14 | A fork of 15 | [`prettier-plugin-tailwindcss`](https://www.npmjs.com/package/prettier-plugin-tailwindcss) 16 | with support for [NativeWind](https://www.nativewind.dev) and other use custom use cases. 17 | 18 | ```bash 19 | npm install -D prettier-plugin-nativewind 20 | # or 21 | pnpm add -D prettier-plugin-nativewind 22 | # or 23 | yarn add -D prettier-plugin-nativewind 24 | ``` 25 | 26 | ## Fork features 27 | 28 | Support for automatically sorting tailwind classes found in strings inside of: 29 | 30 | * JSX props: `className` and `tw` 31 | 32 | * Styled components defined using `styled()` 33 | 34 | * Class variants defined using `variants()` 35 | 36 | * Or, customize which props and function calls to look for tailwind classes in 37 | 38 | ## Usage 39 | 40 | Please refer to the 41 | [original package documentation](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/#readme) 42 | for basic usage instructions. 43 | 44 | ### Customizing props 45 | 46 | This fork adds two additional prettier configuration options, which allow you 47 | to customize which props and function calls to search for tailwind classes in. 48 | 49 | * `tailwindCustomProps` 50 | * `tailwindCustomFunctions` 51 | * `tailwindCustomTaggedTemplates` 52 | 53 | **Customize props:** 54 | 55 | Sort custom props which get passed as classnames to child elements: 56 | 57 | ```js 58 | // prettier.config.js 59 | module.exports = { 60 | tailwindCustomProps: ['className', 'containerClassName'] 61 | }; 62 | ``` 63 | 64 | **Customize functions:** 65 | 66 | Sort inside calls to 67 | [`cva()`](https://www.npmjs.com/package/class-variance-authority) 68 | instead of `styled()`: 69 | 70 | ```js 71 | // prettier.config.js 72 | module.exports = { 73 | tailwindCustomFunctions: ['cva'] 74 | }; 75 | ``` 76 | 77 | **Tagged template literals:** 78 | 79 | ```js 80 | // prettier.config.js 81 | module.exports = { 82 | // Sort template strings found in tagged template literal calls named tw`` 83 | tailwindCustomTaggedTemplates: ['tw'] 84 | }; 85 | ``` 86 | 87 | **Regular expressions:** 88 | 89 | You can also use regular expressions by starting a string with `^`. 90 | 91 | Sort any prop which ends in `ClassName`: 92 | 93 | ```js 94 | // prettier.config.js 95 | module.exports = { 96 | tailwindCustomProps: ['className', '^[a-z]+ClassName$'] 97 | }; 98 | ``` 99 | 100 | ## Changelog 101 | 102 | ### v0.2.3 103 | 104 | * Add deprecation to readme 105 | * Add postinstall warning message 106 | 107 | ### v0.2.2 108 | 109 | * Synchronize with upstream `prettier-plugin-tailwindcss@0.2.6` 110 | 111 | ### v0.2.1 112 | 113 | * Synchronize with upstream `prettier-plugin-tailwindcss@0.2.5` 114 | * Add support for tagged template literals 115 | 116 | ### v0.2.0 117 | 118 | * Synchronize with upstream `prettier-plugin-tailwindcss@0.2.1` 119 | * Add support for customizing which props and functions to search 120 | 121 | ### v0.1.1 122 | 123 | * Add support for NativeWind `variants()` calls. 124 | 125 | ### v0.1.0 126 | 127 | * Initial release (forked from `prettier-plugin-tailwindcss@0.2.0`) 128 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const esbuild = require('esbuild') 2 | const path = require('path') 3 | const fs = require('fs') 4 | 5 | esbuild.build({ 6 | entryPoints: [path.resolve(__dirname, './src/index.js')], 7 | outfile: path.resolve(__dirname, './dist/index.js'), 8 | bundle: true, 9 | platform: 'node', 10 | target: 'node12.13.0', 11 | external: ['prettier'], 12 | minify: process.argv.includes('--minify'), 13 | watch: process.argv.includes('--watch'), 14 | plugins: [ 15 | { 16 | // https://github.com/benjamn/recast/issues/611 17 | name: 'patch-recast', 18 | setup(build) { 19 | build.onLoad({ filter: /recast\/lib\/patcher\.js$/ }, async (args) => { 20 | let original = await fs.promises.readFile(args.path, 'utf8') 21 | 22 | return { 23 | contents: original 24 | .replace( 25 | 'var nls = needsLeadingSpace(lines, oldNode.loc, newLines);', 26 | 'var nls = oldNode.type !== "TemplateElement" && needsLeadingSpace(lines, oldNode.loc, newLines);' 27 | ) 28 | .replace( 29 | 'var nts = needsTrailingSpace(lines, oldNode.loc, newLines)', 30 | 'var nts = oldNode.type !== "TemplateElement" && needsTrailingSpace(lines, oldNode.loc, newLines)' 31 | ), 32 | } 33 | }) 34 | }, 35 | }, 36 | ], 37 | }) 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prettier-plugin-nativewind", 3 | "version": "0.2.3", 4 | "description": "(deprecated) A Prettier plugin for sorting NativeWind CSS classes.", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "files": [ 8 | "dist", 9 | "postinstall.js" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/nderscore/prettier-plugin-nativewind" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/nderscore/prettier-plugin-nativewind/issues" 17 | }, 18 | "scripts": { 19 | "_pre": "rimraf dist && cpy node_modules/tailwindcss/lib/css dist/css", 20 | "_esbuild": "node build.js", 21 | "postinstall": "node postinstall.js", 22 | "prebuild": "npm run _pre", 23 | "build": "npm run _esbuild -- --minify", 24 | "predev": "npm run _pre", 25 | "dev": "npm run _esbuild -- --watch", 26 | "pretest": "node scripts/install-fixture-deps.js", 27 | "test": "jest", 28 | "prepublishOnly": "npm run build && npm test && node scripts/copy-licenses.js", 29 | "format": "prettier \"src/**/*.js\" \"scripts/**/*.js\" \"tests/test.js\" --write --print-width 100 --single-quote --no-semi --plugin-search-dir ./tests" 30 | }, 31 | "devDependencies": { 32 | "@ianvs/prettier-plugin-sort-imports": "^3.7.0", 33 | "@prettier/plugin-php": "^0.19.2", 34 | "@prettier/plugin-pug": "^2.3.0", 35 | "@shopify/prettier-plugin-liquid": "^1.0.3", 36 | "@shufo/prettier-plugin-blade": "^1.8.4", 37 | "@trivago/prettier-plugin-sort-imports": "^3.4.0", 38 | "clear-module": "^4.1.2", 39 | "cpy-cli": "^3.1.1", 40 | "esbuild": "^0.14.11", 41 | "escalade": "^3.1.1", 42 | "import-sort-style-module": "^6.0.0", 43 | "jest": "^27.4.7", 44 | "jsesc": "^2.5.2", 45 | "license-checker": "^25.0.1", 46 | "line-column": "^1.0.2", 47 | "object-hash": "^2.2.0", 48 | "prettier": "^2.5.1", 49 | "prettier-plugin-astro": "^0.7.2", 50 | "prettier-plugin-css-order": "^1.3.0", 51 | "prettier-plugin-import-sort": "^0.0.7", 52 | "prettier-plugin-jsdoc": "^0.4.2", 53 | "prettier-plugin-organize-attributes": "^0.0.5", 54 | "prettier-plugin-organize-imports": "^3.2.1", 55 | "prettier-plugin-style-order": "^0.2.2", 56 | "prettier-plugin-svelte": "^2.9.0", 57 | "prettier-plugin-twig-melody": "^0.4.6", 58 | "recast": "^0.20.5", 59 | "resolve-from": "^5.0.0", 60 | "rimraf": "^3.0.2", 61 | "svelte": "^3.55.0", 62 | "tailwindcss": "^3.3.0" 63 | }, 64 | "peerDependencies": { 65 | "@ianvs/prettier-plugin-sort-imports": "*", 66 | "@prettier/plugin-php": "*", 67 | "@prettier/plugin-pug": "*", 68 | "@shopify/prettier-plugin-liquid": "*", 69 | "@shufo/prettier-plugin-blade": "*", 70 | "@trivago/prettier-plugin-sort-imports": "*", 71 | "prettier": ">=2.2.0", 72 | "prettier-plugin-astro": "*", 73 | "prettier-plugin-css-order": "*", 74 | "prettier-plugin-import-sort": "*", 75 | "prettier-plugin-jsdoc": "*", 76 | "prettier-plugin-organize-attributes": "*", 77 | "prettier-plugin-organize-imports": "*", 78 | "prettier-plugin-style-order": "*", 79 | "prettier-plugin-svelte": "*", 80 | "prettier-plugin-twig-melody": "*" 81 | }, 82 | "peerDependenciesMeta": { 83 | "@ianvs/prettier-plugin-sort-imports": { 84 | "optional": true 85 | }, 86 | "@prettier/plugin-php": { 87 | "optional": true 88 | }, 89 | "@prettier/plugin-pug": { 90 | "optional": true 91 | }, 92 | "@shopify/prettier-plugin-liquid": { 93 | "optional": true 94 | }, 95 | "@shufo/prettier-plugin-blade": { 96 | "optional": true 97 | }, 98 | "@trivago/prettier-plugin-sort-imports": { 99 | "optional": true 100 | }, 101 | "prettier-plugin-astro": { 102 | "optional": true 103 | }, 104 | "prettier-plugin-css-order": { 105 | "optional": true 106 | }, 107 | "prettier-plugin-import-sort": { 108 | "optional": true 109 | }, 110 | "prettier-plugin-jsdoc": { 111 | "optional": true 112 | }, 113 | "prettier-plugin-organize-attributes": { 114 | "optional": true 115 | }, 116 | "prettier-plugin-organize-imports": { 117 | "optional": true 118 | }, 119 | "prettier-plugin-style-order": { 120 | "optional": true 121 | }, 122 | "prettier-plugin-svelte": { 123 | "optional": true 124 | }, 125 | "prettier-plugin-twig-melody": { 126 | "optional": true 127 | } 128 | }, 129 | "engines": { 130 | "node": ">=12.17.0" 131 | }, 132 | "importSort": { 133 | ".js, .jsx, .ts, .tsx": { 134 | "style": "module" 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /postinstall.js: -------------------------------------------------------------------------------- 1 | console.warn(` 2 | is no longer actively maintained. 3 | 4 | Please switch to which now supports sorting 5 | Tailwind classes in custom locations. 6 | 7 | Or alternatively, downgrade to to remove 8 | this warning. 9 | `); 10 | -------------------------------------------------------------------------------- /scripts/copy-licenses.js: -------------------------------------------------------------------------------- 1 | const checker = require('license-checker') 2 | const { devDependencies } = require('../package.json') 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | let exclude = [ 7 | 'cpy-cli', 8 | 'esbuild', 9 | 'jest', 10 | 'license-checker', 11 | 'prettier', 12 | 'rimraf', 13 | 'svelte', 14 | ] 15 | 16 | checker.init({ start: path.resolve(__dirname, '..') }, (_err, packages) => { 17 | for (let key in packages) { 18 | let name = key.split(/(?<=.)@/)[0] 19 | if (name in devDependencies && !exclude.includes(name) && packages[key].licenseFile) { 20 | let dir = path.resolve(__dirname, '../dist/licenses', name) 21 | fs.mkdirSync(dir, { recursive: true }) 22 | fs.copyFileSync( 23 | packages[key].licenseFile, 24 | path.resolve(dir, path.basename(packages[key].licenseFile)) 25 | ) 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /scripts/install-fixture-deps.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { execSync } = require('child_process') 4 | 5 | let fixturesDir = path.resolve(__dirname, '../tests/fixtures') 6 | let fixtures = fs.readdirSync(fixturesDir).map((name) => path.join(fixturesDir, name)) 7 | 8 | for (let fixture of fixtures) { 9 | if (fs.existsSync(path.join(fixture, 'package.json'))) { 10 | execSync('npm install', { cwd: fixture }) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import prettier from 'prettier' 2 | import prettierParserHTML from 'prettier/parser-html' 3 | import prettierParserAngular from 'prettier/parser-angular' 4 | import prettierParserPostCSS from 'prettier/parser-postcss' 5 | import prettierParserBabel from 'prettier/parser-babel' 6 | import prettierParserEspree from 'prettier/parser-espree' 7 | import prettierParserGlimmer from 'prettier/parser-glimmer' 8 | import prettierParserMeriyah from 'prettier/parser-meriyah' 9 | import prettierParserFlow from 'prettier/parser-flow' 10 | import prettierParserTypescript from 'prettier/parser-typescript' 11 | import { createContext as createContextFallback } from 'tailwindcss/lib/lib/setupContextUtils' 12 | import { generateRules as generateRulesFallback } from 'tailwindcss/lib/lib/generateRules' 13 | import resolveConfigFallback from 'tailwindcss/resolveConfig' 14 | import loadConfigFallback from 'tailwindcss/loadConfig' 15 | import * as recast from 'recast' 16 | import * as astTypes from 'ast-types' 17 | import * as path from 'path' 18 | import objectHash from 'object-hash' 19 | import lineColumn from 'line-column' 20 | import jsesc from 'jsesc' 21 | import escalade from 'escalade/sync' 22 | import clearModule from 'clear-module' 23 | import resolveFrom from 'resolve-from' 24 | 25 | let base = getBasePlugins() 26 | 27 | let contextMap = new Map() 28 | 29 | function bigSign(bigIntValue) { 30 | return (bigIntValue > 0n) - (bigIntValue < 0n) 31 | } 32 | 33 | function prefixCandidate(context, selector) { 34 | let prefix = context.tailwindConfig.prefix 35 | return typeof prefix === 'function' ? prefix(selector) : prefix + selector 36 | } 37 | 38 | // Polyfill for older Tailwind CSS versions 39 | function getClassOrderPolyfill(classes, { env }) { 40 | // A list of utilities that are used by certain Tailwind CSS utilities but 41 | // that don't exist on their own. This will result in them "not existing" and 42 | // sorting could be weird since you still require them in order to make the 43 | // host utitlies work properly. (Thanks Biology) 44 | let parasiteUtilities = new Set([ 45 | prefixCandidate(env.context, 'group'), 46 | prefixCandidate(env.context, 'peer'), 47 | ]) 48 | 49 | let classNamesWithOrder = [] 50 | 51 | for (let className of classes) { 52 | let order = 53 | env 54 | .generateRules(new Set([className]), env.context) 55 | .sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null 56 | 57 | if (order === null && parasiteUtilities.has(className)) { 58 | // This will make sure that it is at the very beginning of the 59 | // `components` layer which technically means 'before any 60 | // components'. 61 | order = env.context.layerOrder.components 62 | } 63 | 64 | classNamesWithOrder.push([className, order]) 65 | } 66 | 67 | return classNamesWithOrder 68 | } 69 | 70 | function sortClasses(classStr, { env, ignoreFirst = false, ignoreLast = false }) { 71 | if (typeof classStr !== 'string' || classStr === '') { 72 | return classStr 73 | } 74 | 75 | // Ignore class attributes containing `{{`, to match Prettier behaviour: 76 | // https://github.com/prettier/prettier/blob/main/src/language-html/embed.js#L83-L88 77 | if (classStr.includes('{{')) { 78 | return classStr 79 | } 80 | 81 | let result = '' 82 | let parts = classStr.split(/(\s+)/) 83 | let classes = parts.filter((_, i) => i % 2 === 0) 84 | let whitespace = parts.filter((_, i) => i % 2 !== 0) 85 | 86 | if (classes[classes.length - 1] === '') { 87 | classes.pop() 88 | } 89 | 90 | let prefix = '' 91 | if (ignoreFirst) { 92 | prefix = `${classes.shift() ?? ''}${whitespace.shift() ?? ''}` 93 | } 94 | 95 | let suffix = '' 96 | if (ignoreLast) { 97 | suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}` 98 | } 99 | 100 | classes = sortClassList(classes, { env }) 101 | 102 | for (let i = 0; i < classes.length; i++) { 103 | result += `${classes[i]}${whitespace[i] ?? ''}` 104 | } 105 | 106 | return prefix + result + suffix 107 | } 108 | 109 | function sortClassList(classList, { env }) { 110 | let classNamesWithOrder = env.context.getClassOrder 111 | ? env.context.getClassOrder(classList) 112 | : getClassOrderPolyfill(classList, { env }) 113 | 114 | return classNamesWithOrder 115 | .sort(([, a], [, z]) => { 116 | if (a === z) return 0 117 | // if (a === null) return options.unknownClassPosition === 'start' ? -1 : 1 118 | // if (z === null) return options.unknownClassPosition === 'start' ? 1 : -1 119 | if (a === null) return -1 120 | if (z === null) return 1 121 | return bigSign(a - z) 122 | }) 123 | .map(([className]) => className) 124 | } 125 | 126 | function createParser(parserFormat, transform) { 127 | return { 128 | ...base.parsers[parserFormat], 129 | preprocess(code, options) { 130 | let original = getCompatibleParser(parserFormat, options) 131 | 132 | if (original.preprocess) { 133 | return original.preprocess(code, options) 134 | } 135 | 136 | return code 137 | }, 138 | 139 | parse(text, parsers, options = {}) { 140 | let original = getCompatibleParser(parserFormat, options) 141 | 142 | if (original.astFormat === 'svelte-ast') { 143 | options.printer = printers['svelte-ast'] 144 | } 145 | 146 | let ast = original.parse(text, parsers, options) 147 | let tailwindConfigPath = '__default__' 148 | let tailwindConfig = {} 149 | let resolveConfig = resolveConfigFallback 150 | let createContext = createContextFallback 151 | let generateRules = generateRulesFallback 152 | let loadConfig = loadConfigFallback 153 | 154 | let baseDir 155 | let prettierConfigPath = prettier.resolveConfigFile.sync(options.filepath) 156 | 157 | if (options.tailwindConfig) { 158 | baseDir = prettierConfigPath ? path.dirname(prettierConfigPath) : process.cwd() 159 | } else { 160 | baseDir = prettierConfigPath 161 | ? path.dirname(prettierConfigPath) 162 | : options.filepath 163 | ? path.dirname(options.filepath) 164 | : process.cwd() 165 | } 166 | 167 | try { 168 | let pkgDir = path.dirname(resolveFrom(baseDir, 'tailwindcss/package.json')) 169 | 170 | resolveConfig = require(path.join(pkgDir, 'resolveConfig')) 171 | createContext = require(path.join(pkgDir, 'lib/lib/setupContextUtils')).createContext 172 | generateRules = require(path.join(pkgDir, 'lib/lib/generateRules')).generateRules 173 | 174 | // Prior to `tailwindcss@3.3.0` this won't exist so we load it last 175 | loadConfig = require(path.join(pkgDir, 'loadConfig')) 176 | } catch {} 177 | 178 | if (options.tailwindConfig) { 179 | tailwindConfigPath = path.resolve(baseDir, options.tailwindConfig) 180 | clearModule(tailwindConfigPath) 181 | const loadedConfig = loadConfig(tailwindConfigPath) 182 | tailwindConfig = loadedConfig.default ?? loadedConfig 183 | } else { 184 | let configPath 185 | try { 186 | configPath = escalade(baseDir, (_dir, names) => { 187 | if (names.includes('tailwind.config.js')) { 188 | return 'tailwind.config.js' 189 | } 190 | if (names.includes('tailwind.config.cjs')) { 191 | return 'tailwind.config.cjs' 192 | } 193 | if (names.includes('tailwind.config.mjs')) { 194 | return 'tailwind.config.mjs' 195 | } 196 | if (names.includes('tailwind.config.ts')) { 197 | return 'tailwind.config.ts' 198 | } 199 | }) 200 | } catch {} 201 | if (configPath) { 202 | tailwindConfigPath = configPath 203 | clearModule(tailwindConfigPath) 204 | const loadedConfig = loadConfig(tailwindConfigPath) 205 | tailwindConfig = loadedConfig.default ?? loadedConfig 206 | } 207 | } 208 | 209 | // suppress "empty content" warning 210 | tailwindConfig.content = ['no-op'] 211 | 212 | let context 213 | let existing = contextMap.get(tailwindConfigPath) 214 | let hash = objectHash(tailwindConfig) 215 | 216 | if (existing && existing.hash === hash) { 217 | context = existing.context 218 | } else { 219 | context = createContext(resolveConfig(tailwindConfig)) 220 | contextMap.set(tailwindConfigPath, { context, hash }) 221 | } 222 | 223 | let customizations = { 224 | checkJSXPropName: createNameChecker(options.tailwindCustomProps ?? ['class', 'className']), 225 | checkFunctionCallName: createNameChecker(options.tailwindCustomFunctions ?? ['styled', 'variants']), 226 | checkTaggedTemplateName: createNameChecker(options.tailwindCustomTaggedTemplates), 227 | } 228 | 229 | transform(ast, { 230 | env: { 231 | context, 232 | customizations, 233 | generateRules, 234 | parsers, 235 | options 236 | } 237 | }) 238 | return ast 239 | }, 240 | } 241 | } 242 | 243 | function tryParseAngularAttribute(value, env) { 244 | let parsers = [ 245 | // Try parsing as an angular directive 246 | prettierParserAngular.parsers.__ng_directive, 247 | 248 | // If this fails we fall back to arbitrary parsing of a JS expression 249 | { parse: env.parsers.__js_expression }, 250 | ] 251 | 252 | let errors = [] 253 | for (const parser of parsers) { 254 | try { 255 | return parser.parse( 256 | value, 257 | env.parsers, 258 | env.options 259 | ) 260 | } catch (err) { 261 | errors.push(err) 262 | } 263 | } 264 | 265 | console.warn("prettier-plugin-tailwindcss: Unable to parse angular directive") 266 | errors.forEach(err => console.warn(err)) 267 | } 268 | 269 | function transformHtml(attributes, computedAttributes = [], computedType = 'js') { 270 | let transform = (ast, { env }) => { 271 | for (let attr of ast.attrs ?? []) { 272 | if (attributes.includes(attr.name)) { 273 | attr.value = sortClasses(attr.value, { env }) 274 | } else if (computedAttributes.includes(attr.name)) { 275 | if (!/[`'"]/.test(attr.value)) { 276 | continue 277 | } 278 | 279 | if (computedType === 'angular') { 280 | let directiveAst = tryParseAngularAttribute(attr.value, env) 281 | 282 | // If we've reached this point we couldn't parse the expression we we should bail 283 | // `tryParseAngularAttribute` will display some warnings/errors 284 | // But we shouldn't fail outright — just miss parsing some attributes 285 | if (!directiveAst) { 286 | continue 287 | } 288 | 289 | visit(directiveAst, { 290 | StringLiteral(node) { 291 | if (!node.value) return 292 | attr.value = 293 | attr.value.slice(0, node.start + 1) + 294 | sortClasses(node.value, { env }) + 295 | attr.value.slice(node.end - 1) 296 | }, 297 | }) 298 | continue 299 | } 300 | 301 | let ast = recast.parse(`let __prettier_temp__ = ${attr.value}`, { 302 | parser: prettierParserBabel.parsers['babel-ts'], 303 | }) 304 | let didChange = false 305 | 306 | astTypes.visit(ast, { 307 | visitLiteral(path) { 308 | if (isStringLiteral(path.node)) { 309 | if (sortStringLiteral(path.node, { env })) { 310 | didChange = true 311 | 312 | // https://github.com/benjamn/recast/issues/171#issuecomment-224996336 313 | let quote = path.node.extra.raw[0] 314 | let value = jsesc(path.node.value, { 315 | quotes: quote === "'" ? 'single' : 'double', 316 | }) 317 | path.node.value = new String(quote + value + quote) 318 | } 319 | } 320 | this.traverse(path) 321 | }, 322 | visitTemplateLiteral(path) { 323 | if (sortTemplateLiteral(path.node, { env })) { 324 | didChange = true 325 | } 326 | this.traverse(path) 327 | }, 328 | }) 329 | 330 | if (didChange) { 331 | attr.value = recast.print(ast.program.body[0].declarations[0].init).code 332 | } 333 | } 334 | } 335 | 336 | for (let child of ast.children ?? []) { 337 | transform(child, { env }) 338 | } 339 | } 340 | return transform 341 | } 342 | 343 | function transformGlimmer(ast, { env }) { 344 | visit(ast, { 345 | AttrNode(attr, parent, key, index, meta) { 346 | let attributes = ['class'] 347 | 348 | if (attributes.includes(attr.name) && attr.value) { 349 | meta.sortTextNodes = true 350 | } 351 | }, 352 | 353 | TextNode(node, parent, key, index, meta) { 354 | if (!meta.sortTextNodes) { 355 | return 356 | } 357 | 358 | let siblings = 359 | parent?.type === 'ConcatStatement' 360 | ? { 361 | prev: parent.parts[index - 1], 362 | next: parent.parts[index + 1], 363 | } 364 | : null 365 | 366 | node.chars = sortClasses(node.chars, { 367 | env, 368 | ignoreFirst: siblings?.prev && !/^\s/.test(node.chars), 369 | ignoreLast: siblings?.next && !/\s$/.test(node.chars), 370 | }) 371 | }, 372 | 373 | StringLiteral(node, parent, key, index, meta) { 374 | if (!meta.sortTextNodes) { 375 | return 376 | } 377 | 378 | const isConcat = parent.type === 'SubExpression' && parent.path.original === 'concat'; 379 | 380 | node.value = sortClasses(node.value, { 381 | env, 382 | ignoreLast: isConcat && !/[^\S\r\n]$/.test(node.value), 383 | }) 384 | }, 385 | }) 386 | } 387 | 388 | function transformLiquid(ast, { env }) { 389 | /** @param {{name: string | {type: string, value: string}[]}} node */ 390 | function isClassAttr(node) { 391 | return Array.isArray(node.name) 392 | ? node.name.every((n) => n.type === 'TextNode' && n.value === 'class') 393 | : node.name === 'class' 394 | } 395 | 396 | function sortAttribute(attr, path) { 397 | visit(attr.value, { 398 | TextNode(node) { 399 | node.value = sortClasses(node.value, { env }); 400 | 401 | let source = node.source.slice(0, node.position.start) + node.value + node.source.slice(node.position.end) 402 | path.forEach(node => (node.source = source)) 403 | }, 404 | 405 | String(node) { 406 | node.value = sortClasses(node.value, { env }); 407 | 408 | // String position includes the quotes even if the value doesn't 409 | // Hence the +1 and -1 when slicing 410 | let source = node.source.slice(0, node.position.start+1) + node.value + node.source.slice(node.position.end-1) 411 | path.forEach(node => (node.source = source)) 412 | }, 413 | }) 414 | } 415 | 416 | visit(ast, { 417 | LiquidTag(node, _parent, _key, _index, meta) { 418 | meta.path = [...meta.path ?? [], node]; 419 | }, 420 | 421 | HtmlElement(node, _parent, _key, _index, meta) { 422 | meta.path = [...meta.path ?? [], node]; 423 | }, 424 | 425 | AttrSingleQuoted(node, _parent, _key, _index, meta) { 426 | if (!isClassAttr(node)) { 427 | return; 428 | } 429 | 430 | meta.path = [...meta.path ?? [], node]; 431 | 432 | sortAttribute(node, meta.path) 433 | }, 434 | 435 | AttrDoubleQuoted(node, _parent, _key, _index, meta) { 436 | if (!isClassAttr(node)) { 437 | return; 438 | } 439 | 440 | meta.path = [...meta.path ?? [], node]; 441 | 442 | sortAttribute(node, meta.path) 443 | }, 444 | }); 445 | } 446 | 447 | function sortStringLiteral(node, { env }) { 448 | let result = sortClasses(node.value, { env }) 449 | let didChange = result !== node.value 450 | node.value = result 451 | if (node.extra) { 452 | // JavaScript (StringLiteral) 453 | let raw = node.extra.raw 454 | node.extra = { 455 | ...node.extra, 456 | rawValue: result, 457 | raw: raw[0] + result + raw.slice(-1), 458 | } 459 | } else { 460 | // TypeScript (Literal) 461 | let raw = node.raw 462 | node.raw = raw[0] + result + raw.slice(-1) 463 | } 464 | return didChange 465 | } 466 | 467 | function isStringLiteral(node) { 468 | return ( 469 | node.type === 'StringLiteral' || (node.type === 'Literal' && typeof node.value === 'string') 470 | ) 471 | } 472 | 473 | function sortTemplateLiteral(node, { env }) { 474 | let didChange = false 475 | 476 | for (let i = 0; i < node.quasis.length; i++) { 477 | let quasi = node.quasis[i] 478 | let same = quasi.value.raw === quasi.value.cooked 479 | let originalRaw = quasi.value.raw 480 | let originalCooked = quasi.value.cooked 481 | 482 | quasi.value.raw = sortClasses(quasi.value.raw, { 483 | env, 484 | ignoreFirst: i > 0 && !/^\s/.test(quasi.value.raw), 485 | ignoreLast: i < node.expressions.length && !/\s$/.test(quasi.value.raw), 486 | }) 487 | 488 | quasi.value.cooked = same 489 | ? quasi.value.raw 490 | : sortClasses(quasi.value.cooked, { 491 | env, 492 | ignoreFirst: i > 0 && !/^\s/.test(quasi.value.cooked), 493 | ignoreLast: i < node.expressions.length && !/\s$/.test(quasi.value.cooked), 494 | }) 495 | 496 | if (quasi.value.raw !== originalRaw || quasi.value.cooked !== originalCooked) { 497 | didChange = true 498 | } 499 | } 500 | 501 | return didChange 502 | } 503 | 504 | function transformJavaScript(ast, { env }) { 505 | const { checkJSXPropName, checkFunctionCallName, checkTaggedTemplateName } = env.customizations 506 | 507 | visit(ast, { 508 | ...(checkJSXPropName && { 509 | JSXAttribute(node) { 510 | if (!node.value) { 511 | return 512 | } 513 | if (checkJSXPropName(node.name.name)) { 514 | if (isStringLiteral(node.value)) { 515 | sortStringLiteral(node.value, { env }) 516 | } else if (node.value.type === 'JSXExpressionContainer') { 517 | visit(node.value, (node, parent, key) => { 518 | if (isStringLiteral(node)) { 519 | sortStringLiteral(node, { env }) 520 | } else if (node.type === 'TemplateLiteral') { 521 | sortTemplateLiteral(node, { env }) 522 | } 523 | }) 524 | } 525 | } 526 | }, 527 | }), 528 | ...(checkFunctionCallName && { 529 | CallExpression(node) { 530 | const calleeName = node.callee?.name ?? '' 531 | if (!node.arguments?.length || !checkFunctionCallName(calleeName)) { 532 | return 533 | } 534 | node.arguments.forEach((arg) => { 535 | visit(arg, (node) => { 536 | if (isStringLiteral(node)) { 537 | sortStringLiteral(node, { env }) 538 | } else if (node.type === 'TemplateLiteral') { 539 | sortTemplateLiteral(node, { env }) 540 | } 541 | }) 542 | }) 543 | }, 544 | }), 545 | ...(checkTaggedTemplateName && { 546 | TaggedTemplateExpression(node) { 547 | if (node.tag.type === 'Identifier' && checkTaggedTemplateName(node.tag.name)) { 548 | sortTemplateLiteral(node.quasi, { env }) 549 | } 550 | }, 551 | }), 552 | }) 553 | } 554 | 555 | function transformCss(ast, { env }) { 556 | ast.walk((node) => { 557 | if (node.type === 'css-atrule' && node.name === 'apply') { 558 | node.params = sortClasses(node.params, { 559 | env, 560 | ignoreLast: /\s+(?:!important|#{!important})\s*$/.test(node.params), 561 | }) 562 | } 563 | }) 564 | } 565 | 566 | export const options = { 567 | tailwindConfig: { 568 | type: 'string', 569 | category: 'Tailwind CSS', 570 | description: 'TODO', 571 | }, 572 | tailwindCustomProps: { 573 | default: [ 574 | { since: '0.2.0', value: ['class', 'className', 'tw'] } 575 | ], 576 | type: 'string', 577 | array: true, 578 | category: 'Tailwind CSS', 579 | description: 'List of React props to sort Tailwind classes in', 580 | }, 581 | tailwindCustomFunctions: { 582 | default: [ 583 | { since: '0.2.0', value: ['styled', 'variants'] }, 584 | ], 585 | type: 'string', 586 | array: true, 587 | category: 'Tailwind CSS', 588 | description: 'List of function names to sort Tailwind classes in', 589 | }, 590 | tailwindCustomTaggedTemplates: { 591 | default: [{ since: '0.2.1', value: [] }], 592 | type: 'string', 593 | array: true, 594 | category: 'Tailwind CSS', 595 | description: 'List of tagged template function names to sort Tailwind classes in', 596 | }, 597 | } 598 | 599 | function createNameChecker(values) { 600 | if (!values?.length) { 601 | return false 602 | } 603 | const processedValues = values.map( 604 | (v) => v.startsWith('^') ? new RegExp(v) : v 605 | ) 606 | const hasRegex = processedValues.some((v) => v instanceof RegExp) 607 | if (!hasRegex) { 608 | return (value) => processedValues.includes(value) 609 | } 610 | return (value) => processedValues.some((v) => { 611 | if (v instanceof RegExp) { 612 | return v.test(value) 613 | } 614 | return v === value 615 | }) 616 | } 617 | 618 | export const printers = { 619 | ...base.printers['svelte-ast'] ? {'svelte-ast': { 620 | ...base.printers['svelte-ast'], 621 | print: (path, options, print) => { 622 | if (!options.__mutatedOriginalText) { 623 | options.__mutatedOriginalText = true 624 | let changes = path.stack[0].changes 625 | if (changes?.length) { 626 | let finder = lineColumn(options.originalText) 627 | 628 | for (let change of changes) { 629 | let start = finder.toIndex(change.loc.start.line, change.loc.start.column + 1) 630 | let end = finder.toIndex(change.loc.end.line, change.loc.end.column + 1) 631 | 632 | options.originalText = 633 | options.originalText.substring(0, start) + 634 | change.text + 635 | options.originalText.substring(end) 636 | } 637 | } 638 | } 639 | 640 | return base.printers['svelte-ast'].print(path, options, print) 641 | }, 642 | } } : {}, 643 | } 644 | 645 | export const parsers = { 646 | html: createParser('html', transformHtml(['class'])), 647 | glimmer: createParser('glimmer', transformGlimmer), 648 | lwc: createParser('lwc', transformHtml(['class'])), 649 | angular: createParser( 650 | 'angular', 651 | transformHtml(['class'], ['[ngClass]'], 'angular') 652 | ), 653 | vue: createParser('vue', transformHtml(['class'], [':class'])), 654 | css: createParser('css', transformCss), 655 | scss: createParser('scss', transformCss), 656 | less: createParser('less', transformCss), 657 | babel: createParser('babel', transformJavaScript), 658 | 'babel-flow': createParser('babel-flow', transformJavaScript), 659 | flow: createParser('flow', transformJavaScript), 660 | typescript: createParser('typescript', transformJavaScript), 661 | 'babel-ts': createParser('babel-ts', transformJavaScript), 662 | espree: createParser('espree', transformJavaScript), 663 | meriyah: createParser('meriyah', transformJavaScript), 664 | __js_expression: createParser('__js_expression', transformJavaScript), 665 | ...base.parsers.svelte ? { svelte: createParser('svelte', (ast, { env }) => { 666 | let changes = [] 667 | transformSvelte(ast.html, { env, changes }) 668 | ast.changes = changes 669 | }) } : {}, 670 | ...base.parsers.astro ? { astro: createParser('astro', transformAstro) } : {}, 671 | ...base.parsers.php ? { php: createParser('php', transformPHP) } : {}, 672 | ...base.parsers.melody ? { melody: createParser('melody', transformMelody) } : {}, 673 | ...base.parsers.pug ? { pug: createParser('pug', transformPug) } : {}, 674 | ...(base.parsers['liquid-html'] 675 | ? { 'liquid-html': createParser("liquid-html", transformLiquid) } 676 | : {}), 677 | // ...base.parsers.blade ? { blade: createParser('blade', transformBlade) } : {}, 678 | } 679 | 680 | function transformAstro(ast, { env, changes }) { 681 | if (ast.type === "element" || ast.type === "custom-element" || ast.type === "component") { 682 | for (let attr of ast.attributes ?? []) { 683 | if (attr.name === "class" && attr.type === "attribute" && attr.kind === "quoted") { 684 | attr.value = sortClasses(attr.value, { 685 | env 686 | }); 687 | } 688 | } 689 | } 690 | 691 | for (let child of ast.children ?? []) { 692 | transformAstro(child, { env, changes }); 693 | } 694 | } 695 | 696 | function transformPHP(ast, { env, changes }) { 697 | if (ast.kind === "inline") { 698 | let leading = ast.raw.match(/^\s*/)[0] 699 | let trailing = ast.raw.match(/\s*$/)[0] 700 | 701 | // If the inline block is just whitespace then we don't need to format 702 | if (ast.raw === leading) { 703 | return 704 | } 705 | 706 | // We have to parse this as HTML with prettier 707 | let parsed = prettier.format(ast.raw, { 708 | ...env.options, 709 | parser: "html", 710 | }) 711 | 712 | let formatted = `${leading}${parsed.trimEnd()}${trailing}` 713 | 714 | ast.raw = formatted 715 | ast.value = formatted 716 | } 717 | 718 | for (let child of ast.children ?? []) { 719 | transformPHP(child, { env, changes }); 720 | } 721 | } 722 | 723 | /* 724 | function transformBlade(ast, { env, changes }) { 725 | // Blade gets formatted on parse 726 | // This means we'd have to parse the blade ourselves and figure out what's HTML and what isn't 727 | } 728 | */ 729 | 730 | function transformMelody(ast, { env, changes }) { 731 | for (let child of ast.expressions ?? []) { 732 | transformMelody(child, { env }) 733 | } 734 | 735 | visit(ast, { 736 | Attribute(node, _parent, _key, _index, meta) { 737 | if (node.name.name !== "class") { 738 | return 739 | } 740 | 741 | meta.sortTextNodes = true 742 | }, 743 | 744 | StringLiteral(node, _parent, _key, _index, meta) { 745 | if (!meta.sortTextNodes) { 746 | return 747 | } 748 | 749 | node.value = sortClasses(node.value, { 750 | env, 751 | }); 752 | } 753 | }) 754 | } 755 | 756 | function transformPug(ast, { env }) { 757 | 758 | // This isn't optimal 759 | // We should merge the classes together across class attributes and class tokens 760 | // And then we sort them 761 | // But this is good enough for now 762 | 763 | // First sort the classes in attributes 764 | for (const token of ast.tokens) { 765 | if (token.type === 'attribute' && token.name === 'class') { 766 | token.val = [ 767 | token.val.slice(0, 1), 768 | sortClasses(token.val.slice(1, -1), { env }), 769 | token.val.slice(-1) 770 | ].join('') 771 | } 772 | } 773 | 774 | // Collect lists of consecutive class tokens 775 | let startIdx = -1; 776 | let endIdx = -1; 777 | let ranges = []; 778 | 779 | for (let i = 0; i < ast.tokens.length; i++) { 780 | const token = ast.tokens[i]; 781 | 782 | if (token.type === 'class') { 783 | startIdx = startIdx === -1 ? i : startIdx; 784 | endIdx = i; 785 | } else if (startIdx !== -1) { 786 | ranges.push([startIdx, endIdx]); 787 | startIdx = -1; 788 | endIdx = -1; 789 | } 790 | } 791 | 792 | if (startIdx !== -1) { 793 | ranges.push([startIdx, endIdx]); 794 | startIdx = -1; 795 | endIdx = -1; 796 | } 797 | 798 | // Sort the lists of class tokens 799 | for (const [startIdx, endIdx] of ranges) { 800 | const classes = ast.tokens.slice(startIdx, endIdx + 1).map(token => token.val); 801 | const classList = sortClassList(classes, { env }) 802 | 803 | for (let i = startIdx; i <= endIdx; i++) { 804 | ast.tokens[i].val = classList[i - startIdx]; 805 | } 806 | } 807 | } 808 | 809 | function transformSvelte(ast, { env, changes }) { 810 | for (let attr of ast.attributes ?? []) { 811 | if (attr.name === 'class' && attr.type === 'Attribute') { 812 | for (let i = 0; i < attr.value.length; i++) { 813 | let value = attr.value[i] 814 | if (value.type === 'Text') { 815 | let same = value.raw === value.data 816 | value.raw = sortClasses(value.raw, { 817 | env, 818 | ignoreFirst: i > 0 && !/^\s/.test(value.raw), 819 | ignoreLast: i < attr.value.length - 1 && !/\s$/.test(value.raw), 820 | }) 821 | value.data = same 822 | ? value.raw 823 | : sortClasses(value.data, { 824 | env, 825 | ignoreFirst: i > 0 && !/^\s/.test(value.data), 826 | ignoreLast: i < attr.value.length - 1 && !/\s$/.test(value.data), 827 | }) 828 | } else if (value.type === 'MustacheTag') { 829 | visit(value.expression, { 830 | Literal(node) { 831 | if (isStringLiteral(node)) { 832 | if (sortStringLiteral(node, { env })) { 833 | changes.push({ text: node.raw, loc: node.loc }) 834 | } 835 | } 836 | }, 837 | TemplateLiteral(node) { 838 | if (sortTemplateLiteral(node, { env })) { 839 | for (let quasi of node.quasis) { 840 | changes.push({ text: quasi.value.raw, loc: quasi.loc }) 841 | } 842 | } 843 | }, 844 | }) 845 | } 846 | } 847 | } 848 | } 849 | 850 | for (let child of ast.children ?? []) { 851 | transformSvelte(child, { env, changes }) 852 | } 853 | 854 | if (ast.type === 'IfBlock') { 855 | for (let child of ast.else?.children ?? []) { 856 | transformSvelte(child, { env, changes }) 857 | } 858 | } 859 | 860 | if (ast.type === "AwaitBlock") { 861 | let nodes = [ast.pending, ast.then, ast.catch]; 862 | 863 | for (let child of nodes) { 864 | transformSvelte(child, { env, changes }); 865 | } 866 | } 867 | } 868 | 869 | // https://lihautan.com/manipulating-ast-with-javascript/ 870 | function visit(ast, callbackMap) { 871 | function _visit(node, parent, key, index, meta = {}) { 872 | if (typeof callbackMap === 'function') { 873 | if (callbackMap(node, parent, key, index, meta) === false) { 874 | return 875 | } 876 | } else if (node.type in callbackMap) { 877 | if (callbackMap[node.type](node, parent, key, index, meta) === false) { 878 | return 879 | } 880 | } 881 | 882 | const keys = Object.keys(node) 883 | for (let i = 0; i < keys.length; i++) { 884 | const child = node[keys[i]] 885 | if (Array.isArray(child)) { 886 | for (let j = 0; j < child.length; j++) { 887 | if (child[j] !== null) { 888 | _visit(child[j], node, keys[i], j, { ...meta }) 889 | } 890 | } 891 | } else if (typeof child?.type === 'string') { 892 | _visit(child, node, keys[i], i, { ...meta }) 893 | } 894 | } 895 | } 896 | _visit(ast) 897 | } 898 | 899 | // For loading prettier plugins only if they exist 900 | function loadIfExists(name) { 901 | try { 902 | if (require.resolve(name)) { 903 | return require(name) 904 | } 905 | } catch (e) { 906 | return null 907 | } 908 | } 909 | 910 | function getBasePlugins() { 911 | // We need to load this plugin dynamically because it's not available by default 912 | // And we are not bundling it with the main Prettier plugin 913 | let astro = loadIfExists('prettier-plugin-astro') 914 | let svelte = loadIfExists('prettier-plugin-svelte') 915 | let php = loadIfExists('@prettier/plugin-php') 916 | let melody = loadIfExists('prettier-plugin-twig-melody') 917 | let pug = loadIfExists('@prettier/plugin-pug') 918 | let liquid = loadIfExists('@shopify/prettier-plugin-liquid') 919 | // let blade = loadIfExists('@shufo/prettier-plugin-blade') 920 | 921 | return { 922 | parsers: { 923 | html: prettierParserHTML.parsers.html, 924 | glimmer: prettierParserGlimmer.parsers.glimmer, 925 | lwc: prettierParserHTML.parsers.lwc, 926 | angular: prettierParserHTML.parsers.angular, 927 | vue: prettierParserHTML.parsers.vue, 928 | css: prettierParserPostCSS.parsers.css, 929 | scss: prettierParserPostCSS.parsers.scss, 930 | less: prettierParserPostCSS.parsers.less, 931 | babel: prettierParserBabel.parsers.babel, 932 | 'babel-flow': prettierParserBabel.parsers['babel-flow'], 933 | flow: prettierParserFlow.parsers.flow, 934 | typescript: prettierParserTypescript.parsers.typescript, 935 | 'babel-ts': prettierParserBabel.parsers['babel-ts'], 936 | espree: prettierParserEspree.parsers.espree, 937 | meriyah: prettierParserMeriyah.parsers.meriyah, 938 | __js_expression: prettierParserBabel.parsers.__js_expression, 939 | 940 | ...(svelte?.parsers ?? {}), 941 | ...(astro?.parsers ?? {}), 942 | ...(php?.parsers ?? {}), 943 | ...(melody?.parsers ?? {}), 944 | ...(pug?.parsers ?? {}), 945 | ...(liquid?.parsers ?? {}), 946 | // ...(blade?.parsers ?? {}), 947 | }, 948 | printers: { 949 | ...(svelte ? { 'svelte-ast': svelte.printers['svelte-ast'] } : {}), 950 | }, 951 | }; 952 | } 953 | 954 | function getCompatibleParser(parserFormat, options) { 955 | if (!options.plugins) { 956 | return base.parsers[parserFormat] 957 | } 958 | 959 | let parser = { 960 | ...base.parsers[parserFormat], 961 | } 962 | 963 | // Now load parsers from plugins 964 | let compatiblePlugins = [ 965 | '@ianvs/prettier-plugin-sort-imports', 966 | '@trivago/prettier-plugin-sort-imports', 967 | 'prettier-plugin-organize-imports', 968 | '@prettier/plugin-php', 969 | '@prettier/plugin-pug', 970 | '@shopify/prettier-plugin-liquid', 971 | '@shufo/prettier-plugin-blade', 972 | 'prettier-plugin-css-order', 973 | 'prettier-plugin-import-sort', 974 | 'prettier-plugin-jsdoc', 975 | 'prettier-plugin-organize-attributes', 976 | 'prettier-plugin-style-order', 977 | 'prettier-plugin-twig-melody', 978 | ] 979 | 980 | for (const name of compatiblePlugins) { 981 | let path = null 982 | 983 | try { 984 | path = require.resolve(name) 985 | } catch (err) { 986 | continue 987 | } 988 | 989 | let plugin = options.plugins.find(plugin => plugin.name === name || plugin.name === path) 990 | 991 | // The plugin is not loaded 992 | if (!plugin) { 993 | continue 994 | } 995 | 996 | Object.assign(parser, plugin.parsers[parserFormat]) 997 | } 998 | 999 | return parser 1000 | } 1001 | -------------------------------------------------------------------------------- /tests/fixtures/basic/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/basic/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/fixtures/basic/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | colors: { 5 | tomato: 'tomato', 6 | }, 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/cjs/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/cjs/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/fixtures/cjs/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | colors: { 5 | hotpink: 'hotpink', 6 | }, 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/custom-js/index.js: -------------------------------------------------------------------------------- 1 | const sortMeFn = () => {}; 2 | const dontSortFn = () => {}; 3 | const a = sortMeFn("sm:p-1 p-2"); 4 | const b = sortMeFn({ 5 | foo: "sm:p-1 p-2" 6 | }); 7 | 8 | const c = dontSortFn("sm:p-1 p-2"); 9 | const sortMeTemplate = () => {}; 10 | const dontSortMeTemplate = () => {}; 11 | const d = sortMeTemplate`sm:p-1 p-2`; 12 | const e = dontSortMeTemplate`sm:p-1 p-2`; 13 | -------------------------------------------------------------------------------- /tests/fixtures/custom-js/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tailwindCustomFunctions: ['sortMeFn'], 3 | tailwindCustomTaggedTemplates: ['sortMeTemplate'], 4 | }; 5 | -------------------------------------------------------------------------------- /tests/fixtures/custom-js/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: {}, 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/custom-jsx/index.jsx: -------------------------------------------------------------------------------- 1 | const A = (props) => { 2 | return
; 3 | } 4 | 5 | const B = () => { 6 | return ( 7 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /tests/fixtures/custom-jsx/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tailwindCustomProps: ['sortMe', '^.+ClassName$'], 3 | }; 4 | -------------------------------------------------------------------------------- /tests/fixtures/custom-jsx/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: {}, 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/esm-explicit/config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | theme: { 3 | extend: { 4 | colors: { 5 | hotpink: "hotpink", 6 | }, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /tests/fixtures/esm-explicit/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/esm-explicit/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tailwindConfig: './config.mjs' 3 | }; 4 | -------------------------------------------------------------------------------- /tests/fixtures/esm/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/esm/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/fixtures/esm/tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | theme: { 3 | extend: { 4 | colors: { 5 | hotpink: "hotpink", 6 | }, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /tests/fixtures/no-prettier-config/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/no-prettier-config/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | colors: { 5 | tomato: 'tomato', 6 | }, 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/plugins/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/plugins/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['../../..'], 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/plugins/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const plugin = require("tailwindcss/plugin"); 2 | 3 | module.exports = { 4 | plugins: [ 5 | plugin(function ({ addUtilities }) { 6 | addUtilities({ 7 | ".foo": { color: "red" }, 8 | ".bar": { color: "blue" }, 9 | }); 10 | }), 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /tests/fixtures/ts-explicit/config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | export default { 4 | content: ["index.html"], 5 | theme: { 6 | extend: { 7 | colors: { 8 | hotpink: "hotpink", 9 | }, 10 | }, 11 | }, 12 | } satisfies Config; 13 | -------------------------------------------------------------------------------- /tests/fixtures/ts-explicit/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/ts-explicit/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tailwindConfig: './config.ts' 3 | }; 4 | -------------------------------------------------------------------------------- /tests/fixtures/ts/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/ts/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/fixtures/ts/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | export default { 4 | content: ["index.html"], 5 | theme: { 6 | extend: { 7 | colors: { 8 | hotpink: "hotpink", 9 | }, 10 | }, 11 | }, 12 | } satisfies Config; 13 | -------------------------------------------------------------------------------- /tests/fixtures/v3-2/index.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /tests/fixtures/v3-2/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v3-2", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "tailwindcss": "3.2.7" 9 | } 10 | }, 11 | "node_modules/@nodelib/fs.scandir": { 12 | "version": "2.1.5", 13 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 14 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 15 | "dependencies": { 16 | "@nodelib/fs.stat": "2.0.5", 17 | "run-parallel": "^1.1.9" 18 | }, 19 | "engines": { 20 | "node": ">= 8" 21 | } 22 | }, 23 | "node_modules/@nodelib/fs.stat": { 24 | "version": "2.0.5", 25 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 26 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 27 | "engines": { 28 | "node": ">= 8" 29 | } 30 | }, 31 | "node_modules/@nodelib/fs.walk": { 32 | "version": "1.2.8", 33 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 34 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 35 | "dependencies": { 36 | "@nodelib/fs.scandir": "2.1.5", 37 | "fastq": "^1.6.0" 38 | }, 39 | "engines": { 40 | "node": ">= 8" 41 | } 42 | }, 43 | "node_modules/acorn": { 44 | "version": "7.4.1", 45 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 46 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 47 | "bin": { 48 | "acorn": "bin/acorn" 49 | }, 50 | "engines": { 51 | "node": ">=0.4.0" 52 | } 53 | }, 54 | "node_modules/acorn-node": { 55 | "version": "1.8.2", 56 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 57 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 58 | "dependencies": { 59 | "acorn": "^7.0.0", 60 | "acorn-walk": "^7.0.0", 61 | "xtend": "^4.0.2" 62 | } 63 | }, 64 | "node_modules/acorn-walk": { 65 | "version": "7.2.0", 66 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 67 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", 68 | "engines": { 69 | "node": ">=0.4.0" 70 | } 71 | }, 72 | "node_modules/anymatch": { 73 | "version": "3.1.3", 74 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 75 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 76 | "dependencies": { 77 | "normalize-path": "^3.0.0", 78 | "picomatch": "^2.0.4" 79 | }, 80 | "engines": { 81 | "node": ">= 8" 82 | } 83 | }, 84 | "node_modules/arg": { 85 | "version": "5.0.2", 86 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 87 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 88 | }, 89 | "node_modules/binary-extensions": { 90 | "version": "2.2.0", 91 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 92 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 93 | "engines": { 94 | "node": ">=8" 95 | } 96 | }, 97 | "node_modules/braces": { 98 | "version": "3.0.2", 99 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 100 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 101 | "dependencies": { 102 | "fill-range": "^7.0.1" 103 | }, 104 | "engines": { 105 | "node": ">=8" 106 | } 107 | }, 108 | "node_modules/camelcase-css": { 109 | "version": "2.0.1", 110 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 111 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 112 | "engines": { 113 | "node": ">= 6" 114 | } 115 | }, 116 | "node_modules/chokidar": { 117 | "version": "3.5.3", 118 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 119 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 120 | "funding": [ 121 | { 122 | "type": "individual", 123 | "url": "https://paulmillr.com/funding/" 124 | } 125 | ], 126 | "dependencies": { 127 | "anymatch": "~3.1.2", 128 | "braces": "~3.0.2", 129 | "glob-parent": "~5.1.2", 130 | "is-binary-path": "~2.1.0", 131 | "is-glob": "~4.0.1", 132 | "normalize-path": "~3.0.0", 133 | "readdirp": "~3.6.0" 134 | }, 135 | "engines": { 136 | "node": ">= 8.10.0" 137 | }, 138 | "optionalDependencies": { 139 | "fsevents": "~2.3.2" 140 | } 141 | }, 142 | "node_modules/chokidar/node_modules/glob-parent": { 143 | "version": "5.1.2", 144 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 145 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 146 | "dependencies": { 147 | "is-glob": "^4.0.1" 148 | }, 149 | "engines": { 150 | "node": ">= 6" 151 | } 152 | }, 153 | "node_modules/color-name": { 154 | "version": "1.1.4", 155 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 156 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 157 | }, 158 | "node_modules/cssesc": { 159 | "version": "3.0.0", 160 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 161 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 162 | "bin": { 163 | "cssesc": "bin/cssesc" 164 | }, 165 | "engines": { 166 | "node": ">=4" 167 | } 168 | }, 169 | "node_modules/defined": { 170 | "version": "1.0.1", 171 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 172 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", 173 | "funding": { 174 | "url": "https://github.com/sponsors/ljharb" 175 | } 176 | }, 177 | "node_modules/detective": { 178 | "version": "5.2.1", 179 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 180 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 181 | "dependencies": { 182 | "acorn-node": "^1.8.2", 183 | "defined": "^1.0.0", 184 | "minimist": "^1.2.6" 185 | }, 186 | "bin": { 187 | "detective": "bin/detective.js" 188 | }, 189 | "engines": { 190 | "node": ">=0.8.0" 191 | } 192 | }, 193 | "node_modules/didyoumean": { 194 | "version": "1.2.2", 195 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 196 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" 197 | }, 198 | "node_modules/dlv": { 199 | "version": "1.1.3", 200 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 201 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" 202 | }, 203 | "node_modules/fast-glob": { 204 | "version": "3.2.12", 205 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 206 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 207 | "dependencies": { 208 | "@nodelib/fs.stat": "^2.0.2", 209 | "@nodelib/fs.walk": "^1.2.3", 210 | "glob-parent": "^5.1.2", 211 | "merge2": "^1.3.0", 212 | "micromatch": "^4.0.4" 213 | }, 214 | "engines": { 215 | "node": ">=8.6.0" 216 | } 217 | }, 218 | "node_modules/fast-glob/node_modules/glob-parent": { 219 | "version": "5.1.2", 220 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 221 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 222 | "dependencies": { 223 | "is-glob": "^4.0.1" 224 | }, 225 | "engines": { 226 | "node": ">= 6" 227 | } 228 | }, 229 | "node_modules/fastq": { 230 | "version": "1.15.0", 231 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 232 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 233 | "dependencies": { 234 | "reusify": "^1.0.4" 235 | } 236 | }, 237 | "node_modules/fill-range": { 238 | "version": "7.0.1", 239 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 240 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 241 | "dependencies": { 242 | "to-regex-range": "^5.0.1" 243 | }, 244 | "engines": { 245 | "node": ">=8" 246 | } 247 | }, 248 | "node_modules/fsevents": { 249 | "version": "2.3.2", 250 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 251 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 252 | "hasInstallScript": true, 253 | "optional": true, 254 | "os": [ 255 | "darwin" 256 | ], 257 | "engines": { 258 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 259 | } 260 | }, 261 | "node_modules/function-bind": { 262 | "version": "1.1.1", 263 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 264 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 265 | }, 266 | "node_modules/glob-parent": { 267 | "version": "6.0.2", 268 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 269 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 270 | "dependencies": { 271 | "is-glob": "^4.0.3" 272 | }, 273 | "engines": { 274 | "node": ">=10.13.0" 275 | } 276 | }, 277 | "node_modules/has": { 278 | "version": "1.0.3", 279 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 280 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 281 | "dependencies": { 282 | "function-bind": "^1.1.1" 283 | }, 284 | "engines": { 285 | "node": ">= 0.4.0" 286 | } 287 | }, 288 | "node_modules/is-binary-path": { 289 | "version": "2.1.0", 290 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 291 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 292 | "dependencies": { 293 | "binary-extensions": "^2.0.0" 294 | }, 295 | "engines": { 296 | "node": ">=8" 297 | } 298 | }, 299 | "node_modules/is-core-module": { 300 | "version": "2.11.0", 301 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 302 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 303 | "dependencies": { 304 | "has": "^1.0.3" 305 | }, 306 | "funding": { 307 | "url": "https://github.com/sponsors/ljharb" 308 | } 309 | }, 310 | "node_modules/is-extglob": { 311 | "version": "2.1.1", 312 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 313 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 314 | "engines": { 315 | "node": ">=0.10.0" 316 | } 317 | }, 318 | "node_modules/is-glob": { 319 | "version": "4.0.3", 320 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 321 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 322 | "dependencies": { 323 | "is-extglob": "^2.1.1" 324 | }, 325 | "engines": { 326 | "node": ">=0.10.0" 327 | } 328 | }, 329 | "node_modules/is-number": { 330 | "version": "7.0.0", 331 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 332 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 333 | "engines": { 334 | "node": ">=0.12.0" 335 | } 336 | }, 337 | "node_modules/lilconfig": { 338 | "version": "2.1.0", 339 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", 340 | "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", 341 | "engines": { 342 | "node": ">=10" 343 | } 344 | }, 345 | "node_modules/merge2": { 346 | "version": "1.4.1", 347 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 348 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 349 | "engines": { 350 | "node": ">= 8" 351 | } 352 | }, 353 | "node_modules/micromatch": { 354 | "version": "4.0.5", 355 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 356 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 357 | "dependencies": { 358 | "braces": "^3.0.2", 359 | "picomatch": "^2.3.1" 360 | }, 361 | "engines": { 362 | "node": ">=8.6" 363 | } 364 | }, 365 | "node_modules/minimist": { 366 | "version": "1.2.8", 367 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 368 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 369 | "funding": { 370 | "url": "https://github.com/sponsors/ljharb" 371 | } 372 | }, 373 | "node_modules/nanoid": { 374 | "version": "3.3.6", 375 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 376 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 377 | "funding": [ 378 | { 379 | "type": "github", 380 | "url": "https://github.com/sponsors/ai" 381 | } 382 | ], 383 | "bin": { 384 | "nanoid": "bin/nanoid.cjs" 385 | }, 386 | "engines": { 387 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 388 | } 389 | }, 390 | "node_modules/normalize-path": { 391 | "version": "3.0.0", 392 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 393 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 394 | "engines": { 395 | "node": ">=0.10.0" 396 | } 397 | }, 398 | "node_modules/object-hash": { 399 | "version": "3.0.0", 400 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 401 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 402 | "engines": { 403 | "node": ">= 6" 404 | } 405 | }, 406 | "node_modules/path-parse": { 407 | "version": "1.0.7", 408 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 409 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 410 | }, 411 | "node_modules/picocolors": { 412 | "version": "1.0.0", 413 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 414 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 415 | }, 416 | "node_modules/picomatch": { 417 | "version": "2.3.1", 418 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 419 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 420 | "engines": { 421 | "node": ">=8.6" 422 | }, 423 | "funding": { 424 | "url": "https://github.com/sponsors/jonschlinkert" 425 | } 426 | }, 427 | "node_modules/pify": { 428 | "version": "2.3.0", 429 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 430 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 431 | "engines": { 432 | "node": ">=0.10.0" 433 | } 434 | }, 435 | "node_modules/postcss": { 436 | "version": "8.4.21", 437 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", 438 | "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", 439 | "funding": [ 440 | { 441 | "type": "opencollective", 442 | "url": "https://opencollective.com/postcss/" 443 | }, 444 | { 445 | "type": "tidelift", 446 | "url": "https://tidelift.com/funding/github/npm/postcss" 447 | } 448 | ], 449 | "dependencies": { 450 | "nanoid": "^3.3.4", 451 | "picocolors": "^1.0.0", 452 | "source-map-js": "^1.0.2" 453 | }, 454 | "engines": { 455 | "node": "^10 || ^12 || >=14" 456 | } 457 | }, 458 | "node_modules/postcss-import": { 459 | "version": "14.1.0", 460 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 461 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 462 | "dependencies": { 463 | "postcss-value-parser": "^4.0.0", 464 | "read-cache": "^1.0.0", 465 | "resolve": "^1.1.7" 466 | }, 467 | "engines": { 468 | "node": ">=10.0.0" 469 | }, 470 | "peerDependencies": { 471 | "postcss": "^8.0.0" 472 | } 473 | }, 474 | "node_modules/postcss-js": { 475 | "version": "4.0.1", 476 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", 477 | "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", 478 | "dependencies": { 479 | "camelcase-css": "^2.0.1" 480 | }, 481 | "engines": { 482 | "node": "^12 || ^14 || >= 16" 483 | }, 484 | "funding": { 485 | "type": "opencollective", 486 | "url": "https://opencollective.com/postcss/" 487 | }, 488 | "peerDependencies": { 489 | "postcss": "^8.4.21" 490 | } 491 | }, 492 | "node_modules/postcss-load-config": { 493 | "version": "3.1.4", 494 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 495 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 496 | "dependencies": { 497 | "lilconfig": "^2.0.5", 498 | "yaml": "^1.10.2" 499 | }, 500 | "engines": { 501 | "node": ">= 10" 502 | }, 503 | "funding": { 504 | "type": "opencollective", 505 | "url": "https://opencollective.com/postcss/" 506 | }, 507 | "peerDependencies": { 508 | "postcss": ">=8.0.9", 509 | "ts-node": ">=9.0.0" 510 | }, 511 | "peerDependenciesMeta": { 512 | "postcss": { 513 | "optional": true 514 | }, 515 | "ts-node": { 516 | "optional": true 517 | } 518 | } 519 | }, 520 | "node_modules/postcss-nested": { 521 | "version": "6.0.0", 522 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 523 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 524 | "dependencies": { 525 | "postcss-selector-parser": "^6.0.10" 526 | }, 527 | "engines": { 528 | "node": ">=12.0" 529 | }, 530 | "funding": { 531 | "type": "opencollective", 532 | "url": "https://opencollective.com/postcss/" 533 | }, 534 | "peerDependencies": { 535 | "postcss": "^8.2.14" 536 | } 537 | }, 538 | "node_modules/postcss-selector-parser": { 539 | "version": "6.0.11", 540 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 541 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 542 | "dependencies": { 543 | "cssesc": "^3.0.0", 544 | "util-deprecate": "^1.0.2" 545 | }, 546 | "engines": { 547 | "node": ">=4" 548 | } 549 | }, 550 | "node_modules/postcss-value-parser": { 551 | "version": "4.2.0", 552 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 553 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" 554 | }, 555 | "node_modules/queue-microtask": { 556 | "version": "1.2.3", 557 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 558 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 559 | "funding": [ 560 | { 561 | "type": "github", 562 | "url": "https://github.com/sponsors/feross" 563 | }, 564 | { 565 | "type": "patreon", 566 | "url": "https://www.patreon.com/feross" 567 | }, 568 | { 569 | "type": "consulting", 570 | "url": "https://feross.org/support" 571 | } 572 | ] 573 | }, 574 | "node_modules/quick-lru": { 575 | "version": "5.1.1", 576 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 577 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 578 | "engines": { 579 | "node": ">=10" 580 | }, 581 | "funding": { 582 | "url": "https://github.com/sponsors/sindresorhus" 583 | } 584 | }, 585 | "node_modules/read-cache": { 586 | "version": "1.0.0", 587 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 588 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 589 | "dependencies": { 590 | "pify": "^2.3.0" 591 | } 592 | }, 593 | "node_modules/readdirp": { 594 | "version": "3.6.0", 595 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 596 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 597 | "dependencies": { 598 | "picomatch": "^2.2.1" 599 | }, 600 | "engines": { 601 | "node": ">=8.10.0" 602 | } 603 | }, 604 | "node_modules/resolve": { 605 | "version": "1.22.1", 606 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 607 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 608 | "dependencies": { 609 | "is-core-module": "^2.9.0", 610 | "path-parse": "^1.0.7", 611 | "supports-preserve-symlinks-flag": "^1.0.0" 612 | }, 613 | "bin": { 614 | "resolve": "bin/resolve" 615 | }, 616 | "funding": { 617 | "url": "https://github.com/sponsors/ljharb" 618 | } 619 | }, 620 | "node_modules/reusify": { 621 | "version": "1.0.4", 622 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 623 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 624 | "engines": { 625 | "iojs": ">=1.0.0", 626 | "node": ">=0.10.0" 627 | } 628 | }, 629 | "node_modules/run-parallel": { 630 | "version": "1.2.0", 631 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 632 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 633 | "funding": [ 634 | { 635 | "type": "github", 636 | "url": "https://github.com/sponsors/feross" 637 | }, 638 | { 639 | "type": "patreon", 640 | "url": "https://www.patreon.com/feross" 641 | }, 642 | { 643 | "type": "consulting", 644 | "url": "https://feross.org/support" 645 | } 646 | ], 647 | "dependencies": { 648 | "queue-microtask": "^1.2.2" 649 | } 650 | }, 651 | "node_modules/source-map-js": { 652 | "version": "1.0.2", 653 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 654 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 655 | "engines": { 656 | "node": ">=0.10.0" 657 | } 658 | }, 659 | "node_modules/supports-preserve-symlinks-flag": { 660 | "version": "1.0.0", 661 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 662 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 663 | "engines": { 664 | "node": ">= 0.4" 665 | }, 666 | "funding": { 667 | "url": "https://github.com/sponsors/ljharb" 668 | } 669 | }, 670 | "node_modules/tailwindcss": { 671 | "version": "3.2.7", 672 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", 673 | "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", 674 | "dependencies": { 675 | "arg": "^5.0.2", 676 | "chokidar": "^3.5.3", 677 | "color-name": "^1.1.4", 678 | "detective": "^5.2.1", 679 | "didyoumean": "^1.2.2", 680 | "dlv": "^1.1.3", 681 | "fast-glob": "^3.2.12", 682 | "glob-parent": "^6.0.2", 683 | "is-glob": "^4.0.3", 684 | "lilconfig": "^2.0.6", 685 | "micromatch": "^4.0.5", 686 | "normalize-path": "^3.0.0", 687 | "object-hash": "^3.0.0", 688 | "picocolors": "^1.0.0", 689 | "postcss": "^8.0.9", 690 | "postcss-import": "^14.1.0", 691 | "postcss-js": "^4.0.0", 692 | "postcss-load-config": "^3.1.4", 693 | "postcss-nested": "6.0.0", 694 | "postcss-selector-parser": "^6.0.11", 695 | "postcss-value-parser": "^4.2.0", 696 | "quick-lru": "^5.1.1", 697 | "resolve": "^1.22.1" 698 | }, 699 | "bin": { 700 | "tailwind": "lib/cli.js", 701 | "tailwindcss": "lib/cli.js" 702 | }, 703 | "engines": { 704 | "node": ">=12.13.0" 705 | }, 706 | "peerDependencies": { 707 | "postcss": "^8.0.9" 708 | } 709 | }, 710 | "node_modules/to-regex-range": { 711 | "version": "5.0.1", 712 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 713 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 714 | "dependencies": { 715 | "is-number": "^7.0.0" 716 | }, 717 | "engines": { 718 | "node": ">=8.0" 719 | } 720 | }, 721 | "node_modules/util-deprecate": { 722 | "version": "1.0.2", 723 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 724 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 725 | }, 726 | "node_modules/xtend": { 727 | "version": "4.0.2", 728 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 729 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 730 | "engines": { 731 | "node": ">=0.4" 732 | } 733 | }, 734 | "node_modules/yaml": { 735 | "version": "1.10.2", 736 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 737 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 738 | "engines": { 739 | "node": ">= 6" 740 | } 741 | } 742 | }, 743 | "dependencies": { 744 | "@nodelib/fs.scandir": { 745 | "version": "2.1.5", 746 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 747 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 748 | "requires": { 749 | "@nodelib/fs.stat": "2.0.5", 750 | "run-parallel": "^1.1.9" 751 | } 752 | }, 753 | "@nodelib/fs.stat": { 754 | "version": "2.0.5", 755 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 756 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" 757 | }, 758 | "@nodelib/fs.walk": { 759 | "version": "1.2.8", 760 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 761 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 762 | "requires": { 763 | "@nodelib/fs.scandir": "2.1.5", 764 | "fastq": "^1.6.0" 765 | } 766 | }, 767 | "acorn": { 768 | "version": "7.4.1", 769 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 770 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" 771 | }, 772 | "acorn-node": { 773 | "version": "1.8.2", 774 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 775 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 776 | "requires": { 777 | "acorn": "^7.0.0", 778 | "acorn-walk": "^7.0.0", 779 | "xtend": "^4.0.2" 780 | } 781 | }, 782 | "acorn-walk": { 783 | "version": "7.2.0", 784 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 785 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" 786 | }, 787 | "anymatch": { 788 | "version": "3.1.3", 789 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 790 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 791 | "requires": { 792 | "normalize-path": "^3.0.0", 793 | "picomatch": "^2.0.4" 794 | } 795 | }, 796 | "arg": { 797 | "version": "5.0.2", 798 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 799 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 800 | }, 801 | "binary-extensions": { 802 | "version": "2.2.0", 803 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 804 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 805 | }, 806 | "braces": { 807 | "version": "3.0.2", 808 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 809 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 810 | "requires": { 811 | "fill-range": "^7.0.1" 812 | } 813 | }, 814 | "camelcase-css": { 815 | "version": "2.0.1", 816 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 817 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" 818 | }, 819 | "chokidar": { 820 | "version": "3.5.3", 821 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 822 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 823 | "requires": { 824 | "anymatch": "~3.1.2", 825 | "braces": "~3.0.2", 826 | "fsevents": "~2.3.2", 827 | "glob-parent": "~5.1.2", 828 | "is-binary-path": "~2.1.0", 829 | "is-glob": "~4.0.1", 830 | "normalize-path": "~3.0.0", 831 | "readdirp": "~3.6.0" 832 | }, 833 | "dependencies": { 834 | "glob-parent": { 835 | "version": "5.1.2", 836 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 837 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 838 | "requires": { 839 | "is-glob": "^4.0.1" 840 | } 841 | } 842 | } 843 | }, 844 | "color-name": { 845 | "version": "1.1.4", 846 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 847 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 848 | }, 849 | "cssesc": { 850 | "version": "3.0.0", 851 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 852 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" 853 | }, 854 | "defined": { 855 | "version": "1.0.1", 856 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 857 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" 858 | }, 859 | "detective": { 860 | "version": "5.2.1", 861 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 862 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 863 | "requires": { 864 | "acorn-node": "^1.8.2", 865 | "defined": "^1.0.0", 866 | "minimist": "^1.2.6" 867 | } 868 | }, 869 | "didyoumean": { 870 | "version": "1.2.2", 871 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 872 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" 873 | }, 874 | "dlv": { 875 | "version": "1.1.3", 876 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 877 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" 878 | }, 879 | "fast-glob": { 880 | "version": "3.2.12", 881 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 882 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 883 | "requires": { 884 | "@nodelib/fs.stat": "^2.0.2", 885 | "@nodelib/fs.walk": "^1.2.3", 886 | "glob-parent": "^5.1.2", 887 | "merge2": "^1.3.0", 888 | "micromatch": "^4.0.4" 889 | }, 890 | "dependencies": { 891 | "glob-parent": { 892 | "version": "5.1.2", 893 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 894 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 895 | "requires": { 896 | "is-glob": "^4.0.1" 897 | } 898 | } 899 | } 900 | }, 901 | "fastq": { 902 | "version": "1.15.0", 903 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 904 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 905 | "requires": { 906 | "reusify": "^1.0.4" 907 | } 908 | }, 909 | "fill-range": { 910 | "version": "7.0.1", 911 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 912 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 913 | "requires": { 914 | "to-regex-range": "^5.0.1" 915 | } 916 | }, 917 | "fsevents": { 918 | "version": "2.3.2", 919 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 920 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 921 | "optional": true 922 | }, 923 | "function-bind": { 924 | "version": "1.1.1", 925 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 926 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 927 | }, 928 | "glob-parent": { 929 | "version": "6.0.2", 930 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 931 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 932 | "requires": { 933 | "is-glob": "^4.0.3" 934 | } 935 | }, 936 | "has": { 937 | "version": "1.0.3", 938 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 939 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 940 | "requires": { 941 | "function-bind": "^1.1.1" 942 | } 943 | }, 944 | "is-binary-path": { 945 | "version": "2.1.0", 946 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 947 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 948 | "requires": { 949 | "binary-extensions": "^2.0.0" 950 | } 951 | }, 952 | "is-core-module": { 953 | "version": "2.11.0", 954 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 955 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 956 | "requires": { 957 | "has": "^1.0.3" 958 | } 959 | }, 960 | "is-extglob": { 961 | "version": "2.1.1", 962 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 963 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 964 | }, 965 | "is-glob": { 966 | "version": "4.0.3", 967 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 968 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 969 | "requires": { 970 | "is-extglob": "^2.1.1" 971 | } 972 | }, 973 | "is-number": { 974 | "version": "7.0.0", 975 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 976 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 977 | }, 978 | "lilconfig": { 979 | "version": "2.1.0", 980 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", 981 | "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" 982 | }, 983 | "merge2": { 984 | "version": "1.4.1", 985 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 986 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" 987 | }, 988 | "micromatch": { 989 | "version": "4.0.5", 990 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 991 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 992 | "requires": { 993 | "braces": "^3.0.2", 994 | "picomatch": "^2.3.1" 995 | } 996 | }, 997 | "minimist": { 998 | "version": "1.2.8", 999 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 1000 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" 1001 | }, 1002 | "nanoid": { 1003 | "version": "3.3.6", 1004 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1005 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" 1006 | }, 1007 | "normalize-path": { 1008 | "version": "3.0.0", 1009 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1010 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 1011 | }, 1012 | "object-hash": { 1013 | "version": "3.0.0", 1014 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 1015 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" 1016 | }, 1017 | "path-parse": { 1018 | "version": "1.0.7", 1019 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1020 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 1021 | }, 1022 | "picocolors": { 1023 | "version": "1.0.0", 1024 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1025 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 1026 | }, 1027 | "picomatch": { 1028 | "version": "2.3.1", 1029 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1030 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 1031 | }, 1032 | "pify": { 1033 | "version": "2.3.0", 1034 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1035 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" 1036 | }, 1037 | "postcss": { 1038 | "version": "8.4.21", 1039 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", 1040 | "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", 1041 | "requires": { 1042 | "nanoid": "^3.3.4", 1043 | "picocolors": "^1.0.0", 1044 | "source-map-js": "^1.0.2" 1045 | } 1046 | }, 1047 | "postcss-import": { 1048 | "version": "14.1.0", 1049 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 1050 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 1051 | "requires": { 1052 | "postcss-value-parser": "^4.0.0", 1053 | "read-cache": "^1.0.0", 1054 | "resolve": "^1.1.7" 1055 | } 1056 | }, 1057 | "postcss-js": { 1058 | "version": "4.0.1", 1059 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", 1060 | "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", 1061 | "requires": { 1062 | "camelcase-css": "^2.0.1" 1063 | } 1064 | }, 1065 | "postcss-load-config": { 1066 | "version": "3.1.4", 1067 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 1068 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 1069 | "requires": { 1070 | "lilconfig": "^2.0.5", 1071 | "yaml": "^1.10.2" 1072 | } 1073 | }, 1074 | "postcss-nested": { 1075 | "version": "6.0.0", 1076 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 1077 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 1078 | "requires": { 1079 | "postcss-selector-parser": "^6.0.10" 1080 | } 1081 | }, 1082 | "postcss-selector-parser": { 1083 | "version": "6.0.11", 1084 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 1085 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 1086 | "requires": { 1087 | "cssesc": "^3.0.0", 1088 | "util-deprecate": "^1.0.2" 1089 | } 1090 | }, 1091 | "postcss-value-parser": { 1092 | "version": "4.2.0", 1093 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 1094 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" 1095 | }, 1096 | "queue-microtask": { 1097 | "version": "1.2.3", 1098 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1099 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 1100 | }, 1101 | "quick-lru": { 1102 | "version": "5.1.1", 1103 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 1104 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" 1105 | }, 1106 | "read-cache": { 1107 | "version": "1.0.0", 1108 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 1109 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 1110 | "requires": { 1111 | "pify": "^2.3.0" 1112 | } 1113 | }, 1114 | "readdirp": { 1115 | "version": "3.6.0", 1116 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1117 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1118 | "requires": { 1119 | "picomatch": "^2.2.1" 1120 | } 1121 | }, 1122 | "resolve": { 1123 | "version": "1.22.1", 1124 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1125 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1126 | "requires": { 1127 | "is-core-module": "^2.9.0", 1128 | "path-parse": "^1.0.7", 1129 | "supports-preserve-symlinks-flag": "^1.0.0" 1130 | } 1131 | }, 1132 | "reusify": { 1133 | "version": "1.0.4", 1134 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1135 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" 1136 | }, 1137 | "run-parallel": { 1138 | "version": "1.2.0", 1139 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1140 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1141 | "requires": { 1142 | "queue-microtask": "^1.2.2" 1143 | } 1144 | }, 1145 | "source-map-js": { 1146 | "version": "1.0.2", 1147 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1148 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" 1149 | }, 1150 | "supports-preserve-symlinks-flag": { 1151 | "version": "1.0.0", 1152 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1153 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" 1154 | }, 1155 | "tailwindcss": { 1156 | "version": "3.2.7", 1157 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", 1158 | "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", 1159 | "requires": { 1160 | "arg": "^5.0.2", 1161 | "chokidar": "^3.5.3", 1162 | "color-name": "^1.1.4", 1163 | "detective": "^5.2.1", 1164 | "didyoumean": "^1.2.2", 1165 | "dlv": "^1.1.3", 1166 | "fast-glob": "^3.2.12", 1167 | "glob-parent": "^6.0.2", 1168 | "is-glob": "^4.0.3", 1169 | "lilconfig": "^2.0.6", 1170 | "micromatch": "^4.0.5", 1171 | "normalize-path": "^3.0.0", 1172 | "object-hash": "^3.0.0", 1173 | "picocolors": "^1.0.0", 1174 | "postcss": "^8.0.9", 1175 | "postcss-import": "^14.1.0", 1176 | "postcss-js": "^4.0.0", 1177 | "postcss-load-config": "^3.1.4", 1178 | "postcss-nested": "6.0.0", 1179 | "postcss-selector-parser": "^6.0.11", 1180 | "postcss-value-parser": "^4.2.0", 1181 | "quick-lru": "^5.1.1", 1182 | "resolve": "^1.22.1" 1183 | } 1184 | }, 1185 | "to-regex-range": { 1186 | "version": "5.0.1", 1187 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1188 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1189 | "requires": { 1190 | "is-number": "^7.0.0" 1191 | } 1192 | }, 1193 | "util-deprecate": { 1194 | "version": "1.0.2", 1195 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1196 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1197 | }, 1198 | "xtend": { 1199 | "version": "4.0.2", 1200 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1201 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 1202 | }, 1203 | "yaml": { 1204 | "version": "1.10.2", 1205 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 1206 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" 1207 | } 1208 | } 1209 | } 1210 | -------------------------------------------------------------------------------- /tests/fixtures/v3-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "tailwindcss": "3.2.7" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/fixtures/v3-2/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /tests/fixtures/v3-2/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | colors: { 5 | tomato: 'tomato', 6 | }, 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/plugins.test.js: -------------------------------------------------------------------------------- 1 | const prettier = require('prettier') 2 | const path = require('path') 3 | 4 | function format(str, options = {}) { 5 | return prettier 6 | .format(str, { 7 | pluginSearchDirs: [__dirname], // disable plugin autoload 8 | plugins: [ 9 | path.resolve(__dirname, '..'), 10 | ], 11 | semi: false, 12 | singleQuote: true, 13 | printWidth: 9999, 14 | parser: 'html', 15 | ...options, 16 | }) 17 | .trim() 18 | } 19 | 20 | 21 | let tests = [ 22 | { 23 | plugins: [ 24 | '@trivago/prettier-plugin-sort-imports', 25 | ], 26 | options: { 27 | importOrder: ["^@one/(.*)$", "^@two/(.*)$", "^[./]"], 28 | importOrderSortSpecifiers: true, 29 | }, 30 | tests: { 31 | babel: [ 32 | [ 33 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 34 | `import '@one/file'\nimport '@two/file'\nimport './three'`, 35 | ], 36 | ], 37 | typescript: [ 38 | [ 39 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 40 | `import '@one/file'\nimport '@two/file'\nimport './three'`, 41 | ], 42 | ], 43 | 44 | // This plugin does not support babel-ts 45 | 'babel-ts': [ 46 | [ 47 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 48 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 49 | ], 50 | ], 51 | } 52 | }, 53 | { 54 | plugins: [ 55 | '@ianvs/prettier-plugin-sort-imports', 56 | ], 57 | options: { 58 | importOrder: ["^@tailwindcss/(.*)$", "^@babel/(.*)$", "^[./]"], 59 | importOrderSortSpecifiers: true, 60 | }, 61 | tests: { 62 | babel: [ 63 | [ 64 | `import './i-haz-side-effects'\nimport i3 from './three'\nimport i2 from '@two/file'\nimport i1 from '@one/file'`, 65 | `import './i-haz-side-effects'\nimport i1 from '@one/file'\nimport i2 from '@two/file'\nimport i3 from './three'`, 66 | ], 67 | ], 68 | typescript: [ 69 | [ 70 | `import './i-haz-side-effects'\nimport i3 from './three'\nimport i2 from '@two/file'\nimport i1 from '@one/file'`, 71 | `import './i-haz-side-effects'\nimport i1 from '@one/file'\nimport i2 from '@two/file'\nimport i3 from './three'`, 72 | ], 73 | ], 74 | 75 | // This plugin does not support babel-ts 76 | 'babel-ts': [ 77 | [ 78 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 79 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 80 | ], 81 | ], 82 | } 83 | }, 84 | { 85 | plugins: [ 86 | 'prettier-plugin-organize-imports', 87 | ], 88 | options: {}, 89 | tests: { 90 | babel: [ 91 | [ 92 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 93 | `import '@one/file'\nimport '@two/file'\nimport './three'`, 94 | ], 95 | ], 96 | typescript: [ 97 | [ 98 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 99 | `import '@one/file'\nimport '@two/file'\nimport './three'`, 100 | ], 101 | ], 102 | 'babel-ts': [ 103 | [ 104 | `import './three'\nimport '@two/file'\nimport '@one/file'`, 105 | `import '@one/file'\nimport '@two/file'\nimport './three'`, 106 | ], 107 | ], 108 | } 109 | }, 110 | { 111 | plugins: [ 112 | 'prettier-plugin-twig-melody', 113 | ], 114 | options: { 115 | twigAlwaysBreakObjects: false, 116 | }, 117 | tests: { 118 | melody: [ 119 | [ 120 | `
`, 121 | `
`, 122 | ], 123 | [ 124 | `
`, 125 | `
`, 126 | ], 127 | ], 128 | } 129 | }, 130 | { 131 | plugins: [ 132 | '@prettier/plugin-pug', 133 | ], 134 | tests: { 135 | pug: [ 136 | [ 137 | `a(class='md:p-4 sm:p-0 p-4 bg-blue-600' href='//example.com') Example`, 138 | `a.bg-blue-600.p-4(class='sm:p-0 md:p-4', href='//example.com') Example`, 139 | ], 140 | [ 141 | `a.p-4.bg-blue-600(class='sm:p-0 md:p-4', href='//example.com') Example`, 142 | `a.bg-blue-600.p-4(class='sm:p-0 md:p-4', href='//example.com') Example`, 143 | ], 144 | 145 | // These two tests show how our sorting the two class lists separately is suboptimal 146 | // Two consecutive saves will result in different output 147 | // Where the second save is the most correct 148 | [ 149 | `a.p-4(class='bg-blue-600 sm:p-0 md:p-4', href='//example.com') Example`, 150 | `a.p-4.bg-blue-600(class='sm:p-0 md:p-4', href='//example.com') Example`, 151 | ], 152 | [ 153 | `a.p-4.bg-blue-600(class='sm:p-0 md:p-4', href='//example.com') Example`, 154 | `a.bg-blue-600.p-4(class='sm:p-0 md:p-4', href='//example.com') Example`, 155 | ], 156 | ], 157 | } 158 | }, 159 | { 160 | plugins: [ 161 | '@prettier/plugin-php', 162 | ], 163 | tests: { 164 | php: [ 165 | [ 166 | `\n
Example
\n \n
Example
\n
} 184 | `, 185 | `import '@one/file'\nimport '@two/file'\n\nimport './three'\n\nexport default function Foo() {\n return
\n}`, 186 | ], 187 | ], 188 | } 189 | }, 190 | { 191 | plugins: [ 192 | 'prettier-plugin-jsdoc', 193 | ], 194 | tests: { 195 | babel: [ 196 | [ 197 | `/**\n * @param { string } param0 description\n */\n export default function Foo(param0) { return
}`, 198 | `/** @param {string} param0 Description */\nexport default function Foo(param0) {\n return
\n}`, 199 | ], 200 | ], 201 | } 202 | }, 203 | { 204 | plugins: [ 205 | 'prettier-plugin-css-order', 206 | ], 207 | tests: { 208 | css: [ 209 | [ 210 | `.foo {\n color: red;\n background-color: blue;\n @apply sm:p-0 p-4 bg-blue-600;\n}`, 211 | `.foo {\n background-color: blue;\n color: red;\n @apply bg-blue-600 p-4 sm:p-0;\n}`, 212 | ], 213 | ], 214 | } 215 | }, 216 | { 217 | plugins: [ 218 | 'prettier-plugin-style-order', 219 | ], 220 | tests: { 221 | css: [ 222 | [ 223 | `.foo {\n color: red;\n margin-left: 1px;\n background-color: blue;\n margin-right: 1px;\n @apply sm:p-0 p-4 bg-blue-600;\n}`, 224 | `.foo {\n margin-right: 1px;\n margin-left: 1px;\n color: red;\n background-color: blue;\n @apply bg-blue-600 p-4 sm:p-0;\n}`, 225 | ], 226 | ], 227 | } 228 | }, 229 | { 230 | plugins: [ 231 | 'prettier-plugin-organize-attributes', 232 | ], 233 | tests: { 234 | html: [ 235 | [ 236 | `
Example`, 237 | `Example`, 238 | ], 239 | ], 240 | } 241 | }, 242 | { 243 | plugins: [ 244 | '@shopify/prettier-plugin-liquid', 245 | ], 246 | tests: { 247 | 'liquid-html': [ 248 | [ 249 | `Example`, 250 | `Example`, 251 | ], 252 | [ 253 | `{% if state == true %}\n Example\n{% endif %}`, 254 | `{% if state == true %}\n Example\n{% endif %}`, 255 | ], 256 | [ 257 | `{%- capture class_ordering -%}
{%- endcapture -%}`, 258 | `{%- capture class_ordering -%}
{%- endcapture -%}`, 259 | ], 260 | ], 261 | } 262 | }, 263 | ] 264 | 265 | for (const group of tests) { 266 | let name = group.plugins.join(', ') 267 | 268 | for (let parser in group.tests) { 269 | test(`parsing ${parser} works with: ${name}`, () => { 270 | let plugins = [ 271 | ...group.plugins.map(name => require.resolve(name)), 272 | path.resolve(__dirname, '..'), 273 | ] 274 | 275 | for (const [input, expected] of group.tests[parser]) { 276 | expect(format(input, { parser, plugins, ...group.options })).toEqual(expected) 277 | } 278 | }) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | const prettier = require('prettier') 2 | const path = require('path') 3 | const { execSync } = require('child_process') 4 | 5 | function format(str, options = {}) { 6 | options.plugins = options.plugins ?? [ 7 | require.resolve('prettier-plugin-astro'), 8 | require.resolve('prettier-plugin-svelte'), 9 | ] 10 | 11 | options.plugins = [ 12 | ...options.plugins, 13 | path.resolve(__dirname, '..'), 14 | ] 15 | 16 | return prettier 17 | .format(str, { 18 | pluginSearchDirs: [__dirname], // disable plugin autoload 19 | semi: false, 20 | singleQuote: true, 21 | printWidth: 9999, 22 | parser: 'html', 23 | ...options, 24 | }) 25 | .trim() 26 | } 27 | 28 | function formatFixture(name, extension = 'html') { 29 | let binPath = path.resolve(__dirname, '../node_modules/.bin/prettier') 30 | let filePath = path.resolve(__dirname, `fixtures/${name}/index.${extension}`) 31 | return execSync( 32 | `${binPath} ${filePath} --plugin-search-dir ${__dirname} --plugin ${path.resolve( 33 | __dirname, 34 | '..' 35 | )}` 36 | ) 37 | .toString() 38 | .trim() 39 | } 40 | 41 | let yes = '__YES__' 42 | let no = '__NO__' 43 | let testClassName = 'sm:p-0 p-0' 44 | let testClassNameSorted = 'p-0 sm:p-0' 45 | 46 | function t(strings, ...values) { 47 | let input = '' 48 | strings.forEach((string, i) => { 49 | input += string + (values[i] ? testClassName : '') 50 | }) 51 | 52 | let output = '' 53 | strings.forEach((string, i) => { 54 | let value = values[i] || '' 55 | if (value === yes) value = testClassNameSorted 56 | else if (value === no) value = testClassName 57 | output += string + value 58 | }) 59 | 60 | return [input, output] 61 | } 62 | 63 | let html = [ 64 | t`
`, 65 | t``, 66 | t`
`, 67 | t`
`, 68 | ['
', '
'], 69 | t`
`, 70 | t`
`, 71 | ] 72 | 73 | let css = [ 74 | t`@apply ${yes};`, 75 | t`/* @apply ${no}; */`, 76 | t`@not-apply ${no};`, 77 | ['@apply sm:p-0\n p-0;', '@apply p-0\n sm:p-0;'], 78 | ] 79 | 80 | let javascript = [ 81 | t`;
`, 82 | t`/*
*/`, 83 | t`//
`, 84 | t`;
`, 85 | t`;
`, 86 | t`;
`, 87 | t`;
`, 88 | t`;
`, 89 | t`;
`, 90 | t`;
`, 91 | t`;
`, 92 | t`;
`, 93 | t`;
`, 94 | t`;
`, 95 | [ 96 | `;
`, 97 | `;
`, 98 | ], 99 | [ 100 | `;
`, 101 | `;
`, 102 | ], 103 | [ 104 | `;
`, 105 | `;
`, 106 | ], 107 | ] 108 | javascript = javascript.concat( 109 | javascript.map((test) => test.map((t) => t.replace(/class/g, 'className'))) 110 | ) 111 | 112 | let vue = [ 113 | ...html, 114 | t`
`, 115 | t``, 116 | t`
`, 117 | t`
`, 118 | t`
`, 119 | t`
`, 120 | t`
`, 121 | t`
`, 122 | t`
`, 123 | t`
`, 124 | t`
`, 125 | t`
`, 126 | t`
`, // ts 127 | t`
`, // ts 128 | [ 129 | `
`, 130 | `
`, 131 | ], 132 | [ 133 | `
`, 134 | `
`, 135 | ], 136 | [ 137 | `
`, 138 | `
`, 139 | ], 140 | ] 141 | 142 | let glimmer = [ 143 | t`
`, 144 | t``, 145 | t`
`, 146 | t`
`, 147 | t`
`, 148 | t`
`, 149 | [ 150 | `
`, 151 | `
`, 152 | ], 153 | [ 154 | `
`, 155 | `
`, 156 | ], 157 | t`
`, 158 | ["
", "
"], 159 | t`
`, 160 | t`
`, 161 | t`{{link 'Some page' href=person.url class='${no}'}}`, 162 | t`
`, 163 | [ 164 | `
`, 165 | `
`, 166 | ], 167 | [ 168 | `
`, 169 | `
`, 170 | ], 171 | [ 172 | `
`, 173 | `
`, 174 | ], 175 | ] 176 | 177 | let tests = { 178 | html, 179 | glimmer, 180 | lwc: html, 181 | vue: [...vue, t`
`], 182 | angular: [ 183 | ...html, 184 | t`
`, 185 | t``, 186 | t`
`, 187 | t`
`, 188 | t`
`, 189 | t`
`, 190 | t`
`, 191 | t`
`, 192 | t`
`, 193 | t`
`, 194 | t`
`, 195 | t`
`, 196 | 197 | // TODO: Enable this test — it causes console noise but not a failure 198 | // t`
`, 199 | ], 200 | css: [...css, t`@apply ${yes} !important;`], 201 | scss: [...css, t`@apply ${yes} #{!important};`], 202 | less: [...css, t`@apply ${yes} !important;`], 203 | babel: javascript, 204 | typescript: javascript, 205 | 'babel-ts': javascript, 206 | flow: javascript, 207 | 'babel-flow': javascript, 208 | espree: javascript, 209 | meriyah: javascript, 210 | mdx: javascript 211 | .filter((test) => !test.find((t) => /^\/\*/.test(t))) 212 | .map((test) => test.map((t) => t.replace(/^;/, ''))), 213 | svelte: [ 214 | t`
`, 215 | t`
`, 216 | t`
`, 217 | t`
`, 218 | t`
`, 219 | t`
`, 220 | t`
`, 221 | t`
`, 222 | t`
`, 223 | t`
`, 224 | t`
`, 225 | t`
`, 226 | t`
`, 227 | t`{#if something}
{:else}
{/if}`, 228 | [ 229 | `
`, 230 | `
`, 231 | ], 232 | [ 233 | `
`, 234 | `
`, 235 | ], 236 | [ 237 | `
`, 238 | `
`, 239 | ], 240 | ['
', ''], 241 | [ 242 | `{#await promise()}
{:then}
{/await}`, 243 | `{#await promise()}
{:then}
{/await}`, 244 | ], 245 | [ 246 | `{#await promise() then}
{/await}`, 247 | `{#await promise() then}
{/await}`, 248 | ], 249 | ], 250 | astro: [ 251 | ...html, 252 | [ 253 | `{
}`, 254 | `{(
)}`, 255 | ], 256 | [ 257 | ``, 262 | ``, 267 | ], 268 | t`--- 269 | import Layout from '../layouts/Layout.astro' 270 | import Custom from '../components/Custom.astro' 271 | --- 272 | 273 | 274 |
275 | 276 | 277 |
`, 278 | ], 279 | }; 280 | 281 | describe('parsers', () => { 282 | for (let parser in tests) { 283 | test(parser, () => { 284 | for (let [input, expected] of tests[parser]) { 285 | expect(format(input, { parser })).toEqual(expected) 286 | } 287 | }) 288 | } 289 | }) 290 | 291 | test('non-tailwind classes', () => { 292 | expect(format('
')).toEqual( 293 | '
' 294 | ) 295 | }) 296 | 297 | test('no prettier config', () => { 298 | expect(formatFixture('no-prettier-config')).toEqual('
') 299 | }) 300 | 301 | test('parasite utilities', () => { 302 | expect(format('
')).toEqual( 303 | '
' 304 | ) 305 | }) 306 | 307 | test('inferred config path', () => { 308 | expect(formatFixture('basic')).toEqual('
') 309 | }) 310 | 311 | test('inferred config path (.cjs)', () => { 312 | expect(formatFixture('cjs')).toEqual('
') 313 | }) 314 | 315 | test('using esm config', () => { 316 | expect(formatFixture('esm')).toEqual('
') 317 | }) 318 | 319 | test('using esm config (explicit path)', () => { 320 | expect(formatFixture('esm-explicit')).toEqual('
') 321 | }) 322 | 323 | test('using ts config', () => { 324 | expect(formatFixture('ts')).toEqual('
') 325 | }) 326 | 327 | test('using ts config (explicit path)', () => { 328 | expect(formatFixture('ts-explicit')).toEqual('
') 329 | }) 330 | 331 | test('using v3.2.7', () => { 332 | expect(formatFixture('v3-2')).toEqual('
') 333 | }) 334 | 335 | test('explicit config path', () => { 336 | expect( 337 | format('
', { 338 | tailwindConfig: path.resolve(__dirname, 'fixtures/basic/tailwind.config.js'), 339 | }) 340 | ).toEqual('
') 341 | }) 342 | 343 | test('plugins', () => { 344 | expect(formatFixture('plugins')).toEqual( 345 | '
' 346 | ) 347 | }) 348 | 349 | test('jsx customizations', () => { 350 | expect(formatFixture('custom-jsx', 'jsx').trim()).toEqual( 351 | `const A = (props) => { 352 | return