diff --git a/package-lock.json b/package-lock.json index 20a37e4..3bd917b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@microsoft/api-extractor": "^7.52.9", "@prettier/plugin-hermes": "^0.0.3", "@prettier/plugin-oxc": "^0.0.4", - "@prettier/plugin-pug": "^3.4", + "@prettier/plugin-pug": "^3.4.0", "@shopify/prettier-plugin-liquid": "^1.9.3", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/node": "^24.1.0", @@ -32,11 +32,11 @@ "license-checker": "^25.0.1", "line-column": "^1.0.2", "marko": "^5.37.42", - "postcss": "^8.4.35", + "postcss": "^8.5.6", "postcss-import": "^16.1.1", - "prettier": "^3.6", + "prettier": "^3.6.2", "prettier-plugin-astro": "^0.14.1", - "prettier-plugin-css-order": "^2.0.0", + "prettier-plugin-css-order": "^2.1.2", "prettier-plugin-jsdoc": "^1.3.3", "prettier-plugin-marko": "^3.3.0", "prettier-plugin-multiline-arrays": "^4.0.3", @@ -2822,18 +2822,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -2878,39 +2866,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", - "dev": true, - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-callsite/node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", - "dev": true, - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3176,21 +3131,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/cp-file": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-10.0.0.tgz", @@ -3407,15 +3347,6 @@ "node": ">=6" } }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -3496,15 +3427,6 @@ "node": ">=10.13.0" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/error-stack-parser": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", @@ -3704,18 +3626,6 @@ "node": ">=8" } }, - "node_modules/find-line-column": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/find-line-column/-/find-line-column-0.5.2.tgz", - "integrity": "sha512-eNhNkDt5RbxY4X++JwyDURP62FYhV1bh9LF4dfOiwpVCTk5vvfEANhnui5ypUEELGR02QZSrWFtaTgd4ulW5tw==", - "dev": true - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, "node_modules/fix-dts-default-cjs-exports": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", @@ -4009,28 +3919,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", - "dev": true, - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -4041,87 +3929,6 @@ "node": ">=8" } }, - "node_modules/import-sort": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort/-/import-sort-6.0.0.tgz", - "integrity": "sha512-XUwSQMGAGmcW/wfshFE0gXgb1NPF6ibbQD6wDr3KRDykZf/lZj0jf58Bwa02xNb8EE59oz7etFe9OHnJocUW5Q==", - "dev": true, - "dependencies": { - "detect-newline": "^2.1.0", - "import-sort-parser": "^6.0.0", - "import-sort-style": "^6.0.0", - "is-builtin-module": "^3.0.0", - "resolve": "^1.8.1" - } - }, - "node_modules/import-sort-config": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort-config/-/import-sort-config-6.0.0.tgz", - "integrity": "sha512-FJpF2F3+30JXqH1rJKeajxoSCHCueai3/0ntDN4y3GJL5pjnLDt/VjCy5FzjH7u0NHnllL/zVEf1wfmsVxJlPQ==", - "dev": true, - "dependencies": { - "cosmiconfig": "^5.0.5", - "find-root": "^1.0.0", - "minimatch": "^3.0.4", - "resolve-from": "^4.0.0" - } - }, - "node_modules/import-sort-config/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-sort-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort-parser/-/import-sort-parser-6.0.0.tgz", - "integrity": "sha512-H5L+d6HnqHvThB0GmAA3/43Sv74oCwL0iMk3/ixOv0LRJ69rCyHXeG/+UadMHrD2FefEmgPIWboEPAG7gsQrkA==", - "dev": true - }, - "node_modules/import-sort-parser-babylon": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort-parser-babylon/-/import-sort-parser-babylon-6.0.0.tgz", - "integrity": "sha512-NyShTiNhTh4Vy7kJUVe6CuvOaQAzzfSIT72wtp3CzGjz8bHjNj59DCAjncuviicmDOgVAgmLuSh1WMcLYAMWGg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.2.2", - "@babel/parser": "^7.0.0-beta.54", - "@babel/traverse": "^7.0.0-beta.54", - "@babel/types": "^7.0.0-beta.54", - "find-line-column": "^0.5.2" - } - }, - "node_modules/import-sort-parser-typescript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort-parser-typescript/-/import-sort-parser-typescript-6.0.0.tgz", - "integrity": "sha512-pgxnr3I156DonupQriNsgDb2zJN9TxrqCCIN1rwT/6SDO1rkJb+a0fjqshCjlgacTSA92oPAp1eAwmQUeZi3dw==", - "dev": true, - "dependencies": { - "typescript": "^3.2.4" - } - }, - "node_modules/import-sort-parser-typescript/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/import-sort-style": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/import-sort-style/-/import-sort-style-6.0.0.tgz", - "integrity": "sha512-z0H5PKs7YoDeKxNYXv2AA1mjjZFY07fjeNCXUdTM3ymJtWeeEoTm8CQkFm2l+KPZoMczIvdwzJpWkkOamBnsPw==", - "dev": true - }, "node_modules/import-sort-style-module": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/import-sort-style-module/-/import-sort-style-module-6.0.0.tgz", @@ -4157,12 +3964,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4175,21 +3976,6 @@ "node": ">=8" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-core-module": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", @@ -4205,15 +3991,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-expression": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", @@ -4367,19 +4144,6 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -4393,12 +4157,6 @@ "node": ">=6" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5534,19 +5292,6 @@ "node": ">=8" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -5935,21 +5680,6 @@ "prettier": "3.x" } }, - "node_modules/prettier-plugin-import-sort": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/prettier-plugin-import-sort/-/prettier-plugin-import-sort-0.0.7.tgz", - "integrity": "sha512-O0KlUSq+lwvh+UiN3wZDT6wWkf7TNxTVv2/XXE5KqpRNbFJq3nRg2ftzBYFFO8QGpdWIrOB0uCTCtFjIxmVKQw==", - "dev": true, - "dependencies": { - "import-sort": "^6.0.0", - "import-sort-config": "^6.0.0", - "import-sort-parser-babylon": "^6.0.0", - "import-sort-parser-typescript": "^6.0.0" - }, - "peerDependencies": { - "prettier": ">= 2.0" - } - }, "node_modules/prettier-plugin-jsdoc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/prettier-plugin-jsdoc/-/prettier-plugin-jsdoc-1.3.3.tgz", diff --git a/prettier.config.js b/prettier.config.js index eb3d94a..9db12a9 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -5,4 +5,5 @@ export default { trailingComma: 'all', plugins: ['@ianvs/prettier-plugin-sort-imports'], importOrder: ['^@', '^[a-zA-Z0-9-]+', '^[./]'], + printWidth: 120, } diff --git a/scripts/install-fixture-deps.js b/scripts/install-fixture-deps.js index cfc7b5c..8ed2d99 100644 --- a/scripts/install-fixture-deps.js +++ b/scripts/install-fixture-deps.js @@ -7,7 +7,11 @@ import glob from 'fast-glob' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const fixtures = glob.sync( - ['tests/fixtures/*/package.json', 'tests/fixtures/v4/*/package.json'], + [ + 'tests/fixtures/*/package.json', + 'tests/fixtures/v4/*/package.json', + 'tests/fixtures/monorepo/*/package.json', + ], { cwd: path.resolve(__dirname, '..'), }, diff --git a/src/config.ts b/src/config.ts index 9c3db9e..47da03d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,374 +1,213 @@ // @ts-check -import * as fs from 'fs/promises' import * as path from 'path' import { pathToFileURL } from 'url' -import clearModule from 'clear-module' import escalade from 'escalade/sync' -import { createJiti, type Jiti } from 'jiti' -import postcss from 'postcss' -// @ts-ignore -import postcssImport from 'postcss-import' import prettier from 'prettier' import type { ParserOptions } from 'prettier' -// @ts-ignore -import { generateRules as generateRulesFallback } from 'tailwindcss/lib/lib/generateRules' -// @ts-ignore -import { createContext as createContextFallback } from 'tailwindcss/lib/lib/setupContextUtils' -import loadConfigFallback from 'tailwindcss/loadConfig' -import resolveConfigFallback from 'tailwindcss/resolveConfig' -import type { RequiredConfig } from 'tailwindcss/types/config.js' import { expiringMap } from './expiring-map.js' -import { resolveCssFrom, resolveJsFrom } from './resolve' -import type { ContextContainer } from './types' - -let sourceToPathMap = new Map() -let sourceToEntryMap = new Map() -let pathToContextMap = expiringMap(10_000) -let prettierConfigCache = expiringMap(10_000) - -export async function getTailwindConfig( - options: ParserOptions, -): Promise { - let pkgName = options.tailwindPackageName ?? 'tailwindcss' - - let key = [ - options.filepath, - options.tailwindStylesheet ?? '', - options.tailwindEntryPoint ?? '', - options.tailwindConfig ?? '', - pkgName, - ].join(':') - - let baseDir = await getBaseDir(options) - - // Map the source file to it's associated Tailwind config file - let configPath = sourceToPathMap.get(key) - if (configPath === undefined) { - configPath = getConfigPath(options, baseDir) - sourceToPathMap.set(key, configPath) - } - - let entryPoint = sourceToEntryMap.get(key) - if (entryPoint === undefined) { - entryPoint = getEntryPoint(options, baseDir) - sourceToEntryMap.set(key, entryPoint) - } - - // Now see if we've loaded the Tailwind config file before (and it's still valid) - let contextKey = `${pkgName}:${configPath}:${entryPoint}` - let existing = pathToContextMap.get(contextKey) - if (existing) { - return existing +import { resolveJsFrom } from './resolve' +import type { UnifiedApi } from './types' +import { loadV3 } from './versions/v3' +import { loadV4 } from './versions/v4' + +let pathToApiMap = expiringMap>(10_000) + +export async function getTailwindConfig(options: ParserOptions): Promise { + let cwd = process.cwd() + + // Locate the file being processed + // + // We'll resolve auto-detected paths relative to this path. + // + // Examples: + // - Tailwind CSS itself + // - Automatically found v3 configs + let inputDir = options.filepath ? path.dirname(options.filepath) : cwd + + // Locate the prettier config + // + // We'll resolve paths defined in the config relative to this path. + // + // Examples: + // - A project's stylesheet + // + // These lookups can take a bit so we cache them. This is especially important + // for files with lots of embedded languages (e.g. Vue bindings). + let configDir = await resolvePrettierConfigPath(options.filepath) + + // Locate Tailwind CSS itself + // + // We resolve this like we're in `inputDir` for better monorepo support as + // Prettier may be configured at the workspace root but Tailwind CSS is + // installed for a workspace package rather than the entire monorepo + let [mod, pkgDir] = await resolveTailwindPath(options, inputDir) + + // Locate the v4 stylesheet relative to the prettier config file + // + // We resolve this relative to the config file because it is *required* + // to work with a project's custom config. Given that, resolving it + // relative to where the path is defined makes the most sense. + let stylesheet = resolveStylesheet(options, configDir) + + // Locate the closest v3 config file + // + // Note: + // We only need to do this when a stylesheet has not been provided otherwise + // we'd know for sure this was a v4 project regardless of what local Tailwind + // CSS installation is present. Additionally, if the local version is v4 we + // can skip this as well. + // + // The config path is resolved in one of two ways: + // + // 1. When automatic, relative to the input file + // + // This ensures monorepos can load the "closest" JS config for a given file + // which is important when a workspace package includes Tailwind CSS *and* + // Prettier is configured globally instead of per-package. + // + // + // 2. When explicit via `tailwindConfig`, relative to the prettier config + // + // For the same reasons as the v4 stylesheet, it's important that the config + // file be resolved relative to the file it's configured in. + if (!stylesheet && !mod?.__unstable__loadDesignSystem) { + let jsConfig = resolveJsConfigPath(options, configDir, inputDir) + if (jsConfig) { + return pathToApiMap.remember(`${pkgDir}:${jsConfig}`, () => loadV3(pkgDir, jsConfig)) + } } - // By this point we know we need to load the Tailwind config file - let result = await loadTailwindConfig( - baseDir, - pkgName, - configPath, - entryPoint, - ) - - pathToContextMap.set(contextKey, result) - - return result -} - -async function getPrettierConfigPath( - options: ParserOptions, -): Promise { - // Locating the config file can be mildly expensive so we cache it temporarily - let existingPath = prettierConfigCache.get(options.filepath) - if (existingPath !== undefined) { - return existingPath + // The user specified a stylesheet but their local install is not v4 + // so we'll load the fallback version instead + if (mod && !mod.__unstable__loadDesignSystem) { + mod = null + if (stylesheet) { + console.error( + 'You have specified a Tailwind CSS stylesheet but your version of Tailwind CSS does not support this feature.', + ) + } + } else if (mod && pkgDir) { + // If the user hasn't provided a stylesheet then we'll load the default + // theme from Tailwind CSS itself. + stylesheet ??= `${pkgDir}/theme.css` } - let path = await prettier.resolveConfigFile(options.filepath) - prettierConfigCache.set(options.filepath, path) - - return path -} - -async function getBaseDir(options: ParserOptions): Promise { - let prettierConfigPath = await getPrettierConfigPath(options) - - if (options.tailwindConfig) { - return prettierConfigPath ? path.dirname(prettierConfigPath) : process.cwd() + if (stylesheet) { + return pathToApiMap.remember(`${pkgDir}:${stylesheet}`, () => loadV4(mod, stylesheet)) } - if (options.tailwindEntryPoint) { - return prettierConfigPath ? path.dirname(prettierConfigPath) : process.cwd() - } + // Current fallback is v3 + return pathToApiMap.remember(null, () => loadV3(null, null)) - return prettierConfigPath - ? path.dirname(prettierConfigPath) - : options.filepath - ? path.dirname(options.filepath) - : process.cwd() + // Move to default fallback of v4 + return pathToApiMap.remember(null, () => loadV4(null, null)) } -async function loadTailwindConfig( - baseDir: string, - pkgName: string, - tailwindConfigPath: string | null, - entryPoint: string | null, -): Promise { - let createContext = createContextFallback - let generateRules = generateRulesFallback - let resolveConfig = resolveConfigFallback - let loadConfig = loadConfigFallback - let tailwindConfig: RequiredConfig = { content: [] } - - try { - let pkgFile = resolveJsFrom(baseDir, `${pkgName}/package.json`) - let pkgDir = path.dirname(pkgFile) - - try { - let v4 = await loadV4(baseDir, pkgDir, pkgName, entryPoint) - if (v4) { - return v4 - } - } catch {} - - resolveConfig = require(path.join(pkgDir, 'resolveConfig')) - createContext = require( - path.join(pkgDir, 'lib/lib/setupContextUtils'), - ).createContext - generateRules = require( - path.join(pkgDir, 'lib/lib/generateRules'), - ).generateRules - - // Prior to `tailwindcss@3.3.0` this won't exist so we load it last - loadConfig = require(path.join(pkgDir, 'loadConfig')) - } catch {} - - if (tailwindConfigPath) { - clearModule(tailwindConfigPath) - const loadedConfig = loadConfig(tailwindConfigPath) - tailwindConfig = loadedConfig.default ?? loadedConfig - } - - // suppress "empty content" warning - tailwindConfig.content = ['no-op'] - - // Create the context - let context = createContext(resolveConfig(tailwindConfig)) - - return { - context, - generateRules, - } -} +let prettierConfigCache = expiringMap>(10_000) -/** - * Create a loader function that can load plugins and config files relative to - * the CSS file that uses them. However, we don't want missing files to prevent - * everything from working so we'll let the error handler decide how to proceed. - */ -function createLoader({ - legacy, - jiti, - filepath, - onError, -}: { - legacy: boolean - jiti: Jiti - filepath: string - onError: (id: string, error: unknown, resourceType: string) => T -}) { - let cacheKey = `${+Date.now()}` - - async function loadFile(id: string, base: string, resourceType: string) { +async function resolvePrettierConfigPath(filePath: string): Promise { + let prettierConfig = await prettierConfigCache.remember(filePath, async () => { try { - let resolved = resolveJsFrom(base, id) - - let url = pathToFileURL(resolved) - url.searchParams.append('t', cacheKey) - - return await jiti.import(url.href, { default: true }) + return await prettier.resolveConfigFile(filePath) } catch (err) { - return onError(id, err, resourceType) + console.error('Failed to resolve Prettier Config') + console.error(err) + return null } - } - - if (legacy) { - let baseDir = path.dirname(filepath) - return (id: string) => loadFile(id, baseDir, 'module') - } + }) - return async (id: string, base: string, resourceType: string) => { - return { - base, - module: await loadFile(id, base, resourceType), - } - } + return prettierConfig ? path.dirname(prettierConfig) : process.cwd() } -async function loadV4( - baseDir: string, - pkgDir: string, - pkgName: string, - entryPoint: string | null, -) { - // Import Tailwind — if this is v4 it'll have APIs we can use directly - let pkgPath = resolveJsFrom(baseDir, pkgName) - - let tw = await import(pathToFileURL(pkgPath).toString()) +let resolvedModCache = expiringMap>(10_000) - // This is not Tailwind v4 - if (!tw.__unstable__loadDesignSystem) { - return null - } +async function resolveTailwindPath(options: ParserOptions, baseDir: string): Promise<[any | null, string | null]> { + let pkgName = options.tailwindPackageName ?? 'tailwindcss' - // If the user doesn't define an entrypoint then we use the default theme - entryPoint = entryPoint ?? `${pkgDir}/theme.css` + return await resolvedModCache.remember(`${pkgName}:${baseDir}`, async () => { + let pkgDir: string | null = null + let mod: any | null = null - // Create a Jiti instance that can be used to load plugins and config files - let jiti = createJiti(import.meta.url, { - moduleCache: false, - fsCache: false, - }) + try { + let pkgPath = resolveJsFrom(baseDir, pkgName) + mod = await import(pathToFileURL(pkgPath).toString()) - let importBasePath = path.dirname(entryPoint) - - // Resolve imports in the entrypoint to a flat CSS tree - let css = await fs.readFile(entryPoint, 'utf-8') - - // Determine if the v4 API supports resolving `@import` - let supportsImports = false - try { - await tw.__unstable__loadDesignSystem('@import "./empty";', { - loadStylesheet: () => { - supportsImports = true - return { - base: importBasePath, - content: '', - } - }, - }) - } catch {} - - if (!supportsImports) { - let resolveImports = postcss([postcssImport()]) - let result = await resolveImports.process(css, { from: entryPoint }) - css = result.css - } + let pkgFile = resolveJsFrom(baseDir, `${pkgName}/package.json`) + pkgDir = path.dirname(pkgFile) + } catch {} - // Load the design system and set up a compatible context object that is - // usable by the rest of the plugin - let design = await tw.__unstable__loadDesignSystem(css, { - base: importBasePath, - - // v4.0.0-alpha.25+ - loadModule: createLoader({ - legacy: false, - jiti, - filepath: entryPoint, - onError: (id, err, resourceType) => { - console.error(`Unable to load ${resourceType}: ${id}`, err) - - if (resourceType === 'config') { - return {} - } else if (resourceType === 'plugin') { - return () => {} - } - }, - }), - - loadStylesheet: async (id: string, base: string) => { - let resolved = resolveCssFrom(base, id) - - return { - base: path.dirname(resolved), - content: await fs.readFile(resolved, 'utf-8'), - } - }, - - // v4.0.0-alpha.24 and below - loadPlugin: createLoader({ - legacy: true, - jiti, - filepath: entryPoint, - onError(id, err) { - console.error(`Unable to load plugin: ${id}`, err) - - return () => {} - }, - }), - - loadConfig: createLoader({ - legacy: true, - jiti, - filepath: entryPoint, - onError(id, err) { - console.error(`Unable to load config: ${id}`, err) - - return {} - }, - }), + return [mod, pkgDir] as const }) - - return { - context: { - getClassOrder: (classList: string[]) => design.getClassOrder(classList), - }, - - // Stubs that are not needed for v4 - generateRules: () => [], - } } -function getConfigPath(options: ParserOptions, baseDir: string): string | null { +let configPathCache = new Map() +function resolveJsConfigPath(options: ParserOptions, configDir: string, inputDir: string): string | null { if (options.tailwindConfig) { - if (options.tailwindConfig.endsWith('.css')) { - return null - } + if (options.tailwindConfig.endsWith('.css')) return null - return path.resolve(baseDir, options.tailwindConfig) + return path.resolve(configDir, options.tailwindConfig) } - let configPath: string | void = undefined - try { - configPath = escalade(baseDir, (_dir, names) => { - if (names.includes('tailwind.config.js')) { - return 'tailwind.config.js' - } - if (names.includes('tailwind.config.cjs')) { - return 'tailwind.config.cjs' - } - if (names.includes('tailwind.config.mjs')) { - return 'tailwind.config.mjs' - } - if (names.includes('tailwind.config.ts')) { - return 'tailwind.config.ts' - } - }) - } catch {} - - if (configPath) { - return configPath + let configPath: string | null | undefined = configPathCache.get(inputDir) + + if (configPath === undefined) { + try { + let foundPath = escalade(inputDir, (_, names) => { + if (names.includes('tailwind.config.js')) return 'tailwind.config.js' + if (names.includes('tailwind.config.cjs')) return 'tailwind.config.cjs' + if (names.includes('tailwind.config.mjs')) return 'tailwind.config.mjs' + if (names.includes('tailwind.config.ts')) return 'tailwind.config.ts' + }) + + configPath = foundPath ?? null + } catch {} + + configPath ??= null + configPathCache.set(inputDir, configPath) } - return null + return configPath } -function getEntryPoint(options: ParserOptions, baseDir: string): string | null { +function resolveStylesheet(options: ParserOptions, baseDir: string): string | null { if (options.tailwindStylesheet) { + if ( + options.tailwindStylesheet.endsWith('.js') || + options.tailwindStylesheet.endsWith('.mjs') || + options.tailwindStylesheet.endsWith('.cjs') || + options.tailwindStylesheet.endsWith('.ts') || + options.tailwindStylesheet.endsWith('.mts') || + options.tailwindStylesheet.endsWith('.cts') + ) { + console.error( + "Your `tailwindStylesheet` option points to a JS/TS config file. You must point to your project's `.css` file for v4 projects.", + ) + } else if ( + options.tailwindStylesheet.endsWith('.sass') || + options.tailwindStylesheet.endsWith('.scss') || + options.tailwindStylesheet.endsWith('.less') || + options.tailwindStylesheet.endsWith('.styl') + ) { + console.error( + 'Your `tailwindStylesheet` option points to a preprocessor file. This is unsupported and you may get unexpected results.', + ) + } else if (!options.tailwindStylesheet.endsWith('.css')) { + console.error( + 'Your `tailwindStylesheet` option does not point to a CSS file. This is unsupported and you may get unexpected results.', + ) + } + return path.resolve(baseDir, options.tailwindStylesheet) } if (options.tailwindEntryPoint) { - console.warn( - 'Use the `tailwindStylesheet` option for v4 projects instead of `tailwindEntryPoint`.', - ) + console.warn('Deprecated: Use the `tailwindStylesheet` option for v4 projects instead of `tailwindEntryPoint`.') return path.resolve(baseDir, options.tailwindEntryPoint) } if (options.tailwindConfig && options.tailwindConfig.endsWith('.css')) { - console.warn( - 'Use the `tailwindStylesheet` option for v4 projects instead of `tailwindConfig`.', - ) + console.warn('Deprecated: Use the `tailwindStylesheet` option for v4 projects instead of `tailwindConfig`.') return path.resolve(baseDir, options.tailwindConfig) } diff --git a/src/expiring-map.ts b/src/expiring-map.ts index 58d1816..7a2bc38 100644 --- a/src/expiring-map.ts +++ b/src/expiring-map.ts @@ -1,5 +1,6 @@ interface ExpiringMap { get(key: K): V | undefined + remember(key: K, factory: () => V): V set(key: K, value: V): void } @@ -9,13 +10,28 @@ export function expiringMap(duration: number): ExpiringMap { return { get(key: K) { let result = map.get(key) - if (!result) return undefined - if (result.expiration <= new Date()) { - map.delete(key) - return undefined + + if (result && result.expiration > new Date()) { + return result.value } - return result.value + map.delete(key) + + return undefined + }, + + remember(key: K, factory: () => V) { + let result = map.get(key) + + if (result && result.expiration > new Date()) { + return result.value + } + + map.delete(key) + + let value = factory() + this.set(key, value) + return value }, set(key: K, value: V) { diff --git a/src/index.ts b/src/index.ts index 933d3ea..e611a78 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,5 @@ // @ts-ignore -import type { - AttrDoubleQuoted, - AttrSingleQuoted, -} from '@shopify/prettier-plugin-liquid/dist/types.js' +import type { AttrDoubleQuoted, AttrSingleQuoted } from '@shopify/prettier-plugin-liquid/dist/types.js' import * as astTypes from 'ast-types' // @ts-ignore import jsesc from 'jsesc' @@ -17,13 +14,7 @@ import { getTailwindConfig } from './config.js' import { getCustomizations } from './options.js' import { loadPlugins } from './plugins.js' import { sortClasses, sortClassList } from './sorting.js' -import type { - Customizations, - StringChange, - TransformerContext, - TransformerEnv, - TransformerMetadata, -} from './types' +import type { Customizations, StringChange, TransformerContext, TransformerEnv, TransformerMetadata } from './types' import { spliceChangesIntoString, visit } from './utils.js' let base = await loadPlugins() @@ -51,7 +42,7 @@ function createParser( }, async parse(text: string, options: ParserOptions) { - let { context, generateRules } = await getTailwindConfig(options) + let context = await getTailwindConfig(options) let original = base.originalParser(parserFormat, options) @@ -62,16 +53,12 @@ function createParser( // @ts-ignore: We pass three options in the case of plugins that support Prettier 2 _and_ 3. let ast = await original.parse(text, options, options) - let customizations = getCustomizations( - options, - parserFormat, - customizationDefaults, - ) + let customizations = getCustomizations(options, parserFormat, customizationDefaults) let changes: any[] = [] transform(ast, { - env: { context, customizations, generateRules, parsers: {}, options }, + env: { context, customizations, parsers: {}, options }, changes, }) @@ -123,11 +110,7 @@ function transformDynamicAngularAttribute(attr: any, env: TransformerEnv) { if (!node.value) return let concat = path.find((entry) => { - return ( - entry.parent && - entry.parent.type === 'BinaryExpression' && - entry.parent.operator === '+' - ) + return entry.parent && entry.parent.type === 'BinaryExpression' && entry.parent.operator === '+' }) changes.push({ @@ -148,11 +131,7 @@ function transformDynamicAngularAttribute(attr: any, env: TransformerEnv) { if (!node.quasis.length) return let concat = path.find((entry) => { - return ( - entry.parent && - entry.parent.type === 'BinaryExpression' && - entry.parent.operator === '+' - ) + return entry.parent && entry.parent.type === 'BinaryExpression' && entry.parent.operator === '+' }) for (let i = 0; i < node.quasis.length; i++) { @@ -170,8 +149,7 @@ function transformDynamicAngularAttribute(attr: any, env: TransformerEnv) { // Is between two expressions // And does not end with a space - ignoreLast: - i < node.expressions.length && !/\s$/.test(quasi.value.raw), + ignoreLast: i < node.expressions.length && !/\s$/.test(quasi.value.raw), collapseWhitespace: { start: concat?.key !== 'right' && i === 0, @@ -193,9 +171,7 @@ function transformDynamicJsAttribute(attr: any, env: TransformerEnv) { parser: prettierParserBabel.parsers['babel-ts'], }) - function* ancestors( - path: import('ast-types/lib/node-path').NodePath, - ) { + function* ancestors(path: import('ast-types/lib/node-path').NodePath) { yield path while (path.parentPath) { @@ -369,11 +345,7 @@ function transformGlimmer(ast: any, { env }: TransformerContext) { } let concat = path.find((entry) => { - return ( - entry.parent && - entry.parent.type === 'SubExpression' && - entry.parent.path.original === 'concat' - ) + return entry.parent && entry.parent.type === 'SubExpression' && entry.parent.path.original === 'concat' }) node.value = sortClasses(node.value, { @@ -391,13 +363,9 @@ function transformGlimmer(ast: any, { env }: TransformerContext) { function transformLiquid(ast: any, { env }: TransformerContext) { let { staticAttrs } = env.customizations - function isClassAttr(node: { - name: string | { type: string; value: string }[] - }) { + function isClassAttr(node: { name: string | { type: string; value: string }[] }) { return Array.isArray(node.name) - ? node.name.every( - (n) => n.type === 'TextNode' && staticAttrs.has(n.value), - ) + ? node.name.every((n) => n.type === 'TextNode' && staticAttrs.has(n.value)) : staticAttrs.has(node.name) } @@ -545,10 +513,7 @@ function sortStringLiteral( } function isStringLiteral(node: any) { - return ( - node.type === 'StringLiteral' || - (node.type === 'Literal' && typeof node.value === 'string') - ) + return node.type === 'StringLiteral' || (node.type === 'Literal' && typeof node.value === 'string') } function sortTemplateLiteral( @@ -583,10 +548,7 @@ function sortTemplateLiteral( collapseWhitespace: collapseWhitespace && { start: collapseWhitespace && collapseWhitespace.start && i === 0, - end: - collapseWhitespace && - collapseWhitespace.end && - i >= node.expressions.length, + end: collapseWhitespace && collapseWhitespace.end && i >= node.expressions.length, }, }) @@ -595,22 +557,15 @@ function sortTemplateLiteral( : sortClasses(quasi.value.cooked, { env, ignoreFirst: i > 0 && !/^\s/.test(quasi.value.cooked), - ignoreLast: - i < node.expressions.length && !/\s$/.test(quasi.value.cooked), + ignoreLast: i < node.expressions.length && !/\s$/.test(quasi.value.cooked), removeDuplicates, collapseWhitespace: collapseWhitespace && { start: collapseWhitespace && collapseWhitespace.start && i === 0, - end: - collapseWhitespace && - collapseWhitespace.end && - i >= node.expressions.length, + end: collapseWhitespace && collapseWhitespace.end && i >= node.expressions.length, }, }) - if ( - quasi.value.raw !== originalRaw || - quasi.value.cooked !== originalCooked - ) { + if (quasi.value.raw !== originalRaw || quasi.value.cooked !== originalCooked) { didChange = true } } @@ -619,18 +574,14 @@ function sortTemplateLiteral( } function isSortableTemplateExpression( - node: - | import('@babel/types').TaggedTemplateExpression - | import('ast-types').namedTypes.TaggedTemplateExpression, + node: import('@babel/types').TaggedTemplateExpression | import('ast-types').namedTypes.TaggedTemplateExpression, functions: Set, ): boolean { return isSortableExpression(node.tag, functions) } function isSortableCallExpression( - node: - | import('@babel/types').CallExpression - | import('ast-types').namedTypes.CallExpression, + node: import('@babel/types').CallExpression | import('ast-types').namedTypes.CallExpression, functions: Set, ): boolean { if (!node.arguments?.length) return false @@ -665,20 +616,13 @@ function isSortableExpression( // // We cross several parsers that share roughly the same shape so things are // good enough. The actual AST we should be using is probably estree + ts. -function transformJavaScript( - ast: import('@babel/types').Node, - { env }: TransformerContext, -) { +function transformJavaScript(ast: import('@babel/types').Node, { env }: TransformerContext) { let { staticAttrs, functions } = env.customizations function sortInside(ast: import('@babel/types').Node) { visit(ast, (node, path) => { let concat = path.find((entry) => { - return ( - entry.parent && - entry.parent.type === 'BinaryExpression' && - entry.parent.operator === '+' - ) + return entry.parent && entry.parent.type === 'BinaryExpression' && entry.parent.operator === '+' }) if (isStringLiteral(node)) { @@ -754,11 +698,7 @@ function transformJavaScript( } let concat = path.find((entry) => { - return ( - entry.parent && - entry.parent.type === 'BinaryExpression' && - entry.parent.operator === '+' - ) + return entry.parent && entry.parent.type === 'BinaryExpression' && entry.parent.operator === '+' }) sortTemplateLiteral(node.quasi, { @@ -797,18 +737,12 @@ function transformCss(ast: any, { env }: TransformerContext) { } ast.walk((node: any) => { - if ( - node.name === 'plugin' || - node.name === 'config' || - node.name === 'source' - ) { + if (node.name === 'plugin' || node.name === 'config' || node.name === 'source') { node.params = tryParseAtRuleParams(node.name, node.params) } if (node.type === 'css-atrule' && node.name === 'apply') { - let isImportant = /\s+(?:!important|#{(['"]*)!important\1})\s*$/.test( - node.params, - ) + let isImportant = /\s+(?:!important|#{(['"]*)!important\1})\s*$/.test(node.params) node.params = sortClasses(node.params, { env, @@ -825,17 +759,9 @@ function transformCss(ast: any, { env }: TransformerContext) { function transformAstro(ast: any, { env, changes }: TransformerContext) { let { staticAttrs, dynamicAttrs } = env.customizations - if ( - ast.type === 'element' || - ast.type === 'custom-element' || - ast.type === 'component' - ) { + if (ast.type === 'element' || ast.type === 'custom-element' || ast.type === 'component') { for (let attr of ast.attributes ?? []) { - if ( - staticAttrs.has(attr.name) && - attr.type === 'attribute' && - attr.kind === 'quoted' - ) { + if (staticAttrs.has(attr.name) && attr.type === 'attribute' && attr.kind === 'quoted') { attr.value = sortClasses(attr.value, { env, }) @@ -919,8 +845,7 @@ function transformTwig(ast: any, { env, changes }: TransformerContext) { const concat = path.find((entry) => { return ( entry.parent && - (entry.parent.type === 'BinaryConcatExpression' || - entry.parent.type === 'BinaryAddExpression') + (entry.parent.type === 'BinaryConcatExpression' || entry.parent.type === 'BinaryAddExpression') ) }) @@ -948,11 +873,7 @@ function transformPug(ast: any, { env }: TransformerContext) { // First sort the classes in attributes for (const token of ast.tokens) { if (token.type === 'attribute' && staticAttrs.has(token.name)) { - token.val = [ - token.val.slice(0, 1), - sortClasses(token.val.slice(1, -1), { env }), - token.val.slice(-1), - ].join('') + token.val = [token.val.slice(0, 1), sortClasses(token.val.slice(1, -1), { env }), token.val.slice(-1)].join('') } } @@ -982,9 +903,7 @@ function transformPug(ast: any, { env }: TransformerContext) { // Sort the lists of class tokens for (const [startIdx, endIdx] of ranges) { - const classes = ast.tokens - .slice(startIdx, endIdx + 1) - .map((token: any) => token.val) + const classes = ast.tokens.slice(startIdx, endIdx + 1).map((token: any) => token.val) const { classList } = sortClassList(classes, { env, @@ -1119,10 +1038,7 @@ export const printers: Record = (function () { } }) - options.originalText = spliceChangesIntoString( - options.originalText, - changes, - ) + options.originalText = spliceChangesIntoString(options.originalText, changes) } } @@ -1228,14 +1144,10 @@ export const parsers: Record = { : {}), ...(base.parsers.astroExpressionParser ? { - astroExpressionParser: createParser( - 'astroExpressionParser', - transformJavaScript, - { - staticAttrs: ['class'], - dynamicAttrs: ['class:list'], - }, - ), + astroExpressionParser: createParser('astroExpressionParser', transformJavaScript, { + staticAttrs: ['class'], + dynamicAttrs: ['class:list'], + }), } : {}), ...(base.parsers.marko diff --git a/src/sorting.ts b/src/sorting.ts index a1ac02f..f11ad33 100644 --- a/src/sorting.ts +++ b/src/sorting.ts @@ -1,57 +1,8 @@ -import type { LegacyTailwindContext, TransformerEnv } from './types' -import './index' - -export function bigSign(bigIntValue: bigint) { - return Number(bigIntValue > 0n) - Number(bigIntValue < 0n) -} - -function prefixCandidate( - context: LegacyTailwindContext, - selector: string, -): string { - let prefix = context.tailwindConfig.prefix - return typeof prefix === 'function' ? prefix(selector) : prefix + selector -} - -// Polyfill for older Tailwind CSS versions -function getClassOrderPolyfill( - classes: string[], - { env }: { env: TransformerEnv }, -): [string, bigint | null][] { - // A list of utilities that are used by certain Tailwind CSS utilities but - // that don't exist on their own. This will result in them "not existing" and - // sorting could be weird since you still require them in order to make the - // host utitlies work properly. (Thanks Biology) - let parasiteUtilities = new Set([ - prefixCandidate(env.context, 'group'), - prefixCandidate(env.context, 'peer'), - ]) - - let classNamesWithOrder: [string, bigint | null][] = [] - - for (let className of classes) { - let order: bigint | null = - env - .generateRules(new Set([className]), env.context) - .sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null - - if (order === null && parasiteUtilities.has(className)) { - // This will make sure that it is at the very beginning of the - // `components` layer which technically means 'before any - // components'. - order = env.context.layerOrder.components - } - - classNamesWithOrder.push([className, order]) - } - - return classNamesWithOrder -} +import type { TransformerEnv } from './types' +import { bigSign } from './utils' function reorderClasses(classList: string[], { env }: { env: TransformerEnv }) { - let orderedClasses = env.context.getClassOrder - ? env.context.getClassOrder(classList) - : getClassOrderPolyfill(classList, { env }) + let orderedClasses = env.context.getClassOrder(classList) return orderedClasses.sort(([nameA, a], [nameZ, z]) => { // Move `...` to the end of the list diff --git a/src/types.ts b/src/types.ts index b0bd57d..763aab0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,9 +2,9 @@ import type { ParserOptions } from 'prettier' export interface TransformerMetadata { // Default customizations for a given transformer + functions?: string[] staticAttrs?: string[] dynamicAttrs?: string[] - functions?: string[] } export interface Customizations { @@ -18,34 +18,17 @@ export interface TransformerContext { changes: StringChange[] } -export interface LegacyTailwindContext { - tailwindConfig: { - prefix: string | ((selector: string) => string) - } - - getClassOrder?: (classList: string[]) => [string, bigint | null][] - - layerOrder: { - components: bigint - } +export interface UnifiedApi { + getClassOrder(classList: string[]): [string, bigint | null][] } export interface TransformerEnv { - context: LegacyTailwindContext + context: UnifiedApi customizations: Customizations - generateRules: ( - classes: Iterable, - context: LegacyTailwindContext, - ) => [bigint][] parsers: any options: ParserOptions } -export interface ContextContainer { - context: any - generateRules: () => any -} - export interface StringChange { start: number end: number diff --git a/src/utils.ts b/src/utils.ts index a2a6dd7..4201358 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -27,10 +27,7 @@ type Visitor> = ( meta: Partial, ) => void | false -type Visitors> = Record< - string, - Visitor -> +type Visitors> = Record> // https://lihautan.com/manipulating-ast-with-javascript/ export function visit>( @@ -137,3 +134,7 @@ export function spliceChangesIntoString(str: string, changes: StringChange[]) { return result } + +export function bigSign(bigIntValue: bigint) { + return Number(bigIntValue > 0n) - Number(bigIntValue < 0n) +} diff --git a/src/versions/v3.ts b/src/versions/v3.ts new file mode 100644 index 0000000..c60394e --- /dev/null +++ b/src/versions/v3.ts @@ -0,0 +1,103 @@ +// @ts-check +import * as path from 'path' +import clearModule from 'clear-module' +// @ts-ignore +import { generateRules as generateRulesFallback } from 'tailwindcss/lib/lib/generateRules' +// @ts-ignore +import { createContext as createContextFallback } from 'tailwindcss/lib/lib/setupContextUtils' +import loadConfigFallback from 'tailwindcss/loadConfig' +import resolveConfigFallback from 'tailwindcss/resolveConfig' +import type { RequiredConfig } from 'tailwindcss/types/config.js' +import type { UnifiedApi } from '../types' +import { bigSign } from '../utils' + +interface LegacyTailwindContext { + tailwindConfig: { + prefix: string | ((selector: string) => string) + } + + getClassOrder?: (classList: string[]) => [string, bigint | null][] + + layerOrder: { + components: bigint + } +} + +interface GenerateRules { + (classes: Iterable, context: LegacyTailwindContext): [bigint][] +} + +function prefixCandidate(context: LegacyTailwindContext, selector: string): string { + let prefix = context.tailwindConfig.prefix + return typeof prefix === 'function' ? prefix(selector) : prefix + selector +} + +export async function loadV3(pkgDir: string | null, jsConfig: string | null): Promise { + let createContext = createContextFallback + let generateRules: GenerateRules = generateRulesFallback + let resolveConfig = resolveConfigFallback + let loadConfig = loadConfigFallback + let tailwindConfig: RequiredConfig = { content: [] } + + try { + if (pkgDir) { + resolveConfig = require(path.join(pkgDir, 'resolveConfig')) + createContext = require(path.join(pkgDir, 'lib/lib/setupContextUtils')).createContext + generateRules = require(path.join(pkgDir, 'lib/lib/generateRules')).generateRules + // Prior to `tailwindcss@3.3.0` this won't exist so we load it last + loadConfig = require(path.join(pkgDir, 'loadConfig')) + } + } catch {} + + try { + if (jsConfig) { + clearModule(jsConfig) + let loadedConfig = loadConfig(jsConfig) + tailwindConfig = loadedConfig.default ?? loadedConfig + } + } catch (err) { + console.error(`Unable to load your Tailwind CSS v3 config: ${jsConfig}`) + throw err + } + + // suppress "empty content" warning + tailwindConfig.content = ['no-op'] + + // Create the context + let context: LegacyTailwindContext = createContext(resolveConfig(tailwindConfig)) + + // Polyfill for older Tailwind CSS versions + function getClassOrderPolyfill(classes: string[]): [string, bigint | null][] { + // A list of utilities that are used by certain Tailwind CSS utilities but + // that don't exist on their own. This will result in them "not existing" and + // sorting could be weird since you still require them in order to make the + // host utitlies work properly. (Thanks Biology) + let parasiteUtilities = new Set([prefixCandidate(context, 'group'), prefixCandidate(context, 'peer')]) + + let classNamesWithOrder: [string, bigint | null][] = [] + + for (let className of classes) { + let order: bigint | null = + generateRules(new Set([className]), context).sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null + + if (order === null && parasiteUtilities.has(className)) { + // This will make sure that it is at the very beginning of the + // `components` layer which technically means 'before any + // components'. + order = context.layerOrder.components + } + + classNamesWithOrder.push([className, order]) + } + + return classNamesWithOrder + } + + context.getClassOrder ??= getClassOrderPolyfill + + return { + getClassOrder: (classList: string[]) => { + return context.getClassOrder ? context.getClassOrder(classList) : getClassOrderPolyfill(classList) + }, + } +} diff --git a/src/versions/v4.ts b/src/versions/v4.ts new file mode 100644 index 0000000..2e497b3 --- /dev/null +++ b/src/versions/v4.ts @@ -0,0 +1,207 @@ +// @ts-check +import * as fs from 'fs/promises' +import * as path from 'path' +import { pathToFileURL } from 'url' +import { createJiti, type Jiti } from 'jiti' +import postcss from 'postcss' +// @ts-ignore +import postcssImport from 'postcss-import' +import { resolveCssFrom, resolveJsFrom } from '../resolve' +import type { UnifiedApi } from '../types' + +interface DesignSystem { + getClassOrder(classList: string[]): [string, bigint | null][] +} + +interface LoadOptions { + base: string + + loadModule?( + id: string, + base: string, + resourceType: string, + ): Promise<{ + base: string + module: unknown + }> + + loadPlugin?(id: string, base: string, resourceType: string): Promise + loadConfig?(id: string, base: string, resourceType: string): Promise + + loadStylesheet?( + id: string, + base: string, + ): Promise<{ + base: string + content: string + }> +} + +interface ApiV4 { + __unstable__loadDesignSystem(css: string, options: LoadOptions): Promise +} + +export async function loadV4(mod: ApiV4 | null, stylesheet: string | null): Promise { + // This is not Tailwind v4 + if (!mod || !mod.__unstable__loadDesignSystem) { + throw new Error('TODO') + // TODO + // mod = (await import('tailwindcss-v4')) as ApiV4 + } + + // Create a Jiti instance that can be used to load plugins and config files + let jiti = createJiti(import.meta.url, { + moduleCache: false, + fsCache: false, + }) + + let css: string + let importBasePath: string + + if (stylesheet) { + // Resolve imports in the entrypoint to a flat CSS tree + css = await fs.readFile(stylesheet, 'utf-8') + importBasePath = path.dirname(stylesheet) + } else { + importBasePath = process.cwd() + stylesheet = path.join(importBasePath, 'fake.css') + + // TODO: bundled theme.css file? + css = '' + } + + // Load the design system and set up a compatible context object that is + // usable by the rest of the plugin + let design = await mod.__unstable__loadDesignSystem(css, { + base: importBasePath, + + // v4.0.0-alpha.25+ + loadModule: createLoader({ + legacy: false, + jiti, + filepath: stylesheet, + onError: (id, err, resourceType) => { + console.error(`Unable to load ${resourceType}: ${id}`, err) + + if (resourceType === 'config') { + return {} + } else if (resourceType === 'plugin') { + return () => {} + } + }, + }), + + loadStylesheet: async (id: string, base: string) => { + let resolved = resolveCssFrom(base, id) + + return { + base: path.dirname(resolved), + content: await fs.readFile(resolved, 'utf-8'), + } + }, + + // v4.0.0-alpha.24 and below + loadPlugin: createLoader({ + legacy: true, + jiti, + filepath: stylesheet, + onError(id, err) { + console.error(`Unable to load plugin: ${id}`, err) + + return () => {} + }, + }), + + loadConfig: createLoader({ + legacy: true, + jiti, + filepath: stylesheet, + onError(id, err) { + console.error(`Unable to load config: ${id}`, err) + + return {} + }, + }), + }) + + return { + getClassOrder: (classList: string[]) => { + return design.getClassOrder(classList) + }, + } +} + +function createLoader({ + legacy, + jiti, + filepath, + onError, +}: { + legacy: true + jiti: Jiti + filepath: string + onError: (id: string, error: unknown, resourceType: string) => T +}): (id: string) => Promise + +function createLoader({ + legacy, + jiti, + filepath, + onError, +}: { + legacy: false + jiti: Jiti + filepath: string + onError: (id: string, error: unknown, resourceType: string) => T +}): ( + id: string, + base: string, + resourceType: string, +) => Promise<{ + base: string + module: unknown +}> + +/** + * Create a loader function that can load plugins and config files relative to + * the CSS file that uses them. However, we don't want missing files to prevent + * everything from working so we'll let the error handler decide how to proceed. + */ +function createLoader({ + legacy, + jiti, + filepath, + onError, +}: { + legacy: boolean + jiti: Jiti + filepath: string + onError: (id: string, error: unknown, resourceType: string) => T +}) { + let cacheKey = `${+Date.now()}` + + async function loadFile(id: string, base: string, resourceType: string) { + try { + let resolved = resolveJsFrom(base, id) + + let url = pathToFileURL(resolved) + url.searchParams.append('t', cacheKey) + + return await jiti.import(url.href, { default: true }) + } catch (err) { + return onError(id, err, resourceType) + } + } + + if (legacy) { + let baseDir = path.dirname(filepath) + return (id: string) => loadFile(id, baseDir, 'module') + } + + return async (id: string, base: string, resourceType: string) => { + return { + base, + module: await loadFile(id, base, resourceType), + } + } +} diff --git a/tests/fixtures.test.ts b/tests/fixtures.test.ts index c83ad8b..18b4351 100644 --- a/tests/fixtures.test.ts +++ b/tests/fixtures.test.ts @@ -82,6 +82,17 @@ let fixtures = [ dir: 'custom-pkg-name-v4', ext: 'html', }, + + { + name: 'monorepo / v4', + dir: 'monorepo/package-1', + ext: 'jsx', + }, + { + name: 'monorepo / v3', + dir: 'monorepo/package-2', + ext: 'jsx', + }, ] let configs = [ @@ -98,10 +109,7 @@ let configs = [ test.concurrent('explicit config path', async ({ expect }) => { expect( await format('
', { - tailwindConfig: path.resolve( - __dirname, - 'fixtures/basic/tailwind.config.js', - ), + tailwindConfig: path.resolve(__dirname, 'fixtures/basic/tailwind.config.js'), }), ).toEqual('
') }) @@ -127,9 +135,7 @@ describe('fixtures', () => { test.concurrent(name, async ({ expect }) => { let results = await execAsync(cmd) let formatted = results.stdout.replace(/\r\n/g, '\n') - let expected = await fs - .readFile(outputPath, 'utf-8') - .then((c) => c.replace(/\r\n/g, '\n')) + let expected = await fs.readFile(outputPath, 'utf-8').then((c) => c.replace(/\r\n/g, '\n')) expect(formatted.trim()).toEqual(expected.trim()) }) diff --git a/tests/fixtures/monorepo/.prettierrc b/tests/fixtures/monorepo/.prettierrc new file mode 100644 index 0000000..ec44ef3 --- /dev/null +++ b/tests/fixtures/monorepo/.prettierrc @@ -0,0 +1,15 @@ +{ + "tailwindFunctions": ["tw"], + "overrides": [ + { + "files": "package-1/**", + "options": { + "tailwindStylesheet": "./package-1/app.css" + } + }, + { + "files": "package-2/**", + "options": {} + } + ] +} diff --git a/tests/fixtures/monorepo/package-1/app.css b/tests/fixtures/monorepo/package-1/app.css new file mode 100644 index 0000000..ac52a79 --- /dev/null +++ b/tests/fixtures/monorepo/package-1/app.css @@ -0,0 +1,5 @@ +@import 'tailwindcss'; + +@theme { + --color-tomato: tomato; +} diff --git a/tests/fixtures/monorepo/package-1/index.jsx b/tests/fixtures/monorepo/package-1/index.jsx new file mode 100644 index 0000000..9ecd6e8 --- /dev/null +++ b/tests/fixtures/monorepo/package-1/index.jsx @@ -0,0 +1 @@ +const a = tw`sm:bg-tomato bg-red-500`; diff --git a/tests/fixtures/monorepo/package-1/output.jsx b/tests/fixtures/monorepo/package-1/output.jsx new file mode 100644 index 0000000..de2102e --- /dev/null +++ b/tests/fixtures/monorepo/package-1/output.jsx @@ -0,0 +1 @@ +const a = tw`bg-red-500 sm:bg-tomato`; diff --git a/tests/fixtures/monorepo/package-1/package-lock.json b/tests/fixtures/monorepo/package-1/package-lock.json new file mode 100644 index 0000000..12cf8d5 --- /dev/null +++ b/tests/fixtures/monorepo/package-1/package-lock.json @@ -0,0 +1,18 @@ +{ + "name": "package-1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "tailwindcss": "^4.0.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" + } + } +} diff --git a/tests/fixtures/monorepo/package-1/package.json b/tests/fixtures/monorepo/package-1/package.json new file mode 100644 index 0000000..7c77ab9 --- /dev/null +++ b/tests/fixtures/monorepo/package-1/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "tailwindcss": "^4.0.0" + } +} diff --git a/tests/fixtures/monorepo/package-2/index.jsx b/tests/fixtures/monorepo/package-2/index.jsx new file mode 100644 index 0000000..9ecd6e8 --- /dev/null +++ b/tests/fixtures/monorepo/package-2/index.jsx @@ -0,0 +1 @@ +const a = tw`sm:bg-tomato bg-red-500`; diff --git a/tests/fixtures/monorepo/package-2/output.jsx b/tests/fixtures/monorepo/package-2/output.jsx new file mode 100644 index 0000000..de2102e --- /dev/null +++ b/tests/fixtures/monorepo/package-2/output.jsx @@ -0,0 +1 @@ +const a = tw`bg-red-500 sm:bg-tomato`; diff --git a/tests/fixtures/monorepo/package-2/package-lock.json b/tests/fixtures/monorepo/package-2/package-lock.json new file mode 100644 index 0000000..2f86256 --- /dev/null +++ b/tests/fixtures/monorepo/package-2/package-lock.json @@ -0,0 +1,1367 @@ +{ + "name": "package-2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "tailwindcss": "^3.4.17" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/tests/fixtures/monorepo/package-2/package.json b/tests/fixtures/monorepo/package-2/package.json new file mode 100644 index 0000000..43f4abf --- /dev/null +++ b/tests/fixtures/monorepo/package-2/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "tailwindcss": "^3.4.17" + } +} diff --git a/tests/fixtures/monorepo/package-2/tailwind.config.js b/tests/fixtures/monorepo/package-2/tailwind.config.js new file mode 100644 index 0000000..1c33ae8 --- /dev/null +++ b/tests/fixtures/monorepo/package-2/tailwind.config.js @@ -0,0 +1 @@ +module.exports = { theme: { extend: { colors: { tomato: 'tomato' } } } } diff --git a/tests/fixtures/monorepo/package-lock.json b/tests/fixtures/monorepo/package-lock.json new file mode 100644 index 0000000..4616485 --- /dev/null +++ b/tests/fixtures/monorepo/package-lock.json @@ -0,0 +1,19 @@ +{ + "name": "monorepo", + "lockfileVersion": 3, + "requires": true, + "packages": { + "package-1": { + "extraneous": true, + "dependencies": { + "tailwindcss": "^4.0.0" + } + }, + "package-2": { + "extraneous": true, + "dependencies": { + "tailwindcss": "^3.4.17" + } + } + } +} diff --git a/tests/fixtures/monorepo/package.json b/tests/fixtures/monorepo/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/fixtures/monorepo/package.json @@ -0,0 +1 @@ +{}