From 581cf6abcca8b99498c93bf43f81435fa3dd5549 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Thu, 25 May 2023 04:10:00 +0200 Subject: [PATCH] Merge engines (#11275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Simplify CI, make oxide engine the default (#11281) * remove integration tests for the `stable` engine * make `integration-tests-oxide` the default integration tests workflow * remove unnecessary CACHE_PREFIX * drop `ci-stable.yml` workflow * make `CI — Oxide` the default + drop testing against 3.3 branch (since this is stable only) + remove unnecessary CACHE_PREFIX * drop `release-insiders-stable.yml` workflow * make `Release Insiders — Oxide` the default + change release channel to just `insiders` instead of `oxide-insders` * drop 3.3 branch from integration tests * change job name for insiders release This makes it consistent with the other job names * prep `release-oxide.yml` workflow to be the default workflow * add Tailwind Play update Currently commented out until we figure out how this will work exactly. * use `env.VERSION` for the version we want to release * drop `release-stable.yml` workflow * make `release-oxide.yml` the default `release.yml` workflow * inline "Calculate environment variables" step * cache node_modules and cargo related files/folders in the release step * always use the default CLI * drop oxide specific implementation * ensure we test `--postcss` in the CLI Even for the Oxide engine * always process `@import` rules * remove `generalizedModifiers` flag This was already enabled by default. This commits removes some of the outstanding references to it. * remove built-in peer dependencies for the CLI * drop unused esbuild dependency * fix type for `configBag` * setup Lightning CSS for the CLI * drop `cssnano` dependency We only used this in the CLI and we now use Lighting CSS which has this built in already. Therefore we can get rid of this as a dependency. * ensure `imported.css` file exists in integration tests * passthrough `options` to lightningcss Ideally we can read this from `result.map` instead. However this doesn't get filled in for whatever reason even though we use `map: {inline:true}`. You can run the `tests/cli.test.js`, and more specifically the `--postcss supports process options with custom config` test. * Always process CSS with Lightning CSS Some tests temporarily skipped. Autoprefixer removed from `postcss.config.js` stubs since vendor prefixing handled by Lightning. * Remove code for normalizing `@import "tailwindcss/*"` statements * ensure tailwind doesn't crash when a `tailwind.config.js` file is not present * add `log.group` to group multiple messages together with different types * remove engine specific checks in integration tests * remove unused imports/variables Make linting check happy * support `content: ['auto']` This will allow us to still use auto content detection, but since this is part of the `content` array, it also allows you to add custom paths if you want. E.g.: ```js content: ['auto', './node_modules/my-library/*.{jsx,tsx}'] ``` * ensure `content.files` is always an array This allows us to simplify some checks because 'auto' will now be part of the array instead of checking if `content.files` is auto or if it is an array containing 'auto'. * fix tests, ensure default config is not injected * simplify `auto` normalization * always use the defaultFullConfig No need to override the defaultFullConfig with `{content: 'auto'}` because this will be included in the default config already. * drop condition which ensured `files` was an empty array This is not needed anymore because when `content` is omitted it behaves as if it was set to `auto`. * drop removal of `content` We used to remove the `content` from the actual configuration files when using `tailwindcss init` for the oxide engine. This was to ensure that `auto content detection` was used instead (by default). But now it is always enabled by default therefore this is not needed anymore. * ensure setting `content: 'auto'` works * improve file cache handling We have to make sure that we get into the previous state whenver a test is done. We were using some `!fileCache[filePath]` checks, however we also used `null` as a sentinel value when something wasn't found. But, `null` is falsey as well so some checks where incorrect. Using a dedicated value and a `Map` makes this safer and more correct because we can now use `.has()`. * drop `cleanupFile`, `removeFile` will already take care of it * drop oxide check in `resolvedChangedContent` At this point the `context.tailwindConfig.content.files` is already fully resolved regardless of the engine we are in. * refactor `parseCandidateStringsFromFiles` strategy, to make use of a proper feature flag * use feature flag for sorting classes Once the Oxide engine is the default default, then we can drop this entirely because the result from the Rust parser will already be sorted. * drop `crosscheck` from tests where it is safe First pass of deleting `crosscheck`. These are all the places where we didn't have any differences between the Oxide and Stable engine. * replace `__OXIDE__` in corePlugins with feature flag * use `globalThis.__OXIDE__ ?? false` This will ensure that in places where we don't have `__OXIDE__` that we can still properly fallback to `false`. * use `flex` as the go-to utility when testing features * prefer `space-utilities` tests instead of `oxide` test * drop crosscheck check Since the `oxideParser` is disabled by default, all the tests can use the `stable` output. * use simple `false` default values for feature flag defaults * drop `__OXIDE__` injections in build scripts * use stable `flex` and `z-{...}` utilities instead of color utilities flex and z-index related utilities are more stable than the color utilities right now because the color utilities will use a raw color value or a combination with a css variable depending on a feature flag. * drop unused `ENGINE` environment variable * drop stable engine manifest files * drop `swap-engines` tooling --------- Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com> --- .github/workflows/ci-stable.yml | 59 - .github/workflows/ci.yml | 9 +- .../workflows/integration-tests-stable.yml | 83 - ...-tests-oxide.yml => integration-tests.yml} | 9 +- .github/workflows/release-insiders-stable.yml | 78 - ...nsiders-oxide.yml => release-insiders.yml} | 6 +- .github/workflows/release-stable.yml | 68 - .../{release-oxide.yml => release.yml} | 263 +- integrations/io.js | 28 +- integrations/parcel/tests/integration.test.js | 205 +- .../postcss-cli/tests/integration.test.js | 138 +- .../rollup-sass/tests/integration.test.js | 183 +- integrations/rollup/tests/integration.test.js | 208 +- .../tailwindcss-cli/tests/cli.test.js | 184 +- .../tailwindcss-cli/tests/integration.test.js | 1441 +- integrations/vite/tests/integration.test.js | 214 +- .../webpack-4/tests/integration.test.js | 208 +- .../webpack-5/tests/integration.test.js | 258 +- package-lock.json | 272 +- package-lock.stable.json | 13997 ---------------- package.json | 11 +- package.stable.json | 126 - scripts/swap-engines.js | 40 - src/cli-peer-dependencies.js | 15 - src/cli.js | 6 +- src/cli/build/deps.js | 56 - src/cli/build/plugin.js | 102 +- src/cli/init/index.js | 5 - src/corePlugins.js | 8 +- src/featureFlags.js | 14 +- src/lib/content.js | 37 +- src/lib/expandTailwindAtRules.js | 8 +- src/lib/generateRules.js | 4 - src/lib/handleImportAtRules.js | 3 +- src/lib/normalizeTailwindDirectives.js | 29 - src/lib/setupContextUtils.js | 18 +- src/lib/setupTrackingContext.js | 4 +- src/lib/sharedState.js | 4 - src/oxide/cli.ts | 1 - src/oxide/cli/build/deps.ts | 91 - src/oxide/cli/build/index.ts | 47 - src/oxide/cli/build/plugin.ts | 419 - src/oxide/cli/build/utils.ts | 74 - src/oxide/cli/build/watching.ts | 225 - src/oxide/cli/help/index.ts | 69 - src/oxide/cli/index.ts | 204 - src/oxide/cli/init/index.ts | 64 - src/oxide/postcss-plugin.ts | 1 - src/plugin.js | 112 +- src/util/getAllConfigs.js | 9 +- src/util/log.js | 24 + src/util/normalizeConfig.js | 52 +- src/util/pluginUtils.js | 4 - src/util/validateConfig.js | 15 +- stubs/config.full.js | 2 +- stubs/config.simple.js | 2 +- stubs/postcss.config.cjs | 1 - stubs/postcss.config.js | 1 - tests/animations.test.js | 536 +- tests/any-type.test.js | 2057 +-- tests/apply.test.js | 4057 ++--- tests/arbitrary-properties.test.js | 906 +- tests/arbitrary-values.oxide.test.css | 1073 -- tests/arbitrary-values.test.css | 2 +- tests/arbitrary-values.test.js | 1169 +- tests/arbitrary-variants.test.js | 2070 ++- tests/basic-usage.oxide.test.css | 1028 -- tests/basic-usage.test.js | 1904 +-- tests/blocklist.test.js | 211 +- tests/collapse-adjacent-rules.test.js | 350 +- tests/collapse-duplicate-declarations.test.js | 334 +- tests/color-opacity-modifiers.test.js | 490 +- tests/color.test.js | 177 +- tests/combined-selectors.test.js | 130 +- tests/configurePlugins.test.js | 43 +- tests/containerPlugin.test.js | 664 +- tests/context-reuse.test.js | 202 +- tests/custom-extractors.test.js | 192 +- tests/custom-plugins.test.js | 3703 ++-- tests/custom-separator.test.js | 98 +- tests/custom-transformers.test.js | 123 +- tests/customConfig.test.js | 720 +- tests/dark-mode.test.js | 234 +- tests/detect-nesting.test.js | 210 +- tests/escapeClassName.test.js | 7 +- tests/evaluateTailwindFunctions.test.js | 2639 ++- tests/experimental.test.js | 382 +- tests/extractor-edge-cases.test.js | 58 +- tests/flattenColorPalette.test.js | 153 +- tests/format-variant-selector.test.js | 633 +- tests/generate-rules.test.js | 78 +- tests/getClassList.test.js | 377 +- tests/getSortOrder.test.js | 195 +- tests/getVariants.test.js | 317 +- tests/import-processing.test.js | 135 +- tests/import-syntax.test.js | 185 +- tests/important-boolean.test.js | 406 +- tests/important-modifier-prefix.test.js | 130 +- tests/important-modifier.test.js | 274 +- tests/important-selector.test.js | 362 +- tests/kitchen-sink.test.js | 1994 +-- tests/layer-at-rules.test.js | 546 +- tests/layer-without-tailwind.test.js | 126 +- tests/match-components.test.js | 152 +- tests/match-utilities.test.js | 1126 +- tests/match-variants.test.js | 1524 +- tests/min-max-screen-variants.test.js | 1186 +- tests/minimum-impact-selector.test.js | 55 +- tests/modify-selectors.test.js | 158 +- tests/mutable.test.js | 84 +- tests/negateValue.test.js | 25 +- tests/negated-content.test.js | 44 +- tests/negative-prefix.test.js | 678 +- tests/normalize-config.test.js | 309 +- tests/normalize-data-types.test.js | 7 +- tests/normalize-screens.test.js | 121 +- tests/opacity.test.js | 2144 ++- tests/oxide.test.js | 22 - tests/parallel-variants.test.js | 266 +- tests/parseAnimationValue.test.js | 387 +- tests/parseObjectStyles.test.js | 556 +- tests/plugins/divide.test.js | 183 +- tests/plugins/fontFamily.test.js | 156 +- tests/plugins/fontSize.test.js | 382 +- tests/plugins/gradientColorStops.test.js | 330 +- tests/prefers-contrast.test.js | 157 +- tests/prefix.test.js | 1180 +- tests/prefixSelector.test.js | 31 +- tests/preflight.test.js | 38 +- tests/raw-content.oxide.test.css | 759 - tests/raw-content.test.js | 323 +- tests/relative-purge-paths.test.js | 36 +- tests/resolve-defaults-at-rules.test.js | 1469 +- tests/resolveConfig.test.js | 3153 ++-- tests/responsive-and-variants-atrules.test.js | 264 +- tests/safelist.test.js | 1119 +- tests/screenAtRule.test.js | 76 +- tests/shared-state.test.js | 37 +- tests/source-maps.test.js | 1045 +- tests/space-utilities.test.js | 43 + tests/syntax-lit-html.test.js | 109 +- tests/syntax-svelte.test.js | 106 +- tests/tailwind-screens.test.js | 110 +- tests/to-path.test.js | 31 +- tests/util/apply-important-selector.test.js | 39 +- tests/util/crosscheck.test.js | 18 - tests/util/run.js | 61 - tests/variants.oxide.test.css | 412 - tests/variants.test.css | 487 +- tests/variants.test.html | 274 +- tests/variants.test.js | 2067 ++- tests/warnings.test.js | 115 +- tests/withAlphaVariable.test.js | 471 +- turbo.json | 2 +- 154 files changed, 26652 insertions(+), 48773 deletions(-) delete mode 100644 .github/workflows/ci-stable.yml delete mode 100644 .github/workflows/integration-tests-stable.yml rename .github/workflows/{integration-tests-oxide.yml => integration-tests.yml} (89%) delete mode 100644 .github/workflows/release-insiders-stable.yml rename .github/workflows/{release-insiders-oxide.yml => release-insiders.yml} (99%) delete mode 100644 .github/workflows/release-stable.yml rename .github/workflows/{release-oxide.yml => release.yml} (52%) delete mode 100644 package-lock.stable.json delete mode 100644 package.stable.json delete mode 100644 scripts/swap-engines.js delete mode 100644 src/cli-peer-dependencies.js delete mode 100644 src/cli/build/deps.js delete mode 100644 src/oxide/cli.ts delete mode 100644 src/oxide/cli/build/deps.ts delete mode 100644 src/oxide/cli/build/index.ts delete mode 100644 src/oxide/cli/build/plugin.ts delete mode 100644 src/oxide/cli/build/utils.ts delete mode 100644 src/oxide/cli/build/watching.ts delete mode 100644 src/oxide/cli/help/index.ts delete mode 100644 src/oxide/cli/index.ts delete mode 100644 src/oxide/cli/init/index.ts delete mode 100644 src/oxide/postcss-plugin.ts delete mode 100644 tests/arbitrary-values.oxide.test.css delete mode 100644 tests/basic-usage.oxide.test.css delete mode 100644 tests/oxide.test.js delete mode 100644 tests/raw-content.oxide.test.css create mode 100644 tests/space-utilities.test.js delete mode 100644 tests/util/crosscheck.test.js delete mode 100644 tests/variants.oxide.test.css diff --git a/.github/workflows/ci-stable.yml b/.github/workflows/ci-stable.yml deleted file mode 100644 index 80d19dca0..000000000 --- a/.github/workflows/ci-stable.yml +++ /dev/null @@ -1,59 +0,0 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: CI — Stable - -on: - push: - branches: [master] - pull_request: - branches: [master, 3.3] - -permissions: - contents: read - -env: - CI: true - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - CACHE_PREFIX: stable - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14, 18] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - - name: Use the `stable` engine - run: | - node ./scripts/swap-engines.js - - - name: Cache node_modules - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }} - - - name: Install dependencies - run: npm install - - - name: Build Tailwind CSS - run: npm run build - - - name: Test - run: | - npm run test || \ - npm run test || \ - npm run test || exit 1 - - - name: Lint - run: npm run style diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7e4a4674..5488096e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,10 @@ -name: CI — Oxide +name: CI on: push: - branches: [master, 3.3] + branches: [master] pull_request: - branches: [master, 3.3] + branches: [master] permissions: contents: read @@ -13,7 +13,6 @@ env: CI: true TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - CACHE_PREFIX: oxide jobs: build: @@ -34,7 +33,7 @@ jobs: uses: actions/cache@v3 with: path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }} # Cargo already skips downloading dependencies if they already exist - name: Cache cargo diff --git a/.github/workflows/integration-tests-stable.yml b/.github/workflows/integration-tests-stable.yml deleted file mode 100644 index b28207196..000000000 --- a/.github/workflows/integration-tests-stable.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Integration Tests — Stable - -on: - push: - branches: [master, 3.3] - pull_request: - branches: [master, 3.3] - -permissions: - contents: read - -env: - CI: true - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - CACHE_PREFIX: stable - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - integration: - - content-resolution - - parcel - - postcss-cli - - rollup - - rollup-sass - - tailwindcss-cli - - vite - - webpack-4 - - webpack-5 - node-version: [18] - fail-fast: false - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - - name: Use the `stable` engine - run: | - node ./scripts/swap-engines.js - - - name: Cache node_modules (tailwindcss) - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('package-lock.json') }} - - - name: Cache node_modules (integrations) - uses: actions/cache@v3 - with: - path: ./integrations/node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-integrations-${{ hashFiles('./integrations/package-lock.json') }} - - - name: Cache node_modules (integration) - uses: actions/cache@v3 - with: - path: ./integrations/${{ matrix.integration }}/node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-integrations-${{ matrix.integration }}-${{ hashFiles('./integrations/${{ matrix.integration }}/package-lock.json') }} - - - name: Install dependencies - run: npm install - - - name: Install dependencies (shared integrations) - run: npm install --prefix ./integrations - - - name: Install dependencies (integration) - run: npm install --prefix ./integrations/${{ matrix.integration }} - - - name: Build Tailwind CSS - run: npm run build - - - name: Test ${{ matrix.integration }} - run: | - npm run test --prefix ./integrations/${{ matrix.integration }} || \ - npm run test --prefix ./integrations/${{ matrix.integration }} || \ - npm run test --prefix ./integrations/${{ matrix.integration }} || exit 1 diff --git a/.github/workflows/integration-tests-oxide.yml b/.github/workflows/integration-tests.yml similarity index 89% rename from .github/workflows/integration-tests-oxide.yml rename to .github/workflows/integration-tests.yml index 9da82b4b8..722a08ad3 100644 --- a/.github/workflows/integration-tests-oxide.yml +++ b/.github/workflows/integration-tests.yml @@ -1,10 +1,10 @@ -name: Integration Tests — Oxide +name: Integration Tests on: push: - branches: [master, 3.3] + branches: [master] pull_request: - branches: [master, 3.3] + branches: [master] permissions: contents: read @@ -13,7 +13,6 @@ env: CI: true TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - CACHE_PREFIX: oxide jobs: test: @@ -46,7 +45,7 @@ jobs: uses: actions/cache@v3 with: path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }} # Cargo already skips downloading dependencies if they already exist - name: Cache cargo diff --git a/.github/workflows/release-insiders-stable.yml b/.github/workflows/release-insiders-stable.yml deleted file mode 100644 index f8bb322d4..000000000 --- a/.github/workflows/release-insiders-stable.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Release Insiders — Stable - -on: - push: - branches: [master] - -permissions: - contents: read - -env: - CI: true - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - CACHE_PREFIX: stable - NODE_VERSION: 16 - RELEASE_CHANNEL: insiders - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - registry-url: 'https://registry.npmjs.org' - - - name: Use the `stable` engine - run: | - node ./scripts/swap-engines.js - - - name: Cache node_modules - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-${{ env.NODE_VERSION }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }} - - - name: Install dependencies - run: npm install - - - name: Build Tailwind CSS - run: npm run build - - - name: Test - run: | - npm run test || \ - npm run test || \ - npm run test || exit 1 - - - name: Resolve version - id: vars - run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - - name: 'Version based on commit: 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}' - run: npm version 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }} --force --no-git-tag-version - - - name: Publish - run: npm publish --tag ${{ env.RELEASE_CHANNEL }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Trigger Tailwind Play update - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'tailwindlabs', - repo: 'play.tailwindcss.com', - ref: 'master', - workflow_id: 'upgrade-tailwindcss.yml', - inputs: { - insidersVersion: '0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}' - } - }) diff --git a/.github/workflows/release-insiders-oxide.yml b/.github/workflows/release-insiders.yml similarity index 99% rename from .github/workflows/release-insiders-oxide.yml rename to .github/workflows/release-insiders.yml index e074d064d..26e1b1167 100644 --- a/.github/workflows/release-insiders-oxide.yml +++ b/.github/workflows/release-insiders.yml @@ -1,4 +1,4 @@ -name: Release Insiders — Oxide +name: Release Insiders on: push: @@ -8,7 +8,7 @@ env: APP_NAME: tailwindcss-oxide NODE_VERSION: 16 OXIDE_LOCATION: ./oxide/crates/node - RELEASE_CHANNEL: oxide-insiders + RELEASE_CHANNEL: insiders jobs: build: @@ -353,7 +353,7 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - tailwind_oxide_release: + tailwind-release: runs-on: ubuntu-latest name: Build and release Tailwind CSS diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml deleted file mode 100644 index 23a3f2dc0..000000000 --- a/.github/workflows/release-stable.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Release — Stable - -on: - release: - types: [published] - -permissions: - contents: read - -env: - CI: true - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14] - - steps: - - uses: actions/checkout@v3 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - registry-url: 'https://registry.npmjs.org' - cache: 'npm' - - - name: Use the `stable` engine - run: | - node ./scripts/swap-engines.js - - - name: Install dependencies - run: npm install - - - name: Build Tailwind CSS - run: npm run build - - - name: Test - run: npm run test - - - name: Calculate environment variables - run: | - echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV - echo "TAILWINDCSS_VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV - - - name: Publish - run: npm publish --tag ${{ env.RELEASE_CHANNEL }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Trigger Tailwind Play update - if: env.RELEASE_CHANNEL == 'latest' - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'tailwindlabs', - repo: 'play.tailwindcss.com', - ref: 'master', - workflow_id: 'upgrade-tailwindcss.yml', - inputs: { - version: '${{ env.TAILWINDCSS_VERSION }}' - } - }) diff --git a/.github/workflows/release-oxide.yml b/.github/workflows/release.yml similarity index 52% rename from .github/workflows/release-oxide.yml rename to .github/workflows/release.yml index ad71ec657..3a1ab964d 100644 --- a/.github/workflows/release-oxide.yml +++ b/.github/workflows/release.yml @@ -1,15 +1,19 @@ -name: Release — Oxide +name: Release on: - workflow_dispatch: + release: + types: [published] + +permissions: + contents: read env: APP_NAME: tailwindcss-oxide - NODE_VERSION: 14 + NODE_VERSION: 16 OXIDE_LOCATION: ./oxide/crates/node jobs: - oxide-build: + build: strategy: fail-fast: false matrix: @@ -27,6 +31,35 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }} + + # Cargo already skips downloading dependencies if they already exist + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + # Cache the `oxide` Rust build + - name: Cache oxide build + uses: actions/cache@v3 + with: + path: | + ./oxide/target/ + ./oxide/crates/node/*.node + ./oxide/crates/node/index.js + ./oxide/crates/node/index.d.ts + key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }} + - name: Install Node.JS uses: actions/setup-node@v3 with: @@ -42,18 +75,8 @@ jobs: - name: Setup rust target run: rustup target add ${{ matrix.target }} - # - name: Use cached oxide node_modules - # id: cache-oxide - # uses: actions/cache@v3 - # with: - # path: ${{ env.OXIDE_LOCATION }}/node_modules - # key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-${{ matrix.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }} - # restore-keys: | - # nodeModules- - - - name: Oxide — npm install - # if: steps.cache-oxide.outputs.cache-hit != 'true' - run: cd ${{ env.OXIDE_LOCATION }} && npm install + - name: Install dependencies + run: npm install - name: Build release run: cd ${{ env.OXIDE_LOCATION }} && npm run build @@ -72,7 +95,7 @@ jobs: ${{ env.OXIDE_LOCATION }}/*.node ${{ matrix.binary }} - oxide-build-apple-silicon: + build-apple-silicon: name: Build Apple Silicon (OXIDE) runs-on: macos-latest steps: @@ -82,7 +105,7 @@ jobs: uses: actions/cache@v3 with: path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }} # Cargo already skips downloading dependencies if they already exist - name: Cache cargo @@ -94,7 +117,18 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('./oxide/**/Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + # Cache the `oxide` Rust build + - name: Cache oxide build + uses: actions/cache@v3 + with: + path: | + ./oxide/target/ + ./oxide/crates/node/*.node + ./oxide/crates/node/index.js + ./oxide/crates/node/index.d.ts + key: ${{ runner.os }}-oxide-${{ hashFiles('./oxide/crates/**/*') }} - name: Install Node.JS uses: actions/setup-node@v3 @@ -131,7 +165,7 @@ jobs: ${{ env.OXIDE_LOCATION }}/*.node ${{ env.APP_NAME }} - oxide-build-linux: + build-linux: strategy: fail-fast: false matrix: @@ -162,6 +196,35 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }} + + # Cargo already skips downloading dependencies if they already exist + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + # Cache the `oxide` Rust build + - name: Cache oxide build + uses: actions/cache@v3 + with: + path: | + ./oxide/target/ + ./oxide/crates/node/*.node + ./oxide/crates/node/index.js + ./oxide/crates/node/index.d.ts + key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }} + - name: Install Node.JS uses: actions/setup-node@v3 with: @@ -181,18 +244,8 @@ jobs: - name: Setup rust target run: rustup target add ${{ matrix.target }} - # - name: Use cached oxide node_modules - # id: cache-oxide - # uses: actions/cache@v3 - # with: - # path: ${{ env.OXIDE_LOCATION }}/node_modules - # key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-linux-${{ matrix.target }}-${{ env.NODE_VERSION }} - # restore-keys: | - # nodeModules- - - - name: Oxide — npm install - # if: steps.cache-oxide.outputs.cache-hit != 'true' - run: cd ${{ env.OXIDE_LOCATION }} && npm install + - name: Install dependencies + run: npm install - name: Build release run: cd ${{ env.OXIDE_LOCATION }} && npm run build @@ -211,28 +264,52 @@ jobs: ${{ env.OXIDE_LOCATION }}/*.node ${{ env.APP_NAME }} - oxide-release: + release: runs-on: ubuntu-latest name: Build and release needs: - - oxide-build - - oxide-build-linux - - oxide-build-apple-silicon + - build + - build-linux + - build-apple-silicon steps: - uses: actions/checkout@v3 - # - name: Use cached oxide node_modules - # id: cache-oxide - # uses: actions/cache@v3 - # with: - # path: ${{ env.OXIDE_LOCATION }}/node_modules - # key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-ubuntu-latest-${{ env.NODE_VERSION }} - # restore-keys: | - # nodeModules- + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }} - - name: Oxide — npm install - # if: steps.cache-oxide.outputs.cache-hit != 'true' - run: cd ${{ env.OXIDE_LOCATION }} && npm install + # Cargo already skips downloading dependencies if they already exist + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + # Cache the `oxide` Rust build + - name: Cache oxide build + uses: actions/cache@v3 + with: + path: | + ./oxide/target/ + ./oxide/crates/node/*.node + ./oxide/crates/node/index.js + ./oxide/crates/node/index.d.ts + key: ${{ runner.os }}-oxide-${{ hashFiles('./oxide/crates/**/*') }} + + - name: Install dependencies + run: npm install + + - name: Calculate environment variables + run: | + echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV + echo "VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV - name: Download artifacts uses: actions/download-artifact@v3 @@ -255,11 +332,6 @@ jobs: cp bindings-x86_64-unknown-linux-gnu/oxide/crates/node/* ./npm/linux-x64-gnu/ cp bindings-x86_64-unknown-linux-musl/oxide/crates/node/* ./npm/linux-x64-musl/ - - name: Resolve version - id: vars - run: | - echo "NPM_VERSION=0.0.0-insiders.$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - name: Generate entry point run: npm run build --prefix ${{ env.OXIDE_LOCATION }} @@ -268,50 +340,74 @@ jobs: for pkg in ${{ env.OXIDE_LOCATION }}/npm/*; do echo "Publishing $pkg..." cd $pkg - npm version ${{ env.NPM_VERSION }} --force --no-git-tag-version - npm publish --tag insiders --access public + npm version ${{ env.VERSION }} --force --no-git-tag-version + npm publish --tag ${{ env.RELEASE_CHANNEL }} --access public cd - done echo "Publishing ${{ env.APP_NAME }}..."; cd ${{ env.OXIDE_LOCATION }} - npm version ${{ env.NPM_VERSION }} --force --no-git-tag-version - sed "s#\"0.0.0\"#\"${{ env.NPM_VERSION }}\"#g" package.json > package_updated.json + npm version ${{ env.VERSION }} --force --no-git-tag-version + sed "s#\"0.0.0\"#\"${{ env.VERSION }}\"#g" package.json > package_updated.json mv package_updated.json package.json - npm publish --tag insiders --access public + npm publish --tag ${{ env.RELEASE_CHANNEL }} --access public cd - env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - tailwind_oxide_release: + tailwind-release: runs-on: ubuntu-latest name: Build and release Tailwind CSS needs: - - oxide-release - - strategy: - matrix: - node-version: [14] + - release steps: - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }} + + # Cargo already skips downloading dependencies if they already exist + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + # Cache the `oxide` Rust build + - name: Cache oxide build + uses: actions/cache@v3 + with: + path: | + ./oxide/target/ + ./oxide/crates/node/*.node + ./oxide/crates/node/index.js + ./oxide/crates/node/index.d.ts + key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }} + + - name: Use Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: ${{ env.NODE_VERSION }} registry-url: 'https://registry.npmjs.org' - - name: Resolve version - id: vars + - name: Calculate environment variables run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - echo "OXIDE_NPM_VERSION=0.0.0-insiders.$(git rev-parse --short HEAD)" >> $GITHUB_ENV + echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV + echo "VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV - name: Setup `@tailwindcss/oxide` version run: | - sed "s#\"@tailwindcss/oxide\": \".*\"#\"@tailwindcss/oxide\": \"${{ env.OXIDE_NPM_VERSION }}\"#" package.json > package_updated.json + sed "s#\"@tailwindcss/oxide\": \".*\"#\"@tailwindcss/oxide\": \"${{ env.VERSION }}\"#" package.json > package_updated.json mv package_updated.json package.json - name: Install dependencies @@ -321,12 +417,31 @@ jobs: run: npm run build - name: Test - run: npm test + run: | + npm test || \ + npm test || \ + npm test || exit 1 - - name: 'Version based on commit: 0.0.0-oxide.${{ env.SHA_SHORT }}' - run: npm version 0.0.0-oxide.${{ env.SHA_SHORT }} --force --no-git-tag-version + - name: 'Version: ${{ env.VERSION }}' + run: npm version ${{ env.VERSION }} --force --no-git-tag-version - name: Publish - run: npm publish --tag oxide + run: npm publish --tag ${{ env.RELEASE_CHANNEL }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + # - name: Trigger Tailwind Play update + # if: env.RELEASE_CHANNEL == 'latest' + # uses: actions/github-script@v6 + # with: + # github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }} + # script: | + # await github.rest.actions.createWorkflowDispatch({ + # owner: 'tailwindlabs', + # repo: 'play.tailwindcss.com', + # ref: 'master', + # workflow_id: 'upgrade-tailwindcss.yml', + # inputs: { + # version: '${{ env.TAILWINDCSS_VERSION }}' + # } + # }) diff --git a/integrations/io.js b/integrations/io.js index d30570d3a..0716aac7e 100644 --- a/integrations/io.js +++ b/integrations/io.js @@ -5,6 +5,9 @@ let fs = require('fs/promises') let chokidar = require('chokidar') let resolveToolRoot = require('./resolve-tool-root') +let FILE_STATE = { + NotFound: Symbol(), +} function getWatcherOptions() { return { @@ -28,7 +31,7 @@ module.exports = function ({ cleanup = true, } = {}) { let toolRoot = resolveToolRoot() - let fileCache = {} + let fileCache = new Map() let absoluteOutputFolder = path.resolve(toolRoot, output) let absoluteInputFolder = path.resolve(toolRoot, input) @@ -41,9 +44,9 @@ module.exports = function ({ // Restore all written files afterEach(async () => { await Promise.all( - Object.entries(fileCache).map(async ([file, content]) => { + Array.from(fileCache.entries()).map(async ([file, content]) => { try { - if (content === null) { + if (content === FILE_STATE.NotFound) { return await fs.unlink(file) } else { return await fs.writeFile(file, content, 'utf8') @@ -89,7 +92,7 @@ module.exports = function ({ return { cleanupFile(file) { let filePath = path.resolve(toolRoot, file) - fileCache[filePath] = null + fileCache.set(filePath, FILE_STATE.NotFound) }, async fileExists(file) { let filePath = path.resolve(toolRoot, file) @@ -98,8 +101,11 @@ module.exports = function ({ async removeFile(file) { let filePath = path.resolve(toolRoot, file) - if (!fileCache[filePath]) { - fileCache[filePath] = await fs.readFile(filePath, 'utf8').catch(() => null) + if (!fileCache.has(filePath)) { + fileCache.set( + filePath, + await fs.readFile(filePath, 'utf8').catch(() => FILE_STATE.NotFound) + ) } await fs.unlink(filePath).catch(() => null) @@ -114,8 +120,8 @@ module.exports = function ({ }, async appendToInputFile(file, contents) { let filePath = path.resolve(absoluteInputFolder, file) - if (!fileCache[filePath]) { - fileCache[filePath] = await fs.readFile(filePath, 'utf8') + if (!fileCache.has(filePath)) { + fileCache.set(filePath, await fs.readFile(filePath, 'utf8')) } return fs.appendFile(filePath, contents, 'utf8') @@ -133,12 +139,12 @@ module.exports = function ({ await fs.mkdir(path.dirname(filePath), { recursive: true }) } - if (!fileCache[filePath]) { + if (!fileCache.has(filePath)) { try { - fileCache[filePath] = await fs.readFile(filePath, 'utf8') + fileCache.set(filePath, await fs.readFile(filePath, 'utf8')) } catch (err) { if (err.code === 'ENOENT') { - fileCache[filePath] = null // Sentinel value to `delete` the file afterwards. This also means that we are writing to a `new` file inside the test. + fileCache.set(filePath, FILE_STATE.NotFound) } else { throw err } diff --git a/integrations/parcel/tests/integration.test.js b/integrations/parcel/tests/integration.test.js index b86af727e..15b007ac9 100644 --- a/integrations/parcel/tests/integration.test.js +++ b/integrations/parcel/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { appendToInputFile, @@ -40,7 +39,7 @@ describe('static build', () => { 'index.html', html` -
+
` ) await writeInputFile( @@ -58,8 +57,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -74,26 +73,13 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { @@ -102,7 +88,7 @@ describe('static build', () => { 'index.html', html` -
+
` ) await writeInputFile( @@ -122,8 +108,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -138,26 +124,13 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) }) @@ -199,41 +172,22 @@ describe('watcher', () => { ) await waitForOutputFileChange(/index\.\w+\.css$/, async () => { - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -271,41 +225,22 @@ describe('watcher', () => { ) await waitForOutputFileChange(/index\.\w+\.css$/, async () => { - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -438,43 +373,25 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .btn { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( - css` - .btn { - background-color: #ef4444; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss( + css` + .btn { + z-index: 0; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/postcss-cli/tests/integration.test.js b/integrations/postcss-cli/tests/integration.test.js index 46f5a61a6..cd8d2a904 100644 --- a/integrations/postcss-cli/tests/integration.test.js +++ b/integrations/postcss-cli/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({ output: 'dist', @@ -58,41 +57,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -125,41 +105,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -277,47 +238,26 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { + expect(await readOutputFile('main.css')).toIncludeCss( + css` + /* prettier-ignore */ + .btn { border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - /* prettier-ignore */ - .btn { - border-radius: 0.25rem; - background-color: #ef4444; + z-index: 0; padding: 0.25rem 0.5rem; } - .font-bold { - font-weight: 700; - } - ` - ) - } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/rollup-sass/tests/integration.test.js b/integrations/rollup-sass/tests/integration.test.js index 19a769565..90a3831ff 100644 --- a/integrations/rollup-sass/tests/integration.test.js +++ b/integrations/rollup-sass/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({ output: 'dist', @@ -58,41 +57,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -125,41 +105,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -275,43 +236,25 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - background-color: #ef4444; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .btn { + z-index: 0; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) @@ -370,43 +313,25 @@ describe('watcher', () => { css` @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - background-color: #ef4444; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .btn { + z-index: 0; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/rollup/tests/integration.test.js b/integrations/rollup/tests/integration.test.js index c3209af65..970d985f8 100644 --- a/integrations/rollup/tests/integration.test.js +++ b/integrations/rollup/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { readOutputFile, appendToInputFile, writeInputFile, removeFile } = require('../../io')({ output: 'dist', @@ -30,7 +29,7 @@ describe('static build', () => { it('can use a tailwind.config.js configuration file with ESM syntax', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -46,8 +45,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -62,31 +61,18 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -104,8 +90,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -120,26 +106,13 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) }) @@ -172,41 +145,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -239,41 +193,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -389,46 +324,25 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('index.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - background-color: #ef4444; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('index.css')).toIncludeCss( + css` + .btn { + border-radius: 0.25rem; + z-index: 0; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/tailwindcss-cli/tests/cli.test.js b/integrations/tailwindcss-cli/tests/cli.test.js index 0c42ce0cc..cf15425bb 100644 --- a/integrations/tailwindcss-cli/tests/cli.test.js +++ b/integrations/tailwindcss-cli/tests/cli.test.js @@ -2,7 +2,6 @@ let path = require('path') let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') let resolveToolRoot = require('../../resolve-tool-root') -let { env } = require('../../../lib/lib/sharedState') let version = require('../../../package.json').version @@ -19,7 +18,6 @@ let { }) let EXECUTABLE = 'node ../../lib/cli.js' -let testStable = env.ENGINE === 'stable' ? test : test.skip function dedent(input) { let lines = input.split('\n') @@ -98,7 +96,8 @@ describe('Build command', () => { expect(withoutMinify.length).toBeGreaterThan(withMinify.length) }) - testStable('--no-autoprefixer', async () => { + // TODO: Handle --no-autoprefixer + test.skip('--no-autoprefixer', async () => { await writeInputFile('index.html', html`
`) await $(`${EXECUTABLE} --output ./dist/main.css`) @@ -184,7 +183,7 @@ describe('Build command', () => { ) }) - testStable('--postcss (postcss.config.js)', async () => { + test('--postcss (postcss.config.js)', async () => { await writeInputFile('index.html', html`
`) let customConfig = javascript` @@ -196,7 +195,7 @@ describe('Build command', () => { function before(root, result) { // Inject a custom component with @apply rules to prove that we run // this _before_ the actual tailwind plugin. - let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }') + let btn = postcss.parse('.btn { @apply flex px-2 py-1 }') root.append(btn.nodes) }, function tailwindcss() { @@ -217,45 +216,24 @@ describe('Build command', () => { await $(`${EXECUTABLE} --output ./dist/main.css --postcss`) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold-after { - font-weight: 700; - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold-after { + font-weight: 700; + } - .btn-after { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold-after { - font-weight: 700; - } - - .btn-after { - background-color: #ef4444; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - ` - ) - } + .btn-after { + display: flex; + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + } + ` + ) }) - testStable('--postcss (custom.postcss.config.js)', async () => { + test('--postcss (custom.postcss.config.js)', async () => { await writeInputFile('index.html', html`
`) let customConfig = javascript` @@ -267,7 +245,7 @@ describe('Build command', () => { function before(root, result) { // Inject a custom component with @apply rules to prove that we run // this _before_ the actual tailwind plugin. - let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }') + let btn = postcss.parse('.btn { @apply flex px-2 py-1 }') root.append(btn.nodes) }, function tailwindcss() { @@ -288,45 +266,24 @@ describe('Build command', () => { await $(`${EXECUTABLE} --output ./dist/main.css --postcss ./custom.postcss.config.js`) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold-after { - font-weight: 700; - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold-after { + font-weight: 700; + } - .btn-after { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold-after { - font-weight: 700; - } - - .btn-after { - background-color: #ef4444; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - ` - ) - } + .btn-after { + display: flex; + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + } + ` + ) }) - testStable('--postcss supports process options', async () => { + test('--postcss supports process options', async () => { await writeInputFile('index.html', html`
`) let customConfig = javascript` @@ -360,7 +317,7 @@ describe('Build command', () => { expect(contents).toContain(`/*# sourceMappingURL`) }) - testStable('--postcss supports process options with custom config', async () => { + test('--postcss supports process options with custom config', async () => { await writeInputFile('index.html', html`
`) let customConfig = javascript` @@ -481,10 +438,18 @@ describe('Build command', () => { return runningProcess.stop() }) - testStable('postcss-import is included when using a custom postcss configuration', async () => { + test('postcss-import is included when using a custom postcss configuration', async () => { cleanupFile('src/test.css') await writeInputFile('index.html', html`
`) + await writeInputFile( + 'imported.css', + css` + .foo { + color: white; + } + ` + ) await writeInputFile( 'test.css', css` @@ -501,7 +466,9 @@ describe('Build command', () => { expect(await readOutputFile('main.css')).toIncludeCss( css` - @import './imported.css'; + .foo { + color: #fff; + } ` ) }) @@ -509,28 +476,8 @@ describe('Build command', () => { test('--help', async () => { let { combined } = await $(`${EXECUTABLE} --help`) - if (env.ENGINE === 'oxide') { - expect(dedent(combined)).toEqual( - dedent(` - tailwindcss v${version} - - Usage: - tailwindcss build [options] - - Options: - -i, --input Input file - -o, --output Output file - -w, --watch Watch for changes and rebuild as needed - -p, --poll Use polling instead of filesystem events when watching - --content Content paths to use for removing unused classes - -m, --minify Minify the output - -c, --config Path to a custom config file - -h, --help Display usage information - `) - ) - } else if (env.ENGINE === 'stable') { - expect(dedent(combined)).toEqual( - dedent(` + expect(dedent(combined)).toEqual( + dedent(` tailwindcss v${version} Usage: @@ -548,8 +495,7 @@ describe('Build command', () => { --no-autoprefixer Disable autoprefixer -h, --help Display usage information `) - ) - } + ) }) }) @@ -562,7 +508,6 @@ describe('Init command', () => { { flags: ['--ts', '--full'], name: 'tailwind.config.ts' }, { flags: ['--esm', '--full'], name: 'tailwind.config.js' }, ])('works with all these flags: %j', async ({ flags, name }) => { - cleanupFile(name) await removeFile(name) let { combined } = await $(`${EXECUTABLE} init ${flags.join(' ')}`) @@ -611,7 +556,7 @@ describe('Init command', () => { expect((await readOutputFile('../full.config.js')).split('\n').length).toBeGreaterThan(50) }) - testStable('--postcss', async () => { + test('--postcss', async () => { expect(await fileExists('postcss.config.js')).toBe(true) await removeFile('postcss.config.js') expect(await fileExists('postcss.config.js')).toBe(false) @@ -631,24 +576,8 @@ describe('Init command', () => { test('--help', async () => { let { combined } = await $(`${EXECUTABLE} init --help`) - if (env.ENGINE === 'oxide') { - expect(dedent(combined)).toEqual( - dedent(` - tailwindcss v${version} - - Usage: - tailwindcss init [options] - - Options: - --esm Initialize configuration file as ESM - --ts Initialize configuration file as TypeScript - -f, --full Include the default values for all options in the generated configuration file - -h, --help Display usage information - `) - ) - } else if (env.ENGINE === 'stable') { - expect(dedent(combined)).toEqual( - dedent(` + expect(dedent(combined)).toEqual( + dedent(` tailwindcss v${version} Usage: @@ -661,12 +590,10 @@ describe('Init command', () => { -f, --full Include the default values for all options in the generated configuration file -h, --help Display usage information `) - ) - } + ) }) test('ESM config is created by default in an ESM project', async () => { - cleanupFile('tailwind.config.js') await removeFile('tailwind.config.js') let pkg = await readOutputFile('../package.json') @@ -696,7 +623,6 @@ describe('Init command', () => { }) test('CJS config is created by default in a non-ESM project', async () => { - cleanupFile('tailwind.config.js') await removeFile('tailwind.config.js') let pkg = await readOutputFile('../package.json') diff --git a/integrations/tailwindcss-cli/tests/integration.test.js b/integrations/tailwindcss-cli/tests/integration.test.js index de806dd27..c72728dd6 100644 --- a/integrations/tailwindcss-cli/tests/integration.test.js +++ b/integrations/tailwindcss-cli/tests/integration.test.js @@ -2,7 +2,6 @@ let fs = require('fs') let path = require('path') let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { readOutputFile, appendToInputFile, writeInputFile, removeFile } = require('../../io')({ output: 'dist', @@ -56,7 +55,7 @@ describe('static build', () => { content: { files: ['./src/index.html'], }, - safelist: ['bg-red-500','bg-red-600'], + safelist: ['flex','block'], theme: { extend: { }, @@ -73,48 +72,26 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .block { + display: block; + } - .bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } + .flex { + display: flex; + } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - - .bg-red-600 { - background-color: #dc2626; - } - - .font-bold { - font-weight: 700; - } - ` - ) - } + .font-bold { + font-weight: 700; + } + ` + ) }) it('can use a tailwind.config.js configuration file with ESM syntax', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -130,8 +107,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0 }, }, }, @@ -146,31 +123,18 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -188,8 +152,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0 }, }, }, @@ -204,30 +168,17 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can read from a config file from an @config directive', async () => { - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -247,8 +198,8 @@ describe('static build', () => { }, theme: { extend: { - colors: { - yellow: '#ff0', + zIndex: { + primary: 0 } }, }, @@ -263,32 +214,19 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - --tw-bg-opacity: 1; - background-color: rgb(255 255 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - background-color: #ff0; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can read from a config file from an @config directive inside an @import from postcss-import', async () => { await fs.promises.mkdir('./src/config', { recursive: true }) - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'config/myconfig.css', css` @@ -314,8 +252,8 @@ describe('static build', () => { }, theme: { extend: { - colors: { - yellow: '#ff0', + zIndex: { + primary: 0 } }, }, @@ -330,26 +268,13 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - --tw-bg-opacity: 1; - background-color: rgb(255 255 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - background-color: #ff0; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('should work with raw content', async () => { @@ -358,7 +283,7 @@ describe('static build', () => { javascript` module.exports = { content: { - files: [{ raw: 'bg-red-500'}], + files: [{ raw: 'flex'}], }, theme: { extend: { @@ -376,26 +301,13 @@ describe('static build', () => { env: { NODE_ENV: 'production' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .flex { + display: flex; + } + ` + ) }) }) @@ -428,41 +340,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .flex { + display: flex; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -495,41 +388,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .flex { + display: flex; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -697,55 +571,31 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded flex px-2 py-1; } } ` ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - background-color: #ef4444; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .btn { + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + display: flex; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) test('listens for changes to the @config directive', async () => { - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -765,8 +615,8 @@ describe('watcher', () => { }, theme: { extend: { - colors: { - yellow: '#ff0', + zIndex: { + primary: 0 } }, }, @@ -786,8 +636,8 @@ describe('watcher', () => { }, theme: { extend: { - colors: { - yellow: '#ff7', + zIndex: { + primary: 10 } }, }, @@ -801,26 +651,13 @@ describe('watcher', () => { let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w') await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - --tw-bg-opacity: 1; - background-color: rgb(255 255 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - background-color: #ff0; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) await writeInputFile( 'index.css', @@ -833,26 +670,13 @@ describe('watcher', () => { ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - --tw-bg-opacity: 1; - background-color: rgb(255 255 119 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - background-color: #ff7; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 10; + } + ` + ) await writeInputFile( 'tailwind.2.config.js', @@ -864,8 +688,8 @@ describe('watcher', () => { }, theme: { extend: { - colors: { - yellow: '#fff', + zIndex: { + primary: 20 } }, }, @@ -877,26 +701,13 @@ describe('watcher', () => { ) await runningProcess.onStderr(ready) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-yellow { - background-color: #fff; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 20; + } + ` + ) return runningProcess.stop() }) @@ -1002,435 +813,565 @@ describe('watcher', () => { return runningProcess.stop() }) - if (env.ENGINE === 'oxide') { - describe('auto content', () => { - let { readOutputFile, writeInputFile } = require('../../io')({ - output: 'fixtures/example-app/dist', - input: 'fixtures/example-app/src', - }) - let options = { - cwd: path.resolve(__dirname, '..', 'fixtures', 'example-app'), - } - - it('should detect classes in existing files', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - - await writeInputFile('index.html', html`
`) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - return runningProcess.stop() - }) - - it('should detect changes in existing files', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - // Make a change - - await writeInputFile('index.html', html`
`) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - return runningProcess.stop() - }) - - it('should detect changes in new files in existing folders with a known extension', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - // Make a change to a new file in an existing folder with a known extension. - - await writeInputFile('other.html', html`
`) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - return runningProcess.stop() - }) - - it('should not scan ignored files', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - await writeInputFile('../.gitignore', 'generated-folder/') - await writeInputFile('../generated-folder/bad.html', html`
`) - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .italic { - font-style: italic; - } - ` - ) - - return runningProcess.stop() - }) - - it('should not scan for known binary files', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - await writeInputFile('example-1.png', html`
`) - await writeInputFile('example-2.mp4', html`
`) - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .italic { - font-style: italic; - } - - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - return runningProcess.stop() - }) - - it('should not scan for explicitly ignored extensions (such as css/scss/less/...)', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - await writeInputFile('example.css', html`
`) - await writeInputFile('example.less', html`
`) - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .italic { - font-style: italic; - } - - .underline { - -webkit-text-decoration-line: underline; - text-decoration-line: underline; - } - ` - ) - - return runningProcess.stop() - }) - - it('should not scan for explicitly ignored files (such as package-lock.json)', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - await writeInputFile('package-lock.json', html`
`) - await writeInputFile('yarn.lock', html`
`) - await writeInputFile('pnpm-lock.yaml', html`
`) - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .italic { - font-style: italic; - } - ` - ) - - return runningProcess.stop() - }) - - it('should not include the tailwind.config.js file as a template file', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - // Example class that should not be included: flex italic - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - await writeInputFile('index.html', html`
`) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .font-bold { - font-weight: 700; - } - ` - ) - - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .flex { - display: flex; - } - ` - ) - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .italic { - font-style: italic; - } - ` - ) - - return runningProcess.stop() - }) - - it('should optimize the globs and ensure that nested ignored folders are not scanned', async () => { - await writeInputFile( - '../tailwind.config.js', - javascript` - module.exports = { - corePlugins: { - preflight: false, - }, - } - ` - ) - - await writeInputFile('../.gitignore', 'node_modules') - await writeInputFile('../node_modules/a.html', html`
`) - await writeInputFile('index.html', html`
`) - await writeInputFile('nested/index.html', html`
`) - await writeInputFile( - 'nested/node_modules/index.html', - html`
` - ) - - let runningProcess = $( - 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', - options - ) - await runningProcess.onStderr(ready) - - // Root node_modules - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .text-red-100 { - color: #fee2e2; - } - ` - ) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .text-red-200 { - color: #fecaca; - } - ` - ) - - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .text-red-300 { - color: #fca5a5; - } - ` - ) - - // Nested node_modules - expect(await readOutputFile('main.css')).not.toIncludeCss( - css` - .text-red-400 { - color: #f87171; - } - ` - ) - - return runningProcess.stop() - }) + describe('auto content', () => { + let { readOutputFile, writeInputFile } = require('../../io')({ + output: 'fixtures/example-app/dist', + input: 'fixtures/example-app/src', }) - } + let options = { + cwd: path.resolve(__dirname, '..', 'fixtures', 'example-app'), + } + + it('should detect classes in existing files', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + + await writeInputFile('index.html', html`
`) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + return runningProcess.stop() + }) + + it('should detect changes in existing files', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + // Make a change + + await writeInputFile('index.html', html`
`) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + return runningProcess.stop() + }) + + it('should detect changes in new files in existing folders with a known extension', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + // Make a change to a new file in an existing folder with a known extension. + + await writeInputFile('other.html', html`
`) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + return runningProcess.stop() + }) + + it('should not scan ignored files', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + await writeInputFile('../.gitignore', 'generated-folder/') + await writeInputFile('../generated-folder/bad.html', html`
`) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .italic { + font-style: italic; + } + ` + ) + + return runningProcess.stop() + }) + + it('should not scan for known binary files', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + await writeInputFile('example-1.png', html`
`) + await writeInputFile('example-2.mp4', html`
`) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .italic { + font-style: italic; + } + + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + return runningProcess.stop() + }) + + it('should not scan for explicitly ignored extensions (such as css/scss/less/...)', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + await writeInputFile('example.css', html`
`) + await writeInputFile('example.less', html`
`) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .italic { + font-style: italic; + } + + .underline { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; + } + ` + ) + + return runningProcess.stop() + }) + + it('should not scan for explicitly ignored files (such as package-lock.json)', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + await writeInputFile('package-lock.json', html`
`) + await writeInputFile('yarn.lock', html`
`) + await writeInputFile('pnpm-lock.yaml', html`
`) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .italic { + font-style: italic; + } + ` + ) + + return runningProcess.stop() + }) + + it('should not include the tailwind.config.js file as a template file', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + // Example class that should not be included: flex italic + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .font-bold { + font-weight: 700; + } + ` + ) + + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .flex { + display: flex; + } + ` + ) + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .italic { + font-style: italic; + } + ` + ) + + return runningProcess.stop() + }) + + it('should optimize the globs and ensure that nested ignored folders are not scanned', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + corePlugins: { + preflight: false, + }, + } + ` + ) + + await writeInputFile('../.gitignore', 'node_modules') + await writeInputFile('../node_modules/a.html', html`
`) + await writeInputFile('index.html', html`
`) + await writeInputFile('nested/index.html', html`
`) + await writeInputFile('nested/node_modules/index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + // Root node_modules + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .z-10 { + z-index: 10; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-20 { + z-index: 20; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-30 { + z-index: 30; + } + ` + ) + + // Nested node_modules + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .z-40 { + z-index: 40; + } + ` + ) + + return runningProcess.stop() + }) + + it("should use auto content when content is explicitly set to 'auto'", async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + content: 'auto', + corePlugins: { + preflight: false, + }, + } + ` + ) + + await writeInputFile('../.gitignore', 'node_modules') + await writeInputFile('../node_modules/a.html', html`
`) + await writeInputFile('index.html', html`
`) + await writeInputFile('nested/index.html', html`
`) + await writeInputFile('nested/node_modules/index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + // Root node_modules + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .z-10 { + z-index: 10; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-20 { + z-index: 20; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-30 { + z-index: 30; + } + ` + ) + + // Nested node_modules + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .z-40 { + z-index: 40; + } + ` + ) + + return runningProcess.stop() + }) + + it('should be possible to merge "auto" and custom defined paths', async () => { + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + content: ['auto'], // Ignoring the library-example.html file for now + corePlugins: { + preflight: false, + }, + } + ` + ) + + await writeInputFile('../.gitignore', 'node_modules') + await writeInputFile('../node_modules/library-example.html', html`
`) + await writeInputFile('index.html', html`
`) + + let runningProcess = $( + 'node ../../../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w', + options + ) + await runningProcess.onStderr(ready) + + // example.html should be ignored right now + expect(await readOutputFile('main.css')).not.toIncludeCss( + css` + .z-10 { + z-index: 10; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-20 { + z-index: 20; + } + ` + ) + + await writeInputFile( + '../tailwind.config.js', + javascript` + module.exports = { + content: ['auto', 'node_modules/library-example.html'], // Explicitly adding the library-example.html file + corePlugins: { + preflight: false, + }, + } + ` + ) + await runningProcess.onStderr(ready) + + // z-10 from `example.html` should be available now + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-10 { + z-index: 10; + } + ` + ) + + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-20 { + z-index: 20; + } + ` + ) + + return runningProcess.stop() + }) + }) }) diff --git a/integrations/vite/tests/integration.test.js b/integrations/vite/tests/integration.test.js index b1a8163b2..cb106ac59 100644 --- a/integrations/vite/tests/integration.test.js +++ b/integrations/vite/tests/integration.test.js @@ -2,7 +2,6 @@ require('isomorphic-fetch') let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { readOutputFile, appendToInputFile, writeInputFile, removeFile } = require('../../io')({ output: 'dist', @@ -48,7 +47,7 @@ describe('static build', () => { 'index.html', html` -
+
` ) await removeFile('tailwind.config.js') @@ -59,8 +58,8 @@ describe('static build', () => { content: ['index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -75,26 +74,13 @@ describe('static build', () => { env: { NODE_ENV: 'production', NO_COLOR: '1' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { @@ -102,7 +88,7 @@ describe('static build', () => { 'index.html', html` -
+
` ) await removeFile('tailwind.config.js') @@ -115,8 +101,8 @@ describe('static build', () => { content: ['index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -131,26 +117,13 @@ describe('static build', () => { env: { NODE_ENV: 'production', NO_COLOR: '1' }, }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) }) @@ -191,41 +164,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) await runningProcess.onStdout((message) => message.includes('page reload')) - if (env.ENGINE === 'stable') { - expect(await fetchCSS()).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await fetchCSS()).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await fetchCSS()).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -262,41 +216,22 @@ describe('watcher', () => { ` ) - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) await runningProcess.onStdout((message) => message.includes('page reload')) - if (env.ENGINE === 'stable') { - expect(await fetchCSS()).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await fetchCSS()).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await fetchCSS()).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -428,49 +363,28 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) await runningProcess.onStdout((message) => message.includes('hmr update /index.css')) - if (env.ENGINE === 'stable') { - expect(await fetchCSS()).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await fetchCSS()).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - background-color: #ef4444; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await fetchCSS()).toIncludeCss( + css` + .btn { + border-radius: 0.25rem; + z-index: 0; + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/webpack-4/tests/integration.test.js b/integrations/webpack-4/tests/integration.test.js index e2e24cf45..ae7011edd 100644 --- a/integrations/webpack-4/tests/integration.test.js +++ b/integrations/webpack-4/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { appendToInputFile, @@ -28,7 +27,7 @@ describe('static build', () => { it('can use a tailwind.config.js configuration file with ESM syntax', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -44,8 +43,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -58,31 +57,18 @@ describe('static build', () => { await $('webpack --mode=production') - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -100,8 +86,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -114,26 +100,13 @@ describe('static build', () => { await $('webpack --mode=production') - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) }) @@ -169,41 +142,22 @@ describe('watcher', () => { ) await waitForOutputFileChange('main.css', async () => { - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -239,41 +193,22 @@ describe('watcher', () => { ) await waitForOutputFileChange('main.css', async () => { - await appendToInputFile('glob/index.html', html`
`) + await appendToInputFile('glob/index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -394,46 +329,25 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - background-color: #ef4444; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .btn { + z-index: 0; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/integrations/webpack-5/tests/integration.test.js b/integrations/webpack-5/tests/integration.test.js index 83b42f5f8..c1f0a0629 100644 --- a/integrations/webpack-5/tests/integration.test.js +++ b/integrations/webpack-5/tests/integration.test.js @@ -1,6 +1,5 @@ let $ = require('../../execute') let { css, html, javascript } = require('../../syntax') -let { env } = require('../../../lib/lib/sharedState') let { appendToInputFile, @@ -28,7 +27,7 @@ describe('static build', () => { it('can use a tailwind.config.js configuration file with ESM syntax', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -44,8 +43,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -58,31 +57,18 @@ describe('static build', () => { await $('webpack --mode=production') - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) it('can use a tailwind.config.ts configuration file', async () => { await removeFile('tailwind.config.js') - await writeInputFile('index.html', html`
`) + await writeInputFile('index.html', html`
`) await writeInputFile( 'index.css', css` @@ -100,8 +86,8 @@ describe('static build', () => { content: ['./src/index.html'], theme: { extend: { - colors: { - primary: 'black', + zIndex: { + primary: 0, }, }, }, @@ -114,26 +100,13 @@ describe('static build', () => { await $('webpack --mode=production') - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-primary { - background-color: black; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-primary { + z-index: 0; + } + ` + ) }) }) @@ -169,41 +142,22 @@ describe('watcher', () => { ) await waitForOutputFileChange('main.css', async () => { - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -239,41 +193,22 @@ describe('watcher', () => { ) await waitForOutputFileChange('main.css', async () => { - await appendToInputFile('index.html', html`
`) + await appendToInputFile('index.html', html`
`) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - .font-bold { - font-weight: 700; - } - .font-normal { - font-weight: 400; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } + .font-bold { + font-weight: 700; + } + .font-normal { + font-weight: 400; + } + ` + ) return runningProcess.stop() }) @@ -394,46 +329,25 @@ describe('watcher', () => { @layer components { .btn { - @apply rounded bg-red-500 px-2 py-1; + @apply rounded z-0 px-2 py-1; } } ` ) }) - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - border-radius: 0.25rem; - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .btn { - background-color: #ef4444; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - } - .font-bold { - font-weight: 700; - } - ` - ) - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .btn { + z-index: 0; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) @@ -447,7 +361,7 @@ describe('watcher', () => { content: { files: ['./src/index.html'], }, - safelist: ['bg-red-500','bg-red-600'], + safelist: ['z-0', 'z-10'], theme: { extend: { }, @@ -464,43 +378,21 @@ describe('watcher', () => { await waitForOutputFileCreation('main.css') - if (env.ENGINE === 'stable') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } + expect(await readOutputFile('main.css')).toIncludeCss( + css` + .z-0 { + z-index: 0; + } - .bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } + .z-10 { + z-index: 10; + } - .font-bold { - font-weight: 700; - } - ` - ) - } - - if (env.ENGINE === 'oxide') { - expect(await readOutputFile('main.css')).toIncludeCss( - css` - .bg-red-500 { - background-color: #ef4444; - } - - .bg-red-600 { - background-color: #dc2626; - } - - .font-bold { - font-weight: 700; - } - ` - ) - } + .font-bold { + font-weight: 700; + } + ` + ) return runningProcess.stop() }) diff --git a/package-lock.json b/package-lock.json index 4467139ea..ad784d3cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,8 +51,6 @@ "@swc/register": "^0.1.10", "autoprefixer": "^10.4.14", "concurrently": "^8.0.1", - "cssnano": "^6.0.1", - "esbuild": "^0.17.18", "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", @@ -6585,6 +6583,8 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -6601,6 +6601,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -6615,6 +6617,8 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -6630,6 +6634,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -6644,6 +6650,8 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.12" }, @@ -6656,6 +6664,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -6691,6 +6701,8 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.1.tgz", "integrity": "sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "cssnano-preset-default": "^6.0.1", "lilconfig": "^2.1.0" @@ -6711,6 +6723,8 @@ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.1.tgz", "integrity": "sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "css-declaration-sorter": "^6.3.1", "cssnano-utils": "^4.0.0", @@ -6754,6 +6768,8 @@ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" @@ -6770,6 +6786,8 @@ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz", "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -6782,6 +6800,8 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "css-tree": "~2.2.0" }, @@ -6795,6 +6815,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -6808,7 +6830,9 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/cyclist": { "version": "1.0.1", @@ -10828,7 +10852,9 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/memory-fs": { "version": "0.4.1", @@ -12013,6 +12039,8 @@ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz", "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -12031,6 +12059,8 @@ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz", "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -12047,6 +12077,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz", "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12059,6 +12091,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz", "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12071,6 +12105,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz", "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12083,6 +12119,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz", "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12222,6 +12260,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz", "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^6.0.0" @@ -12238,6 +12278,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.1.tgz", "integrity": "sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -12256,6 +12298,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz", "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12271,6 +12315,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz", "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "colord": "^2.9.1", "cssnano-utils": "^4.0.0", @@ -12288,6 +12334,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz", "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "cssnano-utils": "^4.0.0", @@ -12305,6 +12353,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz", "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-selector-parser": "^6.0.5" }, @@ -12416,6 +12466,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz", "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -12428,6 +12480,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz", "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12443,6 +12497,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz", "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12458,6 +12514,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz", "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12473,6 +12531,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz", "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12488,6 +12548,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz", "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12503,6 +12565,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz", "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -12519,6 +12583,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz", "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12534,6 +12600,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz", "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12549,6 +12617,8 @@ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz", "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "cssnano-utils": "^4.0.0", "postcss-value-parser": "^4.2.0" @@ -12565,6 +12635,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz", "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" @@ -12581,6 +12653,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz", "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12628,6 +12702,8 @@ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz", "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-value-parser": "^4.2.0", "svgo": "^3.0.2" @@ -12644,6 +12720,8 @@ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz", "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "postcss-selector-parser": "^6.0.5" }, @@ -14785,6 +14863,8 @@ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz", "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" @@ -14864,6 +14944,8 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -21154,6 +21236,8 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "optional": true, + "peer": true, "requires": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -21167,6 +21251,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "optional": true, + "peer": true, "requires": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -21178,6 +21264,8 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "optional": true, + "peer": true, "requires": { "domelementtype": "^2.3.0" } @@ -21187,6 +21275,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "dev": true, + "optional": true, + "peer": true, "requires": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -21197,7 +21287,9 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -21206,6 +21298,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "optional": true, + "peer": true, "requires": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -21225,6 +21319,8 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.1.tgz", "integrity": "sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==", "dev": true, + "optional": true, + "peer": true, "requires": { "cssnano-preset-default": "^6.0.1", "lilconfig": "^2.1.0" @@ -21235,6 +21331,8 @@ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.1.tgz", "integrity": "sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "css-declaration-sorter": "^6.3.1", "cssnano-utils": "^4.0.0", @@ -21272,6 +21370,8 @@ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" @@ -21284,6 +21384,8 @@ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz", "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "csso": { @@ -21291,6 +21393,8 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "css-tree": "~2.2.0" }, @@ -21300,6 +21404,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "optional": true, + "peer": true, "requires": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -21309,7 +21415,9 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -24259,7 +24367,9 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "memory-fs": { "version": "0.4.1", @@ -25114,6 +25224,8 @@ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz", "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -25126,6 +25238,8 @@ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz", "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -25136,6 +25250,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz", "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-duplicates": { @@ -25143,6 +25259,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz", "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-empty": { @@ -25150,6 +25268,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz", "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-overridden": { @@ -25157,6 +25277,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz", "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-import": { @@ -25233,6 +25355,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz", "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0", "stylehacks": "^6.0.0" @@ -25243,6 +25367,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.1.tgz", "integrity": "sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -25255,6 +25381,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz", "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25264,6 +25392,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz", "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==", "dev": true, + "optional": true, + "peer": true, "requires": { "colord": "^2.9.1", "cssnano-utils": "^4.0.0", @@ -25275,6 +25405,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz", "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "cssnano-utils": "^4.0.0", @@ -25286,6 +25418,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz", "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.5" } @@ -25355,6 +25489,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz", "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-normalize-display-values": { @@ -25362,6 +25498,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz", "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25371,6 +25509,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz", "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25380,6 +25520,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz", "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25389,6 +25531,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz", "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25398,6 +25542,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz", "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25407,6 +25553,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz", "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -25417,6 +25565,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz", "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25426,6 +25576,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz", "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25435,6 +25587,8 @@ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz", "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==", "dev": true, + "optional": true, + "peer": true, "requires": { "cssnano-utils": "^4.0.0", "postcss-value-parser": "^4.2.0" @@ -25445,6 +25599,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz", "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" @@ -25455,6 +25611,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz", "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -25483,6 +25641,8 @@ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz", "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0", "svgo": "^3.0.2" @@ -25493,6 +25653,8 @@ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz", "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.5" } @@ -27038,6 +27200,8 @@ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz", "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" @@ -27089,6 +27253,8 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -27121,10 +27287,8 @@ "browserslist": "^4.21.5", "chokidar": "^3.5.3", "concurrently": "^8.0.1", - "cssnano": "^6.0.1", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "esbuild": "^0.17.18", "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", @@ -31818,6 +31982,8 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "optional": true, + "peer": true, "requires": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -31831,6 +31997,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "optional": true, + "peer": true, "requires": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -31842,6 +32010,8 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "optional": true, + "peer": true, "requires": { "domelementtype": "^2.3.0" } @@ -31851,6 +32021,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "dev": true, + "optional": true, + "peer": true, "requires": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -31861,7 +32033,9 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -31870,6 +32044,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "optional": true, + "peer": true, "requires": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -31889,6 +32065,8 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.1.tgz", "integrity": "sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==", "dev": true, + "optional": true, + "peer": true, "requires": { "cssnano-preset-default": "^6.0.1", "lilconfig": "^2.1.0" @@ -31899,6 +32077,8 @@ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.1.tgz", "integrity": "sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "css-declaration-sorter": "^6.3.1", "cssnano-utils": "^4.0.0", @@ -31936,6 +32116,8 @@ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" @@ -31948,6 +32130,8 @@ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz", "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "csso": { @@ -31955,6 +32139,8 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "css-tree": "~2.2.0" }, @@ -31964,6 +32150,8 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "optional": true, + "peer": true, "requires": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -31973,7 +32161,9 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -34923,7 +35113,9 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "memory-fs": { "version": "0.4.1", @@ -35778,6 +35970,8 @@ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz", "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -35790,6 +35984,8 @@ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz", "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -35800,6 +35996,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz", "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-duplicates": { @@ -35807,6 +36005,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz", "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-empty": { @@ -35814,6 +36014,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz", "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-discard-overridden": { @@ -35821,6 +36023,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz", "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-import": { @@ -35897,6 +36101,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz", "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0", "stylehacks": "^6.0.0" @@ -35907,6 +36113,8 @@ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.1.tgz", "integrity": "sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -35919,6 +36127,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz", "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -35928,6 +36138,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz", "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==", "dev": true, + "optional": true, + "peer": true, "requires": { "colord": "^2.9.1", "cssnano-utils": "^4.0.0", @@ -35939,6 +36151,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz", "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "cssnano-utils": "^4.0.0", @@ -35950,6 +36164,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz", "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.5" } @@ -36019,6 +36235,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz", "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "postcss-normalize-display-values": { @@ -36026,6 +36244,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz", "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36035,6 +36255,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz", "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36044,6 +36266,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz", "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36053,6 +36277,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz", "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36062,6 +36288,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz", "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36071,6 +36299,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz", "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -36081,6 +36311,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz", "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36090,6 +36322,8 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz", "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36099,6 +36333,8 @@ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz", "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==", "dev": true, + "optional": true, + "peer": true, "requires": { "cssnano-utils": "^4.0.0", "postcss-value-parser": "^4.2.0" @@ -36109,6 +36345,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz", "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" @@ -36119,6 +36357,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz", "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0" } @@ -36147,6 +36387,8 @@ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz", "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-value-parser": "^4.2.0", "svgo": "^3.0.2" @@ -36157,6 +36399,8 @@ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz", "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==", "dev": true, + "optional": true, + "peer": true, "requires": { "postcss-selector-parser": "^6.0.5" } @@ -37702,6 +37946,8 @@ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz", "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==", "dev": true, + "optional": true, + "peer": true, "requires": { "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" @@ -37753,6 +37999,8 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", diff --git a/package-lock.stable.json b/package-lock.stable.json deleted file mode 100644 index 4d422e6a2..000000000 --- a/package-lock.stable.json +++ /dev/null @@ -1,13997 +0,0 @@ -{ - "name": "tailwindcss", - "version": "3.3.2", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "tailwindcss", - "version": "3.3.2", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.18.2", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "devDependencies": { - "@swc/cli": "^0.1.62", - "@swc/core": "^1.3.55", - "@swc/jest": "^0.2.26", - "@swc/register": "^0.1.10", - "autoprefixer": "^10.4.14", - "browserslist": "^4.21.5", - "concurrently": "^8.0.1", - "cssnano": "^6.0.0", - "esbuild": "^0.17.18", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "jest": "^29.5.0", - "jest-diff": "^29.5.0", - "lightningcss": "1.18.0", - "prettier": "^2.8.8", - "rimraf": "^5.0.0", - "source-map-js": "^1.0.2", - "turbo": "^1.9.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "integrations/content-resolution": { - "name": "@tailwindcss/integrations-content-resolution", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "postcss": "^8.4.21", - "postcss-cli": "^9.1.0" - } - }, - "integrations/parcel": { - "name": "@tailwindcss/integrations-parcel", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "parcel": "^2.5.0" - } - }, - "integrations/postcss-cli": { - "name": "@tailwindcss/integrations-postcss-cli", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "postcss": "^8.4.21", - "postcss-cli": "^9.1.0" - } - }, - "integrations/rollup": { - "name": "@tailwindcss/integrations-rollup", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "rollup": "^2.74.1", - "rollup-plugin-postcss": "^4.0.2" - } - }, - "integrations/rollup-sass": { - "name": "@tailwindcss/integrations-rollup-sass", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "rollup": "^2.74.1", - "rollup-plugin-postcss": "^4.0.2", - "sass": "^1.51.0" - } - }, - "integrations/tailwindcss-cli": { - "name": "@tailwindcss/integrations-tailwindcss-cli", - "version": "0.0.0", - "extraneous": true, - "dependencies": { - "tailwindcss": "file:../../" - } - }, - "integrations/vite": { - "name": "@tailwindcss/integrations-vite", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "isomorphic-fetch": "^3.0.0", - "vite": "^2.9.9" - } - }, - "integrations/webpack-4": { - "name": "@tailwindcss/integrations-webpack-4", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "css-loader": "^5.2.7", - "mini-css-extract-plugin": "^1.6.2", - "postcss-loader": "^4.3.0", - "webpack": "^4.46.0", - "webpack-cli": "^4.9.2" - } - }, - "integrations/webpack-5": { - "name": "@tailwindcss/integrations-webpack-5", - "version": "0.0.0", - "extraneous": true, - "devDependencies": { - "css-loader": "^6.7.1", - "mini-css-extract-plugin": "^2.7.2", - "postcss-loader": "^6.2.1", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" - } - }, - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.4", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.21.4", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@esbuild/android-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", - "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", - "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", - "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", - "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", - "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", - "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", - "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", - "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", - "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", - "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", - "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", - "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", - "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", - "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", - "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", - "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", - "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", - "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", - "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", - "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", - "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", - "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", - "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/create-cache-key-function": { - "version": "27.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^27.4.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", - "dev": true, - "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.25.16" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/types": { - "version": "27.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "node_modules/@mole-inc/bin-wrapper": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", - "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", - "dev": true, - "dependencies": { - "bin-check": "^4.1.0", - "bin-version-check": "^5.0.0", - "content-disposition": "^0.5.4", - "ext-name": "^5.0.0", - "file-type": "^17.1.6", - "filenamify": "^5.0.2", - "got": "^11.8.5", - "os-filter-obj": "^2.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "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", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "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==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "dev": true - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0" - } - }, - "node_modules/@swc/cli": { - "version": "0.1.62", - "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.62.tgz", - "integrity": "sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==", - "dev": true, - "dependencies": { - "@mole-inc/bin-wrapper": "^8.0.1", - "commander": "^7.1.0", - "fast-glob": "^3.2.5", - "semver": "^7.3.8", - "slash": "3.0.0", - "source-map": "^0.7.3" - }, - "bin": { - "spack": "bin/spack.js", - "swc": "bin/swc.js", - "swcx": "bin/swcx.js" - }, - "engines": { - "node": ">= 12.13" - }, - "peerDependencies": { - "@swc/core": "^1.2.66", - "chokidar": "^3.5.1" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@swc/cli/node_modules/source-map": { - "version": "0.7.3", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@swc/core": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz", - "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==", - "dev": true, - "hasInstallScript": true, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.55", - "@swc/core-darwin-x64": "1.3.55", - "@swc/core-linux-arm-gnueabihf": "1.3.55", - "@swc/core-linux-arm64-gnu": "1.3.55", - "@swc/core-linux-arm64-musl": "1.3.55", - "@swc/core-linux-x64-gnu": "1.3.55", - "@swc/core-linux-x64-musl": "1.3.55", - "@swc/core-win32-arm64-msvc": "1.3.55", - "@swc/core-win32-ia32-msvc": "1.3.55", - "@swc/core-win32-x64-msvc": "1.3.55" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz", - "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz", - "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz", - "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz", - "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz", - "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz", - "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz", - "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz", - "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz", - "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz", - "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/jest": { - "version": "0.2.26", - "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.26.tgz", - "integrity": "sha512-7lAi7q7ShTO3E5Gt1Xqf3pIhRbERxR1DUxvtVa9WKzIB+HGQ7wZP5sYx86zqnaEoKKGhmOoZ7gyW0IRu8Br5+A==", - "dev": true, - "dependencies": { - "@jest/create-cache-key-function": "^27.4.2", - "jsonc-parser": "^3.2.0" - }, - "engines": { - "npm": ">= 7.0.0" - }, - "peerDependencies": { - "@swc/core": "*" - } - }, - "node_modules/@swc/register": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@swc/register/-/register-0.1.10.tgz", - "integrity": "sha512-6STwH/q4dc3pitXLVkV7sP0Hiy+zBsU2wOF1aXpXR95pnH3RYHKIsDC+gvesfyB7jxNT9OOZgcqOp9RPxVTx9A==", - "dev": true, - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "pirates": "^4.0.1", - "source-map-support": "^0.5.13" - }, - "bin": { - "swc-node": "bin/swc-node" - }, - "peerDependencies": { - "@swc/core": "^1.0.46" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "16.10.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "16.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "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==" - }, - "node_modules/anymatch": { - "version": "3.1.2", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/arg": { - "version": "5.0.2", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.5.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/bin-check": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", - "dev": true, - "dependencies": { - "execa": "^0.7.0", - "executable": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/bin-version": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", - "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "find-versions": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bin-version-check": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.0.0.tgz", - "integrity": "sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==", - "dev": true, - "dependencies": { - "bin-version": "^6.0.0", - "semver": "^7.3.5", - "semver-truncate": "^2.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bin-version/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/bin-version/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bin-version/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bin-version/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001481", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", - "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "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" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/braces": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/fill-range": { - "version": "7.0.1", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chokidar/node_modules/is-number": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/chokidar/node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true - }, - "node_modules/commander": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/concurrently": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.0.1.tgz", - "integrity": "sha512-Sh8bGQMEL0TAmAm2meAXMjcASHZa7V0xXQVDBLknCPa9TPtkY9yYs+0cnGGgfdkW0SV1Mlg+hVGfXcoI8d3MJA==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "date-fns": "^2.29.3", - "lodash": "^4.17.21", - "rxjs": "^7.8.0", - "shell-quote": "^1.8.0", - "spawn-command": "0.0.2-1", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.1" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": "^14.13.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.0.tgz", - "integrity": "sha512-RGlcbzGhzEBCHuQe3k+Udyj5M00z0pm9S+VurHXFEOXxH+y0sVrJH2sMzoyz2d8N1EScazg+DVvmgyx0lurwwA==", - "dev": true, - "dependencies": { - "cssnano-preset-default": "^6.0.0", - "lilconfig": "^2.1.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.0.tgz", - "integrity": "sha512-BDxlaFzObRDXUiCCBQUNQcI+f1/aX2mgoNtXGjV6PG64POcHoDUoX+LgMWw+Q4609QhxwkcSnS65YFs42RA6qQ==", - "dev": true, - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^4.0.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^6.0.0", - "postcss-convert-values": "^6.0.0", - "postcss-discard-comments": "^6.0.0", - "postcss-discard-duplicates": "^6.0.0", - "postcss-discard-empty": "^6.0.0", - "postcss-discard-overridden": "^6.0.0", - "postcss-merge-longhand": "^6.0.0", - "postcss-merge-rules": "^6.0.0", - "postcss-minify-font-values": "^6.0.0", - "postcss-minify-gradients": "^6.0.0", - "postcss-minify-params": "^6.0.0", - "postcss-minify-selectors": "^6.0.0", - "postcss-normalize-charset": "^6.0.0", - "postcss-normalize-display-values": "^6.0.0", - "postcss-normalize-positions": "^6.0.0", - "postcss-normalize-repeat-style": "^6.0.0", - "postcss-normalize-string": "^6.0.0", - "postcss-normalize-timing-functions": "^6.0.0", - "postcss-normalize-unicode": "^6.0.0", - "postcss-normalize-url": "^6.0.0", - "postcss-normalize-whitespace": "^6.0.0", - "postcss-ordered-values": "^6.0.0", - "postcss-reduce-initial": "^6.0.0", - "postcss-reduce-transforms": "^6.0.0", - "postcss-svgo": "^6.0.0", - "postcss-unique-selectors": "^6.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz", - "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true - }, - "node_modules/date-fns": { - "version": "2.29.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "license": "Apache-2.0" - }, - "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "3.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.371", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz", - "integrity": "sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "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/esbuild": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", - "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.18", - "@esbuild/android-arm64": "0.17.18", - "@esbuild/android-x64": "0.17.18", - "@esbuild/darwin-arm64": "0.17.18", - "@esbuild/darwin-x64": "0.17.18", - "@esbuild/freebsd-arm64": "0.17.18", - "@esbuild/freebsd-x64": "0.17.18", - "@esbuild/linux-arm": "0.17.18", - "@esbuild/linux-arm64": "0.17.18", - "@esbuild/linux-ia32": "0.17.18", - "@esbuild/linux-loong64": "0.17.18", - "@esbuild/linux-mips64el": "0.17.18", - "@esbuild/linux-ppc64": "0.17.18", - "@esbuild/linux-riscv64": "0.17.18", - "@esbuild/linux-s390x": "0.17.18", - "@esbuild/linux-x64": "0.17.18", - "@esbuild/netbsd-x64": "0.17.18", - "@esbuild/openbsd-x64": "0.17.18", - "@esbuild/sunos-x64": "0.17.18", - "@esbuild/win32-arm64": "0.17.18", - "@esbuild/win32-ia32": "0.17.18", - "@esbuild/win32-x64": "0.17.18" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", - "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.39.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", - "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", - "dev": true, - "dependencies": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/execa/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/execa/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/ext-list": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", - "dev": true, - "dependencies": { - "mime-db": "^1.28.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ext-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", - "dev": true, - "dependencies": { - "ext-list": "^2.0.0", - "sort-keys-length": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-type": { - "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", - "dev": true, - "dependencies": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/filename-reserved-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/filenamify": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", - "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", - "dev": true, - "dependencies": { - "filename-reserved-regex": "^3.0.0", - "strip-outer": "^2.0.0", - "trim-repeated": "^2.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-versions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", - "dev": true, - "dependencies": { - "semver-regex": "^4.0.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fraction.js": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "dev": true, - "license": "MIT" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "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", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz", - "integrity": "sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==", - "dev": true, - "dependencies": { - "cliui": "^7.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jackspeak/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-changed-files/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-changed-files/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.5.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.5.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-sdsl": { - "version": "4.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "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", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/levn/node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/levn/node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.18.0.tgz", - "integrity": "sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==", - "dev": true, - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.18.0", - "lightningcss-darwin-x64": "1.18.0", - "lightningcss-linux-arm-gnueabihf": "1.18.0", - "lightningcss-linux-arm64-gnu": "1.18.0", - "lightningcss-linux-arm64-musl": "1.18.0", - "lightningcss-linux-x64-gnu": "1.18.0", - "lightningcss-linux-x64-musl": "1.18.0", - "lightningcss-win32-x64-msvc": "1.18.0" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz", - "integrity": "sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz", - "integrity": "sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz", - "integrity": "sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz", - "integrity": "sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz", - "integrity": "sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz", - "integrity": "sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz", - "integrity": "sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz", - "integrity": "sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/braces": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "7.0.1", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/micromatch/node_modules/is-number": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/optionator/node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/optionator/node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-filter-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", - "dev": true, - "dependencies": { - "arch": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", - "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", - "dev": true, - "dependencies": { - "lru-cache": "^9.0.0", - "minipass": "^5.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.4", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-colormin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz", - "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz", - "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-comments": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz", - "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz", - "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz", - "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz", - "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "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==", - "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==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz", - "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.0.tgz", - "integrity": "sha512-rCXkklftzEkniyv3f4mRCQzxD6oE4Quyh61uyWTUbCJ26Pv2hoz+fivJSsSBWxDBeScR4fKCfF3HHTcD7Ybqnw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.0", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz", - "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz", - "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==", - "dev": true, - "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz", - "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz", - "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz", - "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz", - "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz", - "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz", - "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz", - "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz", - "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz", - "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz", - "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz", - "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-ordered-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz", - "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==", - "dev": true, - "dependencies": { - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz", - "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz", - "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz", - "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.0.2" - }, - "engines": { - "node": "^14 || ^16 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz", - "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "license": "MIT" - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "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/quick-lru": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/read-cache": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.0.tgz", - "integrity": "sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g==", - "dev": true, - "dependencies": { - "glob": "^10.0.0" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.2.tgz", - "integrity": "sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "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/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-regex": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver-truncate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-2.0.0.tgz", - "integrity": "sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-truncate/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", - "dev": true, - "dependencies": { - "is-plain-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sort-keys-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", - "dev": true, - "dependencies": { - "sort-keys": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spawn-command": { - "version": "0.0.2-1", - "dev": true, - "license": "MIT" - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-outer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", - "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", - "dev": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/stylehacks": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz", - "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svgo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", - "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", - "dev": true, - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.2.1", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "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==", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/token-types": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", - "dev": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/trim-repeated": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", - "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==" - }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/turbo": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.9.3.tgz", - "integrity": "sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "turbo": "bin/turbo" - }, - "optionalDependencies": { - "turbo-darwin-64": "1.9.3", - "turbo-darwin-arm64": "1.9.3", - "turbo-linux-64": "1.9.3", - "turbo-linux-arm64": "1.9.3", - "turbo-windows-64": "1.9.3", - "turbo-windows-arm64": "1.9.3" - } - }, - "node_modules/turbo-darwin-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.9.3.tgz", - "integrity": "sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/turbo-darwin-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.9.3.tgz", - "integrity": "sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/turbo-linux-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.9.3.tgz", - "integrity": "sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/turbo-linux-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.9.3.tgz", - "integrity": "sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/turbo-windows-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.9.3.tgz", - "integrity": "sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/turbo-windows-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.9.3.tgz", - "integrity": "sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "oxide": { - "name": "tailwindcss-oxide", - "version": "0.1.0", - "extraneous": true, - "license": "MIT", - "workspaces": [ - "node" - ] - }, - "oxide-node-api-shim": { - "name": "@tailwindcss/oxide-shim", - "extraneous": true, - "license": "MIT" - }, - "oxide/crates/node": { - "name": "@tailwindcss/oxide", - "version": "0.0.0", - "extraneous": true, - "license": "MIT", - "devDependencies": { - "@napi-rs/cli": "^2.14.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-darwin-arm64": "0.0.0", - "@tailwindcss/oxide-darwin-x64": "0.0.0", - "@tailwindcss/oxide-freebsd-x64": "0.0.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "0.0.0", - "@tailwindcss/oxide-linux-arm64-gnu": "0.0.0", - "@tailwindcss/oxide-linux-arm64-musl": "0.0.0", - "@tailwindcss/oxide-linux-x64-gnu": "0.0.0", - "@tailwindcss/oxide-linux-x64-musl": "0.0.0", - "@tailwindcss/oxide-win32-x64-msvc": "0.0.0" - } - } - }, - "dependencies": { - "@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==" - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", - "dev": true - }, - "@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", - "dev": true, - "requires": { - "@babel/types": "^7.21.4", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.21.4", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.21.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@esbuild/android-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", - "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", - "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", - "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", - "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", - "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", - "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", - "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", - "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", - "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", - "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", - "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", - "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", - "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", - "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", - "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", - "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", - "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", - "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", - "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", - "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", - "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", - "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", - "dev": true, - "optional": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", - "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "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, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/create-cache-key-function": { - "version": "27.4.2", - "dev": true, - "requires": { - "@jest/types": "^27.4.2" - } - }, - "@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", - "dev": true, - "requires": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" - } - }, - "@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3" - } - }, - "@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.25.16" - } - }, - "@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", - "dev": true, - "requires": { - "@jest/test-result": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/types": { - "version": "27.5.1", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - } - } - }, - "@mole-inc/bin-wrapper": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", - "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", - "dev": true, - "requires": { - "bin-check": "^4.1.0", - "bin-version-check": "^5.0.0", - "content-disposition": "^0.5.4", - "ext-name": "^5.0.0", - "file-type": "^17.1.6", - "filenamify": "^5.0.2", - "got": "^11.8.5", - "os-filter-obj": "^2.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true - }, - "@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "dev": true - }, - "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true - }, - "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0" - } - }, - "@swc/cli": { - "version": "0.1.62", - "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.62.tgz", - "integrity": "sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==", - "dev": true, - "requires": { - "@mole-inc/bin-wrapper": "^8.0.1", - "commander": "^7.1.0", - "fast-glob": "^3.2.5", - "semver": "^7.3.8", - "slash": "3.0.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "dev": true - } - } - }, - "@swc/core": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz", - "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==", - "dev": true, - "requires": { - "@swc/core-darwin-arm64": "1.3.55", - "@swc/core-darwin-x64": "1.3.55", - "@swc/core-linux-arm-gnueabihf": "1.3.55", - "@swc/core-linux-arm64-gnu": "1.3.55", - "@swc/core-linux-arm64-musl": "1.3.55", - "@swc/core-linux-x64-gnu": "1.3.55", - "@swc/core-linux-x64-musl": "1.3.55", - "@swc/core-win32-arm64-msvc": "1.3.55", - "@swc/core-win32-ia32-msvc": "1.3.55", - "@swc/core-win32-x64-msvc": "1.3.55" - } - }, - "@swc/core-darwin-arm64": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz", - "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz", - "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm-gnueabihf": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz", - "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz", - "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz", - "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-gnu": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz", - "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-musl": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz", - "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==", - "dev": true, - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz", - "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==", - "dev": true, - "optional": true - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz", - "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==", - "dev": true, - "optional": true - }, - "@swc/core-win32-x64-msvc": { - "version": "1.3.55", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz", - "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==", - "dev": true, - "optional": true - }, - "@swc/jest": { - "version": "0.2.26", - "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.26.tgz", - "integrity": "sha512-7lAi7q7ShTO3E5Gt1Xqf3pIhRbERxR1DUxvtVa9WKzIB+HGQ7wZP5sYx86zqnaEoKKGhmOoZ7gyW0IRu8Br5+A==", - "dev": true, - "requires": { - "@jest/create-cache-key-function": "^27.4.2", - "jsonc-parser": "^3.2.0" - } - }, - "@swc/register": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@swc/register/-/register-0.1.10.tgz", - "integrity": "sha512-6STwH/q4dc3pitXLVkV7sP0Hiy+zBsU2wOF1aXpXR95pnH3RYHKIsDC+gvesfyB7jxNT9OOZgcqOp9RPxVTx9A==", - "dev": true, - "requires": { - "lodash.clonedeep": "^4.5.0", - "pirates": "^4.0.1", - "source-map-support": "^0.5.13" - } - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "16.10.2", - "dev": true - }, - "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/yargs": { - "version": "16.0.4", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.1", - "dev": true - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" - }, - "anymatch": { - "version": "3.1.2", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, - "arg": { - "version": "5.0.2" - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "dev": true, - "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", - "dev": true, - "requires": { - "@jest/transform": "^29.5.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.5.0", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.0" - }, - "bin-check": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "executable": "^4.1.0" - } - }, - "bin-version": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", - "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "find-versions": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - } - } - }, - "bin-version-check": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.0.0.tgz", - "integrity": "sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==", - "dev": true, - "requires": { - "bin-version": "^6.0.0", - "semver": "^7.3.5", - "semver-truncate": "^2.0.0" - } - }, - "binary-extensions": { - "version": "2.2.0" - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "dev": true - }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true - }, - "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-css": { - "version": "2.0.1" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001481", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", - "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.2", - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-number": { - "version": "7.0.0" - }, - "to-regex-range": { - "version": "5.0.1", - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true - }, - "commander": { - "version": "7.2.0", - "dev": true - }, - "concat-map": { - "version": "0.0.1" - }, - "concurrently": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.0.1.tgz", - "integrity": "sha512-Sh8bGQMEL0TAmAm2meAXMjcASHZa7V0xXQVDBLknCPa9TPtkY9yYs+0cnGGgfdkW0SV1Mlg+hVGfXcoI8d3MJA==", - "dev": true, - "requires": { - "chalk": "^4.1.2", - "date-fns": "^2.29.3", - "lodash": "^4.17.21", - "rxjs": "^7.8.0", - "shell-quote": "^1.8.0", - "spawn-command": "0.0.2-1", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.1" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - } - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", - "dev": true, - "requires": {} - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true - }, - "cssesc": { - "version": "3.0.0" - }, - "cssnano": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.0.tgz", - "integrity": "sha512-RGlcbzGhzEBCHuQe3k+Udyj5M00z0pm9S+VurHXFEOXxH+y0sVrJH2sMzoyz2d8N1EScazg+DVvmgyx0lurwwA==", - "dev": true, - "requires": { - "cssnano-preset-default": "^6.0.0", - "lilconfig": "^2.1.0" - } - }, - "cssnano-preset-default": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.0.tgz", - "integrity": "sha512-BDxlaFzObRDXUiCCBQUNQcI+f1/aX2mgoNtXGjV6PG64POcHoDUoX+LgMWw+Q4609QhxwkcSnS65YFs42RA6qQ==", - "dev": true, - "requires": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^4.0.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^6.0.0", - "postcss-convert-values": "^6.0.0", - "postcss-discard-comments": "^6.0.0", - "postcss-discard-duplicates": "^6.0.0", - "postcss-discard-empty": "^6.0.0", - "postcss-discard-overridden": "^6.0.0", - "postcss-merge-longhand": "^6.0.0", - "postcss-merge-rules": "^6.0.0", - "postcss-minify-font-values": "^6.0.0", - "postcss-minify-gradients": "^6.0.0", - "postcss-minify-params": "^6.0.0", - "postcss-minify-selectors": "^6.0.0", - "postcss-normalize-charset": "^6.0.0", - "postcss-normalize-display-values": "^6.0.0", - "postcss-normalize-positions": "^6.0.0", - "postcss-normalize-repeat-style": "^6.0.0", - "postcss-normalize-string": "^6.0.0", - "postcss-normalize-timing-functions": "^6.0.0", - "postcss-normalize-unicode": "^6.0.0", - "postcss-normalize-url": "^6.0.0", - "postcss-normalize-whitespace": "^6.0.0", - "postcss-ordered-values": "^6.0.0", - "postcss-reduce-initial": "^6.0.0", - "postcss-reduce-transforms": "^6.0.0", - "postcss-svgo": "^6.0.0", - "postcss-unique-selectors": "^6.0.0" - } - }, - "cssnano-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz", - "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==", - "dev": true, - "requires": {} - }, - "csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "requires": { - "css-tree": "~2.2.0" - }, - "dependencies": { - "css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "requires": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - } - }, - "mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true - } - } - }, - "date-fns": { - "version": "2.29.3", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "didyoumean": { - "version": "1.2.2" - }, - "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true - }, - "dlv": { - "version": "1.1.3" - }, - "doctrine": { - "version": "3.0.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "dev": true, - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - } - }, - "electron-to-chromium": { - "version": "1.4.371", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz", - "integrity": "sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true - }, - "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, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "esbuild": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", - "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.18", - "@esbuild/android-arm64": "0.17.18", - "@esbuild/android-x64": "0.17.18", - "@esbuild/darwin-arm64": "0.17.18", - "@esbuild/darwin-x64": "0.17.18", - "@esbuild/freebsd-arm64": "0.17.18", - "@esbuild/freebsd-x64": "0.17.18", - "@esbuild/linux-arm": "0.17.18", - "@esbuild/linux-arm64": "0.17.18", - "@esbuild/linux-ia32": "0.17.18", - "@esbuild/linux-loong64": "0.17.18", - "@esbuild/linux-mips64el": "0.17.18", - "@esbuild/linux-ppc64": "0.17.18", - "@esbuild/linux-riscv64": "0.17.18", - "@esbuild/linux-s390x": "0.17.18", - "@esbuild/linux-x64": "0.17.18", - "@esbuild/netbsd-x64": "0.17.18", - "@esbuild/openbsd-x64": "0.17.18", - "@esbuild/sunos-x64": "0.17.18", - "@esbuild/win32-arm64": "0.17.18", - "@esbuild/win32-ia32": "0.17.18", - "@esbuild/win32-x64": "0.17.18" - } - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "eslint": { - "version": "8.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz", - "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.39.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-locate": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - } - } - }, - "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", - "dev": true - }, - "espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "dev": true - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - } - } - }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "requires": { - "pify": "^2.2.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" - } - }, - "ext-list": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", - "dev": true, - "requires": { - "mime-db": "^1.28.0" - } - }, - "ext-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", - "dev": true, - "requires": { - "ext-list": "^2.0.0", - "sort-keys-length": "^1.0.0" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "dev": true - }, - "fastq": { - "version": "1.11.0", - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-type": { - "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", - "dev": true, - "requires": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" - } - }, - "filename-reserved-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", - "dev": true - }, - "filenamify": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", - "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", - "dev": true, - "requires": { - "filename-reserved-regex": "^3.0.0", - "strip-outer": "^2.0.0", - "trim-repeated": "^2.0.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "find-versions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", - "dev": true, - "requires": { - "semver-regex": "^4.0.5" - } - }, - "flat-cache": { - "version": "3.0.4", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.1.1", - "dev": true - }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "dependencies": { - "signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", - "dev": true - } - } - }, - "fraction.js": { - "version": "4.2.0", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0" - }, - "fsevents": { - "version": "2.3.2", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4" - }, - "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 - }, - "is-binary-path": { - "version": "2.1.0", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-path-inside": { - "version": "3.0.3", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jackspeak": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz", - "integrity": "sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==", - "dev": true, - "requires": { - "@pkgjs/parseargs": "^0.11.0", - "cliui": "^7.0.4" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - } - } - }, - "jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "requires": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - } - } - }, - "jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", - "dev": true, - "requires": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true - }, - "jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-leak-detector": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-util": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", - "dev": true - }, - "jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", - "dev": true, - "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" - } - }, - "jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "jest-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", - "dev": true, - "requires": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.5.0", - "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.5.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==" - }, - "js-sdsl": { - "version": "4.1.4", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "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", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "dev": true - }, - "keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "dependencies": { - "prelude-ls": { - "version": "1.2.1", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - } - } - }, - "lightningcss": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.18.0.tgz", - "integrity": "sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "lightningcss-darwin-arm64": "1.18.0", - "lightningcss-darwin-x64": "1.18.0", - "lightningcss-linux-arm-gnueabihf": "1.18.0", - "lightningcss-linux-arm64-gnu": "1.18.0", - "lightningcss-linux-arm64-musl": "1.18.0", - "lightningcss-linux-x64-gnu": "1.18.0", - "lightningcss-linux-x64-musl": "1.18.0", - "lightningcss-win32-x64-msvc": "1.18.0" - } - }, - "lightningcss-darwin-arm64": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz", - "integrity": "sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==", - "dev": true, - "optional": true - }, - "lightningcss-darwin-x64": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz", - "integrity": "sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==", - "dev": true, - "optional": true - }, - "lightningcss-linux-arm-gnueabihf": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz", - "integrity": "sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==", - "dev": true, - "optional": true - }, - "lightningcss-linux-arm64-gnu": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz", - "integrity": "sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==", - "dev": true, - "optional": true - }, - "lightningcss-linux-arm64-musl": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz", - "integrity": "sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==", - "dev": true, - "optional": true - }, - "lightningcss-linux-x64-gnu": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz", - "integrity": "sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==", - "dev": true, - "optional": true - }, - "lightningcss-linux-x64-musl": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz", - "integrity": "sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==", - "dev": true, - "optional": true - }, - "lightningcss-win32-x64-msvc": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz", - "integrity": "sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==", - "dev": true, - "optional": true - }, - "lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" - }, - "lines-and-columns": { - "version": "1.2.4" - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1" - }, - "micromatch": { - "version": "4.0.5", - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0" - }, - "to-regex-range": { - "version": "5.0.1", - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "dev": true - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" - }, - "natural-compare": { - "version": "1.4.0", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0" - }, - "normalize-range": { - "version": "0.1.2", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "requires": { - "path-key": "^2.0.0" - }, - "dependencies": { - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true - } - } - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1" - }, - "object-hash": { - "version": "3.0.0" - }, - "once": { - "version": "1.4.0", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.1", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "dependencies": { - "prelude-ls": { - "version": "1.2.1", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - } - } - }, - "os-filter-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", - "dev": true, - "requires": { - "arch": "^2.1.0" - } - }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1" - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7" - }, - "path-scurry": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz", - "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==", - "dev": true, - "requires": { - "lru-cache": "^9.0.0", - "minipass": "^5.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", - "dev": true - } - } - }, - "peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", - "dev": true - }, - "picocolors": { - "version": "1.0.0" - }, - "picomatch": { - "version": "2.3.1" - }, - "pify": { - "version": "2.3.0" - }, - "pirates": { - "version": "4.0.4" - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "requires": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-colormin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz", - "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-convert-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz", - "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-discard-comments": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz", - "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==", - "dev": true, - "requires": {} - }, - "postcss-discard-duplicates": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz", - "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==", - "dev": true, - "requires": {} - }, - "postcss-discard-empty": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz", - "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==", - "dev": true, - "requires": {} - }, - "postcss-discard-overridden": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz", - "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==", - "dev": true, - "requires": {} - }, - "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==", - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "dependencies": { - "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==" - } - } - }, - "postcss-merge-longhand": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz", - "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.0.0" - } - }, - "postcss-merge-rules": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.0.tgz", - "integrity": "sha512-rCXkklftzEkniyv3f4mRCQzxD6oE4Quyh61uyWTUbCJ26Pv2hoz+fivJSsSBWxDBeScR4fKCfF3HHTcD7Ybqnw==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.0", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-minify-font-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz", - "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-gradients": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz", - "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==", - "dev": true, - "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-params": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz", - "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz", - "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "requires": { - "postcss-selector-parser": "^6.0.11" - } - }, - "postcss-normalize-charset": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz", - "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==", - "dev": true, - "requires": {} - }, - "postcss-normalize-display-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz", - "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-positions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz", - "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz", - "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-string": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz", - "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz", - "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-unicode": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz", - "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz", - "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-whitespace": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz", - "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-ordered-values": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz", - "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==", - "dev": true, - "requires": { - "cssnano-utils": "^4.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-reduce-initial": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz", - "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz", - "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.11", - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-svgo": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz", - "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.0.2" - } - }, - "postcss-unique-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz", - "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-value-parser": { - "version": "4.2.0" - }, - "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3" - }, - "quick-lru": { - "version": "5.1.1", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "read-cache": { - "version": "1.0.0", - "requires": { - "pify": "^2.3.0" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "requires": { - "readable-stream": "^3.6.0" - } - }, - "readdirp": { - "version": "3.6.0", - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "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 - }, - "resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true - }, - "responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "reusify": { - "version": "1.0.4" - }, - "rimraf": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.0.tgz", - "integrity": "sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g==", - "dev": true, - "requires": { - "glob": "^10.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.2.tgz", - "integrity": "sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0", - "path-scurry": "^1.7.0" - } - }, - "minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "run-parallel": { - "version": "1.2.0", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "semver-regex": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", - "dev": true - }, - "semver-truncate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-2.0.0.tgz", - "integrity": "sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "dev": true - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "sort-keys-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", - "dev": true, - "requires": { - "sort-keys": "^1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "dev": true - }, - "source-map-js": { - "version": "1.0.2" - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "spawn-command": { - "version": "0.0.2-1", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "dev": true - }, - "strip-outer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", - "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", - "dev": true - }, - "strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" - } - }, - "stylehacks": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz", - "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==", - "dev": true, - "requires": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - } - }, - "sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - } - } - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0" - }, - "svgo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", - "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", - "dev": true, - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.2.1", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "dev": true - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "token-types": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "dev": true - }, - "trim-repeated": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", - "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", - "dev": true, - "requires": { - "escape-string-regexp": "^5.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true - } - } - }, - "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==" - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "turbo": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.9.3.tgz", - "integrity": "sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==", - "dev": true, - "requires": { - "turbo-darwin-64": "1.9.3", - "turbo-darwin-arm64": "1.9.3", - "turbo-linux-64": "1.9.3", - "turbo-linux-arm64": "1.9.3", - "turbo-windows-64": "1.9.3", - "turbo-windows-arm64": "1.9.3" - } - }, - "turbo-darwin-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.9.3.tgz", - "integrity": "sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==", - "dev": true, - "optional": true - }, - "turbo-darwin-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.9.3.tgz", - "integrity": "sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==", - "dev": true, - "optional": true - }, - "turbo-linux-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.9.3.tgz", - "integrity": "sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==", - "dev": true, - "optional": true - }, - "turbo-linux-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.9.3.tgz", - "integrity": "sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==", - "dev": true, - "optional": true - }, - "turbo-windows-64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.9.3.tgz", - "integrity": "sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==", - "dev": true, - "optional": true - }, - "turbo-windows-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.9.3.tgz", - "integrity": "sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==", - "dev": true, - "optional": true - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2" - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2" - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "dev": true - }, - "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "dev": true - } - } -} diff --git a/package.json b/package.json index d74c2dcae..5a90f70a5 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,9 @@ "integrations/*", "oxide/crates/node" ], - "tailwindcss": { - "engine": "oxide" - }, "scripts": { "dev": "concurrently -n tailwind,oxide-node -c green,yellow 'npm run dev:js' 'npm run dev:rust'", - "build": "npm run build:rust && npm run build:js && npm run build:peers", + "build": "npm run build:rust && npm run build:js", "test": "jest", "test:integrations": "npm run test -w ./integrations", "style": "eslint .", @@ -29,8 +26,7 @@ "dev:rust": "npm run --prefix ./oxide dev:node", "dev:js": "npm run build:js -- --watch", "build:rust": "npm run --prefix ./oxide build:node", - "build:js": "npm run generate:plugin-list && swc src --out-dir lib --copy-files --delete-dir-on-start --config jsc.transform.optimizer.globals.vars.__OXIDE__='\"true\"'", - "build:peers": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false", + "build:js": "npm run generate:plugin-list && swc src --out-dir lib --copy-files --delete-dir-on-start", "generate:plugin-list": "node -r @swc/register scripts/create-plugin-list.js", "generate:types": "node -r @swc/register scripts/generate-types.js" }, @@ -38,7 +34,6 @@ "src/*", "cli/*", "lib/*", - "peers/*", "scripts/*.js", "stubs/*", "nesting/*", @@ -54,8 +49,6 @@ "@swc/register": "^0.1.10", "autoprefixer": "^10.4.14", "concurrently": "^8.0.1", - "cssnano": "^6.0.1", - "esbuild": "^0.17.18", "eslint": "^8.39.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", diff --git a/package.stable.json b/package.stable.json deleted file mode 100644 index 0e83f619f..000000000 --- a/package.stable.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "name": "tailwindcss", - "version": "3.3.2", - "description": "A utility-first CSS framework for rapidly building custom user interfaces.", - "license": "MIT", - "main": "lib/index.js", - "types": "types/index.d.ts", - "repository": "https://github.com/tailwindlabs/tailwindcss.git", - "bugs": "https://github.com/tailwindlabs/tailwindcss/issues", - "homepage": "https://tailwindcss.com", - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "tailwindcss": { - "engine": "stable" - }, - "scripts": { - "prebuild": "npm run generate && rimraf lib", - "build": "swc src --out-dir lib --copy-files --config jsc.transform.optimizer.globals.vars.__OXIDE__='\"false\"'", - "postbuild": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false", - "rebuild-fixtures": "npm run build && node -r @swc/register scripts/rebuildFixtures.js", - "style": "eslint .", - "pretest": "npm run generate", - "test": "jest", - "test:integrations": "npm run test --prefix ./integrations", - "install:integrations": "node scripts/install-integrations.js", - "generate:plugin-list": "node -r @swc/register scripts/create-plugin-list.js", - "generate:types": "node -r @swc/register scripts/generate-types.js", - "generate": "npm run generate:plugin-list && npm run generate:types", - "release-channel": "node ./scripts/release-channel.js", - "release-notes": "node ./scripts/release-notes.js", - "prepublishOnly": "npm install --force && npm run build" - }, - "files": [ - "src/*", - "cli/*", - "lib/*", - "peers/*", - "scripts/*.js", - "stubs/*", - "nesting/*", - "types/**/*", - "*.d.ts", - "*.js", - "base.css", - "components.css", - "screens.css", - "tailwind.css", - "utilities.css", - "variants.css" - ], - "devDependencies": { - "@swc/cli": "^0.1.62", - "@swc/core": "^1.3.55", - "@swc/jest": "^0.2.26", - "@swc/register": "^0.1.10", - "autoprefixer": "^10.4.14", - "browserslist": "^4.21.5", - "concurrently": "^8.0.1", - "cssnano": "^6.0.0", - "esbuild": "^0.17.18", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "jest": "^29.5.0", - "jest-diff": "^29.5.0", - "lightningcss": "1.18.0", - "prettier": "^2.8.8", - "rimraf": "^5.0.0", - "source-map-js": "^1.0.2", - "turbo": "^1.9.3" - }, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.18.2", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "browserslist": [ - "> 1%", - "not edge <= 18", - "not ie 11", - "not op_mini all" - ], - "jest": { - "testTimeout": 30000, - "setupFilesAfterEnv": [ - "/jest/customMatchers.js" - ], - "testPathIgnorePatterns": [ - "/node_modules/", - "/integrations/", - "/standalone-cli/", - "\\.test\\.skip\\.js$" - ], - "transformIgnorePatterns": [ - "node_modules/(?!lightningcss)" - ], - "transform": { - "\\.js$": "@swc/jest", - "\\.ts$": "@swc/jest" - } - }, - "engines": { - "node": ">=14.0.0" - } -} diff --git a/scripts/swap-engines.js b/scripts/swap-engines.js deleted file mode 100644 index ef8308aeb..000000000 --- a/scripts/swap-engines.js +++ /dev/null @@ -1,40 +0,0 @@ -let fs = require('fs') -let path = require('path') - -let engines = { - stable: { - files: [ - path.resolve(__dirname, '..', 'package.stable.json'), - path.resolve(__dirname, '..', 'package-lock.stable.json'), - ], - }, - oxide: { - files: [ - path.resolve(__dirname, '..', 'package.oxide.json'), - path.resolve(__dirname, '..', 'package-lock.oxide.json'), - ], - }, -} - -// Find out what the current engine is that we are using: -let [otherEngine, info] = Object.entries(engines).find(([, info]) => - info.files.every((file) => fs.existsSync(file)) -) -let currentEngine = otherEngine === 'oxide' ? 'stable' : 'oxide' - -console.log(`Current engine: \`${currentEngine}\`, swapping to \`${otherEngine}\``) - -// Swap the engines -for (let file of info.files) { - fs.renameSync( - file.replace(`.${otherEngine}`, ''), - file.replace(`.${otherEngine}`, `.${currentEngine}`) - ) -} -for (let file of engines[otherEngine].files) { - fs.renameSync(file, file.replace(`.${otherEngine}`, '')) -} - -console.log( - 'Engines have been swapped. Make sure to run `npm install` to update your dependencies.' -) diff --git a/src/cli-peer-dependencies.js b/src/cli-peer-dependencies.js deleted file mode 100644 index 6b9f986aa..000000000 --- a/src/cli-peer-dependencies.js +++ /dev/null @@ -1,15 +0,0 @@ -export function lazyPostcss() { - return require('postcss') -} - -export function lazyPostcssImport() { - return require('postcss-import') -} - -export function lazyAutoprefixer() { - return require('autoprefixer') -} - -export function lazyCssnano() { - return require('cssnano') -} diff --git a/src/cli.js b/src/cli.js index 4b73accbb..9a5aff27b 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,7 +1,3 @@ #!/usr/bin/env node -if (__OXIDE__) { - module.exports = require('./oxide/cli') -} else { - module.exports = require('./cli/index') -} +module.exports = require('./cli/index') diff --git a/src/cli/build/deps.js b/src/cli/build/deps.js deleted file mode 100644 index 9435b929a..000000000 --- a/src/cli/build/deps.js +++ /dev/null @@ -1,56 +0,0 @@ -// @ts-check - -import { - // @ts-ignore - lazyPostcss, - - // @ts-ignore - lazyPostcssImport, - - // @ts-ignore - lazyCssnano, - - // @ts-ignore - lazyAutoprefixer, -} from '../../../peers/index.js' - -/** - * @returns {import('postcss')} - */ -export function loadPostcss() { - // Try to load a local `postcss` version first - try { - return require('postcss') - } catch {} - - return lazyPostcss() -} - -export function loadPostcssImport() { - // Try to load a local `postcss-import` version first - try { - return require('postcss-import') - } catch {} - - return lazyPostcssImport() -} - -export function loadCssNano() { - let options = { preset: ['default', { cssDeclarationSorter: false }] } - - // Try to load a local `cssnano` version first - try { - return require('cssnano') - } catch {} - - return lazyCssnano()(options) -} - -export function loadAutoprefixer() { - // Try to load a local `autoprefixer` version first - try { - return require('autoprefixer') - } catch {} - - return lazyAutoprefixer() -} diff --git a/src/cli/build/plugin.js b/src/cli/build/plugin.js index a5045593d..ee30eb442 100644 --- a/src/cli/build/plugin.js +++ b/src/cli/build/plugin.js @@ -1,14 +1,17 @@ // @ts-check +import pkg from '../../../package.json' import path from 'path' import fs from 'fs' +import postcss from 'postcss' import postcssrc from 'postcss-load-config' +import browserslist from 'browserslist' +import lightning from 'lightningcss' import { lilconfig } from 'lilconfig' import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API import tailwind from '../../processTailwindFeatures' -import { loadAutoprefixer, loadCssNano, loadPostcss, loadPostcssImport } from './deps' import { formatNodes, drainStdin, outputFile } from './utils' import { env } from '../../lib/sharedState' import resolveConfig from '../../../resolveConfig.js' @@ -20,6 +23,41 @@ import log from '../../util/log' import { loadConfig } from '../../lib/load-config' import getModuleDependencies from '../../lib/getModuleDependencies' import { validateConfig } from '../../util/validateConfig' +import { handleImportAtRules } from '../../lib/handleImportAtRules' +import { flagEnabled } from '../../featureFlags' + +async function lightningcss(shouldMinify, result, options = {}) { + // TODO: handle --no-autoprefixer option if possible + try { + let transformed = lightning.transform({ + filename: result.opts.from || 'input.css', + code: Buffer.from(result.css, 'utf-8'), + minify: shouldMinify, + sourceMap: result.map === undefined ? options.map : !!result.map, + inputSourceMap: result.map ? result.map.toString() : undefined, + targets: lightning.browserslistToTargets(browserslist(pkg.browserslist)), + drafts: { + nesting: true, + }, + }) + + return Object.assign(result, { + css: transformed.code.toString('utf8'), + map: result.map + ? Object.assign(result.map, { + toString() { + return transformed.map.toString() + }, + }) + : result.map, + }) + } catch (err) { + console.error('Unable to use Lightning CSS. Using raw version instead.') + console.error(err) + + return result + } +} /** * @@ -76,39 +114,6 @@ async function loadPostCssPlugins(customPostCssPath) { return [beforePlugins, afterPlugins, config.options] } -function loadBuiltinPostcssPlugins() { - let postcss = loadPostcss() - let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: ' - return [ - [ - (root) => { - root.walkAtRules('import', (rule) => { - if (rule.params.slice(1).startsWith('tailwindcss/')) { - rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params })) - rule.remove() - } - }) - }, - loadPostcssImport(), - (root) => { - root.walkComments((rule) => { - if (rule.text.startsWith(IMPORT_COMMENT)) { - rule.after( - postcss.atRule({ - name: 'import', - params: rule.text.replace(IMPORT_COMMENT, ''), - }) - ) - rule.remove() - } - }) - }, - ], - [], - {}, - ] -} - let state = { /** @type {any} */ context: null, @@ -119,7 +124,7 @@ let state = { /** @type {{content: string, extension: string}[]} */ changedContent: [], - /** @type {ReturnType | null} */ + /** @type {{config: import('../../../types').Config, dependencies: Set, dispose: Function } | null} */ configBag: null, contextDependencies: new Set(), @@ -161,14 +166,7 @@ let state = { }, } - // @ts-ignore - if (__OXIDE__) { - this.configBag.config = validateConfig(resolveConfig(this.configBag.config)) - } else { - this.configBag.config = validateConfig( - resolveConfig(this.configBag.config, { content: { files: [] } }) - ) - } + this.configBag.config = validateConfig(resolveConfig(this.configBag.config)) // Override content files if `--content` has been passed explicitly if (content?.length > 0) { @@ -192,7 +190,7 @@ let state = { let files = fastGlob.sync(this.contentPatterns.all) for (let file of files) { - if (__OXIDE__) { + if (flagEnabled(this.config, 'oxideParser')) { content.push({ file, extension: path.extname(file).slice(1), @@ -258,8 +256,6 @@ let state = { } export async function createProcessor(args, cliConfigPath) { - let postcss = loadPostcss() - let input = args['--input'] let output = args['--output'] let includePostCss = args['--postcss'] @@ -267,7 +263,9 @@ export async function createProcessor(args, cliConfigPath) { let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins(customPostCssPath) - : loadBuiltinPostcssPlugins() + : [[], [], {}] + + beforePlugins.unshift(...handleImportAtRules()) if (args['--purge']) { log.warn('purge-flag-deprecated', [ @@ -313,8 +311,6 @@ export async function createProcessor(args, cliConfigPath) { tailwindPlugin, !args['--minify'] && formatNodes, ...afterPlugins, - !args['--no-autoprefixer'] && loadAutoprefixer(), - args['--minify'] && loadCssNano(), ].filter(Boolean) /** @type {import('postcss').Processor} */ @@ -338,9 +334,15 @@ export async function createProcessor(args, cliConfigPath) { async function build() { let start = process.hrtime.bigint() + let options = { + ...postcssOptions, + from: input, + to: output, + } return readInput() - .then((css) => processor.process(css, { ...postcssOptions, from: input, to: output })) + .then((css) => processor.process(css, options)) + .then((result) => lightningcss(!!args['--minify'], result, options)) .then((result) => { if (!state.watcher) { return result diff --git a/src/cli/init/index.js b/src/cli/init/index.js index b344b5b2e..6bd7e41e2 100644 --- a/src/cli/init/index.js +++ b/src/cli/init/index.js @@ -38,11 +38,6 @@ export function init(args) { 'utf8' ) - // Drop `content` in the oxide engine to promote auto content - if (__OXIDE__) { - stubContentsFile = stubContentsFile.replace(/ *content: \[\],\n/, '') - } - // Change colors import stubContentsFile = stubContentsFile.replace('../colors', 'tailwindcss/colors') diff --git a/src/corePlugins.js b/src/corePlugins.js index e4d2ff279..fc373a77f 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -1255,13 +1255,13 @@ export let corePlugins = { ], ]), - space: ({ matchUtilities, addUtilities, theme }) => { + space: ({ matchUtilities, addUtilities, theme, config }) => { matchUtilities( { 'space-x': (value) => { value = value === '0' ? '0px' : value - if (__OXIDE__) { + if (flagEnabled(config(), 'logicalSiblingUtilities')) { return { '& > :not([hidden]) ~ :not([hidden])': { '--tw-space-x-reverse': '0', @@ -1300,13 +1300,13 @@ export let corePlugins = { }) }, - divideWidth: ({ matchUtilities, addUtilities, theme }) => { + divideWidth: ({ matchUtilities, addUtilities, theme, config }) => { matchUtilities( { 'divide-x': (value) => { value = value === '0' ? '0px' : value - if (__OXIDE__) { + if (flagEnabled(config(), 'logicalSiblingUtilities')) { return { '& > :not([hidden]) ~ :not([hidden])': { '@defaults border-width': {}, diff --git a/src/featureFlags.js b/src/featureFlags.js index dc9f2d7c4..b4eca6665 100644 --- a/src/featureFlags.js +++ b/src/featureFlags.js @@ -3,13 +3,10 @@ import log from './util/log' let defaults = { optimizeUniversalDefaults: false, - generalizedModifiers: true, - get disableColorOpacityUtilitiesByDefault() { - return __OXIDE__ - }, - get relativeContentPathsByDefault() { - return __OXIDE__ - }, + disableColorOpacityUtilitiesByDefault: false, + relativeContentPathsByDefault: false, + oxideParser: false, + logicalSiblingUtilities: false, } let featureFlags = { @@ -18,10 +15,11 @@ let featureFlags = { 'respectDefaultRingColorOpacity', 'disableColorOpacityUtilitiesByDefault', 'relativeContentPathsByDefault', + 'logicalSiblingUtilities', ], experimental: [ 'optimizeUniversalDefaults', - 'generalizedModifiers', + 'oxideParser', // 'variantGrouping', ], } diff --git a/src/lib/content.js b/src/lib/content.js index 7b58e9d67..9979d251d 100644 --- a/src/lib/content.js +++ b/src/lib/content.js @@ -7,6 +7,7 @@ import fastGlob from 'fast-glob' import normalizePath from 'normalize-path' import { parseGlob } from '../util/parseGlob' import { env } from './sharedState' +import { resolveContentPaths } from '@tailwindcss/oxide' /** @typedef {import('../../types/config.js').RawFile} RawFile */ /** @typedef {import('../../types/config.js').FilePath} FilePath */ @@ -17,17 +18,24 @@ import { env } from './sharedState' * @returns {ContentPath[]} */ function resolveContentFiles(tailwindConfig, { skip = [] } = {}) { - if (tailwindConfig.content.files === 'auto' && __OXIDE__) { - env.DEBUG && console.time('Calculating resolve content paths') - tailwindConfig.content.files = require('@tailwindcss/oxide').resolveContentPaths({ - base: process.cwd(), - }) - if (skip.length > 0) { - tailwindConfig.content.files = tailwindConfig.content.files.filter( - (filePath) => !skip.includes(filePath) - ) + if ( + Array.isArray(tailwindConfig.content.files) && + tailwindConfig.content.files.includes('auto') + ) { + let idx = tailwindConfig.content.files.indexOf('auto') + if (idx !== -1) { + env.DEBUG && console.time('Calculating resolve content paths') + let resolved = resolveContentPaths({ base: process.cwd() }) + env.DEBUG && console.timeEnd('Calculating resolve content paths') + + tailwindConfig.content.files.splice(idx, 1, ...resolved) } - env.DEBUG && console.timeEnd('Calculating resolve content paths') + } + + if (skip.length > 0) { + tailwindConfig.content.files = tailwindConfig.content.files.filter( + (filePath) => !skip.includes(filePath) + ) } return tailwindConfig.content.files @@ -191,12 +199,9 @@ function resolvePathSymlinks(contentPath) { * @returns {[{ content: string, extension: string }[], Map]} */ export function resolvedChangedContent(context, candidateFiles, fileModifiedMap) { - let changedContent = - context.tailwindConfig.content.files === 'auto' && __OXIDE__ - ? [] - : context.tailwindConfig.content.files - .filter((item) => typeof item.raw === 'string') - .map(({ raw, extension = 'html' }) => ({ content: raw, extension })) + let changedContent = context.tailwindConfig.content.files + .filter((item) => typeof item.raw === 'string') + .map(({ raw, extension = 'html' }) => ({ content: raw, extension })) let [changedFiles, mTimesToCommit] = resolveChangedFiles(candidateFiles, fileModifiedMap) diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index bf7d8f17f..c3fbc8cc2 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -1,10 +1,12 @@ import fs from 'fs' import LRU from '@alloc/quick-lru' +import { parseCandidateStringsFromFiles } from '@tailwindcss/oxide' import * as sharedState from './sharedState' import { generateRules } from './generateRules' import log from '../util/log' import cloneNodes from '../util/cloneNodes' import { defaultExtractor } from './defaultExtractor' +import { flagEnabled } from '../featureFlags' let env = sharedState.env @@ -130,9 +132,9 @@ export default function expandTailwindAtRules(context) { env.DEBUG && console.time('Reading changed files') - if (__OXIDE__) { + if (flagEnabled(context.tailwindConfig, 'oxideParser')) { // TODO: Pass through or implement `extractor` - for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles( + for (let candidate of parseCandidateStringsFromFiles( context.changedContent // Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform) )) { @@ -162,7 +164,7 @@ export default function expandTailwindAtRules(context) { env.DEBUG && console.time('Generate rules') env.DEBUG && console.time('Sorting candidates') - let sortedCandidates = __OXIDE__ + let sortedCandidates = flagEnabled(context.tailwindConfig, 'oxideParser') ? candidates : new Set( [...candidates].sort((a, z) => { diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index cbd671075..3aec15951 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -175,10 +175,6 @@ function applyVariant(variant, matches, context) { if (modifiers.length && !context.variantMap.has(variant)) { variant = baseVariant args.modifier = modifiers[0] - - if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) { - return [] - } } } diff --git a/src/lib/handleImportAtRules.js b/src/lib/handleImportAtRules.js index 57587d309..bcb55b5db 100644 --- a/src/lib/handleImportAtRules.js +++ b/src/lib/handleImportAtRules.js @@ -1,8 +1,9 @@ import postcss from 'postcss' +import postcssImport from 'postcss-import' const TAILWIND = Symbol() -export function handleImportAtRules(postcssImport = require('postcss-import')) { +export function handleImportAtRules() { let RESTORE_ATRULE_COMMENT = '__TAILWIND_RESTORE__' let atRulesToRestore = ['tailwind', 'config'] diff --git a/src/lib/normalizeTailwindDirectives.js b/src/lib/normalizeTailwindDirectives.js index 530ff35b3..0366d1662 100644 --- a/src/lib/normalizeTailwindDirectives.js +++ b/src/lib/normalizeTailwindDirectives.js @@ -10,35 +10,6 @@ export default function normalizeTailwindDirectives(root) { applyDirectives.add(atRule) } - if (!__OXIDE__) { - if (atRule.name === 'import') { - if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") { - atRule.name = 'tailwind' - atRule.params = 'base' - } else if ( - atRule.params === '"tailwindcss/components"' || - atRule.params === "'tailwindcss/components'" - ) { - atRule.name = 'tailwind' - atRule.params = 'components' - } else if ( - atRule.params === '"tailwindcss/utilities"' || - atRule.params === "'tailwindcss/utilities'" - ) { - atRule.name = 'tailwind' - atRule.params = 'utilities' - } else if ( - atRule.params === '"tailwindcss/screens"' || - atRule.params === "'tailwindcss/screens'" || - atRule.params === '"tailwindcss/variants"' || - atRule.params === "'tailwindcss/variants'" - ) { - atRule.name = 'tailwind' - atRule.params = 'variants' - } - } - } - if (atRule.name === 'tailwind') { if (atRule.params === 'screens') { atRule.params = 'variants' diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 0749da68d..017f31170 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -21,7 +21,6 @@ import isSyntacticallyValidPropertyValue from '../util/isSyntacticallyValidPrope import { generateRules, getClassNameFromSelector } from './generateRules' import { hasContentChanged } from './cacheInvalidation.js' import { Offsets } from './offsets.js' -import { flagEnabled } from '../featureFlags.js' import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector' const VARIANT_TYPES = { @@ -434,10 +433,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs }, } - let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') - let ruleSets = [] - .concat(modifiersEnabled ? rule(value, extras) : rule(value)) + .concat(rule(value, extras)) .filter(Boolean) .map((declaration) => ({ [nameClass(identifier, modifier)]: declaration, @@ -514,10 +511,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs }, } - let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') - let ruleSets = [] - .concat(modifiersEnabled ? rule(value, extras) : rule(value)) + .concat(rule(value, extras)) .filter(Boolean) .map((declaration) => ({ [nameClass(identifier, modifier)]: declaration, @@ -585,18 +580,13 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs let id = options?.id ?? ++variantIdentifier let isSpecial = variant === '@' - let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') - for (let [key, value] of Object.entries(options?.values ?? {})) { if (key === 'DEFAULT') continue api.addVariant( isSpecial ? `${variant}${key}` : `${variant}-${key}`, ({ args, container }) => { - return variantFn( - value, - modifiersEnabled ? { modifier: args?.modifier, container } : { container } - ) + return variantFn(value, { modifier: args?.modifier, container }) }, { @@ -624,7 +614,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs : // Falling back to args if it is a string, otherwise '' for older intellisense // (JetBrains) plugins. args?.value ?? (typeof args === 'string' ? args : ''), - modifiersEnabled ? { modifier: args?.modifier, container } : { container } + { modifier: args?.modifier, container } ) }, { diff --git a/src/lib/setupTrackingContext.js b/src/lib/setupTrackingContext.js index 2b281377d..70e7cb684 100644 --- a/src/lib/setupTrackingContext.js +++ b/src/lib/setupTrackingContext.js @@ -63,9 +63,7 @@ function getTailwindConfig(configOrPath) { } // It's a plain object, not a path - let newConfig = resolveConfig( - configOrPath.config === undefined ? configOrPath : configOrPath.config - ) + let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {}) newConfig = validateConfig(newConfig) diff --git a/src/lib/sharedState.js b/src/lib/sharedState.js index 97bdf09c5..915dd21cb 100644 --- a/src/lib/sharedState.js +++ b/src/lib/sharedState.js @@ -1,16 +1,12 @@ -import pkg from '../../package.json' - export const env = typeof process !== 'undefined' ? { NODE_ENV: process.env.NODE_ENV, DEBUG: resolveDebug(process.env.DEBUG), - ENGINE: pkg.tailwindcss.engine, } : { NODE_ENV: 'production', DEBUG: false, - ENGINE: pkg.tailwindcss.engine, } export const contextMap = new Map() diff --git a/src/oxide/cli.ts b/src/oxide/cli.ts deleted file mode 100644 index 17552ecb1..000000000 --- a/src/oxide/cli.ts +++ /dev/null @@ -1 +0,0 @@ -import './cli/index' diff --git a/src/oxide/cli/build/deps.ts b/src/oxide/cli/build/deps.ts deleted file mode 100644 index 2b4355b17..000000000 --- a/src/oxide/cli/build/deps.ts +++ /dev/null @@ -1,91 +0,0 @@ -import packageJson from '../../../../package.json' -import browserslist from 'browserslist' -import { Result } from 'postcss' - -import { - // @ts-ignore - lazyPostcss, - - // @ts-ignore - lazyPostcssImport, - - // @ts-ignore - lazyCssnano, - - // @ts-ignore -} from '../../../../peers/index' - -export function lazyLightningCss() { - // TODO: Make this lazy/bundled - return require('lightningcss') -} - -let lightningCss - -function loadLightningCss() { - if (lightningCss) { - return lightningCss - } - - // Try to load a local version first - try { - return (lightningCss = require('lightningcss')) - } catch {} - - return (lightningCss = lazyLightningCss()) -} - -export async function lightningcss(shouldMinify: boolean, result: Result) { - let css = loadLightningCss() - - try { - let transformed = css.transform({ - filename: result.opts.from || 'input.css', - code: Buffer.from(result.css, 'utf-8'), - minify: shouldMinify, - sourceMap: !!result.map, - inputSourceMap: result.map ? result.map.toString() : undefined, - targets: css.browserslistToTargets(browserslist(packageJson.browserslist)), - drafts: { - nesting: true, - }, - }) - - return Object.assign(result, { - css: transformed.code.toString('utf8'), - map: result.map - ? Object.assign(result.map, { - toString() { - return transformed.map.toString() - }, - }) - : result.map, - }) - } catch (err) { - console.error('Unable to use Lightning CSS. Using raw version instead.') - console.error(err) - - return result - } -} - -/** - * @returns {import('postcss')} - */ -export function loadPostcss() { - // Try to load a local `postcss` version first - try { - return require('postcss') - } catch {} - - return lazyPostcss() -} - -export function loadPostcssImport() { - // Try to load a local `postcss-import` version first - try { - return require('postcss-import') - } catch {} - - return lazyPostcssImport() -} diff --git a/src/oxide/cli/build/index.ts b/src/oxide/cli/build/index.ts deleted file mode 100644 index ba7c87459..000000000 --- a/src/oxide/cli/build/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import fs from 'fs' -import path from 'path' -import { resolveDefaultConfigPath } from '../../../util/resolveConfigPath' -import { createProcessor } from './plugin' - -export async function build(args) { - let input = args['--input'] - let shouldWatch = args['--watch'] - - // TODO: Deprecate this in future versions - if (!input && args['_'][1]) { - console.error('[deprecation] Running tailwindcss without -i, please provide an input file.') - input = args['--input'] = args['_'][1] - } - - if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) { - console.error(`Specified input file ${args['--input']} does not exist.`) - process.exit(9) - } - - if (args['--config'] && !fs.existsSync((args['--config'] = path.resolve(args['--config'])))) { - console.error(`Specified config file ${args['--config']} does not exist.`) - process.exit(9) - } - - // TODO: Reference the @config path here if exists - let configPath = args['--config'] ? args['--config'] : resolveDefaultConfigPath() - - let processor = await createProcessor(args, configPath) - - if (shouldWatch) { - // Abort the watcher if stdin is closed to avoid zombie processes - // You can disable this behavior with --watch=always - if (args['--watch'] !== 'always') { - process.stdin.on('end', () => process.exit(0)) - } - - process.stdin.resume() - - await processor.watch() - } else { - await processor.build().catch((e) => { - console.error(e) - process.exit(1) - }) - } -} diff --git a/src/oxide/cli/build/plugin.ts b/src/oxide/cli/build/plugin.ts deleted file mode 100644 index f6193b534..000000000 --- a/src/oxide/cli/build/plugin.ts +++ /dev/null @@ -1,419 +0,0 @@ -import path from 'path' -import fs from 'fs' -import postcssrc from 'postcss-load-config' -import { lilconfig } from 'lilconfig' -import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API -import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API - -import tailwind from '../../../processTailwindFeatures' -import { loadPostcss, loadPostcssImport, lightningcss } from './deps' -import { formatNodes, drainStdin, outputFile } from './utils' -import { env } from '../../../lib/sharedState' -import resolveConfig from '../../../../resolveConfig' -import { parseCandidateFiles } from '../../../lib/content' -import { createWatcher } from './watching' -import fastGlob from 'fast-glob' -import { findAtConfigPath } from '../../../lib/findAtConfigPath' -import log from '../../../util/log' -import { loadConfig } from '../../../lib/load-config' -import getModuleDependencies from '../../../lib/getModuleDependencies' -import type { Config } from '../../../../types' -import { validateConfig } from '../../../util/validateConfig' -import { handleImportAtRules } from '../../../lib/handleImportAtRules' - -/** - * - * @param {string} [customPostCssPath ] - * @returns - */ -async function loadPostCssPlugins(customPostCssPath) { - let config = customPostCssPath - ? await (async () => { - let file = path.resolve(customPostCssPath) - - // Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js - // @ts-ignore - let { config = {} } = await lilconfig('postcss').load(file) - if (typeof config === 'function') { - config = config() - } else { - config = Object.assign({}, config) - } - - if (!config.plugins) { - config.plugins = [] - } - - return { - file, - plugins: loadPlugins(config, file), - options: loadOptions(config, file), - } - })() - : await postcssrc() - - let configPlugins = config.plugins - - let configPluginTailwindIdx = configPlugins.findIndex((plugin) => { - if (typeof plugin === 'function' && plugin.name === 'tailwindcss') { - return true - } - - if (typeof plugin === 'object' && plugin !== null && plugin.postcssPlugin === 'tailwindcss') { - return true - } - - return false - }) - - let beforePlugins = - configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx) - let afterPlugins = - configPluginTailwindIdx === -1 - ? configPlugins - : configPlugins.slice(configPluginTailwindIdx + 1) - - return [beforePlugins, afterPlugins, config.options] -} - -let state = { - /** @type {any} */ - context: null, - - /** @type {ReturnType | null} */ - watcher: null, - - /** @type {{content: string, extension: string}[]} */ - changedContent: [], - - /** @type {{config: Config, dependencies: Set, dispose: Function } | null} */ - configBag: null, - - contextDependencies: new Set(), - - /** @type {import('../../lib/content.js').ContentPath[]} */ - contentPaths: [], - - refreshContentPaths() { - this.contentPaths = parseCandidateFiles(this.context, this.context?.tailwindConfig) - }, - - get config() { - return this.context.tailwindConfig - }, - - get contentPatterns() { - return { - all: this.contentPaths.map((contentPath) => contentPath.pattern), - dynamic: this.contentPaths - .filter((contentPath) => contentPath.glob !== undefined) - .map((contentPath) => contentPath.pattern), - } - }, - - loadConfig(configPath, content) { - if (this.watcher && configPath) { - this.refreshConfigDependencies() - } - - let config = loadConfig(configPath) - let dependencies = getModuleDependencies(configPath) - this.configBag = { - config, - dependencies, - dispose() { - for (let file of dependencies) { - delete require.cache[require.resolve(file)] - } - }, - } - - // @ts-ignore - if (__OXIDE__) { - this.configBag.config = validateConfig(resolveConfig(this.configBag.config)) - } else { - this.configBag.config = validateConfig( - resolveConfig(this.configBag.config, { content: { files: [] } }) - ) - } - - // Override content files if `--content` has been passed explicitly - if (content?.length > 0) { - this.configBag.config.content.files = content - } - - return this.configBag.config - }, - - refreshConfigDependencies(configPath) { - env.DEBUG && console.time('Module dependencies') - this.configBag?.dispose() - env.DEBUG && console.timeEnd('Module dependencies') - }, - - readContentPaths() { - let content = [] - - // Resolve globs from the content config - // TODO: When we make the postcss plugin async-capable this can become async - let files = fastGlob.sync(this.contentPatterns.all) - - for (let file of files) { - if (__OXIDE__) { - content.push({ - file, - extension: path.extname(file).slice(1), - }) - } else { - content.push({ - content: fs.readFileSync(path.resolve(file), 'utf8'), - extension: path.extname(file).slice(1), - }) - } - } - - // Resolve raw content in the tailwind config - let rawContent = this.config.content.files.filter((file) => { - return file !== null && typeof file === 'object' - }) - - for (let { raw: htmlContent, extension = 'html' } of rawContent) { - content.push({ content: htmlContent, extension }) - } - - return content - }, - - getContext({ createContext, cliConfigPath, root, result, content }) { - if (this.context) { - this.context.changedContent = this.changedContent.splice(0) - - return this.context - } - - env.DEBUG && console.time('Searching for config') - let configPath = findAtConfigPath(root, result) ?? cliConfigPath - env.DEBUG && console.timeEnd('Searching for config') - - env.DEBUG && console.time('Loading config') - let config = this.loadConfig(configPath, content) - env.DEBUG && console.timeEnd('Loading config') - - env.DEBUG && console.time('Creating context') - this.context = createContext(config, []) - Object.assign(this.context, { - userConfigPath: configPath, - }) - env.DEBUG && console.timeEnd('Creating context') - - env.DEBUG && console.time('Resolving content paths') - this.refreshContentPaths() - env.DEBUG && console.timeEnd('Resolving content paths') - - if (this.watcher) { - env.DEBUG && console.time('Watch new files') - this.watcher.refreshWatchedFiles() - env.DEBUG && console.timeEnd('Watch new files') - } - - for (let file of this.readContentPaths()) { - this.context.changedContent.push(file) - } - - return this.context - }, -} - -export async function createProcessor(args, cliConfigPath) { - let postcss = loadPostcss() - - let input = args['--input'] - let output = args['--output'] - let includePostCss = args['--postcss'] - let customPostCssPath = typeof args['--postcss'] === 'string' ? args['--postcss'] : undefined - - let [beforePlugins, afterPlugins, postcssOptions] = includePostCss - ? await loadPostCssPlugins(customPostCssPath) - : [[], [], {}] - - beforePlugins.unshift(...handleImportAtRules(loadPostcssImport())) - - if (args['--purge']) { - log.warn('purge-flag-deprecated', [ - 'The `--purge` flag has been deprecated.', - 'Please use `--content` instead.', - ]) - - if (!args['--content']) { - args['--content'] = args['--purge'] - } - } - - let content = args['--content']?.split(/(? { - return { - postcssPlugin: 'tailwindcss', - Once(root, { result }) { - env.DEBUG && console.time('Compiling CSS') - tailwind(({ createContext }) => { - console.error() - console.error('Rebuilding...') - - return () => { - return state.getContext({ - createContext, - cliConfigPath, - root, - result, - content, - }) - } - })(root, result) - env.DEBUG && console.timeEnd('Compiling CSS') - }, - } - } - - tailwindPlugin.postcss = true - - let plugins = [ - ...beforePlugins, - tailwindPlugin, - !args['--minify'] && formatNodes, - ...afterPlugins, - ].filter(Boolean) - - /** @type {import('postcss').Processor} */ - // @ts-ignore - let processor = postcss(plugins) - - async function readInput() { - // Piping in data, let's drain the stdin - if (input === '-') { - return drainStdin() - } - - // Input file has been provided - if (input) { - return fs.promises.readFile(path.resolve(input), 'utf8') - } - - // No input file provided, fallback to default atrules - return '@tailwind base; @tailwind components; @tailwind utilities' - } - - async function build() { - let start = process.hrtime.bigint() - - return readInput() - .then((css) => processor.process(css, { ...postcssOptions, from: input, to: output })) - .then((result) => lightningcss(!!args['--minify'], result)) - .then((result) => { - if (!state.watcher) { - return result - } - - env.DEBUG && console.time('Recording PostCSS dependencies') - for (let message of result.messages) { - if (message.type === 'dependency') { - state.contextDependencies.add(message.file) - } - } - env.DEBUG && console.timeEnd('Recording PostCSS dependencies') - - // TODO: This needs to be in a different spot - env.DEBUG && console.time('Watch new files') - state.watcher.refreshWatchedFiles() - env.DEBUG && console.timeEnd('Watch new files') - - return result - }) - .then((result) => { - if (!output) { - process.stdout.write(result.css) - return - } - - return Promise.all([ - outputFile(result.opts.to, result.css), - result.map && outputFile(result.opts.to + '.map', result.map.toString()), - ]) - }) - .then(() => { - let end = process.hrtime.bigint() - console.error() - console.error('Done in', (end - start) / BigInt(1e6) + 'ms.') - }) - .then( - () => {}, - (err) => { - // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies - // that were collected before the error occurred - // The result is not stored on the error so we have to store it externally - // and pull the messages off of it here somehow - - // This results in a less than ideal DX because the watcher will not pick up - // changes to imported CSS if one of them caused an error during the initial build - // If you fix it and then save the main CSS file so there's no error - // The watcher will start watching the imported CSS files and will be - // resilient to future errors. - - if (state.watcher) { - console.error(err) - } else { - return Promise.reject(err) - } - } - ) - } - - /** - * @param {{file: string, content(): Promise, extension: string}[]} changes - */ - async function parseChanges(changes) { - return Promise.all( - changes.map(async (change) => ({ - content: await change.content(), - extension: change.extension, - })) - ) - } - - if (input !== undefined && input !== '-') { - state.contextDependencies.add(path.resolve(input)) - } - - return { - build, - watch: async () => { - state.watcher = createWatcher(args, { - state, - - /** - * @param {{file: string, content(): Promise, extension: string}[]} changes - */ - async rebuild(changes) { - let needsNewContext = changes.some((change) => { - return ( - state.configBag?.dependencies.has(change.file) || - state.contextDependencies.has(change.file) - ) - }) - - if (needsNewContext) { - state.context = null - } else { - for (let change of await parseChanges(changes)) { - state.changedContent.push(change) - } - } - - return build() - }, - }) - - await build() - }, - } -} diff --git a/src/oxide/cli/build/utils.ts b/src/oxide/cli/build/utils.ts deleted file mode 100644 index 001857a91..000000000 --- a/src/oxide/cli/build/utils.ts +++ /dev/null @@ -1,74 +0,0 @@ -import fs from 'fs' -import path from 'path' - -export function indentRecursive(node, indent = 0) { - node.each && - node.each((child, i) => { - if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes('\n')) { - child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${' '.repeat(indent)}` - } - child.raws.after = `\n${' '.repeat(indent)}` - indentRecursive(child, indent + 1) - }) -} - -export function formatNodes(root) { - indentRecursive(root) - if (root.first) { - root.first.raws.before = '' - } -} - -/** - * When rapidly saving files atomically a couple of situations can happen: - * - The file is missing since the external program has deleted it by the time we've gotten around to reading it from the earlier save. - * - The file is being written to by the external program by the time we're going to read it and is thus treated as busy because a lock is held. - * - * To work around this we retry reading the file a handful of times with a delay between each attempt - * - * @param {string} path - * @param {number} tries - * @returns {Promise} - * @throws {Error} If the file is still missing or busy after the specified number of tries - */ -export async function readFileWithRetries(path, tries = 5) { - for (let n = 0; n <= tries; n++) { - try { - return await fs.promises.readFile(path, 'utf8') - } catch (err) { - if (n !== tries) { - if (err.code === 'ENOENT' || err.code === 'EBUSY') { - await new Promise((resolve) => setTimeout(resolve, 10)) - - continue - } - } - - throw err - } - } -} - -export function drainStdin() { - return new Promise((resolve, reject) => { - let result = '' - process.stdin.on('data', (chunk) => { - result += chunk - }) - process.stdin.on('end', () => resolve(result)) - process.stdin.on('error', (err) => reject(err)) - }) -} - -export async function outputFile(file, newContents) { - try { - let currentContents = await fs.promises.readFile(file, 'utf8') - if (currentContents === newContents) { - return // Skip writing the file - } - } catch {} - - // Write the file - await fs.promises.mkdir(path.dirname(file), { recursive: true }) - await fs.promises.writeFile(file, newContents, 'utf8') -} diff --git a/src/oxide/cli/build/watching.ts b/src/oxide/cli/build/watching.ts deleted file mode 100644 index 9d5680a16..000000000 --- a/src/oxide/cli/build/watching.ts +++ /dev/null @@ -1,225 +0,0 @@ -import chokidar from 'chokidar' -import fs from 'fs' -import micromatch from 'micromatch' -import normalizePath from 'normalize-path' -import path from 'path' - -import { readFileWithRetries } from './utils' - -/** - * The core idea of this watcher is: - * 1. Whenever a file is added, changed, or renamed we queue a rebuild - * 2. Perform as few rebuilds as possible by batching them together - * 3. Coalesce events that happen in quick succession to avoid unnecessary rebuilds - * 4. Ensure another rebuild happens _if_ changed while a rebuild is in progress - */ - -/** - * - * @param {*} args - * @param {{ state, rebuild(changedFiles: any[]): Promise }} param1 - * @returns {{ - * fswatcher: import('chokidar').FSWatcher, - * refreshWatchedFiles(): void, - * }} - */ -export function createWatcher(args, { state, rebuild }) { - let shouldPoll = args['--poll'] - let shouldCoalesceWriteEvents = shouldPoll || process.platform === 'win32' - - // Polling interval in milliseconds - // Used only when polling or coalescing add/change events on Windows - let pollInterval = 10 - - let watcher = chokidar.watch([], { - // Force checking for atomic writes in all situations - // This causes chokidar to wait up to 100ms for a file to re-added after it's been unlinked - // This only works when watching directories though - atomic: true, - - usePolling: shouldPoll, - interval: shouldPoll ? pollInterval : undefined, - ignoreInitial: true, - awaitWriteFinish: shouldCoalesceWriteEvents - ? { - stabilityThreshold: 50, - pollInterval: pollInterval, - } - : false, - }) - - // A queue of rebuilds, file reads, etc… to run - let chain = Promise.resolve() - - /** - * A list of files that have been changed since the last rebuild - * - * @type {{file: string, content: () => Promise, extension: string}[]} - */ - let changedContent = [] - - /** - * A list of files for which a rebuild has already been queued. - * This is used to prevent duplicate rebuilds when multiple events are fired for the same file. - * The rebuilt file is cleared from this list when it's associated rebuild has _started_ - * This is because if the file is changed during a rebuild it won't trigger a new rebuild which it should - **/ - let pendingRebuilds = new Set() - - let _timer - let _reject - - /** - * Rebuilds the changed files and resolves when the rebuild is - * complete regardless of whether it was successful or not - */ - async function rebuildAndContinue() { - let changes = changedContent.splice(0) - - // There are no changes to rebuild so we can just do nothing - if (changes.length === 0) { - return Promise.resolve() - } - - // Clear all pending rebuilds for the about-to-be-built files - changes.forEach((change) => pendingRebuilds.delete(change.file)) - - // Resolve the promise even when the rebuild fails - return rebuild(changes).then( - () => {}, - () => {} - ) - } - - /** - * - * @param {*} file - * @param {(() => Promise) | null} content - * @param {boolean} skipPendingCheck - * @returns {Promise} - */ - function recordChangedFile(file, content = null, skipPendingCheck = false) { - file = path.resolve(file) - - // Applications like Vim/Neovim fire both rename and change events in succession for atomic writes - // In that case rebuild has already been queued by rename, so can be skipped in change - if (pendingRebuilds.has(file) && !skipPendingCheck) { - return Promise.resolve() - } - - // Mark that a rebuild of this file is going to happen - // It MUST happen synchronously before the rebuild is queued for this to be effective - pendingRebuilds.add(file) - - changedContent.push({ - file, - content: content ?? (() => fs.promises.readFile(file, 'utf8')), - extension: path.extname(file).slice(1), - }) - - if (_timer) { - clearTimeout(_timer) - _reject() - } - - // If a rebuild is already in progress we don't want to start another one until the 10ms timer has expired - chain = chain.then( - () => - new Promise((resolve, reject) => { - _timer = setTimeout(resolve, 10) - _reject = reject - }) - ) - - // Resolves once this file has been rebuilt (or the rebuild for this file has failed) - // This queues as many rebuilds as there are changed files - // But those rebuilds happen after some delay - // And will immediately resolve if there are no changes - chain = chain.then(rebuildAndContinue, rebuildAndContinue) - - return chain - } - - watcher.on('change', (file) => recordChangedFile(file)) - watcher.on('add', (file) => recordChangedFile(file)) - - // Restore watching any files that are "removed" - // This can happen when a file is pseudo-atomically replaced (a copy is created, overwritten, the old one is unlinked, and the new one is renamed) - // TODO: An an optimization we should allow removal when the config changes - watcher.on('unlink', (file) => { - file = normalizePath(file) - - // Only re-add the file if it's not covered by a dynamic pattern - if (!micromatch.some([file], state.contentPatterns.dynamic)) { - watcher.add(file) - } - }) - - // Some applications such as Visual Studio (but not VS Code) - // will only fire a rename event for atomic writes and not a change event - // This is very likely a chokidar bug but it's one we need to work around - // We treat this as a change event and rebuild the CSS - watcher.on('raw', (evt, filePath, meta) => { - if (evt !== 'rename') { - return - } - - let watchedPath = meta.watchedPath - - // Watched path might be the file itself - // Or the directory it is in - filePath = watchedPath.endsWith(filePath) ? watchedPath : path.join(watchedPath, filePath) - - // Skip this event since the files it is for does not match any of the registered content globs - if (!micromatch.some([filePath], state.contentPatterns.all)) { - return - } - - // Skip since we've already queued a rebuild for this file that hasn't happened yet - if (pendingRebuilds.has(filePath)) { - return - } - - // We'll go ahead and add the file to the pending rebuilds list here - // It'll be removed when the rebuild starts unless the read fails - // which will be taken care of as well - pendingRebuilds.add(filePath) - - async function enqueue() { - try { - // We need to read the file as early as possible outside of the chain - // because it may be gone by the time we get to it. doing the read - // immediately increases the chance that the file is still there - let content = await readFileWithRetries(path.resolve(filePath)) - - if (content === undefined) { - return - } - - // This will push the rebuild onto the chain - // We MUST skip the rebuild check here otherwise the rebuild will never happen on Linux - // This is because the order of events and timing is different on Linux - // @ts-ignore: TypeScript isn't picking up that content is a string here - await recordChangedFile(filePath, () => content, true) - } catch { - // If reading the file fails, it's was probably a deleted temporary file - // So we can ignore it and no rebuild is needed - } - } - - enqueue().then(() => { - // If the file read fails we still need to make sure the file isn't stuck in the pending rebuilds list - pendingRebuilds.delete(filePath) - }) - }) - - return { - fswatcher: watcher, - - refreshWatchedFiles() { - watcher.add(Array.from(state.contextDependencies)) - watcher.add(Array.from(state.configBag.dependencies)) - watcher.add(state.contentPatterns.all) - }, - } -} diff --git a/src/oxide/cli/help/index.ts b/src/oxide/cli/help/index.ts deleted file mode 100644 index c36042a65..000000000 --- a/src/oxide/cli/help/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import packageJson from '../../../../package.json' - -export function help({ message, usage, commands, options }) { - let indent = 2 - - // Render header - console.log() - console.log(`${packageJson.name} v${packageJson.version}`) - - // Render message - if (message) { - console.log() - for (let msg of message.split('\n')) { - console.log(msg) - } - } - - // Render usage - if (usage && usage.length > 0) { - console.log() - console.log('Usage:') - for (let example of usage) { - console.log(' '.repeat(indent), example) - } - } - - // Render commands - if (commands && commands.length > 0) { - console.log() - console.log('Commands:') - for (let command of commands) { - console.log(' '.repeat(indent), command) - } - } - - // Render options - if (options) { - let groupedOptions = {} - for (let [key, value] of Object.entries(options)) { - if (typeof value === 'object') { - groupedOptions[key] = { ...value, flags: [key] } - } else { - groupedOptions[value].flags.push(key) - } - } - - console.log() - console.log('Options:') - for (let { flags, description, deprecated } of Object.values(groupedOptions)) { - if (deprecated) continue - - if (flags.length === 1) { - console.log( - ' '.repeat(indent + 4 /* 4 = "-i, ".length */), - flags.slice().reverse().join(', ').padEnd(20, ' '), - description - ) - } else { - console.log( - ' '.repeat(indent), - flags.slice().reverse().join(', ').padEnd(24, ' '), - description - ) - } - } - } - - console.log() -} diff --git a/src/oxide/cli/index.ts b/src/oxide/cli/index.ts deleted file mode 100644 index 96b284db9..000000000 --- a/src/oxide/cli/index.ts +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env node - -import path from 'path' -import arg from 'arg' -import fs from 'fs' - -import { build } from './build' -import { help } from './help' -import { init } from './init' - -// --- - -function oneOf(...options) { - return Object.assign( - (value = true) => { - for (let option of options) { - let parsed = option(value) - if (parsed === value) { - return parsed - } - } - - throw new Error('...') - }, - { manualParsing: true } - ) -} - -let commands = { - init: { - run: init, - args: { - '--esm': { type: Boolean, description: `Initialize configuration file as ESM` }, - '--ts': { type: Boolean, description: `Initialize configuration file as TypeScript` }, - '--full': { - type: Boolean, - description: `Include the default values for all options in the generated configuration file`, - }, - '-f': '--full', - }, - }, - build: { - run: build, - args: { - '--input': { type: String, description: 'Input file' }, - '--output': { type: String, description: 'Output file' }, - '--watch': { - type: oneOf(String, Boolean), - description: 'Watch for changes and rebuild as needed', - }, - '--poll': { - type: Boolean, - description: 'Use polling instead of filesystem events when watching', - }, - '--content': { - type: String, - description: 'Content paths to use for removing unused classes', - }, - '--minify': { type: Boolean, description: 'Minify the output' }, - '--config': { - type: String, - description: 'Path to a custom config file', - }, - '-c': '--config', - '-i': '--input', - '-o': '--output', - '-m': '--minify', - '-w': '--watch', - '-p': '--poll', - }, - }, -} - -let sharedFlags = { - '--help': { type: Boolean, description: 'Display usage information' }, - '-h': '--help', -} - -if ( - process.stdout.isTTY /* Detect redirecting output to a file */ && - (process.argv[2] === undefined || - process.argv.slice(2).every((flag) => sharedFlags[flag] !== undefined)) -) { - help({ - usage: [ - 'tailwindcss [--input input.css] [--output output.css] [--watch] [options...]', - 'tailwindcss init [--full] [options...]', - ], - commands: Object.keys(commands) - .filter((command) => command !== 'build') - .map((command) => `${command} [options]`), - options: { ...commands.build.args, ...sharedFlags }, - }) - process.exit(0) -} - -let command = ((arg = '') => (arg.startsWith('-') ? undefined : arg))(process.argv[2]) || 'build' - -if (commands[command] === undefined) { - if (fs.existsSync(path.resolve(command))) { - // TODO: Deprecate this in future versions - // Check if non-existing command, might be a file. - command = 'build' - } else { - help({ - message: `Invalid command: ${command}`, - usage: ['tailwindcss [options]'], - commands: Object.keys(commands) - .filter((command) => command !== 'build') - .map((command) => `${command} [options]`), - options: sharedFlags, - }) - process.exit(1) - } -} - -// Execute command -let { args: flags, run } = commands[command] -let args = (() => { - try { - let result = arg( - Object.fromEntries( - Object.entries({ ...flags, ...sharedFlags }) - .filter(([_key, value]) => !value?.type?.manualParsing) - .map(([key, value]) => [key, typeof value === 'object' ? value.type : value]) - ), - { permissive: true } - ) - - // Manual parsing of flags to allow for special flags like oneOf(Boolean, String) - for (let i = result['_'].length - 1; i >= 0; --i) { - let flag = result['_'][i] - if (!flag.startsWith('-')) continue - - let [flagName, flagValue] = flag.split('=') - let handler = flags[flagName] - - // Resolve flagName & handler - while (typeof handler === 'string') { - flagName = handler - handler = flags[handler] - } - - if (!handler) continue - - let args = [] - let offset = i + 1 - - // --flag value syntax was used so we need to pull `value` from `args` - if (flagValue === undefined) { - // Parse args for current flag - while (result['_'][offset] && !result['_'][offset].startsWith('-')) { - args.push(result['_'][offset++]) - } - - // Cleanup manually parsed flags + args - result['_'].splice(i, 1 + args.length) - - // No args were provided, use default value defined in handler - // One arg was provided, use that directly - // Multiple args were provided so pass them all in an array - flagValue = args.length === 0 ? undefined : args.length === 1 ? args[0] : args - } else { - // Remove the whole flag from the args array - result['_'].splice(i, 1) - } - - // Set the resolved value in the `result` object - result[flagName] = handler.type(flagValue, flagName) - } - - // Ensure that the `command` is always the first argument in the `args`. - // This is important so that we don't have to check if a default command - // (build) was used or not from within each plugin. - // - // E.g.: tailwindcss input.css -> _: ['build', 'input.css'] - // E.g.: tailwindcss build input.css -> _: ['build', 'input.css'] - if (result['_'][0] !== command) { - result['_'].unshift(command) - } - - return result - } catch (err) { - if (err.code === 'ARG_UNKNOWN_OPTION') { - help({ - message: err.message, - usage: ['tailwindcss [options]'], - options: sharedFlags, - }) - process.exit(1) - } - throw err - } -})() - -if (args['--help']) { - help({ - options: { ...flags, ...sharedFlags }, - usage: [`tailwindcss ${command} [options]`], - }) - process.exit(0) -} - -run(args) diff --git a/src/oxide/cli/init/index.ts b/src/oxide/cli/init/index.ts deleted file mode 100644 index ed69be95b..000000000 --- a/src/oxide/cli/init/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import fs from 'fs' -import path from 'path' - -function isESM() { - const pkgPath = path.resolve('./package.json') - - try { - let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) - return pkg.type && pkg.type === 'module' - } catch (err) { - return false - } -} - -export function init(args) { - let messages: string[] = [] - - let isProjectESM = args['--ts'] || args['--esm'] || isESM() - let syntax = args['--ts'] ? 'ts' : isProjectESM ? 'js' : 'cjs' - let extension = args['--ts'] ? 'ts' : 'js' - - let tailwindConfigLocation = path.resolve(args['_'][1] ?? `./tailwind.config.${extension}`) - - if (fs.existsSync(tailwindConfigLocation)) { - messages.push(`${path.basename(tailwindConfigLocation)} already exists.`) - } else { - let stubContentsFile = fs.readFileSync( - args['--full'] - ? path.resolve(__dirname, '../../../../stubs/config.full.js') - : path.resolve(__dirname, '../../../../stubs/config.simple.js'), - 'utf8' - ) - - let stubFile = fs.readFileSync( - path.resolve(__dirname, `../../../../stubs/tailwind.config.${syntax}`), - 'utf8' - ) - - // Drop `content` in the oxide engine to promote auto content - if (__OXIDE__) { - stubContentsFile = stubContentsFile.replace(/ *content: \[\],\n/, '') - } - - // Change colors import - stubContentsFile = stubContentsFile.replace('../colors', 'tailwindcss/colors') - - // Replace contents of {ts,js,cjs} file with the stub {simple,full}. - stubFile = - stubFile - .replace('__CONFIG__', stubContentsFile.replace('module.exports =', '').trim()) - .trim() + '\n\n' - - fs.writeFileSync(tailwindConfigLocation, stubFile, 'utf8') - - messages.push(`Created Tailwind CSS config file: ${path.basename(tailwindConfigLocation)}`) - } - - if (messages.length > 0) { - console.log() - for (let message of messages) { - console.log(message) - } - } -} diff --git a/src/oxide/postcss-plugin.ts b/src/oxide/postcss-plugin.ts deleted file mode 100644 index 1be3203f9..000000000 --- a/src/oxide/postcss-plugin.ts +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../plugin.js') diff --git a/src/plugin.js b/src/plugin.js index 86f39d962..bfcdc6583 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -1,3 +1,6 @@ +import postcss from 'postcss' +import lightningcss from 'lightningcss' +import browserslist from 'browserslist' import setupTrackingContext from './lib/setupTrackingContext' import processTailwindFeatures from './processTailwindFeatures' import { env } from './lib/sharedState' @@ -14,7 +17,7 @@ module.exports = function tailwindcss(configOrPath) { console.time('JIT TOTAL') return root }, - ...(__OXIDE__ ? handleImportAtRules() : []), + ...handleImportAtRules(), function (root, result) { // Use the path for the `@config` directive if it exists, otherwise use the // path for the file being processed @@ -36,66 +39,61 @@ module.exports = function tailwindcss(configOrPath) { processTailwindFeatures(context)(root, result) }, - __OXIDE__ && - function lightningCssPlugin(_root, result) { - let postcss = require('postcss') - let lightningcss = require('lightningcss') - let browserslist = require('browserslist') + function lightningCssPlugin(_root, result) { + try { + let transformed = lightningcss.transform({ + filename: result.opts.from, + code: Buffer.from(result.root.toString()), + minify: false, + sourceMap: !!result.map, + inputSourceMap: result.map ? result.map.toString() : undefined, + targets: + typeof process !== 'undefined' && process.env.JEST_WORKER_ID + ? { chrome: 106 << 16 } + : lightningcss.browserslistToTargets( + browserslist(require('../package.json').browserslist) + ), - try { - let transformed = lightningcss.transform({ - filename: result.opts.from, - code: Buffer.from(result.root.toString()), - minify: false, - sourceMap: !!result.map, - inputSourceMap: result.map ? result.map.toString() : undefined, - targets: - typeof process !== 'undefined' && process.env.JEST_WORKER_ID - ? { chrome: 106 << 16 } - : lightningcss.browserslistToTargets( - browserslist(require('../package.json').browserslist) - ), + drafts: { + nesting: true, + customMedia: true, + }, + }) - drafts: { - nesting: true, - customMedia: true, - }, - }) + result.map = Object.assign(result.map ?? {}, { + toJSON() { + return transformed.map.toJSON() + }, + toString() { + return transformed.map.toString() + }, + }) - result.map = Object.assign(result.map ?? {}, { - toJSON() { - return transformed.map.toJSON() - }, - toString() { - return transformed.map.toString() - }, - }) - - result.root = postcss.parse(transformed.code.toString('utf8')) - } catch (err) { - if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) { - let lines = err.source.split('\n') - err = new Error( - [ - 'Error formatting using Lightning CSS:', - '', - ...[ - '```css', - ...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line), - ' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(), - ...lines.slice(err.loc.line, err.loc.line + 2), - '```', - ], - ].join('\n') - ) - } - - if (Error.captureStackTrace) { - Error.captureStackTrace(err, lightningCssPlugin) - } - throw err + result.root = postcss.parse(transformed.code.toString('utf8')) + } catch (err) { + if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) { + let lines = err.source.split('\n') + err = new Error( + [ + 'Error formatting using Lightning CSS:', + '', + ...[ + '```css', + ...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line), + ' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(), + ...lines.slice(err.loc.line, err.loc.line + 2), + '```', + ], + ].join('\n') + ) } - }, + + if (Error.captureStackTrace) { + Error.captureStackTrace(err, lightningCssPlugin) + } + throw err + } + }, env.DEBUG && function (root) { console.timeEnd('JIT TOTAL') diff --git a/src/util/getAllConfigs.js b/src/util/getAllConfigs.js index 90a20e11a..ce3665b97 100644 --- a/src/util/getAllConfigs.js +++ b/src/util/getAllConfigs.js @@ -2,14 +2,7 @@ import defaultFullConfig from '../../stubs/config.full.js' import { flagEnabled } from '../featureFlags' export default function getAllConfigs(config) { - const configs = ( - config?.presets ?? [ - __OXIDE__ - ? // Drop `content` in the oxide engine to promote auto content - Object.assign({}, defaultFullConfig, { content: 'auto' }) - : defaultFullConfig, - ] - ) + const configs = (config?.presets ?? [defaultFullConfig]) .slice() .reverse() .flatMap((preset) => getAllConfigs(preset instanceof Function ? preset() : preset)) diff --git a/src/util/log.js b/src/util/log.js index 0df5c8719..900c2d53f 100644 --- a/src/util/log.js +++ b/src/util/log.js @@ -17,6 +17,30 @@ export function dim(input) { } export default { + group(key, cb) { + if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) return + + if (key && alreadyShown.has(key)) return + if (key) alreadyShown.add(key) + + console.warn('') + + cb({ + info(messages) { + messages.forEach((message) => console.warn(colors.bold(colors.cyan('info')), '-', message)) + }, + warn(messages) { + messages.forEach((message) => + console.warn(colors.bold(colors.yellow('warn')), '-', message) + ) + }, + risk(messages) { + messages.forEach((message) => + console.warn(colors.bold(colors.magenta('risk')), '-', message) + ) + }, + }) + }, info(key, messages) { log(colors.bold(colors.cyan('info')), ...(Array.isArray(key) ? [key] : [messages, key])) }, diff --git a/src/util/normalizeConfig.js b/src/util/normalizeConfig.js index a79aaefd0..3cb98511c 100644 --- a/src/util/normalizeConfig.js +++ b/src/util/normalizeConfig.js @@ -185,31 +185,6 @@ export function normalizeConfig(config) { config.prefix = config.prefix ?? '' } - let auto = (() => { - // Config still has a `purge` option (for backwards compatibility), auto content should not be - // used - if (config.purge) return false - - // - if (config.content === 'auto') return true - - // We don't have content at all, auto content should be used - if (config.content === undefined) return true - - // We do have content as an object, but we don't have any files defined, auto content should - // be used - if ( - typeof config.content === 'object' && - config.content !== null && - !Array.isArray(config.content) - ) { - return config.content.files === undefined - } - - // We do have content defined, auto content should not be used - return false - })() - // Normalize the `content` config.content = { relative: (() => { @@ -222,20 +197,23 @@ export function normalizeConfig(config) { return flagEnabled(config, 'relativeContentPathsByDefault') })(), - files: auto - ? 'auto' - : (() => { - let { content, purge } = config + files: (() => { + let { content, purge } = config - if (content === undefined && purge === undefined) return [] - if (Array.isArray(purge)) return purge - if (Array.isArray(purge?.content)) return purge.content - if (Array.isArray(content)) return content - if (Array.isArray(content?.content)) return content.content - if (Array.isArray(content?.files)) return content.files + if (content === undefined && purge === undefined) return [] + if (purge) { + if (Array.isArray(purge)) return purge + if (Array.isArray(purge?.content)) return purge.content + return [] + } - return [] - })(), + if (Array.isArray(content)) return content + if (Array.isArray(content?.content)) return content.content + if (Array.isArray(content?.files)) return content.files + if (content === 'auto') return ['auto'] + + return [] + })(), extract: (() => { let extract = (() => { diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index bef9fc165..d4df12286 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -18,7 +18,6 @@ import { } from './dataTypes' import negateValue from './negateValue' import { backgroundSize } from './validateFormalSyntax' -import { flagEnabled } from '../featureFlags.js' /** * @param {import('postcss-selector-parser').Container} selectors @@ -243,12 +242,9 @@ export function coerceValue(types, modifier, options, tailwindConfig) { * @returns {Iterator<[value: string, type: string, modifier: string | null]>} */ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) { - let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') - let [modifier, utilityModifier] = splitUtilityModifier(rawModifier) let canUseUtilityModifier = - modifiersEnabled && options.modifiers != null && (options.modifiers === 'any' || (typeof options.modifiers === 'object' && diff --git a/src/util/validateConfig.js b/src/util/validateConfig.js index dfba01844..ebd63d2d2 100644 --- a/src/util/validateConfig.js +++ b/src/util/validateConfig.js @@ -1,7 +1,8 @@ +import pc from 'picocolors' import log from './log' export function validateConfig(config) { - if (config.content.files !== 'auto' && config.content.files.length === 0) { + if (config.content.files.length === 0) { log.warn('content-problems', [ 'The `content` option in your Tailwind CSS configuration is missing or empty.', 'Configure your content sources or your generated CSS will be missing styles.', @@ -9,11 +10,13 @@ export function validateConfig(config) { ]) } - if (config.content.files === 'auto') { - log.warn('auto-content-experimental', [ - 'Automatic content detection in Tailwind CSS is currently in experimental preview.', - 'Preview features are not covered by semver, and may change at any time.', - ]) + if (config.content.files.includes('auto')) { + log.group('auto-content-experimental', (log) => { + log.info([pc.bold('Automatically detecting Tailwind CSS content sources...')]) + log.warn([ + 'Automatic content detection is experimental, and the behavior may change at any time.', + ]) + }) } // Warn if the line-clamp plugin is installed diff --git a/stubs/config.full.js b/stubs/config.full.js index 2dd01c6fd..965d2874a 100644 --- a/stubs/config.full.js +++ b/stubs/config.full.js @@ -1,5 +1,5 @@ module.exports = { - content: [], + content: ['auto'], presets: [], darkMode: 'media', // or 'class' theme: { diff --git a/stubs/config.simple.js b/stubs/config.simple.js index 9843c0557..3e0d35604 100644 --- a/stubs/config.simple.js +++ b/stubs/config.simple.js @@ -1,5 +1,5 @@ module.exports = { - content: [], + content: ['auto'], theme: { extend: {}, }, diff --git a/stubs/postcss.config.cjs b/stubs/postcss.config.cjs index 33ad091d2..08a01d4d1 100644 --- a/stubs/postcss.config.cjs +++ b/stubs/postcss.config.cjs @@ -1,6 +1,5 @@ module.exports = { plugins: { tailwindcss: {}, - autoprefixer: {}, }, } diff --git a/stubs/postcss.config.js b/stubs/postcss.config.js index 2e7af2b7f..284180207 100644 --- a/stubs/postcss.config.js +++ b/stubs/postcss.config.js @@ -1,6 +1,5 @@ export default { plugins: { tailwindcss: {}, - autoprefixer: {}, }, } diff --git a/tests/animations.test.js b/tests/animations.test.js index 958f24252..ab2467476 100644 --- a/tests/animations.test.js +++ b/tests/animations.test.js @@ -1,278 +1,276 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('basic', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .animate-spin { - animation: 1s linear infinite spin; - } - @keyframes ping { - 75%, - 100% { - opacity: 0; - transform: scale(2); - } - } - .hover\:animate-ping:hover { - animation: 1s cubic-bezier(0, 0, 0.2, 1) infinite ping; - } - @keyframes bounce { - 0%, - 100% { - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - transform: translateY(-25%); - } - 50% { - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - transform: none; - } - } - .group:hover .group-hover\:animate-bounce { - animation: 1s infinite bounce; - } - `) - }) - }) - - test('custom', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - extend: { - keyframes: { - one: { to: { transform: 'rotate(360deg)' } }, - }, - animation: { - one: 'one 2s', - }, - }, +test('basic', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, }, - } + ], + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes one { - to { - transform: rotate(360deg); - } + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes spin { + to { + transform: rotate(360deg); } - .animate-one { - animation: 2s one; + } + .animate-spin { + animation: 1s linear infinite spin; + } + @keyframes ping { + 75%, + 100% { + opacity: 0; + transform: scale(2); } - `) - }) - }) - - test('custom prefixed', () => { - let config = { - prefix: 'tw-', - content: [{ raw: `
` }], - theme: { - extend: { - keyframes: { - one: { to: { transform: 'rotate(360deg)' } }, - }, - animation: { - one: 'one 2s', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes tw-one { - to { - transform: rotate(360deg); - } + } + .hover\:animate-ping:hover { + animation: 1s cubic-bezier(0, 0, 0.2, 1) infinite ping; + } + @keyframes bounce { + 0%, + 100% { + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + transform: translateY(-25%); } - .tw-animate-one { - animation: 2s tw-one; + 50% { + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + transform: none; } - `) - }) - }) - - test('multiple', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - extend: { - animation: { - multiple: 'bounce 2s linear, pulse 3s ease-in', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes bounce { - 0%, - 100% { - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - transform: translateY(-25%); - } - 50% { - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - transform: none; - } - } - @keyframes pulse { - 50% { - opacity: 0.5; - } - } - .animate-multiple { - animation: 2s linear bounce, 3s ease-in pulse; - } - `) - }) - }) - - test('multiple custom', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - extend: { - keyframes: { - one: { to: { transform: 'rotate(360deg)' } }, - two: { to: { transform: 'scale(1.23)' } }, - }, - animation: { - multiple: 'one 2s, two 3s', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes one { - to { - transform: rotate(360deg); - } - } - @keyframes two { - to { - transform: scale(1.23); - } - } - .animate-multiple { - animation: 2s one, 3s two; - } - `) - }) - }) - - test('with dots in the name', () => { - let config = { - content: [ - { - raw: html` -
-
- `, - }, - ], - theme: { - extend: { - keyframes: { - 'zoom-.5': { to: { transform: 'scale(0.5)' } }, - 'zoom-1.5': { to: { transform: 'scale(1.5)' } }, - }, - animation: { - 'zoom-.5': 'zoom-.5 2s', - 'zoom-1.5': 'zoom-1.5 2s', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes zoom-\.5 { - to { - transform: scale(0.5); - } - } - .animate-zoom-\.5 { - animation: 2s zoom-\.5; - } - @keyframes zoom-1\.5 { - to { - transform: scale(1.5); - } - } - .animate-zoom-1\.5 { - animation: 2s zoom-1\.5; - } - `) - }) - }) - - test('with dots in the name and prefix', () => { - let config = { - prefix: 'tw-', - content: [ - { - raw: html` -
-
- `, - }, - ], - theme: { - extend: { - keyframes: { - 'zoom-.5': { to: { transform: 'scale(0.5)' } }, - 'zoom-1.5': { to: { transform: 'scale(1.5)' } }, - }, - animation: { - 'zoom-.5': 'zoom-.5 2s', - 'zoom-1.5': 'zoom-1.5 2s', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes tw-zoom-\.5 { - to { - transform: scale(0.5); - } - } - .tw-animate-zoom-\.5 { - animation: 2s tw-zoom-\.5; - } - @keyframes tw-zoom-1\.5 { - to { - transform: scale(1.5); - } - } - .tw-animate-zoom-1\.5 { - animation: 2s tw-zoom-1\.5; - } - `) - }) + } + .group:hover .group-hover\:animate-bounce { + animation: 1s infinite bounce; + } + `) + }) +}) + +test('custom', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + extend: { + keyframes: { + one: { to: { transform: 'rotate(360deg)' } }, + }, + animation: { + one: 'one 2s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes one { + to { + transform: rotate(360deg); + } + } + .animate-one { + animation: 2s one; + } + `) + }) +}) + +test('custom prefixed', () => { + let config = { + prefix: 'tw-', + content: [{ raw: `
` }], + theme: { + extend: { + keyframes: { + one: { to: { transform: 'rotate(360deg)' } }, + }, + animation: { + one: 'one 2s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes tw-one { + to { + transform: rotate(360deg); + } + } + .tw-animate-one { + animation: 2s tw-one; + } + `) + }) +}) + +test('multiple', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + extend: { + animation: { + multiple: 'bounce 2s linear, pulse 3s ease-in', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes bounce { + 0%, + 100% { + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + transform: translateY(-25%); + } + 50% { + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + transform: none; + } + } + @keyframes pulse { + 50% { + opacity: 0.5; + } + } + .animate-multiple { + animation: 2s linear bounce, 3s ease-in pulse; + } + `) + }) +}) + +test('multiple custom', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + extend: { + keyframes: { + one: { to: { transform: 'rotate(360deg)' } }, + two: { to: { transform: 'scale(1.23)' } }, + }, + animation: { + multiple: 'one 2s, two 3s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes one { + to { + transform: rotate(360deg); + } + } + @keyframes two { + to { + transform: scale(1.23); + } + } + .animate-multiple { + animation: 2s one, 3s two; + } + `) + }) +}) + +test('with dots in the name', () => { + let config = { + content: [ + { + raw: html` +
+
+ `, + }, + ], + theme: { + extend: { + keyframes: { + 'zoom-.5': { to: { transform: 'scale(0.5)' } }, + 'zoom-1.5': { to: { transform: 'scale(1.5)' } }, + }, + animation: { + 'zoom-.5': 'zoom-.5 2s', + 'zoom-1.5': 'zoom-1.5 2s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes zoom-\.5 { + to { + transform: scale(0.5); + } + } + .animate-zoom-\.5 { + animation: 2s zoom-\.5; + } + @keyframes zoom-1\.5 { + to { + transform: scale(1.5); + } + } + .animate-zoom-1\.5 { + animation: 2s zoom-1\.5; + } + `) + }) +}) + +test('with dots in the name and prefix', () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: html` +
+
+ `, + }, + ], + theme: { + extend: { + keyframes: { + 'zoom-.5': { to: { transform: 'scale(0.5)' } }, + 'zoom-1.5': { to: { transform: 'scale(1.5)' } }, + }, + animation: { + 'zoom-.5': 'zoom-.5 2s', + 'zoom-1.5': 'zoom-1.5 2s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes tw-zoom-\.5 { + to { + transform: scale(0.5); + } + } + .tw-animate-zoom-\.5 { + animation: 2s tw-zoom-\.5; + } + @keyframes tw-zoom-1\.5 { + to { + transform: scale(1.5); + } + } + .tw-animate-zoom-1\.5 { + animation: 2s tw-zoom-1\.5; + } + `) }) }) diff --git a/tests/any-type.test.js b/tests/any-type.test.js index 7b7873966..34844f2c2 100644 --- a/tests/any-type.test.js +++ b/tests/any-type.test.js @@ -1,1307 +1,760 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - // Hi there, so you are debugging this test because something failed... right? Well we can look into - // the future and guessed that this would happen. So basically it means that we (it was probably - // you, silly) introduced a new plugin that conflicts with an existing plugin that has (either - // implicit or explicit) an `any` type. - // - // Now it is your job to decide which one should win, and mark that one with - // ```diff - // - 'any' - // + ['any', { preferOnConflict: true }] - // ``` - // in the corePlugins.js file. - // - // You probably want to let the original one win for backwards compatible reasons. - // - // Good luck! - test('any types are set on correct plugins', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } +// Hi there, so you are debugging this test because something failed... right? Well we can look into +// the future and guessed that this would happen. So basically it means that we (it was probably +// you, silly) introduced a new plugin that conflicts with an existing plugin that has (either +// implicit or explicit) an `any` type. +// +// Now it is your job to decide which one should win, and mark that one with +// ```diff +// - 'any' +// + ['any', { preferOnConflict: true }] +// ``` +// in the corePlugins.js file. +// +// You probably want to let the original one win for backwards compatible reasons. +// +// Good luck! +test('any types are set on correct plugins', () => { + let config = { + future: { + logicalSiblingUtilities: false, + disableColorOpacityUtilitiesByDefault: false, + }, + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - oxide.expect(result.css).toMatchFormattedCss(css` - .inset-\[var\(--any-value\)\] { - inset: var(--any-value); - } - .inset-x-\[var\(--any-value\)\] { - left: var(--any-value); - right: var(--any-value); - } - .inset-y-\[var\(--any-value\)\] { - top: var(--any-value); - bottom: var(--any-value); - } - .bottom-\[var\(--any-value\)\] { - bottom: var(--any-value); - } - .left-\[var\(--any-value\)\] { - left: var(--any-value); - } - .right-\[var\(--any-value\)\] { - right: var(--any-value); - } - .top-\[var\(--any-value\)\] { - top: var(--any-value); - } - .z-\[var\(--any-value\)\] { - z-index: var(--any-value); - } - .order-\[var\(--any-value\)\] { - order: var(--any-value); - } - .col-\[var\(--any-value\)\] { - grid-column: var(--any-value); - } - .col-start-\[var\(--any-value\)\] { - grid-column-start: var(--any-value); - } - .col-end-\[var\(--any-value\)\] { - grid-column-end: var(--any-value); - } - .row-\[var\(--any-value\)\] { - grid-row: var(--any-value); - } - .row-start-\[var\(--any-value\)\] { - grid-row-start: var(--any-value); - } - .row-end-\[var\(--any-value\)\] { - grid-row-end: var(--any-value); - } - .m-\[var\(--any-value\)\] { - margin: var(--any-value); - } - .mx-\[var\(--any-value\)\] { - margin-left: var(--any-value); - margin-right: var(--any-value); - } - .my-\[var\(--any-value\)\] { - margin-top: var(--any-value); - margin-bottom: var(--any-value); - } - .mb-\[var\(--any-value\)\] { - margin-bottom: var(--any-value); - } - .ml-\[var\(--any-value\)\] { - margin-left: var(--any-value); - } - .mr-\[var\(--any-value\)\] { - margin-right: var(--any-value); - } - .mt-\[var\(--any-value\)\] { - margin-top: var(--any-value); - } - .aspect-\[var\(--any-value\)\] { - aspect-ratio: var(--any-value); - } - .h-\[var\(--any-value\)\] { - height: var(--any-value); - } - .max-h-\[var\(--any-value\)\] { - max-height: var(--any-value); - } - .min-h-\[var\(--any-value\)\] { - min-height: var(--any-value); - } - .w-\[var\(--any-value\)\] { - width: var(--any-value); - } - .min-w-\[var\(--any-value\)\] { - min-width: var(--any-value); - } - .max-w-\[var\(--any-value\)\] { - max-width: var(--any-value); - } - .flex-\[var\(--any-value\)\] { - flex: var(--any-value); - } - .flex-shrink-\[var\(--any-value\)\], - .shrink-\[var\(--any-value\)\] { - flex-shrink: var(--any-value); - } - .flex-grow-\[var\(--any-value\)\], - .grow-\[var\(--any-value\)\] { - flex-grow: var(--any-value); - } - .basis-\[var\(--any-value\)\] { - flex-basis: var(--any-value); - } - .border-spacing-\[var\(--any-value\)\] { - --tw-border-spacing-x: var(--any-value); - --tw-border-spacing-y: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .border-spacing-x-\[var\(--any-value\)\] { - --tw-border-spacing-x: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .border-spacing-y-\[var\(--any-value\)\] { - --tw-border-spacing-y: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .origin-\[var\(--any-value\)\] { - transform-origin: var(--any-value); - } - .translate-x-\[var\(--any-value\)\] { - --tw-translate-x: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .translate-y-\[var\(--any-value\)\] { - --tw-translate-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .rotate-\[var\(--any-value\)\] { - --tw-rotate: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-\[var\(--any-value\)\] { - --tw-scale-x: var(--any-value); - --tw-scale-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-x-\[var\(--any-value\)\] { - --tw-scale-x: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-y-\[var\(--any-value\)\] { - --tw-scale-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .animate-\[var\(--any-value\)\] { - animation: var(--any-value); - } - .cursor-\[var\(--any-value\)\] { - cursor: var(--any-value); - } - .scroll-m-\[var\(--any-value\)\] { - scroll-margin: var(--any-value); - } - .scroll-mx-\[var\(--any-value\)\] { - scroll-margin-left: var(--any-value); - scroll-margin-right: var(--any-value); - } - .scroll-my-\[var\(--any-value\)\] { - scroll-margin-top: var(--any-value); - scroll-margin-bottom: var(--any-value); - } - .scroll-mb-\[var\(--any-value\)\] { - scroll-margin-bottom: var(--any-value); - } - .scroll-ml-\[var\(--any-value\)\] { - scroll-margin-left: var(--any-value); - } - .scroll-mr-\[var\(--any-value\)\] { - scroll-margin-right: var(--any-value); - } - .scroll-mt-\[var\(--any-value\)\] { - scroll-margin-top: var(--any-value); - } - .scroll-p-\[var\(--any-value\)\] { - scroll-padding: var(--any-value); - } - .scroll-px-\[var\(--any-value\)\] { - scroll-padding-left: var(--any-value); - scroll-padding-right: var(--any-value); - } - .scroll-py-\[var\(--any-value\)\] { - scroll-padding-top: var(--any-value); - scroll-padding-bottom: var(--any-value); - } - .scroll-pb-\[var\(--any-value\)\] { - scroll-padding-bottom: var(--any-value); - } - .scroll-pl-\[var\(--any-value\)\] { - scroll-padding-left: var(--any-value); - } - .scroll-pr-\[var\(--any-value\)\] { - scroll-padding-right: var(--any-value); - } - .scroll-pt-\[var\(--any-value\)\] { - scroll-padding-top: var(--any-value); - } - .list-\[var\(--any-value\)\] { - list-style-type: var(--any-value); - } - .columns-\[var\(--any-value\)\] { - columns: var(--any-value); - } - .auto-cols-\[var\(--any-value\)\] { - grid-auto-columns: var(--any-value); - } - .auto-rows-\[var\(--any-value\)\] { - grid-auto-rows: var(--any-value); - } - .grid-cols-\[var\(--any-value\)\] { - grid-template-columns: var(--any-value); - } - .grid-rows-\[var\(--any-value\)\] { - grid-template-rows: var(--any-value); - } - .gap-\[var\(--any-value\)\] { - gap: var(--any-value); - } - .gap-x-\[var\(--any-value\)\] { - column-gap: var(--any-value); - } - .gap-y-\[var\(--any-value\)\] { - row-gap: var(--any-value); - } - .space-x-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(var(--any-value) * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(var(--any-value) * var(--tw-space-x-reverse)); - } - .space-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(var(--any-value) * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(var(--any-value) * var(--tw-space-y-reverse)); - } - .divide-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(var(--any-value) * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(var(--any-value) * var(--tw-divide-y-reverse)); - } - .divide-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - border-color: var(--any-value); - } - .rounded-\[var\(--any-value\)\] { - border-radius: var(--any-value); - } - .rounded-b-\[var\(--any-value\)\] { - border-bottom-right-radius: var(--any-value); - border-bottom-left-radius: var(--any-value); - } - .rounded-l-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - border-bottom-left-radius: var(--any-value); - } - .rounded-r-\[var\(--any-value\)\] { - border-top-right-radius: var(--any-value); - border-bottom-right-radius: var(--any-value); - } - .rounded-t-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - border-top-right-radius: var(--any-value); - } - .rounded-bl-\[var\(--any-value\)\] { - border-bottom-left-radius: var(--any-value); - } - .rounded-br-\[var\(--any-value\)\] { - border-bottom-right-radius: var(--any-value); - } - .rounded-tl-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - } - .rounded-tr-\[var\(--any-value\)\] { - border-top-right-radius: var(--any-value); - } - .border-\[var\(--any-value\)\] { - border-color: var(--any-value); - } - .border-x-\[var\(--any-value\)\] { - border-left-color: var(--any-value); - border-right-color: var(--any-value); - } - .border-y-\[var\(--any-value\)\] { - border-top-color: var(--any-value); - border-bottom-color: var(--any-value); - } - .border-b-\[var\(--any-value\)\] { - border-bottom-color: var(--any-value); - } - .border-l-\[var\(--any-value\)\] { - border-left-color: var(--any-value); - } - .border-r-\[var\(--any-value\)\] { - border-right-color: var(--any-value); - } - .border-t-\[var\(--any-value\)\] { - border-top-color: var(--any-value); - } - .bg-\[var\(--any-value\)\] { - background-color: var(--any-value); - } - .from-\[var\(--any-value\)\] { - --tw-gradient-from: var(--any-value) var(--tw-gradient-from-position); - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .via-\[var\(--any-value\)\] { - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), - var(--any-value) var(--tw-gradient-via-position), var(--tw-gradient-to); - } - .to-\[var\(--any-value\)\] { - --tw-gradient-to: var(--any-value) var(--tw-gradient-to-position); - } - .fill-\[var\(--any-value\)\] { - fill: var(--any-value); - } - .stroke-\[var\(--any-value\)\] { - stroke: var(--any-value); - } - .object-\[var\(--any-value\)\] { - object-position: var(--any-value); - } - .p-\[var\(--any-value\)\] { - padding: var(--any-value); - } - .px-\[var\(--any-value\)\] { - padding-left: var(--any-value); - padding-right: var(--any-value); - } - .py-\[var\(--any-value\)\] { - padding-top: var(--any-value); - padding-bottom: var(--any-value); - } - .pb-\[var\(--any-value\)\] { - padding-bottom: var(--any-value); - } - .pl-\[var\(--any-value\)\] { - padding-left: var(--any-value); - } - .pr-\[var\(--any-value\)\] { - padding-right: var(--any-value); - } - .pt-\[var\(--any-value\)\] { - padding-top: var(--any-value); - } - .indent-\[var\(--any-value\)\] { - text-indent: var(--any-value); - } - .align-\[var\(--any-value\)\] { - vertical-align: var(--any-value); - } - .font-\[var\(--any-value\)\] { - font-weight: var(--any-value); - } - .leading-\[var\(--any-value\)\] { - line-height: var(--any-value); - } - .tracking-\[var\(--any-value\)\] { - letter-spacing: var(--any-value); - } - .text-\[var\(--any-value\)\] { - color: var(--any-value); - } - .decoration-\[var\(--any-value\)\] { - text-decoration-color: var(--any-value); - } - .underline-offset-\[var\(--any-value\)\] { - text-underline-offset: var(--any-value); - } - .placeholder-\[var\(--any-value\)\]::placeholder { - color: var(--any-value); - } - .caret-\[var\(--any-value\)\] { - caret-color: var(--any-value); - } - .accent-\[var\(--any-value\)\] { - accent-color: var(--any-value); - } - .opacity-\[var\(--any-value\)\] { - opacity: var(--any-value); - } - .shadow-\[var\(--any-value\)\] { - --tw-shadow-color: var(--any-value); - --tw-shadow: var(--tw-shadow-colored); - } - .outline-offset-\[var\(--any-value\)\] { - outline-offset: var(--any-value); - } - .outline-\[var\(--any-value\)\] { - outline-color: var(--any-value); - } - .ring-\[var\(--any-value\)\] { - --tw-ring-color: var(--any-value); - } - .ring-offset-\[var\(--any-value\)\] { - --tw-ring-offset-color: var(--any-value); - } - .blur-\[var\(--any-value\)\] { - --tw-blur: blur(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .brightness-\[var\(--any-value\)\] { - --tw-brightness: brightness(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .contrast-\[var\(--any-value\)\] { - --tw-contrast: contrast(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .drop-shadow-\[var\(--any-value\)\] { - --tw-drop-shadow: drop-shadow(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .grayscale-\[var\(--any-value\)\] { - --tw-grayscale: grayscale(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .hue-rotate-\[var\(--any-value\)\] { - --tw-hue-rotate: hue-rotate(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .invert-\[var\(--any-value\)\] { - --tw-invert: invert(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .saturate-\[var\(--any-value\)\] { - --tw-saturate: saturate(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .sepia-\[var\(--any-value\)\] { - --tw-sepia: sepia(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .backdrop-blur-\[var\(--any-value\)\] { - --tw-backdrop-blur: blur(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-brightness-\[var\(--any-value\)\] { - --tw-backdrop-brightness: brightness(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-contrast-\[var\(--any-value\)\] { - --tw-backdrop-contrast: contrast(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-grayscale-\[var\(--any-value\)\] { - --tw-backdrop-grayscale: grayscale(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-hue-rotate-\[var\(--any-value\)\] { - --tw-backdrop-hue-rotate: hue-rotate(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-invert-\[var\(--any-value\)\] { - --tw-backdrop-invert: invert(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-opacity-\[var\(--any-value\)\] { - --tw-backdrop-opacity: opacity(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-saturate-\[var\(--any-value\)\] { - --tw-backdrop-saturate: saturate(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-sepia-\[var\(--any-value\)\] { - --tw-backdrop-sepia: sepia(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .transition-\[var\(--any-value\)\] { - transition-property: var(--any-value); - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - .delay-\[var\(--any-value\)\] { - transition-delay: var(--any-value); - } - .duration-\[var\(--any-value\)\] { - transition-duration: var(--any-value); - } - .ease-\[var\(--any-value\)\] { - transition-timing-function: var(--any-value); - } - .will-change-\[var\(--any-value\)\] { - will-change: var(--any-value); - } - .content-\[var\(--any-value\)\] { - --tw-content: var(--any-value); - content: var(--tw-content); - } - `) - stable.expect(result.css).toMatchFormattedCss(css` - .inset-\[var\(--any-value\)\] { - inset: var(--any-value); - } - .inset-x-\[var\(--any-value\)\] { - left: var(--any-value); - right: var(--any-value); - } - .inset-y-\[var\(--any-value\)\] { - top: var(--any-value); - bottom: var(--any-value); - } - .bottom-\[var\(--any-value\)\] { - bottom: var(--any-value); - } - .left-\[var\(--any-value\)\] { - left: var(--any-value); - } - .right-\[var\(--any-value\)\] { - right: var(--any-value); - } - .top-\[var\(--any-value\)\] { - top: var(--any-value); - } - .z-\[var\(--any-value\)\] { - z-index: var(--any-value); - } - .order-\[var\(--any-value\)\] { - order: var(--any-value); - } - .col-\[var\(--any-value\)\] { - grid-column: var(--any-value); - } - .col-start-\[var\(--any-value\)\] { - grid-column-start: var(--any-value); - } - .col-end-\[var\(--any-value\)\] { - grid-column-end: var(--any-value); - } - .row-\[var\(--any-value\)\] { - grid-row: var(--any-value); - } - .row-start-\[var\(--any-value\)\] { - grid-row-start: var(--any-value); - } - .row-end-\[var\(--any-value\)\] { - grid-row-end: var(--any-value); - } - .m-\[var\(--any-value\)\] { - margin: var(--any-value); - } - .mx-\[var\(--any-value\)\] { - margin-left: var(--any-value); - margin-right: var(--any-value); - } - .my-\[var\(--any-value\)\] { - margin-top: var(--any-value); - margin-bottom: var(--any-value); - } - .mb-\[var\(--any-value\)\] { - margin-bottom: var(--any-value); - } - .ml-\[var\(--any-value\)\] { - margin-left: var(--any-value); - } - .mr-\[var\(--any-value\)\] { - margin-right: var(--any-value); - } - .mt-\[var\(--any-value\)\] { - margin-top: var(--any-value); - } - .aspect-\[var\(--any-value\)\] { - aspect-ratio: var(--any-value); - } - .h-\[var\(--any-value\)\] { - height: var(--any-value); - } - .max-h-\[var\(--any-value\)\] { - max-height: var(--any-value); - } - .min-h-\[var\(--any-value\)\] { - min-height: var(--any-value); - } - .w-\[var\(--any-value\)\] { - width: var(--any-value); - } - .min-w-\[var\(--any-value\)\] { - min-width: var(--any-value); - } - .max-w-\[var\(--any-value\)\] { - max-width: var(--any-value); - } - .flex-\[var\(--any-value\)\] { - flex: var(--any-value); - } - .flex-shrink-\[var\(--any-value\)\], - .shrink-\[var\(--any-value\)\] { - flex-shrink: var(--any-value); - } - .flex-grow-\[var\(--any-value\)\], - .grow-\[var\(--any-value\)\] { - flex-grow: var(--any-value); - } - .basis-\[var\(--any-value\)\] { - flex-basis: var(--any-value); - } - .border-spacing-\[var\(--any-value\)\] { - --tw-border-spacing-x: var(--any-value); - --tw-border-spacing-y: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .border-spacing-x-\[var\(--any-value\)\] { - --tw-border-spacing-x: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .border-spacing-y-\[var\(--any-value\)\] { - --tw-border-spacing-y: var(--any-value); - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - } - .origin-\[var\(--any-value\)\] { - transform-origin: var(--any-value); - } - .translate-x-\[var\(--any-value\)\] { - --tw-translate-x: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .translate-y-\[var\(--any-value\)\] { - --tw-translate-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .rotate-\[var\(--any-value\)\] { - --tw-rotate: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-\[var\(--any-value\)\] { - --tw-scale-x: var(--any-value); - --tw-scale-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-x-\[var\(--any-value\)\] { - --tw-scale-x: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-y-\[var\(--any-value\)\] { - --tw-scale-y: var(--any-value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .animate-\[var\(--any-value\)\] { - animation: var(--any-value); - } - .cursor-\[var\(--any-value\)\] { - cursor: var(--any-value); - } - .scroll-m-\[var\(--any-value\)\] { - scroll-margin: var(--any-value); - } - .scroll-mx-\[var\(--any-value\)\] { - scroll-margin-left: var(--any-value); - scroll-margin-right: var(--any-value); - } - .scroll-my-\[var\(--any-value\)\] { - scroll-margin-top: var(--any-value); - scroll-margin-bottom: var(--any-value); - } - .scroll-mb-\[var\(--any-value\)\] { - scroll-margin-bottom: var(--any-value); - } - .scroll-ml-\[var\(--any-value\)\] { - scroll-margin-left: var(--any-value); - } - .scroll-mr-\[var\(--any-value\)\] { - scroll-margin-right: var(--any-value); - } - .scroll-mt-\[var\(--any-value\)\] { - scroll-margin-top: var(--any-value); - } - .scroll-p-\[var\(--any-value\)\] { - scroll-padding: var(--any-value); - } - .scroll-px-\[var\(--any-value\)\] { - scroll-padding-left: var(--any-value); - scroll-padding-right: var(--any-value); - } - .scroll-py-\[var\(--any-value\)\] { - scroll-padding-top: var(--any-value); - scroll-padding-bottom: var(--any-value); - } - .scroll-pb-\[var\(--any-value\)\] { - scroll-padding-bottom: var(--any-value); - } - .scroll-pl-\[var\(--any-value\)\] { - scroll-padding-left: var(--any-value); - } - .scroll-pr-\[var\(--any-value\)\] { - scroll-padding-right: var(--any-value); - } - .scroll-pt-\[var\(--any-value\)\] { - scroll-padding-top: var(--any-value); - } - .list-\[var\(--any-value\)\] { - list-style-type: var(--any-value); - } - .columns-\[var\(--any-value\)\] { - columns: var(--any-value); - } - .auto-cols-\[var\(--any-value\)\] { - grid-auto-columns: var(--any-value); - } - .auto-rows-\[var\(--any-value\)\] { - grid-auto-rows: var(--any-value); - } - .grid-cols-\[var\(--any-value\)\] { - grid-template-columns: var(--any-value); - } - .grid-rows-\[var\(--any-value\)\] { - grid-template-rows: var(--any-value); - } - .gap-\[var\(--any-value\)\] { - gap: var(--any-value); - } - .gap-x-\[var\(--any-value\)\] { - column-gap: var(--any-value); - } - .gap-y-\[var\(--any-value\)\] { - row-gap: var(--any-value); - } - .space-x-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(var(--any-value) * var(--tw-space-x-reverse)); - margin-left: calc(var(--any-value) * calc(1 - var(--tw-space-x-reverse))); - } - .space-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(var(--any-value) * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(var(--any-value) * var(--tw-space-y-reverse)); - } - .divide-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(var(--any-value) * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(var(--any-value) * var(--tw-divide-y-reverse)); - } - .divide-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - border-color: var(--any-value); - } - .divide-opacity-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: var(--any-value); - } - .rounded-\[var\(--any-value\)\] { - border-radius: var(--any-value); - } - .rounded-b-\[var\(--any-value\)\] { - border-bottom-right-radius: var(--any-value); - border-bottom-left-radius: var(--any-value); - } - .rounded-l-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - border-bottom-left-radius: var(--any-value); - } - .rounded-r-\[var\(--any-value\)\] { - border-top-right-radius: var(--any-value); - border-bottom-right-radius: var(--any-value); - } - .rounded-t-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - border-top-right-radius: var(--any-value); - } - .rounded-bl-\[var\(--any-value\)\] { - border-bottom-left-radius: var(--any-value); - } - .rounded-br-\[var\(--any-value\)\] { - border-bottom-right-radius: var(--any-value); - } - .rounded-tl-\[var\(--any-value\)\] { - border-top-left-radius: var(--any-value); - } - .rounded-tr-\[var\(--any-value\)\] { - border-top-right-radius: var(--any-value); - } - .border-\[var\(--any-value\)\] { - border-color: var(--any-value); - } - .border-x-\[var\(--any-value\)\] { - border-left-color: var(--any-value); - border-right-color: var(--any-value); - } - .border-y-\[var\(--any-value\)\] { - border-top-color: var(--any-value); - border-bottom-color: var(--any-value); - } - .border-b-\[var\(--any-value\)\] { - border-bottom-color: var(--any-value); - } - .border-l-\[var\(--any-value\)\] { - border-left-color: var(--any-value); - } - .border-r-\[var\(--any-value\)\] { - border-right-color: var(--any-value); - } - .border-t-\[var\(--any-value\)\] { - border-top-color: var(--any-value); - } - .border-opacity-\[var\(--any-value\)\] { - --tw-border-opacity: var(--any-value); - } - .bg-\[var\(--any-value\)\] { - background-color: var(--any-value); - } - .bg-opacity-\[var\(--any-value\)\] { - --tw-bg-opacity: var(--any-value); - } - .from-\[var\(--any-value\)\] { - --tw-gradient-from: var(--any-value) var(--tw-gradient-from-position); - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .via-\[var\(--any-value\)\] { - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), - var(--any-value) var(--tw-gradient-via-position), var(--tw-gradient-to); - } - .to-\[var\(--any-value\)\] { - --tw-gradient-to: var(--any-value) var(--tw-gradient-to-position); - } - .fill-\[var\(--any-value\)\] { - fill: var(--any-value); - } - .stroke-\[var\(--any-value\)\] { - stroke: var(--any-value); - } - .object-\[var\(--any-value\)\] { - object-position: var(--any-value); - } - .p-\[var\(--any-value\)\] { - padding: var(--any-value); - } - .px-\[var\(--any-value\)\] { - padding-left: var(--any-value); - padding-right: var(--any-value); - } - .py-\[var\(--any-value\)\] { - padding-top: var(--any-value); - padding-bottom: var(--any-value); - } - .pb-\[var\(--any-value\)\] { - padding-bottom: var(--any-value); - } - .pl-\[var\(--any-value\)\] { - padding-left: var(--any-value); - } - .pr-\[var\(--any-value\)\] { - padding-right: var(--any-value); - } - .pt-\[var\(--any-value\)\] { - padding-top: var(--any-value); - } - .indent-\[var\(--any-value\)\] { - text-indent: var(--any-value); - } - .align-\[var\(--any-value\)\] { - vertical-align: var(--any-value); - } - .font-\[var\(--any-value\)\] { - font-weight: var(--any-value); - } - .leading-\[var\(--any-value\)\] { - line-height: var(--any-value); - } - .tracking-\[var\(--any-value\)\] { - letter-spacing: var(--any-value); - } - .text-\[var\(--any-value\)\] { - color: var(--any-value); - } - .text-opacity-\[var\(--any-value\)\] { - --tw-text-opacity: var(--any-value); - } - .decoration-\[var\(--any-value\)\] { - text-decoration-color: var(--any-value); - } - .underline-offset-\[var\(--any-value\)\] { - text-underline-offset: var(--any-value); - } - .placeholder-\[var\(--any-value\)\]::placeholder { - color: var(--any-value); - } - .placeholder-opacity-\[var\(--any-value\)\]::placeholder { - --tw-placeholder-opacity: var(--any-value); - } - .caret-\[var\(--any-value\)\] { - caret-color: var(--any-value); - } - .accent-\[var\(--any-value\)\] { - accent-color: var(--any-value); - } - .opacity-\[var\(--any-value\)\] { - opacity: var(--any-value); - } - .shadow-\[var\(--any-value\)\] { - --tw-shadow-color: var(--any-value); - --tw-shadow: var(--tw-shadow-colored); - } - .outline-offset-\[var\(--any-value\)\] { - outline-offset: var(--any-value); - } - .outline-\[var\(--any-value\)\] { - outline-color: var(--any-value); - } - .ring-\[var\(--any-value\)\] { - --tw-ring-color: var(--any-value); - } - .ring-opacity-\[var\(--any-value\)\] { - --tw-ring-opacity: var(--any-value); - } - .ring-offset-\[var\(--any-value\)\] { - --tw-ring-offset-color: var(--any-value); - } - .blur-\[var\(--any-value\)\] { - --tw-blur: blur(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .brightness-\[var\(--any-value\)\] { - --tw-brightness: brightness(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .contrast-\[var\(--any-value\)\] { - --tw-contrast: contrast(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .drop-shadow-\[var\(--any-value\)\] { - --tw-drop-shadow: drop-shadow(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .grayscale-\[var\(--any-value\)\] { - --tw-grayscale: grayscale(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .hue-rotate-\[var\(--any-value\)\] { - --tw-hue-rotate: hue-rotate(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .invert-\[var\(--any-value\)\] { - --tw-invert: invert(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .saturate-\[var\(--any-value\)\] { - --tw-saturate: saturate(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .sepia-\[var\(--any-value\)\] { - --tw-sepia: sepia(var(--any-value)); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .backdrop-blur-\[var\(--any-value\)\] { - --tw-backdrop-blur: blur(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-brightness-\[var\(--any-value\)\] { - --tw-backdrop-brightness: brightness(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-contrast-\[var\(--any-value\)\] { - --tw-backdrop-contrast: contrast(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-grayscale-\[var\(--any-value\)\] { - --tw-backdrop-grayscale: grayscale(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-hue-rotate-\[var\(--any-value\)\] { - --tw-backdrop-hue-rotate: hue-rotate(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-invert-\[var\(--any-value\)\] { - --tw-backdrop-invert: invert(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-opacity-\[var\(--any-value\)\] { - --tw-backdrop-opacity: opacity(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-saturate-\[var\(--any-value\)\] { - --tw-backdrop-saturate: saturate(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .backdrop-sepia-\[var\(--any-value\)\] { - --tw-backdrop-sepia: sepia(var(--any-value)); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) - var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) - var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) - var(--tw-backdrop-sepia); - } - .transition-\[var\(--any-value\)\] { - transition-property: var(--any-value); - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - .delay-\[var\(--any-value\)\] { - transition-delay: var(--any-value); - } - .duration-\[var\(--any-value\)\] { - transition-duration: var(--any-value); - } - .ease-\[var\(--any-value\)\] { - transition-timing-function: var(--any-value); - } - .will-change-\[var\(--any-value\)\] { - will-change: var(--any-value); - } - .content-\[var\(--any-value\)\] { - --tw-content: var(--any-value); - content: var(--tw-content); - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .inset-\[var\(--any-value\)\] { + inset: var(--any-value); + } + .inset-x-\[var\(--any-value\)\] { + left: var(--any-value); + right: var(--any-value); + } + .inset-y-\[var\(--any-value\)\] { + top: var(--any-value); + bottom: var(--any-value); + } + .bottom-\[var\(--any-value\)\] { + bottom: var(--any-value); + } + .left-\[var\(--any-value\)\] { + left: var(--any-value); + } + .right-\[var\(--any-value\)\] { + right: var(--any-value); + } + .top-\[var\(--any-value\)\] { + top: var(--any-value); + } + .z-\[var\(--any-value\)\] { + z-index: var(--any-value); + } + .order-\[var\(--any-value\)\] { + order: var(--any-value); + } + .col-\[var\(--any-value\)\] { + grid-column: var(--any-value); + } + .col-start-\[var\(--any-value\)\] { + grid-column-start: var(--any-value); + } + .col-end-\[var\(--any-value\)\] { + grid-column-end: var(--any-value); + } + .row-\[var\(--any-value\)\] { + grid-row: var(--any-value); + } + .row-start-\[var\(--any-value\)\] { + grid-row-start: var(--any-value); + } + .row-end-\[var\(--any-value\)\] { + grid-row-end: var(--any-value); + } + .m-\[var\(--any-value\)\] { + margin: var(--any-value); + } + .mx-\[var\(--any-value\)\] { + margin-left: var(--any-value); + margin-right: var(--any-value); + } + .my-\[var\(--any-value\)\] { + margin-top: var(--any-value); + margin-bottom: var(--any-value); + } + .mb-\[var\(--any-value\)\] { + margin-bottom: var(--any-value); + } + .ml-\[var\(--any-value\)\] { + margin-left: var(--any-value); + } + .mr-\[var\(--any-value\)\] { + margin-right: var(--any-value); + } + .mt-\[var\(--any-value\)\] { + margin-top: var(--any-value); + } + .aspect-\[var\(--any-value\)\] { + aspect-ratio: var(--any-value); + } + .h-\[var\(--any-value\)\] { + height: var(--any-value); + } + .max-h-\[var\(--any-value\)\] { + max-height: var(--any-value); + } + .min-h-\[var\(--any-value\)\] { + min-height: var(--any-value); + } + .w-\[var\(--any-value\)\] { + width: var(--any-value); + } + .min-w-\[var\(--any-value\)\] { + min-width: var(--any-value); + } + .max-w-\[var\(--any-value\)\] { + max-width: var(--any-value); + } + .flex-\[var\(--any-value\)\] { + flex: var(--any-value); + } + .flex-shrink-\[var\(--any-value\)\], + .shrink-\[var\(--any-value\)\] { + flex-shrink: var(--any-value); + } + .flex-grow-\[var\(--any-value\)\], + .grow-\[var\(--any-value\)\] { + flex-grow: var(--any-value); + } + .basis-\[var\(--any-value\)\] { + flex-basis: var(--any-value); + } + .border-spacing-\[var\(--any-value\)\] { + --tw-border-spacing-x: var(--any-value); + --tw-border-spacing-y: var(--any-value); + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); + } + .border-spacing-x-\[var\(--any-value\)\] { + --tw-border-spacing-x: var(--any-value); + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); + } + .border-spacing-y-\[var\(--any-value\)\] { + --tw-border-spacing-y: var(--any-value); + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); + } + .origin-\[var\(--any-value\)\] { + transform-origin: var(--any-value); + } + .translate-x-\[var\(--any-value\)\] { + --tw-translate-x: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .translate-y-\[var\(--any-value\)\] { + --tw-translate-y: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .rotate-\[var\(--any-value\)\] { + --tw-rotate: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .scale-\[var\(--any-value\)\] { + --tw-scale-x: var(--any-value); + --tw-scale-y: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .scale-x-\[var\(--any-value\)\] { + --tw-scale-x: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .scale-y-\[var\(--any-value\)\] { + --tw-scale-y: var(--any-value); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .animate-\[var\(--any-value\)\] { + animation: var(--any-value); + } + .cursor-\[var\(--any-value\)\] { + cursor: var(--any-value); + } + .scroll-m-\[var\(--any-value\)\] { + scroll-margin: var(--any-value); + } + .scroll-mx-\[var\(--any-value\)\] { + scroll-margin-left: var(--any-value); + scroll-margin-right: var(--any-value); + } + .scroll-my-\[var\(--any-value\)\] { + scroll-margin-top: var(--any-value); + scroll-margin-bottom: var(--any-value); + } + .scroll-mb-\[var\(--any-value\)\] { + scroll-margin-bottom: var(--any-value); + } + .scroll-ml-\[var\(--any-value\)\] { + scroll-margin-left: var(--any-value); + } + .scroll-mr-\[var\(--any-value\)\] { + scroll-margin-right: var(--any-value); + } + .scroll-mt-\[var\(--any-value\)\] { + scroll-margin-top: var(--any-value); + } + .scroll-p-\[var\(--any-value\)\] { + scroll-padding: var(--any-value); + } + .scroll-px-\[var\(--any-value\)\] { + scroll-padding-left: var(--any-value); + scroll-padding-right: var(--any-value); + } + .scroll-py-\[var\(--any-value\)\] { + scroll-padding-top: var(--any-value); + scroll-padding-bottom: var(--any-value); + } + .scroll-pb-\[var\(--any-value\)\] { + scroll-padding-bottom: var(--any-value); + } + .scroll-pl-\[var\(--any-value\)\] { + scroll-padding-left: var(--any-value); + } + .scroll-pr-\[var\(--any-value\)\] { + scroll-padding-right: var(--any-value); + } + .scroll-pt-\[var\(--any-value\)\] { + scroll-padding-top: var(--any-value); + } + .list-\[var\(--any-value\)\] { + list-style-type: var(--any-value); + } + .columns-\[var\(--any-value\)\] { + columns: var(--any-value); + } + .auto-cols-\[var\(--any-value\)\] { + grid-auto-columns: var(--any-value); + } + .auto-rows-\[var\(--any-value\)\] { + grid-auto-rows: var(--any-value); + } + .grid-cols-\[var\(--any-value\)\] { + grid-template-columns: var(--any-value); + } + .grid-rows-\[var\(--any-value\)\] { + grid-template-rows: var(--any-value); + } + .gap-\[var\(--any-value\)\] { + gap: var(--any-value); + } + .gap-x-\[var\(--any-value\)\] { + column-gap: var(--any-value); + } + .gap-y-\[var\(--any-value\)\] { + row-gap: var(--any-value); + } + .space-x-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(var(--any-value) * var(--tw-space-x-reverse)); + margin-left: calc(var(--any-value) * calc(1 - var(--tw-space-x-reverse))); + } + .space-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(var(--any-value) * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(var(--any-value) * var(--tw-space-y-reverse)); + } + .divide-y-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(var(--any-value) * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(var(--any-value) * var(--tw-divide-y-reverse)); + } + .divide-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { + border-color: var(--any-value); + } + .divide-opacity-\[var\(--any-value\)\] > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: var(--any-value); + } + .rounded-\[var\(--any-value\)\] { + border-radius: var(--any-value); + } + .rounded-b-\[var\(--any-value\)\] { + border-bottom-right-radius: var(--any-value); + border-bottom-left-radius: var(--any-value); + } + .rounded-l-\[var\(--any-value\)\] { + border-top-left-radius: var(--any-value); + border-bottom-left-radius: var(--any-value); + } + .rounded-r-\[var\(--any-value\)\] { + border-top-right-radius: var(--any-value); + border-bottom-right-radius: var(--any-value); + } + .rounded-t-\[var\(--any-value\)\] { + border-top-left-radius: var(--any-value); + border-top-right-radius: var(--any-value); + } + .rounded-bl-\[var\(--any-value\)\] { + border-bottom-left-radius: var(--any-value); + } + .rounded-br-\[var\(--any-value\)\] { + border-bottom-right-radius: var(--any-value); + } + .rounded-tl-\[var\(--any-value\)\] { + border-top-left-radius: var(--any-value); + } + .rounded-tr-\[var\(--any-value\)\] { + border-top-right-radius: var(--any-value); + } + .border-\[var\(--any-value\)\] { + border-color: var(--any-value); + } + .border-x-\[var\(--any-value\)\] { + border-left-color: var(--any-value); + border-right-color: var(--any-value); + } + .border-y-\[var\(--any-value\)\] { + border-top-color: var(--any-value); + border-bottom-color: var(--any-value); + } + .border-b-\[var\(--any-value\)\] { + border-bottom-color: var(--any-value); + } + .border-l-\[var\(--any-value\)\] { + border-left-color: var(--any-value); + } + .border-r-\[var\(--any-value\)\] { + border-right-color: var(--any-value); + } + .border-t-\[var\(--any-value\)\] { + border-top-color: var(--any-value); + } + .border-opacity-\[var\(--any-value\)\] { + --tw-border-opacity: var(--any-value); + } + .bg-\[var\(--any-value\)\] { + background-color: var(--any-value); + } + .bg-opacity-\[var\(--any-value\)\] { + --tw-bg-opacity: var(--any-value); + } + .from-\[var\(--any-value\)\] { + --tw-gradient-from: var(--any-value) var(--tw-gradient-from-position); + --tw-gradient-to: #fff0 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .via-\[var\(--any-value\)\] { + --tw-gradient-to: #fff0 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), + var(--any-value) var(--tw-gradient-via-position), var(--tw-gradient-to); + } + .to-\[var\(--any-value\)\] { + --tw-gradient-to: var(--any-value) var(--tw-gradient-to-position); + } + .fill-\[var\(--any-value\)\] { + fill: var(--any-value); + } + .stroke-\[var\(--any-value\)\] { + stroke: var(--any-value); + } + .object-\[var\(--any-value\)\] { + object-position: var(--any-value); + } + .p-\[var\(--any-value\)\] { + padding: var(--any-value); + } + .px-\[var\(--any-value\)\] { + padding-left: var(--any-value); + padding-right: var(--any-value); + } + .py-\[var\(--any-value\)\] { + padding-top: var(--any-value); + padding-bottom: var(--any-value); + } + .pb-\[var\(--any-value\)\] { + padding-bottom: var(--any-value); + } + .pl-\[var\(--any-value\)\] { + padding-left: var(--any-value); + } + .pr-\[var\(--any-value\)\] { + padding-right: var(--any-value); + } + .pt-\[var\(--any-value\)\] { + padding-top: var(--any-value); + } + .indent-\[var\(--any-value\)\] { + text-indent: var(--any-value); + } + .align-\[var\(--any-value\)\] { + vertical-align: var(--any-value); + } + .font-\[var\(--any-value\)\] { + font-weight: var(--any-value); + } + .leading-\[var\(--any-value\)\] { + line-height: var(--any-value); + } + .tracking-\[var\(--any-value\)\] { + letter-spacing: var(--any-value); + } + .text-\[var\(--any-value\)\] { + color: var(--any-value); + } + .text-opacity-\[var\(--any-value\)\] { + --tw-text-opacity: var(--any-value); + } + .decoration-\[var\(--any-value\)\] { + text-decoration-color: var(--any-value); + } + .underline-offset-\[var\(--any-value\)\] { + text-underline-offset: var(--any-value); + } + .placeholder-\[var\(--any-value\)\]::placeholder { + color: var(--any-value); + } + .placeholder-opacity-\[var\(--any-value\)\]::placeholder { + --tw-placeholder-opacity: var(--any-value); + } + .caret-\[var\(--any-value\)\] { + caret-color: var(--any-value); + } + .accent-\[var\(--any-value\)\] { + accent-color: var(--any-value); + } + .opacity-\[var\(--any-value\)\] { + opacity: var(--any-value); + } + .shadow-\[var\(--any-value\)\] { + --tw-shadow-color: var(--any-value); + --tw-shadow: var(--tw-shadow-colored); + } + .outline-offset-\[var\(--any-value\)\] { + outline-offset: var(--any-value); + } + .outline-\[var\(--any-value\)\] { + outline-color: var(--any-value); + } + .ring-\[var\(--any-value\)\] { + --tw-ring-color: var(--any-value); + } + .ring-opacity-\[var\(--any-value\)\] { + --tw-ring-opacity: var(--any-value); + } + .ring-offset-\[var\(--any-value\)\] { + --tw-ring-offset-color: var(--any-value); + } + .blur-\[var\(--any-value\)\] { + --tw-blur: blur(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .brightness-\[var\(--any-value\)\] { + --tw-brightness: brightness(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .contrast-\[var\(--any-value\)\] { + --tw-contrast: contrast(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .drop-shadow-\[var\(--any-value\)\] { + --tw-drop-shadow: drop-shadow(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .grayscale-\[var\(--any-value\)\] { + --tw-grayscale: grayscale(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .hue-rotate-\[var\(--any-value\)\] { + --tw-hue-rotate: hue-rotate(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .invert-\[var\(--any-value\)\] { + --tw-invert: invert(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .saturate-\[var\(--any-value\)\] { + --tw-saturate: saturate(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .sepia-\[var\(--any-value\)\] { + --tw-sepia: sepia(var(--any-value)); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .backdrop-blur-\[var\(--any-value\)\] { + --tw-backdrop-blur: blur(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-brightness-\[var\(--any-value\)\] { + --tw-backdrop-brightness: brightness(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-contrast-\[var\(--any-value\)\] { + --tw-backdrop-contrast: contrast(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-grayscale-\[var\(--any-value\)\] { + --tw-backdrop-grayscale: grayscale(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-hue-rotate-\[var\(--any-value\)\] { + --tw-backdrop-hue-rotate: hue-rotate(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-invert-\[var\(--any-value\)\] { + --tw-backdrop-invert: invert(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-opacity-\[var\(--any-value\)\] { + --tw-backdrop-opacity: opacity(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-saturate-\[var\(--any-value\)\] { + --tw-backdrop-saturate: saturate(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .backdrop-sepia-\[var\(--any-value\)\] { + --tw-backdrop-sepia: sepia(var(--any-value)); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) + var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) + var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) + var(--tw-backdrop-sepia); + } + .transition-\[var\(--any-value\)\] { + transition-property: var(--any-value); + transition-duration: 0.15s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + .delay-\[var\(--any-value\)\] { + transition-delay: var(--any-value); + } + .duration-\[var\(--any-value\)\] { + transition-duration: var(--any-value); + } + .ease-\[var\(--any-value\)\] { + transition-timing-function: var(--any-value); + } + .will-change-\[var\(--any-value\)\] { + will-change: var(--any-value); + } + .content-\[var\(--any-value\)\] { + --tw-content: var(--any-value); + content: var(--tw-content); + } + `) }) - test.todo('rewrite the any test to be easier to understand or break it up into multiple tests') }) +test.todo('rewrite the any test to be easier to understand or break it up into multiple tests') diff --git a/tests/apply.test.js b/tests/apply.test.js index e4af6ee58..6f3abd2a9 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -1,2473 +1,2100 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - let sharedHtml = html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+let sharedHtml = html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+` + +test('@apply', () => { + let config = { + darkMode: 'class', + content: [{ raw: sharedHtml }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .basic-example { + @apply rounded-md bg-blue-500 px-4 py-2; + } + .class-order { + @apply p-8 px-3 py-7 pt-4 pr-1; + } + .with-additional-properties { + font-weight: 500; + @apply text-right; + } + .variants { + @apply font-semibold hover:font-bold focus:font-medium lg:font-light xl:focus:font-black; + } + .only-variants { + @apply hover:font-bold focus:font-medium lg:font-light xl:focus:font-black; + } + .apply-group-variant { + @apply group-hover:text-center lg:group-hover:text-left; + } + .apply-dark-variant { + @apply dark:text-center dark:hover:text-right lg:dark:text-left; + } + .apply-custom-utility { + @apply custom-util hover:custom-util lg:custom-util xl:focus:custom-util; + } + .multiple, + .selectors { + @apply rounded-md bg-blue-500 px-4 py-2; + } + .multiple-variants, + .selectors-variants { + @apply hover:text-center active:text-right lg:focus:text-left; + } + .multiple-group, + .selectors-group { + @apply group-hover:text-center lg:group-hover:text-left; + } + .complex-utilities { + @apply ordinal tabular-nums shadow-lg hover:shadow-xl focus:diagonal-fractions; + } + .use-base-only-a { + @apply font-bold; + } + .use-base-only-b { + @apply use-base-only-a font-normal; + } + .use-dependant-only-a { + @apply font-bold; + } + .use-dependant-only-b { + @apply use-dependant-only-a font-normal; + } + .btn { + @apply rounded py-2 px-4 font-bold; + } + .btn-blue { + @apply btn bg-blue-500 text-white hover:bg-blue-700; + } + .recursive-apply-a { + @apply font-black sm:font-thin; + } + .recursive-apply-b { + @apply recursive-apply-a font-semibold md:font-extralight; + } + .recursive-apply-c { + @apply recursive-apply-b font-bold lg:font-light; + } + .use-with-other-properties-base { + color: green; + @apply font-bold; + } + .use-with-other-properties-component { + @apply use-with-other-properties-base; + } + .add-sibling-properties { + padding: 2rem; + @apply px-4 hover:px-2 lg:px-10 xl:focus:px-1; + padding-top: 3px; + @apply use-with-other-properties-base; + } + + h1 { + @apply text-2xl sm:text-3xl lg:text-2xl; + } + h2 { + @apply text-2xl; + @apply lg:text-2xl; + @apply sm:text-2xl; + } + + .important-modifier { + @apply !rounded-md px-4; + } + + .important-modifier-variant { + @apply px-4 hover:!rounded-md; + } + } + + @layer utilities { + .custom-util { + custom: stuff; + } + + .foo { + @apply animate-spin; + } + + .bar { + @apply animate-pulse !important; + } + } ` - test('@apply', () => { - let config = { - darkMode: 'class', - content: [{ raw: sharedHtml }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .basic-example { - @apply rounded-md bg-blue-500 px-4 py-2; - } - .class-order { - @apply p-8 px-3 py-7 pt-4 pr-1; - } - .with-additional-properties { - font-weight: 500; - @apply text-right; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .basic-example { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + border-radius: 0.375rem; + padding: 0.5rem 1rem; + } + .class-order { + padding: 1rem 0.25rem 1.75rem 0.75rem; + } + .with-additional-properties { + text-align: right; + font-weight: 500; + } + .variants { + font-weight: 600; + } + .variants:hover { + font-weight: 700; + } + .variants:focus { + font-weight: 500; + } + @media (min-width: 1024px) { .variants { - @apply font-semibold hover:font-bold focus:font-medium lg:font-light xl:focus:font-black; + font-weight: 300; } + } + @media (min-width: 1280px) { + .variants:focus { + font-weight: 900; + } + } + .only-variants:hover { + font-weight: 700; + } + .only-variants:focus { + font-weight: 500; + } + @media (min-width: 1024px) { .only-variants { - @apply hover:font-bold focus:font-medium lg:font-light xl:focus:font-black; + font-weight: 300; } - .apply-group-variant { - @apply group-hover:text-center lg:group-hover:text-left; + } + @media (min-width: 1280px) { + .only-variants:focus { + font-weight: 900; } - .apply-dark-variant { - @apply dark:text-center dark:hover:text-right lg:dark:text-left; + } + .group:hover .apply-group-variant { + text-align: center; + } + @media (min-width: 1024px) { + .group:hover .apply-group-variant { + text-align: left; } + } + :is(.dark .apply-dark-variant) { + text-align: center; + } + :is(.dark .apply-dark-variant:hover) { + text-align: right; + } + @media (min-width: 1024px) { + :is(.dark .apply-dark-variant) { + text-align: left; + } + } + .apply-custom-utility, + .apply-custom-utility:hover { + custom: stuff; + } + @media (min-width: 1024px) { .apply-custom-utility { - @apply custom-util hover:custom-util lg:custom-util xl:focus:custom-util; - } - .multiple, - .selectors { - @apply rounded-md bg-blue-500 px-4 py-2; - } - .multiple-variants, - .selectors-variants { - @apply hover:text-center active:text-right lg:focus:text-left; - } - .multiple-group, - .selectors-group { - @apply group-hover:text-center lg:group-hover:text-left; - } - .complex-utilities { - @apply ordinal tabular-nums shadow-lg hover:shadow-xl focus:diagonal-fractions; - } - .use-base-only-a { - @apply font-bold; - } - .use-base-only-b { - @apply use-base-only-a font-normal; - } - .use-dependant-only-a { - @apply font-bold; - } - .use-dependant-only-b { - @apply use-dependant-only-a font-normal; - } - .btn { - @apply rounded py-2 px-4 font-bold; - } - .btn-blue { - @apply btn bg-blue-500 text-white hover:bg-blue-700; - } - .recursive-apply-a { - @apply font-black sm:font-thin; - } - .recursive-apply-b { - @apply recursive-apply-a font-semibold md:font-extralight; - } - .recursive-apply-c { - @apply recursive-apply-b font-bold lg:font-light; - } - .use-with-other-properties-base { - color: green; - @apply font-bold; - } - .use-with-other-properties-component { - @apply use-with-other-properties-base; - } - .add-sibling-properties { - padding: 2rem; - @apply px-4 hover:px-2 lg:px-10 xl:focus:px-1; - padding-top: 3px; - @apply use-with-other-properties-base; - } - - h1 { - @apply text-2xl sm:text-3xl lg:text-2xl; - } - h2 { - @apply text-2xl; - @apply lg:text-2xl; - @apply sm:text-2xl; - } - - .important-modifier { - @apply !rounded-md px-4; - } - - .important-modifier-variant { - @apply px-4 hover:!rounded-md; - } - } - - @layer utilities { - .custom-util { custom: stuff; } - - .foo { - @apply animate-spin; - } - - .bar { - @apply animate-pulse !important; - } } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .basic-example { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); - border-radius: 0.375rem; - padding: 0.5rem 1rem; - } - .class-order { - padding: 1rem 0.25rem 1.75rem 0.75rem; - } - .with-additional-properties { - text-align: right; - font-weight: 500; - } - .variants { - font-weight: 600; - } - .variants:hover { - font-weight: 700; - } - .variants:focus { - font-weight: 500; - } - @media (min-width: 1024px) { - .variants { - font-weight: 300; - } - } - @media (min-width: 1280px) { - .variants:focus { - font-weight: 900; - } - } - .only-variants:hover { - font-weight: 700; - } - .only-variants:focus { - font-weight: 500; - } - @media (min-width: 1024px) { - .only-variants { - font-weight: 300; - } - } - @media (min-width: 1280px) { - .only-variants:focus { - font-weight: 900; - } - } - .group:hover .apply-group-variant { - text-align: center; - } - @media (min-width: 1024px) { - .group:hover .apply-group-variant { - text-align: left; - } - } - :is(.dark .apply-dark-variant) { - text-align: center; - } - :is(.dark .apply-dark-variant:hover) { - text-align: right; - } - @media (min-width: 1024px) { - :is(.dark .apply-dark-variant) { - text-align: left; - } - } - .apply-custom-utility, - .apply-custom-utility:hover { + @media (min-width: 1280px) { + .apply-custom-utility:focus { custom: stuff; } - @media (min-width: 1024px) { - .apply-custom-utility { - custom: stuff; - } - } - @media (min-width: 1280px) { - .apply-custom-utility:focus { - custom: stuff; - } - } - .multiple, - .selectors { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); - border-radius: 0.375rem; - padding: 0.5rem 1rem; - } - .multiple-variants:hover, - .selectors-variants:hover { - text-align: center; - } - .multiple-variants:active, - .selectors-variants:active { - text-align: right; - } - @media (min-width: 1024px) { - .multiple-variants:focus, - .selectors-variants:focus { - text-align: left; - } + } + .multiple, + .selectors { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + border-radius: 0.375rem; + padding: 0.5rem 1rem; + } + .multiple-variants:hover, + .selectors-variants:hover { + text-align: center; + } + .multiple-variants:active, + .selectors-variants:active { + text-align: right; + } + @media (min-width: 1024px) { + .multiple-variants:focus, + .selectors-variants:focus { + text-align: left; } + } + .group:hover .multiple-group, + .group:hover .selectors-group { + text-align: center; + } + @media (min-width: 1024px) { .group:hover .multiple-group, .group:hover .selectors-group { - text-align: center; - } - @media (min-width: 1024px) { - .group:hover .multiple-group, - .group:hover .selectors-group { - text-align: left; - } - } - .complex-utilities { - --tw-ordinal: ordinal; - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .complex-utilities:hover { - --tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), - 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .complex-utilities:focus { - --tw-numeric-fraction: diagonal-fractions; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - } - .use-base-only-a { - font-weight: 700; - } - .use-dependant-only-b { - font-weight: 400; - } - .btn { - border-radius: 0.25rem; - padding: 0.5rem 1rem; - font-weight: 700; - } - .btn-blue { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - border-radius: 0.25rem; - padding: 0.5rem 1rem; - font-weight: 700; - } - .btn-blue:hover { - --tw-bg-opacity: 1; - background-color: rgb(29 78 216 / var(--tw-bg-opacity)); + text-align: left; } + } + .complex-utilities { + --tw-ordinal: ordinal; + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) + var(--tw-numeric-spacing) var(--tw-numeric-fraction); + --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), + 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .complex-utilities:hover { + --tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), + 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .complex-utilities:focus { + --tw-numeric-fraction: diagonal-fractions; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) + var(--tw-numeric-spacing) var(--tw-numeric-fraction); + } + .use-base-only-a { + font-weight: 700; + } + .use-dependant-only-b { + font-weight: 400; + } + .btn { + border-radius: 0.25rem; + padding: 0.5rem 1rem; + font-weight: 700; + } + .btn-blue { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + border-radius: 0.25rem; + padding: 0.5rem 1rem; + font-weight: 700; + } + .btn-blue:hover { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); + } + .recursive-apply-a { + font-weight: 900; + } + @media (min-width: 640px) { .recursive-apply-a { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-a { - font-weight: 100; - } + font-weight: 100; } + } + .recursive-apply-b { + font-weight: 900; + } + @media (min-width: 640px) { .recursive-apply-b { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-b { - font-weight: 100; - } + font-weight: 100; } + } + .recursive-apply-b { + font-weight: 600; + } + @media (min-width: 768px) { .recursive-apply-b { - font-weight: 600; - } - @media (min-width: 768px) { - .recursive-apply-b { - font-weight: 200; - } + font-weight: 200; } + } + .recursive-apply-c { + font-weight: 900; + } + @media (min-width: 640px) { .recursive-apply-c { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-c { - font-weight: 100; - } + font-weight: 100; } + } + .recursive-apply-c { + font-weight: 600; + } + @media (min-width: 768px) { .recursive-apply-c { - font-weight: 600; - } - @media (min-width: 768px) { - .recursive-apply-c { - font-weight: 200; - } + font-weight: 200; } + } + .recursive-apply-c { + font-weight: 700; + } + @media (min-width: 1024px) { .recursive-apply-c { - font-weight: 700; - } - @media (min-width: 1024px) { - .recursive-apply-c { - font-weight: 300; - } - } - .use-with-other-properties-base, - .use-with-other-properties-component { - color: green; - font-weight: 700; + font-weight: 300; } + } + .use-with-other-properties-base, + .use-with-other-properties-component { + color: green; + font-weight: 700; + } + .add-sibling-properties { + padding: 2rem 1rem; + } + .add-sibling-properties:hover { + padding-left: 0.5rem; + padding-right: 0.5rem; + } + @media (min-width: 1024px) { .add-sibling-properties { - padding: 2rem 1rem; + padding-left: 2.5rem; + padding-right: 2.5rem; } - .add-sibling-properties:hover { - padding-left: 0.5rem; - padding-right: 0.5rem; + } + @media (min-width: 1280px) { + .add-sibling-properties:focus { + padding-left: 0.25rem; + padding-right: 0.25rem; } - @media (min-width: 1024px) { - .add-sibling-properties { - padding-left: 2.5rem; - padding-right: 2.5rem; - } - } - @media (min-width: 1280px) { - .add-sibling-properties:focus { - padding-left: 0.25rem; - padding-right: 0.25rem; - } - } - .add-sibling-properties { - color: green; - padding-top: 3px; - font-weight: 700; + } + .add-sibling-properties { + color: green; + padding-top: 3px; + font-weight: 700; + } + h1 { + font-size: 1.5rem; + line-height: 2rem; + } + @media (min-width: 640px) { + h1 { + font-size: 1.875rem; + line-height: 2.25rem; } + } + @media (min-width: 1024px) { h1 { font-size: 1.5rem; line-height: 2rem; } - @media (min-width: 640px) { - h1 { - font-size: 1.875rem; - line-height: 2.25rem; - } - } - @media (min-width: 1024px) { - h1 { - font-size: 1.5rem; - line-height: 2rem; - } - } + } + h2 { + font-size: 1.5rem; + line-height: 2rem; + } + @media (min-width: 1024px) { h2 { font-size: 1.5rem; line-height: 2rem; } - @media (min-width: 1024px) { - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - } - @media (min-width: 640px) { - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - } - .important-modifier { - padding-left: 1rem; - padding-right: 1rem; - border-radius: 0.375rem !important; - } - .important-modifier-variant { - padding-left: 1rem; - padding-right: 1rem; - } - .important-modifier-variant:hover { - border-radius: 0.375rem !important; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .foo { - animation: 1s linear infinite spin; - } - @keyframes pulse { - 50% { - opacity: 0.5; - } - } - .bar { - animation: 2s cubic-bezier(0.4, 0, 0.6, 1) infinite pulse !important; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .basic-example { - background-color: #3b82f6; - border-radius: 0.375rem; - padding: 0.5rem 1rem; - } - .class-order { - padding: 1rem 0.25rem 1.75rem 0.75rem; - } - .with-additional-properties { - text-align: right; - font-weight: 500; - } - .variants { - font-weight: 600; - } - .variants:hover { - font-weight: 700; - } - .variants:focus { - font-weight: 500; - } - @media (min-width: 1024px) { - .variants { - font-weight: 300; - } - } - @media (min-width: 1280px) { - .variants:focus { - font-weight: 900; - } - } - .only-variants:hover { - font-weight: 700; - } - .only-variants:focus { - font-weight: 500; - } - @media (min-width: 1024px) { - .only-variants { - font-weight: 300; - } - } - @media (min-width: 1280px) { - .only-variants:focus { - font-weight: 900; - } - } - .group:hover .apply-group-variant { - text-align: center; - } - @media (min-width: 1024px) { - .group:hover .apply-group-variant { - text-align: left; - } - } - :is(.dark .apply-dark-variant) { - text-align: center; - } - :is(.dark .apply-dark-variant:hover) { - text-align: right; - } - @media (min-width: 1024px) { - :is(.dark .apply-dark-variant) { - text-align: left; - } - } - .apply-custom-utility, - .apply-custom-utility:hover { - custom: stuff; - } - @media (min-width: 1024px) { - .apply-custom-utility { - custom: stuff; - } - } - @media (min-width: 1280px) { - .apply-custom-utility:focus { - custom: stuff; - } - } - .multiple, - .selectors { - background-color: #3b82f6; - border-radius: 0.375rem; - padding: 0.5rem 1rem; - } - .multiple-variants:hover, - .selectors-variants:hover { - text-align: center; - } - .multiple-variants:active, - .selectors-variants:active { - text-align: right; - } - @media (min-width: 1024px) { - .multiple-variants:focus, - .selectors-variants:focus { - text-align: left; - } - } - .group:hover .multiple-group, - .group:hover .selectors-group { - text-align: center; - } - @media (min-width: 1024px) { - .group:hover .multiple-group, - .group:hover .selectors-group { - text-align: left; - } - } - .complex-utilities { - --tw-ordinal: ordinal; - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .complex-utilities:hover { - --tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), - 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .complex-utilities:focus { - --tw-numeric-fraction: diagonal-fractions; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - } - .use-base-only-a { - font-weight: 700; - } - .use-dependant-only-b { - font-weight: 400; - } - .btn { - border-radius: 0.25rem; - padding: 0.5rem 1rem; - font-weight: 700; - } - .btn-blue { - color: #fff; - background-color: #3b82f6; - border-radius: 0.25rem; - padding: 0.5rem 1rem; - font-weight: 700; - } - .btn-blue:hover { - background-color: #1d4ed8; - } - .recursive-apply-a { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-a { - font-weight: 100; - } - } - .recursive-apply-b { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-b { - font-weight: 100; - } - } - .recursive-apply-b { - font-weight: 600; - } - @media (min-width: 768px) { - .recursive-apply-b { - font-weight: 200; - } - } - .recursive-apply-c { - font-weight: 900; - } - @media (min-width: 640px) { - .recursive-apply-c { - font-weight: 100; - } - } - .recursive-apply-c { - font-weight: 600; - } - @media (min-width: 768px) { - .recursive-apply-c { - font-weight: 200; - } - } - .recursive-apply-c { - font-weight: 700; - } - @media (min-width: 1024px) { - .recursive-apply-c { - font-weight: 300; - } - } - .use-with-other-properties-base, - .use-with-other-properties-component { - color: green; - font-weight: 700; - } - .add-sibling-properties { - padding: 2rem 1rem; - } - .add-sibling-properties:hover { - padding-left: 0.5rem; - padding-right: 0.5rem; - } - @media (min-width: 1024px) { - .add-sibling-properties { - padding-left: 2.5rem; - padding-right: 2.5rem; - } - } - @media (min-width: 1280px) { - .add-sibling-properties:focus { - padding-left: 0.25rem; - padding-right: 0.25rem; - } - } - .add-sibling-properties { - color: green; - padding-top: 3px; - font-weight: 700; - } - h1 { - font-size: 1.5rem; - line-height: 2rem; - } - @media (min-width: 640px) { - h1 { - font-size: 1.875rem; - line-height: 2.25rem; - } - } - @media (min-width: 1024px) { - h1 { - font-size: 1.5rem; - line-height: 2rem; - } - } + } + @media (min-width: 640px) { h2 { font-size: 1.5rem; line-height: 2rem; } - @media (min-width: 1024px) { - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - } - @media (min-width: 640px) { - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - } - .important-modifier { - padding-left: 1rem; - padding-right: 1rem; - border-radius: 0.375rem !important; - } - .important-modifier-variant { - padding-left: 1rem; - padding-right: 1rem; - } - .important-modifier-variant:hover { - border-radius: 0.375rem !important; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .foo { - animation: 1s linear infinite spin; - } - @keyframes pulse { - 50% { - opacity: 0.5; - } - } - .bar { - animation: 2s cubic-bezier(0.4, 0, 0.6, 1) infinite pulse !important; - } - `) - }) - }) - - test('@apply error with unknown utility', async () => { - let config = { - darkMode: 'class', - content: [{ raw: sharedHtml }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @apply a-utility-that-does-not-exist; + } + .important-modifier { + padding-left: 1rem; + padding-right: 1rem; + border-radius: 0.375rem !important; + } + .important-modifier-variant { + padding-left: 1rem; + padding-right: 1rem; + } + .important-modifier-variant:hover { + border-radius: 0.375rem !important; + } + @keyframes spin { + to { + transform: rotate(360deg); } } - ` - - await expect(run(input, config)).rejects.toThrowError('class does not exist') - }) - - test('@apply error with nested @screen', async () => { - let config = { - darkMode: 'class', - content: [{ raw: sharedHtml }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @screen md { - @apply text-black; - } - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - '@apply is not supported within nested at-rules like @screen' - ) - }) - - test('@apply error with nested @anyatrulehere', async () => { - let config = { - darkMode: 'class', - content: [{ raw: sharedHtml }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @genie { - @apply text-black; - } - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - '@apply is not supported within nested at-rules like @genie' - ) - }) - - test('@apply error when using .group utility', async () => { - let config = { - darkMode: 'class', - content: [{ raw: '
' }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @apply group; - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - `@apply should not be used with the 'group' utility` - ) - }) - - test('@apply error when using a prefixed .group utility', async () => { - let config = { - prefix: 'tw-', - darkMode: 'class', - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @apply tw-group; - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - `@apply should not be used with the 'tw-group' utility` - ) - }) - - test('@apply error when using .peer utility', async () => { - let config = { - darkMode: 'class', - content: [{ raw: '
' }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @apply peer; - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - `@apply should not be used with the 'peer' utility` - ) - }) - - test('@apply error when using a prefixed .peer utility', async () => { - let config = { - prefix: 'tw-', - darkMode: 'class', - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - @apply tw-peer; - } - } - ` - - await expect(run(input, config)).rejects.toThrowError( - `@apply should not be used with the 'tw-peer' utility` - ) - }) - - test('@apply classes from outside a @layer', async () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind components; - @tailwind utilities; - .foo { - @apply font-bold; + animation: 1s linear infinite spin; + } + @keyframes pulse { + 50% { + opacity: 0.5; + } } - .bar { - @apply foo text-red-500 hover:text-green-500; + animation: 2s cubic-bezier(0.4, 0, 0.6, 1) infinite pulse !important; } + `) + }) +}) +test('@apply error with unknown utility', async () => { + let config = { + darkMode: 'class', + content: [{ raw: sharedHtml }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @apply a-utility-that-does-not-exist; + } + } + ` + + await expect(run(input, config)).rejects.toThrowError('class does not exist') +}) + +test('@apply error with nested @screen', async () => { + let config = { + darkMode: 'class', + content: [{ raw: sharedHtml }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @screen md { + @apply text-black; + } + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + '@apply is not supported within nested at-rules like @screen' + ) +}) + +test('@apply error with nested @anyatrulehere', async () => { + let config = { + darkMode: 'class', + content: [{ raw: sharedHtml }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @genie { + @apply text-black; + } + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + '@apply is not supported within nested at-rules like @genie' + ) +}) + +test('@apply error when using .group utility', async () => { + let config = { + darkMode: 'class', + content: [{ raw: '
' }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @apply group; + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + `@apply should not be used with the 'group' utility` + ) +}) + +test('@apply error when using a prefixed .group utility', async () => { + let config = { + prefix: 'tw-', + darkMode: 'class', + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @apply tw-group; + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + `@apply should not be used with the 'tw-group' utility` + ) +}) + +test('@apply error when using .peer utility', async () => { + let config = { + darkMode: 'class', + content: [{ raw: '
' }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @apply peer; + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + `@apply should not be used with the 'peer' utility` + ) +}) + +test('@apply error when using a prefixed .peer utility', async () => { + let config = { + prefix: 'tw-', + darkMode: 'class', + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + @apply tw-peer; + } + } + ` + + await expect(run(input, config)).rejects.toThrowError( + `@apply should not be used with the 'tw-peer' utility` + ) +}) + +test('@apply classes from outside a @layer', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + .foo { + @apply font-bold; + } + + .bar { + @apply foo text-red-500 hover:text-green-500; + } + + .baz { + @apply bar underline; + } + + .keep-me-even-though-I-am-not-used-in-content { + color: green; + } + ` + + await run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-bold, + .foo { + font-weight: 700; + } + .bar { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); + font-weight: 700; + } + .bar:hover { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } .baz { - @apply bar underline; + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); + font-weight: 700; + text-decoration-line: underline; + } + .baz:hover { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); } - .keep-me-even-though-I-am-not-used-in-content { color: green; } - ` - - await run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .font-bold, - .foo { - font-weight: 700; - } - .bar { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); - font-weight: 700; - } - .bar:hover { - --tw-text-opacity: 1; - color: rgb(34 197 94 / var(--tw-text-opacity)); - } - .baz { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); - font-weight: 700; - text-decoration-line: underline; - } - .baz:hover { - --tw-text-opacity: 1; - color: rgb(34 197 94 / var(--tw-text-opacity)); - } - .keep-me-even-though-I-am-not-used-in-content { - color: green; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .font-bold, - .foo { - font-weight: 700; - } - .bar { - color: #ef4444; - font-weight: 700; - } - .bar:hover { - color: #22c55e; - } - .baz { - color: #ef4444; - font-weight: 700; - text-decoration-line: underline; - } - .baz:hover { - color: #22c55e; - } - .keep-me-even-though-I-am-not-used-in-content { - color: green; - } - `) - }) + `) }) +}) - test('@applying classes from outside a @layer respects the source order', async () => { - let config = { - content: [{ raw: html`
` }], +test('@applying classes from outside a @layer respects the source order', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + .baz { + @apply bar underline; } - let input = css` + @tailwind components; + + .keep-me-even-though-I-am-not-used-in-content { + color: green; + } + + @tailwind utilities; + + .foo { + @apply font-bold; + } + + .bar { + @apply no-underline; + } + ` + + await run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` .baz { - @apply bar underline; + text-decoration-line: none; } - - @tailwind components; - - .keep-me-even-though-I-am-not-used-in-content { - color: green; + .container { + width: 100%; } - - @tailwind utilities; - - .foo { - @apply font-bold; - } - - .bar { - @apply no-underline; - } - ` - - await run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .baz { - text-decoration-line: none; - } + @media (min-width: 640px) { .container { - width: 100%; + max-width: 640px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } + } + @media (min-width: 768px) { + .container { + max-width: 768px; } - @media (min-width: 768px) { - .container { - max-width: 768px; - } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - .keep-me-even-though-I-am-not-used-in-content { - color: green; - } - .font-bold, - .foo { - font-weight: 700; - } - .bar { - text-decoration-line: none; - } - `) - }) - }) - - it('should remove duplicate properties when using apply with similar properties', () => { - let config = { - content: [{ raw: 'foo' }], - } - - let input = css` - @tailwind utilities; - + } + .keep-me-even-though-I-am-not-used-in-content { + color: green; + } + .font-bold, .foo { - @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform; + font-weight: 700; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - --tw-translate-x: -50%; - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - position: absolute; - top: 50%; - left: 50%; - } - `) - }) + .bar { + text-decoration-line: none; + } + `) }) +}) - it('should apply all the definitions of a class', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], +it('should remove duplicate properties when using apply with similar properties', () => { + let config = { + content: [{ raw: 'foo' }], + } + + let input = css` + @tailwind utilities; + + .foo { + @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + --tw-translate-x: -50%; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + position: absolute; + top: 50%; + left: 50%; + } + `) + }) +}) + +it('should apply all the definitions of a class', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer utilities { + .aspect-w-1 { + position: relative; + } + + .aspect-w-1 { + --tw-aspect-w: 1; + } } - let input = css` - @tailwind components; - @tailwind utilities; - - @layer utilities { - .aspect-w-1 { - position: relative; - } - - .aspect-w-1 { - --tw-aspect-w: 1; - } + @layer components { + .foo { + @apply aspect-w-1; } - - @layer components { - .foo { - @apply aspect-w-1; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .foo { - --tw-aspect-w: 1; - position: relative; - } - `) - }) - }) - - it('should throw when trying to apply a direct circular dependency', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], } + ` - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo:not(.text-red-500) { - @apply text-red-500; - } + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .foo { + --tw-aspect-w: 1; + position: relative; } - ` - - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'You cannot `@apply` the `text-red-500` utility here because it creates a circular dependency.' - ) - }) + `) }) +}) - it('should throw when trying to apply an indirect circular dependency', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } +it('should throw when trying to apply a direct circular dependency', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } - let input = css` - @tailwind components; - @tailwind utilities; + let input = css` + @tailwind components; + @tailwind utilities; - @layer components { - .a { - @apply b; - } - - .b { - @apply c; - } - - .c { - @apply a; - } + @layer components { + .foo:not(.text-red-500) { + @apply text-red-500; } - ` + } + ` - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'You cannot `@apply` the `a` utility here because it creates a circular dependency.' - ) - }) + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'You cannot `@apply` the `text-red-500` utility here because it creates a circular dependency.' + ) }) +}) - it('should not throw when the selector is different (but contains the base partially)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } +it('should throw when trying to apply an indirect circular dependency', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } - let input = css` - @tailwind components; - @tailwind utilities; + let input = css` + @tailwind components; + @tailwind utilities; - .focus\:bg-gray-500 { - @apply bg-gray-500; - } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-gray-500, - .focus\:bg-gray-500 { - --tw-bg-opacity: 1; - background-color: rgb(107 114 128 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-gray-500, - .focus\:bg-gray-500 { - background-color: #6b7280; - } - `) - }) - }) - - it('should throw when trying to apply an indirect circular dependency with a modifier (1)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .a { - @apply b; - } - - .b { - @apply c; - } - - .c { - @apply hover:a; - } - } - ` - - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'You cannot `@apply` the `hover:a` utility here because it creates a circular dependency.' - ) - }) - }) - - it('should throw when trying to apply an indirect circular dependency with a modifier (2)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .a { - @apply b; - } - - .b { - @apply hover:c; - } - - .c { - @apply a; - } - } - ` - - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'You cannot `@apply` the `a` utility here because it creates a circular dependency.' - ) - }) - }) - - it('should not throw when the circular dependency is part of a different selector (1)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - html.dark .a, - .b { - color: red; - } - } - - html.dark .c { + @layer components { + .a { @apply b; } - ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - html.dark .c { - color: red; - } - `) - }) - }) + .b { + @apply c; + } - it('should not throw when the circular dependency is part of a different selector (2)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], + .c { + @apply a; + } } + ` - let input = css` - @tailwind utilities; - - @layer utilities { - html.dark .a, - .b { - color: red; - } - } - - html.dark .c { - @apply hover:b; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - html.dark .c:hover { - color: red; - } - `) - }) + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'You cannot `@apply` the `a` utility here because it creates a circular dependency.' + ) }) +}) - it('should throw when the circular dependency is part of the same selector', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], +it('should not throw when the selector is different (but contains the base partially)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + .focus\:bg-gray-500 { + @apply bg-gray-500; } + ` - let input = css` - @tailwind utilities; - - @layer utilities { - html.dark .a, - html.dark .b { - color: red; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-gray-500, + .focus\:bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); } - - html.dark .c { - @apply hover:b; - } - ` - - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'You cannot `@apply` the `hover:b` utility here because it creates a circular dependency.' - ) - }) + `) }) +}) - it('rules with vendor prefixes are still separate when optimizing defaults rules', () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +it('should throw when trying to apply an indirect circular dependency with a modifier (1)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; + let input = css` + @tailwind components; + @tailwind utilities; - @layer components { - input[type='range']::-moz-range-thumb { - @apply border; - } + @layer components { + .a { + @apply b; } - ` - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - input[type='range']::-moz-range-thumb { - border-width: 1px; - } - .border { - border-width: 1px; - } - `) - }) - }) + .b { + @apply c; + } - it('should be possible to apply user css', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], + .c { + @apply hover:a; + } } + ` - let input = css` - @tailwind components; - @tailwind utilities; + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'You cannot `@apply` the `hover:a` utility here because it creates a circular dependency.' + ) + }) +}) - .foo { +it('should throw when trying to apply an indirect circular dependency with a modifier (2)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .a { + @apply b; + } + + .b { + @apply hover:c; + } + + .c { + @apply a; + } + } + ` + + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'You cannot `@apply` the `a` utility here because it creates a circular dependency.' + ) + }) +}) + +it('should not throw when the circular dependency is part of a different selector (1)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + html.dark .a, + .b { color: red; } + } + + html.dark .c { + @apply b; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + html.dark .c { + color: red; + } + `) + }) +}) + +it('should not throw when the circular dependency is part of a different selector (2)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + html.dark .a, + .b { + color: red; + } + } + + html.dark .c { + @apply hover:b; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + html.dark .c:hover { + color: red; + } + `) + }) +}) + +it('should throw when the circular dependency is part of the same selector', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + html.dark .a, + html.dark .b { + color: red; + } + } + + html.dark .c { + @apply hover:b; + } + ` + + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'You cannot `@apply` the `hover:b` utility here because it creates a circular dependency.' + ) + }) +}) + +it('rules with vendor prefixes are still separate when optimizing defaults rules', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer components { + input[type='range']::-moz-range-thumb { + @apply border; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + input[type='range']::-moz-range-thumb { + border-width: 1px; + } + .border { + border-width: 1px; + } + `) + }) +}) + +it('should be possible to apply user css', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + .foo { + color: red; + } + + .bar { + @apply foo; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .foo, + .bar { + color: red; + } + `) + }) +}) + +it('should not be possible to apply user css with variants', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + .foo { + color: red; + } + + .bar { + @apply hover:foo; + } + ` + + return run(input, config).catch((err) => { + expect(err.reason).toBe( + 'The `hover:foo` class does not exist. If `hover:foo` is a custom class, make sure it is defined within a `@layer` directive.' + ) + }) +}) + +it('should not apply unrelated siblings when applying something from within atrules', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .foo { + font-weight: bold; + @apply bar; + } .bar { - @apply foo; + color: green; } - ` - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .foo, + @supports (a: b) { .bar { + color: blue; + } + + .something-unrelated { color: red; } - `) - }) - }) - - it('should not be possible to apply user css with variants', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], + } } + ` - let input = css` - @tailwind components; - @tailwind utilities; - + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` .foo { - color: red; + color: green; + font-weight: bold; + } + @supports (a: b) { + .foo { + color: #00f; + } } - .bar { - @apply hover:foo; + color: green; } - ` - - return run(input, config).catch((err) => { - expect(err.reason).toBe( - 'The `hover:foo` class does not exist. If `hover:foo` is a custom class, make sure it is defined within a `@layer` directive.' - ) - }) - }) - - it('should not apply unrelated siblings when applying something from within atrules', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .foo { - font-weight: bold; - @apply bar; - } - + @supports (a: b) { .bar { - color: green; + color: #00f; } - - @supports (a: b) { - .bar { - color: blue; - } - - .something-unrelated { - color: red; - } + .something-unrelated { + color: red; } } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - color: green; - font-weight: bold; - } - @supports (a: b) { - .foo { - color: #00f; - } - } - .bar { - color: green; - } - @supports (a: b) { - .bar { - color: #00f; - } - .something-unrelated { - color: red; - } - } - `) - }) + `) }) +}) - it('should be possible to apply user css without tailwind directives', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], +it('should be possible to apply user css without tailwind directives', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + .bop { + color: red; } + .bar { + background-color: blue; + } + .foo { + @apply bar bop absolute; + } + ` - let input = css` + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` .bop { color: red; } .bar { - background-color: blue; + background-color: #00f; } .foo { - @apply bar bop absolute; + color: red; + background-color: #00f; + position: absolute; + } + `) + }) +}) + +it('should be possible to apply a class from another rule with multiple selectors (2 classes)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + @layer utilities { + .a, + .b { + @apply underline; + } + + .c { + @apply b; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .c { + text-decoration-line: underline; + } + `) + }) +}) + +it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 tag)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + span, + .b { + @apply underline; + } + + .c { + @apply b; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + span, + .b, + .c { + text-decoration-line: underline; + } + `) + }) +}) + +it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 id)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + @layer utilities { + #a, + .b { + @apply underline; + } + + .c { + @apply b; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + #a, + .b, + .c { + text-decoration-line: underline; + } + `) + }) +}) + +describe('multiple instances', () => { + it('should be possible to apply multiple "instances" of the same class', () => { + let config = { + content: [{ raw: html`` }], + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + .a { + @apply b; + } + + .b { + @apply uppercase; + } + + .b { + color: red; } ` return run(input, config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - .bop { - color: red; - } - .bar { - background-color: #00f; - } - .foo { - color: red; - background-color: #00f; - position: absolute; - } - `) - }) - }) - - it('should be possible to apply a class from another rule with multiple selectors (2 classes)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind utilities; - @layer utilities { .a, .b { - @apply underline; - } - - .c { - @apply b; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .c { - text-decoration-line: underline; - } - `) - }) - }) - - it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 tag)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - span, - .b { - @apply underline; - } - - .c { - @apply b; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - span, - .b, - .c { - text-decoration-line: underline; - } - `) - }) - }) - - it('should be possible to apply a class from another rule with multiple selectors (1 class, 1 id)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind utilities; - @layer utilities { - #a, - .b { - @apply underline; - } - - .c { - @apply b; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - #a, - .b, - .c { - text-decoration-line: underline; - } - `) - }) - }) - - describe('multiple instances', () => { - it('should be possible to apply multiple "instances" of the same class', () => { - let config = { - content: [{ raw: html`` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - .a { - @apply b; - } - - .b { - @apply uppercase; - } - - .b { + text-transform: uppercase; color: red; } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .a, - .b { - text-transform: uppercase; - color: red; - } - `) - }) - }) - - it('should be possible to apply a combination of multiple "instances" of the same class', () => { - let config = { - content: [{ raw: html`` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - .a { - @apply b; - } - - .b { - @apply uppercase; - color: red; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .a, - .b { - text-transform: uppercase; - color: red; - } - `) - }) - }) - - it('should generate the same output, even if it was used in a @layer', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind components; - - @layer components { - .a { - @apply b; - } - - .b { - @apply uppercase; - color: red; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .a, - .b { - text-transform: uppercase; - color: red; - } - `) - }) - }) - - it('should be possible to apply a combination of multiple "instances" of the same class (defined in a layer)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind components; - - @layer components { - .a { - color: red; - @apply b; - color: blue; - } - - .b { - @apply text-green-500; - text-decoration: underline; - } - } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .a { - color: red; - --tw-text-opacity: 1; - color: rgb(34 197 94 / var(--tw-text-opacity)); - color: #00f; - text-decoration: underline; - } - .b { - --tw-text-opacity: 1; - color: rgb(34 197 94 / var(--tw-text-opacity)); - text-decoration: underline; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .a { - color: red; - color: #22c55e; - color: #00f; - text-decoration: underline; - } - .b { - color: #22c55e; - text-decoration: underline; - } - `) - }) - }) - - it('should properly maintain the order', () => { - let config = { - content: [{ raw: html`` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - h2 { - @apply text-xl; - @apply lg:text-3xl; - @apply sm:text-2xl; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - h2 { - font-size: 1.25rem; - line-height: 1.75rem; - } - @media (min-width: 1024px) { - h2 { - font-size: 1.875rem; - line-height: 2.25rem; - } - } - @media (min-width: 640px) { - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - } - `) - }) - }) - }) - - it('apply can emit defaults in isolated environments without @tailwind directives', () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - - content: [{ raw: html`
` }], - } - - let input = css` - .foo { - @apply focus:rotate-90; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .foo:focus { - --tw-rotate: 90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } `) }) }) - it('apply does not emit defaults in isolated environments without optimizeUniversalDefaults', () => { + it('should be possible to apply a combination of multiple "instances" of the same class', () => { let config = { - experimental: { optimizeUniversalDefaults: false }, - content: [{ raw: html`
` }], + content: [{ raw: html`` }], + plugins: [], corePlugins: { preflight: false }, } let input = css` - @tailwind base; + .a { + @apply b; + } - .foo { - @apply focus:rotate-90; + .b { + @apply uppercase; + color: red; } ` return run(input, config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .foo:focus { - --tw-rotate: 90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + .a, + .b { + text-transform: uppercase; + color: red; } `) }) }) - it('should work outside of layer', async () => { + it('should generate the same output, even if it was used in a @layer', () => { let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - .input-text { - @apply bg-white; - background-color: red; - } - ` - - let result - result = await run(input, config) - - stable.expect(result.css).toMatchFormattedCss(css` - .input-text { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - background-color: red; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .input-text { - background-color: red; - } - `) - - result = await run(input, config) - - stable.expect(result.css).toMatchFormattedCss(css` - .input-text { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - background-color: red; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .input-text { - background-color: red; - } - `) - }) - - it('should work in layer', async () => { - let config = { - content: [{ raw: html`
` }], + content: [{ raw: html`
` }], + plugins: [], corePlugins: { preflight: false }, } let input = css` @tailwind components; + @layer components { - .input-text { - @apply bg-white; - background-color: red; + .a { + @apply b; + } + + .b { + @apply uppercase; + color: red; } } ` - await run(input, config) - const result = await run(input, config) - - stable.expect(result.css).toMatchFormattedCss(css` - .input-text { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - background-color: red; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .input-text { - background-color: red; - } - `) + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .a, + .b { + text-transform: uppercase; + color: red; + } + `) + }) }) - it('apply partitioning works with media queries', async () => { + it('should be possible to apply a combination of multiple "instances" of the same class (defined in a layer)', () => { let config = { - content: [{ raw: html`` }], + content: [{ raw: html`
` }], + plugins: [], corePlugins: { preflight: false }, } let input = css` - @tailwind base; - @layer base { - html, - body { - @apply text-green-600; - font-size: 1rem; + @tailwind components; + + @layer components { + .a { + color: red; + @apply b; + color: blue; } - @media print { - html, - body { - @apply text-red-600; - font-size: 2rem; - } + .b { + @apply text-green-500; + text-decoration: underline; } } ` - await run(input, config) - const result = await run(input, config) - - stable.expect(result.css).toMatchFormattedCss(css` - html, - body { - --tw-text-opacity: 1; - color: rgb(22 163 74 / var(--tw-text-opacity)); - font-size: 1rem; - } - @media print { - html, - body { - --tw-text-opacity: 1; - color: rgb(220 38 38 / var(--tw-text-opacity)); - font-size: 2rem; - } - } - ${defaults} - `) - oxide.expect(result.css).toMatchFormattedCss(css` - html, - body { - color: #16a34a; - font-size: 1rem; - } - @media print { - html, - body { - color: #dc2626; - font-size: 2rem; - } - } - ${defaults} - `) - }) - - it('should be possible to use apply in plugins', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [ - function ({ addComponents }) { - addComponents({ - '.a': { - color: 'red', - }, - '.b': { - '@apply a': {}, - color: 'blue', - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` .a { color: red; + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + color: #00f; + text-decoration: underline; } .b { - color: red; - color: #00f; + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + text-decoration: underline; } `) }) }) - it('should apply using the updated user CSS when the source has changed', async () => { + it('should properly maintain the order', () => { let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let inputBefore = css` - .foo { - color: green; - } - - .bar { - @apply foo; - } - ` - - let inputAfter = css` - .foo { - color: red; - } - - .bar { - @apply foo; - } - ` - - let result = await run(inputBefore, config) - - expect(result.css).toMatchFormattedCss(css` - .foo, - .bar { - color: green; - } - `) - - result = await run(inputAfter, config) - - expect(result.css).toMatchFormattedCss(css` - .foo, - .bar { - color: red; - } - `) - }) - - it('apply + layer utilities + selector variants (like group) + important selector', async () => { - let config = { - important: '#myselector', - content: [{ raw: html`
` }], + content: [{ raw: html`` }], plugins: [], + corePlugins: { preflight: false }, } let input = css` - @tailwind utilities; - @layer utilities { - .custom-utility { - @apply font-normal group-hover:underline; + h2 { + @apply text-xl; + @apply lg:text-3xl; + @apply sm:text-2xl; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + h2 { + font-size: 1.25rem; + line-height: 1.75rem; + } + @media (min-width: 1024px) { + h2 { + font-size: 1.875rem; + line-height: 2.25rem; + } + } + @media (min-width: 640px) { + h2 { + font-size: 1.5rem; + line-height: 2rem; + } + } + `) + }) + }) +}) + +it('apply can emit defaults in isolated environments without @tailwind directives', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + + content: [{ raw: html`
` }], + } + + let input = css` + .foo { + @apply focus:rotate-90; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .foo:focus { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +it('apply does not emit defaults in isolated environments without optimizeUniversalDefaults', () => { + let config = { + experimental: { optimizeUniversalDefaults: false }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + + .foo { + @apply focus:rotate-90; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .foo:focus { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +it('should work outside of layer', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + .input-text { + @apply bg-white; + background-color: red; + } + ` + + let result + result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .input-text { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + background-color: red; + } + `) + + result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .input-text { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + background-color: red; + } + `) +}) + +it('should work in layer', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind components; + @layer components { + .input-text { + @apply bg-white; + background-color: red; + } + } + ` + + await run(input, config) + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .input-text { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + background-color: red; + } + `) +}) + +it('apply partitioning works with media queries', async () => { + let config = { + content: [{ raw: html`` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @layer base { + html, + body { + @apply text-green-600; + font-size: 1rem; + } + + @media print { + html, + body { + @apply text-red-600; + font-size: 2rem; } } - ` + } + ` - let result = await run(input, config) + await run(input, config) + const result = await run(input, config) - expect(result.css).toMatchFormattedCss(css` - #myselector :is(.custom-utility) { - font-weight: 400; + expect(result.css).toMatchFormattedCss(css` + html, + body { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); + font-size: 1rem; + } + @media print { + html, + body { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); + font-size: 2rem; } - #myselector :is(.group:hover .custom-utility) { - text-decoration-line: underline; + } + ${defaults} + `) +}) + +it('should be possible to use apply in plugins', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [ + function ({ addComponents }) { + addComponents({ + '.a': { + color: 'red', + }, + '.b': { + '@apply a': {}, + color: 'blue', + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .a { + color: red; + } + .b { + color: red; + color: #00f; } `) }) +}) - it('apply + user CSS + selector variants (like group) + important selector (1)', async () => { - let config = { - important: '#myselector', - content: [{ raw: html`
` }], - plugins: [], +it('should apply using the updated user CSS when the source has changed', async () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let inputBefore = css` + .foo { + color: green; } - let input = css` + .bar { + @apply foo; + } + ` + + let inputAfter = css` + .foo { + color: red; + } + + .bar { + @apply foo; + } + ` + + let result = await run(inputBefore, config) + + expect(result.css).toMatchFormattedCss(css` + .foo, + .bar { + color: green; + } + `) + + result = await run(inputAfter, config) + + expect(result.css).toMatchFormattedCss(css` + .foo, + .bar { + color: red; + } + `) +}) + +it('apply + layer utilities + selector variants (like group) + important selector', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + @layer utilities { .custom-utility { @apply font-normal group-hover:underline; } - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .custom-utility { - font-weight: 400; - } - .group:hover .custom-utility { - text-decoration-line: underline; - } - `) - }) - - it('apply + user CSS + selector variants (like group) + important selector (2)', async () => { - let config = { - important: '#myselector', - content: [{ raw: html`
` }], - plugins: [], } + ` - let input = css` - #myselector .custom-utility { - @apply font-normal group-hover:underline; - } - ` + let result = await run(input, config) - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - #myselector .custom-utility { - font-weight: 400; - } - .group:hover #myselector .custom-utility { - text-decoration-line: underline; - } - `) - }) - - it('can apply user utilities that start with a dash', async () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], + expect(result.css).toMatchFormattedCss(css` + #myselector :is(.custom-utility) { + font-weight: 400; } + #myselector :is(.group:hover .custom-utility) { + text-decoration-line: underline; + } + `) +}) - let input = css` - @tailwind utilities; - @layer utilities { - .foo-1 { - margin: 10px; - } - .-foo-1 { - margin: -15px; - } - .new-class { - @apply -foo-1; - } - } - ` +it('apply + user CSS + selector variants (like group) + important selector (1)', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } - let result = await run(input, config) + let input = css` + .custom-utility { + @apply font-normal group-hover:underline; + } + ` - expect(result.css).toMatchFormattedCss(css` + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .custom-utility { + font-weight: 400; + } + .group:hover .custom-utility { + text-decoration-line: underline; + } + `) +}) + +it('apply + user CSS + selector variants (like group) + important selector (2)', async () => { + let config = { + important: '#myselector', + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + #myselector .custom-utility { + @apply font-normal group-hover:underline; + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + #myselector .custom-utility { + font-weight: 400; + } + .group:hover #myselector .custom-utility { + text-decoration-line: underline; + } + `) +}) + +it('can apply user utilities that start with a dash', async () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + @layer utilities { .foo-1 { margin: 10px; } - .-foo-1, - .new-class { + .-foo-1 { margin: -15px; } - `) - }) + .new-class { + @apply -foo-1; + } + } + ` - it('can apply joined classes when using elements', async () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .foo-1 { + margin: 10px; + } + .-foo-1, + .new-class { + margin: -15px; + } + `) +}) + +it('can apply joined classes when using elements', async () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + .foo.bar { + color: red; + } + .bar.foo { + color: green; + } + header:nth-of-type(odd) { + @apply foo; + } + header::after { + @apply foo; + } + main { + @apply foo bar; + } + footer { + @apply bar; + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .foo.bar { + color: red; + } + .bar.foo { + color: green; + } + header:nth-of-type(2n + 1).bar { + color: red; + } + header.bar:nth-of-type(2n + 1) { + color: green; + } + header.bar:after, + main.bar, + main.foo, + footer.foo { + color: red; + color: green; + } + `) +}) + +it('should not replace multiple instances of the same class in a single selector', async () => { + // NOTE: This test is non-normative and is not part of the spec of how `@apply` works per-se + // It describes how it currently works because the "correct" way produces a combinatorial explosion + // of selectors that is not easily doable + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + .foo + .foo { + color: blue; + } + .bar + .bar { + color: fuchsia; + } + header { + @apply foo; + } + main { + @apply foo bar; + } + footer { + @apply bar; + } + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .foo + .foo { + color: #00f; + } + .bar + .bar { + color: #f0f; + } + header + .foo, + main + .foo { + color: #00f; + } + main + .bar, + footer + .bar { + color: #f0f; + } + `) +}) + +it('should maintain the correct selector when applying other utilities', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + } + + let input = css` + @tailwind utilities; + + .foo:hover.bar .baz { + @apply bg-black; + color: red; } - let input = css` - .foo.bar { - color: red; - } - .bar.foo { - color: green; - } - header:nth-of-type(odd) { - @apply foo; - } - header::after { - @apply foo; - } - main { - @apply foo bar; - } - footer { - @apply bar; - } - ` - - let result = await run(input, config) + .foo:hover.bar > .baz { + @apply bg-black; + color: red; + } + ` + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .foo.bar { - color: red; - } - .bar.foo { - color: green; - } - header:nth-of-type(2n + 1).bar { - color: red; - } - header.bar:nth-of-type(2n + 1) { - color: green; - } - header.bar:after, - main.bar, - main.foo, - footer.foo { - color: red; - color: green; - } - `) - }) - - it('should not replace multiple instances of the same class in a single selector', async () => { - // NOTE: This test is non-normative and is not part of the spec of how `@apply` works per-se - // It describes how it currently works because the "correct" way produces a combinatorial explosion - // of selectors that is not easily doable - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - .foo + .foo { - color: blue; - } - .bar + .bar { - color: fuchsia; - } - header { - @apply foo; - } - main { - @apply foo bar; - } - footer { - @apply bar; - } - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .foo + .foo { - color: #00f; - } - .bar + .bar { - color: #f0f; - } - header + .foo, - main + .foo { - color: #00f; - } - main + .bar, - footer + .bar { - color: #f0f; - } - `) - }) - - it('should maintain the correct selector when applying other utilities', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - } - - let input = css` - @tailwind utilities; - - .foo:hover.bar .baz { - @apply bg-black; - color: red; - } - + .foo:hover.bar .baz, .foo:hover.bar > .baz { - @apply bg-black; + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); color: red; } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .foo:hover.bar .baz, - .foo:hover.bar > .baz { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - color: red; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .foo:hover.bar .baz, - .foo:hover.bar > .baz { - color: red; - background-color: #000; - } - `) - }) + `) }) +}) - it('pseudo elements inside apply are moved outside of :is() or :has()', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], +it('pseudo elements inside apply are moved outside of :is() or :has()', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + .foo::before { + @apply dark:bg-black/100; } - let input = css` - .foo::before { - @apply dark:bg-black/100; - } - - .bar::before { - @apply rtl:dark:bg-black/100; - } - - .baz::before { - @apply rtl:dark:hover:bg-black/100; - } - - .qux::file-selector-button { - @apply rtl:dark:hover:bg-black/100; - } - - .steve::before { - @apply rtl:hover:dark:bg-black/100; - } - - .bob::file-selector-button { - @apply rtl:hover:dark:bg-black/100; - } - - .foo::before { - @apply [:has([dir="rtl"]_&)]:hover:bg-black/100; - } - - .bar::file-selector-button { - @apply [:has([dir="rtl"]_&)]:hover:bg-black/100; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - :is(.dark .foo)::before, - :is([dir='rtl'] :is(.dark .bar))::before, - :is([dir='rtl'] :is(.dark .baz:hover))::before { - background-color: #000; - } - :is([dir='rtl'] :is(.dark .qux))::file-selector-button:hover { - background-color: #000; - } - :is([dir='rtl'] :is(.dark .steve):hover):before { - background-color: #000; - } - :is([dir='rtl'] :is(.dark .bob))::file-selector-button:hover { - background-color: #000; - } - :has([dir='rtl'] .foo:hover):before { - background-color: #000; - } - :has([dir='rtl'] .bar)::file-selector-button:hover { - background-color: #000; - } - `) - }) - }) - - stable.test('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], + .bar::before { + @apply rtl:dark:bg-black/100; } - let input = css` + .baz::before { + @apply rtl:dark:hover:bg-black/100; + } + + .qux::file-selector-button { + @apply rtl:dark:hover:bg-black/100; + } + + .steve::before { + @apply rtl:hover:dark:bg-black/100; + } + + .bob::file-selector-button { + @apply rtl:hover:dark:bg-black/100; + } + + .foo::before { + @apply [:has([dir="rtl"]_&)]:hover:bg-black/100; + } + + .bar::file-selector-button { + @apply [:has([dir="rtl"]_&)]:hover:bg-black/100; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + :is(.dark .foo)::before, + :is([dir='rtl'] :is(.dark .bar))::before, + :is([dir='rtl'] :is(.dark .baz:hover))::before { + background-color: #000; + } + :is([dir='rtl'] :is(.dark .qux))::file-selector-button:hover { + background-color: #000; + } + :is([dir='rtl'] :is(.dark .steve):hover):before { + background-color: #000; + } + :is([dir='rtl'] :is(.dark .bob))::file-selector-button:hover { + background-color: #000; + } + :has([dir='rtl'] .foo:hover):before { + background-color: #000; + } + :has([dir='rtl'] .bar)::file-selector-button:hover { + background-color: #000; + } + `) + }) +}) + +test.skip('::ng-deep, ::deep, ::v-deep pseudo elements are left alone', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + ::ng-deep .foo .bar { + @apply font-bold; + } + ::v-deep .foo .bar { + @apply font-bold; + } + ::deep .foo .bar { + @apply font-bold; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` ::ng-deep .foo .bar { - @apply font-bold; + font-weight: 700; } ::v-deep .foo .bar { - @apply font-bold; + font-weight: 700; } ::deep .foo .bar { - @apply font-bold; + font-weight: 700; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ::ng-deep .foo .bar { - font-weight: 700; - } - ::v-deep .foo .bar { - font-weight: 700; - } - ::deep .foo .bar { - font-weight: 700; - } - `) - }) + `) }) - - // 1. `::ng-deep` is deprecated - // 2. `::deep` and `::v-deep` are non-standard - // 3. They all use invalid selector syntax that Lightning CSS does not support - // It may be enough for Oxide to not support it at all - oxide.test.todo('::ng-deep, ::deep, ::v-deep pseudo elements are left alone') }) + +// TODO: Oxide +// 1. `::ng-deep` is deprecated +// 2. `::deep` and `::v-deep` are non-standard +// 3. They all use invalid selector syntax that Lightning CSS does not support +// It may be enough for Oxide to not support it at all +test.todo('::ng-deep, ::deep, ::v-deep pseudo elements are left alone') diff --git a/tests/arbitrary-properties.test.js b/tests/arbitrary-properties.test.js index ce52c1bc7..17f0bcf1f 100644 --- a/tests/arbitrary-properties.test.js +++ b/tests/arbitrary-properties.test.js @@ -1,460 +1,456 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - test('basic arbitrary properties', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +test('basic arbitrary properties', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[paint-order\:markers\] { - paint-order: markers; - } - `) - }) - }) - - test('different arbitrary properties are picked up separately', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[bar\:baz\] { - bar: baz; - } - .\[foo\:bar\] { - foo: bar; - } - `) - }) - }) - - test('arbitrary properties with modifiers', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-color-scheme: dark) { - @media (min-width: 1024px) { - .dark\:lg\:hover\:\[paint-order\:markers\]:hover { - paint-order: markers; - } - } - } - `) - }) - }) - - test('arbitrary properties are sorted after utilities', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .content-none { - --tw-content: none; - content: var(--tw-content); - } - .\[paint-order\:markers\] { - paint-order: markers; - } - .hover\:pointer-events-none:hover { - pointer-events: none; - } - `) - }) - }) - - test('using CSS variables', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[--my-var\:auto\] { - --my-var: auto; - } - `) - }) - }) - - test('using underscores as spaces', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[--my-var\:2px_4px\] { - --my-var: 2px 4px; - } - `) - }) - }) - - test('using the important modifier', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\!\[--my-var\:2px_4px\] { - --my-var: 2px 4px !important; - } - `) - }) - }) - - test('colons are allowed in quotes', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[content\:\'foo\:bar\'\] { - content: 'foo:bar'; - } - `) - }) - }) - - test('colons are allowed in braces', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[background-image\:url\(http\:\/\/example\.com\/picture\.jpg\)\] { - background-image: url('http://example.com/picture.jpg'); - } - `) - }) - }) - - test('invalid class', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - `) - }) - }) - - test('invalid arbitrary property', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - `) - }) - }) - - test('invalid arbitrary property 2', () => { - let config = { - content: [ - { - raw: html`[0:02]`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - `) - }) - }) - - test('using fractional spacing values inside theme() function', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[border\:_calc\(5vw_-_theme\(spacing\[2\.5\]\)\)_double_theme\(\'colors\.fuchsia\.700\'\)\] { - border: calc(5vw - 0.625rem) double #a21caf; - } - `) - }) - }) - - test('using multiple arbitrary props having fractional spacing values', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[box-shadow\:_0_calc\(theme\(spacing\[0\.5\]\)_\*_-1\)_theme\(colors\.red\.400\)_inset\] { - box-shadow: inset 0 -0.125rem #f87171; - } - .\[height\:_calc\(100vh_-_theme\(spacing\[2\.5\]\)\)\] { - height: calc(100vh - 0.625rem); - } - `) - }) - }) - - it('should be possible to read theme values in arbitrary properties (without quotes)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[--a\:theme\(colors\.blue\.500\)\] { - --a: #3b82f6; - } - .\[color\:var\(--a\)\] { - color: var(--a); - } - `) - }) - }) - - it('should be possible to read theme values in arbitrary properties (with quotes)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[--a\:theme\(\'colors\.blue\.500\'\)\] { - --a: #3b82f6; - } - .\[color\:var\(--a\)\] { - color: var(--a); - } - `) - }) - }) - - it('should not generate invalid CSS', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
- `, - - // NOTE: In this case `stillworks:/example` being generated is not ideal - // but it at least doesn't produce invalid CSS when run through prettier - // So we can let it through since it is technically valid - }, - ], - corePlugins: { preflight: false }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .\[stillworks\:\/example\] { - stillworks: /example; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[paint-order\:markers\] { + paint-order: markers; + } + `) + }) +}) + +test('different arbitrary properties are picked up separately', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[bar\:baz\] { + bar: baz; + } + .\[foo\:bar\] { + foo: bar; + } + `) + }) +}) + +test('arbitrary properties with modifiers', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-color-scheme: dark) { + @media (min-width: 1024px) { + .dark\:lg\:hover\:\[paint-order\:markers\]:hover { + paint-order: markers; + } + } + } + `) + }) +}) + +test('arbitrary properties are sorted after utilities', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .content-none { + --tw-content: none; + content: var(--tw-content); + } + .\[paint-order\:markers\] { + paint-order: markers; + } + .hover\:pointer-events-none:hover { + pointer-events: none; + } + `) + }) +}) + +test('using CSS variables', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[--my-var\:auto\] { + --my-var: auto; + } + `) + }) +}) + +test('using underscores as spaces', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[--my-var\:2px_4px\] { + --my-var: 2px 4px; + } + `) + }) +}) + +test('using the important modifier', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\!\[--my-var\:2px_4px\] { + --my-var: 2px 4px !important; + } + `) + }) +}) + +test('colons are allowed in quotes', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[content\:\'foo\:bar\'\] { + content: 'foo:bar'; + } + `) + }) +}) + +test('colons are allowed in braces', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[background-image\:url\(http\:\/\/example\.com\/picture\.jpg\)\] { + background-image: url('http://example.com/picture.jpg'); + } + `) + }) +}) + +test('invalid class', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + `) + }) +}) + +test('invalid arbitrary property', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + `) + }) +}) + +test('invalid arbitrary property 2', () => { + let config = { + content: [ + { + raw: html`[0:02]`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + `) + }) +}) + +test('using fractional spacing values inside theme() function', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[border\:_calc\(5vw_-_theme\(spacing\[2\.5\]\)\)_double_theme\(\'colors\.fuchsia\.700\'\)\] { + border: calc(5vw - 0.625rem) double #a21caf; + } + `) + }) +}) + +test('using multiple arbitrary props having fractional spacing values', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[box-shadow\:_0_calc\(theme\(spacing\[0\.5\]\)_\*_-1\)_theme\(colors\.red\.400\)_inset\] { + box-shadow: inset 0 -0.125rem #f87171; + } + .\[height\:_calc\(100vh_-_theme\(spacing\[2\.5\]\)\)\] { + height: calc(100vh - 0.625rem); + } + `) + }) +}) + +it('should be possible to read theme values in arbitrary properties (without quotes)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[--a\:theme\(colors\.blue\.500\)\] { + --a: #3b82f6; + } + .\[color\:var\(--a\)\] { + color: var(--a); + } + `) + }) +}) + +it('should be possible to read theme values in arbitrary properties (with quotes)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[--a\:theme\(\'colors\.blue\.500\'\)\] { + --a: #3b82f6; + } + .\[color\:var\(--a\)\] { + color: var(--a); + } + `) + }) +}) + +it('should not generate invalid CSS', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+ `, + + // NOTE: In this case `stillworks:/example` being generated is not ideal + // but it at least doesn't produce invalid CSS when run through prettier + // So we can let it through since it is technically valid + }, + ], + corePlugins: { preflight: false }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .\[stillworks\:\/example\] { + stillworks: /example; + } + `) }) }) diff --git a/tests/arbitrary-values.oxide.test.css b/tests/arbitrary-values.oxide.test.css deleted file mode 100644 index 214d08601..000000000 --- a/tests/arbitrary-values.oxide.test.css +++ /dev/null @@ -1,1073 +0,0 @@ -.inset-\[11px\] { - inset: 11px; -} -.inset-\[var\(--value\)\] { - inset: var(--value); -} -.inset-x-\[11px\] { - left: 11px; - right: 11px; -} -.inset-x-\[var\(--value\)\] { - left: var(--value); - right: var(--value); -} -.inset-y-\[11px\] { - top: 11px; - bottom: 11px; -} -.inset-y-\[var\(--value\)\] { - top: var(--value); - bottom: var(--value); -} -.bottom-\[11px\] { - bottom: 11px; -} -.bottom-\[var\(--value\)\] { - bottom: var(--value); -} -.left-\[11px\] { - left: 11px; -} -.left-\[var\(--value\)\] { - left: var(--value); -} -.right-\[11px\] { - right: 11px; -} -.right-\[var\(--value\)\] { - right: var(--value); -} -.top-\[11px\] { - top: 11px; -} -.top-\[var\(--value\)\] { - top: var(--value); -} -.z-\[123\] { - z-index: 123; -} -.z-\[var\(--value\)\] { - z-index: var(--value); -} -.order-\[4\] { - order: 4; -} -.order-\[var\(--value\)\] { - order: var(--value); -} -.col-\[7\] { - grid-column: 7; -} -.col-start-\[7\] { - grid-column-start: 7; -} -.col-end-\[7\] { - grid-column-end: 7; -} -.row-\[7\] { - grid-row: 7; -} -.row-start-\[7\] { - grid-row-start: 7; -} -.row-end-\[7\] { - grid-row-end: 7; -} -.m-\[7px\] { - margin: 7px; -} -.mx-\[7px\] { - margin-left: 7px; - margin-right: 7px; -} -.my-\[7px\] { - margin-top: 7px; - margin-bottom: 7px; -} -.mb-\[7px\] { - margin-bottom: 7px; -} -.ml-\[7px\] { - margin-left: 7px; -} -.mr-\[7px\] { - margin-right: 7px; -} -.mt-\[7px\] { - margin-top: 7px; -} -.mt-\[clamp\(30px\,100px\)\] { - margin-top: clamp(30px, 100px); -} -.aspect-\[16\/9\] { - aspect-ratio: 16 / 9; -} -.aspect-\[var\(--aspect\)\] { - aspect-ratio: var(--aspect); -} -.h-\[3\.23rem\] { - height: 3.23rem; -} -.h-\[calc\(100\%\+1rem\)\] { - height: calc(100% + 1rem); -} -.h-\[var\(--height\)\] { - height: var(--height); -} -.max-h-\[3\.23rem\] { - max-height: 3.23rem; -} -.max-h-\[calc\(100\%\+1rem\)\] { - max-height: calc(100% + 1rem); -} -.max-h-\[var\(--height\)\] { - max-height: var(--height); -} -.min-h-\[3\.23rem\] { - min-height: 3.23rem; -} -.min-h-\[calc\(100\%\+1rem\)\] { - min-height: calc(100% + 1rem); -} -.min-h-\[var\(--height\)\] { - min-height: var(--height); -} -.w-\[\'\)\(\)\'\] { - width: ')()'; -} -.w-\[\'\]\[\]\'\] { - width: '][]'; -} -.w-\[\'\}\{\}\'\] { - width: '}{}'; -} -.w-\[\(\(\)\)\] { - width: (()); -} -.w-\[\(\)\] { - width: (); -} -.w-\[0\] { - width: 0; -} -.w-\[3\.23rem\] { - width: 3.23rem; -} -.w-\[\[\[\]\]\] { - width: [[]]; -} -.w-\[\[\]\] { - width: []; -} -.w-\[calc\(100\%\+1rem\)\] { - width: calc(100% + 1rem); -} -.w-\[calc\(100\%\/3-1rem\*2\)\] { - width: calc(33.3333% - 2rem); -} -.w-\[calc\(var\(--10-10px\,calc\(-20px-\(-30px--40px\)\)\)-50px\)\] { - width: calc(var(--10-10px, calc(-20px - (-30px - -40px))) - 50px); -} -.w-\[var\(--width\)\] { - width: var(--width); -} -.w-\[var\(--width\,calc\(100\%\+1rem\)\)\] { - width: var(--width, calc(100% + 1rem)); -} -.w-\[\{\{\}\}\] { - width: {{}} -} -.w-\[\{\}\] { - width: {} -} -.min-w-\[3\.23rem\] { - min-width: 3.23rem; -} -.min-w-\[calc\(1-\(\(12-3\)\*0\.5\)\)\] { - min-width: -3.5px; -} -.min-w-\[calc\(1-\(var\(--something\)\*0\.5\)\)\] { - min-width: calc(1 - (var(--something) * 0.5)); -} -.min-w-\[calc\(1-var\(--something\)\*0\.5\)\] { - min-width: calc(1 - var(--something) * 0.5); -} -.min-w-\[calc\(100\%\+1rem\)\] { - min-width: calc(100% + 1rem); -} -.min-w-\[var\(--width\)\] { - min-width: var(--width); -} -.max-w-\[3\.23rem\] { - max-width: 3.23rem; -} -.max-w-\[calc\(100\%\+1rem\)\] { - max-width: calc(100% + 1rem); -} -.max-w-\[var\(--width\)\] { - max-width: var(--width); -} -.flex-\[var\(--flex\)\] { - flex: var(--flex); -} -.flex-shrink-\[var\(--shrink\)\], -.shrink-\[var\(--shrink\)\] { - flex-shrink: var(--shrink); -} -.flex-grow-\[var\(--grow\)\], -.grow-\[var\(--grow\)\] { - flex-grow: var(--grow); -} -.basis-\[var\(--basis\)\] { - flex-basis: var(--basis); -} -.origin-\[50px_50px\] { - transform-origin: 50px 50px; -} -.translate-x-\[12\%\] { - --tw-translate-x: 12%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-x-\[var\(--value\)\] { - --tw-translate-x: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-y-\[12\%\] { - --tw-translate-y: 12%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-y-\[var\(--value\)\] { - --tw-translate-y: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-\[1\.5turn\] { - --tw-rotate: 1.5turn; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-\[2\.3rad\] { - --tw-rotate: 131.78deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-\[23deg\] { - --tw-rotate: 23deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-\[401grad\] { - --tw-rotate: 401grad; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-x-\[3px\] { - --tw-skew-x: 3px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-x-\[var\(--value\)\] { - --tw-skew-x: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-y-\[3px\] { - --tw-skew-y: 3px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-y-\[var\(--value\)\] { - --tw-skew-y: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-\[0\.7\] { - --tw-scale-x: 0.7; - --tw-scale-y: 0.7; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-\[var\(--value\)\] { - --tw-scale-x: var(--value); - --tw-scale-y: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-x-\[0\.7\] { - --tw-scale-x: 0.7; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-x-\[var\(--value\)\] { - --tw-scale-x: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-y-\[0\.7\] { - --tw-scale-y: 0.7; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-y-\[var\(--value\)\] { - --tw-scale-y: var(--value); - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.animate-\[pong_1s_cubic-bezier\(0\,0\,0\.2\,1\)_infinite\] { - animation: 1s cubic-bezier(0, 0, 0.2, 1) infinite pong; -} -.animate-\[var\(--value\)\] { - animation: var(--value); -} -.cursor-\[pointer\] { - cursor: pointer; -} -.cursor-\[url\(\'\.\/path_to_hand\.cur\'\)_2_2\,pointer\] { - cursor: url('./path_to_hand.cur') 2 2, pointer; -} -.cursor-\[url\(hand\.cur\)_2_2\,pointer\] { - cursor: url('hand.cur') 2 2, pointer; -} -.cursor-\[var\(--value\)\] { - cursor: var(--value); -} -.scroll-m-\[7px\] { - scroll-margin: 7px; -} -.scroll-mx-\[7px\] { - scroll-margin-left: 7px; - scroll-margin-right: 7px; -} -.scroll-my-\[7px\] { - scroll-margin-top: 7px; - scroll-margin-bottom: 7px; -} -.scroll-mb-\[7px\] { - scroll-margin-bottom: 7px; -} -.scroll-ml-\[7px\] { - scroll-margin-left: 7px; -} -.scroll-mr-\[7px\] { - scroll-margin-right: 7px; -} -.scroll-mt-\[7px\] { - scroll-margin-top: 7px; -} -.scroll-mt-\[var\(--scroll-margin\)\] { - scroll-margin-top: var(--scroll-margin); -} -.scroll-p-\[7px\] { - scroll-padding: 7px; -} -.scroll-px-\[7px\] { - scroll-padding-left: 7px; - scroll-padding-right: 7px; -} -.scroll-py-\[7px\] { - scroll-padding-top: 7px; - scroll-padding-bottom: 7px; -} -.scroll-pb-\[7px\] { - scroll-padding-bottom: 7px; -} -.scroll-pl-\[7px\] { - scroll-padding-left: 7px; -} -.scroll-pr-\[7px\] { - scroll-padding-right: 7px; -} -.scroll-pt-\[7px\] { - scroll-padding-top: 7px; -} -.scroll-pt-\[var\(--scroll-padding\)\] { - scroll-padding-top: var(--scroll-padding); -} -.list-\[\'\\1f44d\'\] { - list-style-type: '👍'; -} -.list-\[var\(--value\)\] { - list-style-type: var(--value); -} -.list-image-\[url\(\.\/my-image\.png\)\] { - list-style-image: url('./my-image.png'); -} -.list-image-\[var\(--value\)\] { - list-style-image: var(--value); -} -.columns-\[20\] { - columns: 20; -} -.columns-\[var\(--columns\)\] { - columns: var(--columns); -} -.auto-cols-\[minmax\(10px\,auto\)\] { - grid-auto-columns: minmax(10px, auto); -} -.auto-rows-\[minmax\(10px\,auto\)\] { - grid-auto-rows: minmax(10px, auto); -} -.grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { - grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; -} -.grid-cols-\[\[linename\]\,1fr\,auto\] { - grid-template-columns: [linename] 1fr auto; -} -.grid-rows-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { - grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; -} -.gap-\[20px\] { - gap: 20px; -} -.gap-\[var\(--value\)\] { - gap: var(--value); -} -.gap-x-\[20px\] { - column-gap: 20px; -} -.gap-x-\[var\(--value\)\] { - column-gap: var(--value); -} -.gap-y-\[20px\] { - row-gap: 20px; -} -.gap-y-\[var\(--value\)\] { - row-gap: var(--value); -} -.space-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(20cm * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(20cm * var(--tw-space-x-reverse)); -} -.space-x-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(calc(20% - 1cm) * var(--tw-space-x-reverse)); -} -.space-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(20cm * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(20cm * var(--tw-space-y-reverse)); -} -.space-y-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(calc(20% - 1cm) * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(calc(20% - 1cm) * var(--tw-space-y-reverse)); -} -.divide-x-\[20cm\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(20cm * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(20cm * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-x-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(calc(20% - 1cm) * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-y-\[20cm\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(20cm * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(20cm * var(--tw-divide-y-reverse)); -} -.divide-y-\[calc\(20\%-1cm\)\] > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(calc(20% - 1cm) * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(calc(20% - 1cm) * var(--tw-divide-y-reverse)); -} -.divide-\[black\] > :not([hidden]) ~ :not([hidden]) { - border-color: #000; -} -.divide-\[var\(--value\)\] > :not([hidden]) ~ :not([hidden]) { - border-color: var(--value); -} -.rounded-\[11px\] { - border-radius: 11px; -} -.rounded-b-\[var\(--radius\)\] { - border-bottom-right-radius: var(--radius); - border-bottom-left-radius: var(--radius); -} -.rounded-l-\[var\(--radius\)\] { - border-top-left-radius: var(--radius); - border-bottom-left-radius: var(--radius); -} -.rounded-r-\[var\(--radius\)\] { - border-top-right-radius: var(--radius); - border-bottom-right-radius: var(--radius); -} -.rounded-t-\[var\(--radius\)\] { - border-top-left-radius: var(--radius); - border-top-right-radius: var(--radius); -} -.rounded-bl-\[var\(--radius\)\] { - border-bottom-left-radius: var(--radius); -} -.rounded-br-\[var\(--radius\)\] { - border-bottom-right-radius: var(--radius); -} -.rounded-tl-\[var\(--radius\)\] { - border-top-left-radius: var(--radius); -} -.rounded-tr-\[var\(--radius\)\] { - border-top-right-radius: var(--radius); -} -.border-\[2\.5px\] { - border-width: 2.5px; -} -.border-\[length\:var\(--value\)\] { - border-width: var(--value); -} -.border-b-\[2\.5px\] { - border-bottom-width: 2.5px; -} -.border-b-\[length\:var\(--value\)\] { - border-bottom-width: var(--value); -} -.border-l-\[2\.5px\] { - border-left-width: 2.5px; -} -.border-l-\[length\:var\(--value\)\] { - border-left-width: var(--value); -} -.border-r-\[2\.5px\] { - border-right-width: 2.5px; -} -.border-r-\[length\:var\(--value\)\] { - border-right-width: var(--value); -} -.border-t-\[2\.5px\] { - border-top-width: 2.5px; -} -.border-t-\[length\:var\(--value\)\] { - border-top-width: var(--value); -} -.border-\[\#f00\] { - border-color: red; -} -.border-\[color\:var\(--value\)\] { - border-color: var(--value); -} -.border-\[red_black\] { - border-color: red #000; -} -.border-b-\[\#f00\] { - border-bottom-color: red; -} -.border-b-\[color\:var\(--value\)\] { - border-bottom-color: var(--value); -} -.border-l-\[\#f00\] { - border-left-color: red; -} -.border-l-\[color\:var\(--value\)\] { - border-left-color: var(--value); -} -.border-r-\[\#f00\] { - border-right-color: red; -} -.border-r-\[color\:var\(--value\)\] { - border-right-color: var(--value); -} -.border-t-\[\#f00\] { - border-top-color: red; -} -.border-t-\[color\:var\(--value\)\] { - border-top-color: var(--value); -} -.bg-\[\#0000ffcc\] { - background-color: #00fc; -} -.bg-\[\#0f0\] { - background-color: #0f0; -} -.bg-\[\#0f0_var\(--value\)\] { - background-color: #0f0 var(--value); -} -.bg-\[\#ff0000\] { - background-color: red; -} -.bg-\[color\:var\(--value1\)_var\(--value2\)\] { - background-color: var(--value1) var(--value2); -} -.bg-\[hsl\(0\,100\%\,50\%\)\], -.bg-\[hsl\(0rad\,100\%\,50\%\)\] { - background-color: red; -} -.bg-\[hsla\(0\,100\%\,50\%\,0\.3\)\], -.bg-\[hsla\(0turn\,100\%\,50\%\,0\.3\)\] { - background-color: #ff00004d; -} -.bg-\[rgb\(123\,123\,123\)\] { - background-color: #7b7b7b; -} -.bg-\[rgb\(123\,_456\,_123\)_black\] { - background-color: #7bff7b black; -} -.bg-\[rgb\(123_456_789\)\] { - background-color: #7bffff; -} -.bg-\[rgba\(123\,123\,123\,0\.5\)\] { - background-color: #7b7b7b80; -} -.bg-\[var\(--value\)\,var\(--value\)\] { - background-color: var(--value), var(--value); -} -.bg-\[var\(--value1\)_var\(--value2\)\] { - background-color: var(--value1) var(--value2); -} -.bg-\[image\(\)\,var\(--value\)\] { - background-image: image(), var(--value); -} -.bg-\[image\:var\(--value\)\,var\(--value\)\] { - background-image: var(--value), var(--value); -} -.bg-\[linear-gradient\(\#eee\,\#fff\)\,conic-gradient\(red\,orange\,yellow\,green\,blue\)\] { - background-image: linear-gradient(#eee, #fff), conic-gradient(red, orange, #ff0, green, #00f); -} -.bg-\[linear-gradient\(\#eee\,\#fff\)\] { - background-image: linear-gradient(#eee, #fff); -} -.bg-\[linear-gradient\(to_left\,rgb\(var\(--green\)\)\,blue\)\] { - background-image: linear-gradient(to left, rgb(var(--green)), blue); -} -.bg-\[repeating-conic-gradient\(\#F8F9FA_0\%_25\%\,_white_0\%_50\%\)\] { - background-image: repeating-conic-gradient(#f8f9fa 0% 25%, white 0% 50%); -} -.bg-\[url\(\'\/path-to-image\.png\'\)\] { - background-image: url('/path-to-image.png'); -} -.bg-\[url\:var\(--url\)\] { - background-image: var(--url); -} -.from-\[\#da5b66\] { - --tw-gradient-from: #da5b66 var(--tw-gradient-from-position); - --tw-gradient-to: #da5b6600 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} -.from-\[var\(--color\)\] { - --tw-gradient-from: var(--color) var(--tw-gradient-from-position); - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} -.via-\[\#da5b66\] { - --tw-gradient-to: #da5b6600 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #da5b66 var(--tw-gradient-via-position), - var(--tw-gradient-to); -} -.via-\[var\(--color\)\] { - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--color) var(--tw-gradient-via-position), - var(--tw-gradient-to); -} -.to-\[\#da5b66\] { - --tw-gradient-to: #da5b66 var(--tw-gradient-to-position); -} -.to-\[var\(--color\)\] { - --tw-gradient-to: var(--color) var(--tw-gradient-to-position); -} -.bg-\[length\:200px_100px\] { - background-size: 200px 100px; -} -.bg-\[length\:var\(--value\)\] { - background-size: var(--value); -} -.bg-\[center_top_1rem\] { - background-position: 50% 1rem; -} -.bg-\[position\:200px_100px\] { - background-position: 200px 100px; -} -.bg-\[position\:var\(--value\)\] { - background-position: var(--value); -} -.fill-\[\#da5b66\] { - fill: #da5b66; -} -.fill-\[url\(\#icon-gradient\)\] { - fill: url('#icon-gradient'); -} -.fill-\[var\(--value\)\] { - fill: var(--value); -} -.stroke-\[\#da5b66\] { - stroke: #da5b66; -} -.stroke-\[color\:var\(--value\)\] { - stroke: var(--value); -} -.stroke-\[url\(\#icon-gradient\)\] { - stroke: url('#icon-gradient'); -} -.stroke-\[20px\] { - stroke-width: 20px; -} -.stroke-\[length\:var\(--value\)\] { - stroke-width: var(--value); -} -.object-\[50\%\,50\%\] { - object-position: 50% 50%; -} -.object-\[top\,right\] { - object-position: top right; -} -.object-\[var\(--position\)\] { - object-position: var(--position); -} -.p-\[7px\] { - padding: 7px; -} -.px-\[7px\] { - padding-left: 7px; - padding-right: 7px; -} -.py-\[7px\] { - padding-top: 7px; - padding-bottom: 7px; -} -.pb-\[7px\] { - padding-bottom: 7px; -} -.pl-\[7px\] { - padding-left: 7px; -} -.pr-\[7px\] { - padding-right: 7px; -} -.pt-\[7px\] { - padding-top: 7px; -} -.pt-\[clamp\(30px\,100px\)\] { - padding-top: clamp(30px, 100px); -} -.indent-\[50\%\] { - text-indent: 50%; -} -.indent-\[var\(--indent\)\] { - text-indent: var(--indent); -} -.align-\[10em\] { - vertical-align: 10em; -} -.font-\[\'Gill_Sans\'\] { - font-family: Gill Sans; -} -.font-\[\'Some_Font\'\,\'Some_Other_Font\'\] { - font-family: Some Font, Some Other Font; -} -.font-\[\'Some_Font\'\,sans-serif\] { - font-family: Some Font, sans-serif; -} -.font-\[\'Some_Font\'\,var\(--other-font\)\] { - font-family: 'Some Font', var(--other-font); -} -.font-\[Georgia\,serif\] { - font-family: Georgia, serif; -} -.font-\[family-name\:var\(--value\)\] { - font-family: var(--value); -} -.font-\[sans-serif\,serif\] { - font-family: sans-serif, serif; -} -.font-\[serif\,var\(--value\)\] { - font-family: serif, var(--value); -} -.text-\[0\] { - font-size: 0; -} -.text-\[2\.23rem\] { - font-size: 2.23rem; -} -.text-\[4cqw\] { - font-size: 4cqw; -} -.text-\[length\:var\(--font-size\)\] { - font-size: var(--font-size); -} -.text-\[min\(10vh\,100px\)\] { - font-size: min(10vh, 100px); -} -.font-\[300\] { - font-weight: 300; -} -.font-\[number\:lighter\] { - font-weight: lighter; -} -.font-\[number\:var\(--value\)\] { - font-weight: var(--value); -} -.leading-\[var\(--leading\)\] { - line-height: var(--leading); -} -.tracking-\[var\(--tracking\)\] { - letter-spacing: var(--tracking); -} -.text-\[black\] { - color: #000; -} -.text-\[color\:var\(--color\)\] { - color: var(--color); -} -.text-\[rgb\(123\,123\,123\)\], -.text-\[rgb\(123\,_123\,_123\)\], -.text-\[rgb\(123_123_123\)\] { - color: #7b7b7b; -} -.decoration-\[black\] { - text-decoration-color: #000; -} -.decoration-\[color\:var\(--color\)\] { - text-decoration-color: var(--color); -} -.decoration-\[rgb\(123\,123\,123\)\], -.decoration-\[rgb\(123\,_123\,_123\)\], -.decoration-\[rgb\(123_123_123\)\] { - text-decoration-color: #7b7b7b; -} -.decoration-\[length\:10px\] { - text-decoration-thickness: 10px; -} -.underline-offset-\[10px\] { - text-underline-offset: 10px; -} -.placeholder-\[var\(--placeholder\)\]::placeholder { - color: var(--placeholder); -} -.caret-\[black\] { - caret-color: #000; -} -.caret-\[var\(--value\)\] { - caret-color: var(--value); -} -.accent-\[\#bada55\] { - accent-color: #bada55; -} -.accent-\[var\(--accent-color\)\] { - accent-color: var(--accent-color); -} -.opacity-\[0\.8\] { - opacity: 0.8; -} -.opacity-\[var\(--opacity\)\] { - opacity: var(--opacity); -} -.shadow-\[0px_1px_2px_black\] { - --tw-shadow: 0px 1px 2px black; - --tw-shadow-colored: 0px 1px 2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-\[shadow\:var\(--value\)\] { - --tw-shadow: var(--value); - --tw-shadow-colored: var(--value); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.outline-\[10px\] { - outline-width: 10px; -} -.outline-\[length\:var\(--outline\)\] { - outline-width: var(--outline); -} -.outline-offset-\[10px\] { - outline-offset: 10px; -} -.outline-\[black\] { - outline-color: #000; -} -.outline-\[color\:var\(--outline\)\] { - outline-color: var(--outline); -} -.ring-\[10px\] { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(10px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-\[length\:\(var\(--value\)\)\] { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc((var(--value)) + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-\[\#76ad65\] { - --tw-ring-color: #76ad65; -} -.ring-\[color\:var\(--value\)\] { - --tw-ring-color: var(--value); -} -.ring-offset-\[19rem\] { - --tw-ring-offset-width: 19rem; -} -.ring-offset-\[length\:var\(--value\)\] { - --tw-ring-offset-width: var(--value); -} -.ring-offset-\[\#76ad65\] { - --tw-ring-offset-color: #76ad65; -} -.ring-offset-\[\#ad672f\] { - --tw-ring-offset-color: #ad672f; -} -.ring-offset-\[color\:var\(--value\)\] { - --tw-ring-offset-color: var(--value); -} -.blur-\[15px\] { - --tw-blur: blur(15px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.brightness-\[300\%\] { - --tw-brightness: brightness(300%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.contrast-\[2\.4\] { - --tw-contrast: contrast(2.4); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.drop-shadow-\[0px_1px_2px_black\] { - --tw-drop-shadow: drop-shadow(0px 1px 2px black); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.grayscale-\[0\.55\] { - --tw-grayscale: grayscale(0.55); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.hue-rotate-\[0\.8turn\] { - --tw-hue-rotate: hue-rotate(0.8turn); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.invert-\[0\.75\] { - --tw-invert: invert(0.75); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.saturate-\[180\%\] { - --tw-saturate: saturate(180%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.sepia-\[0\.2\] { - --tw-sepia: sepia(0.2); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.backdrop-blur-\[11px\] { - --tw-backdrop-blur: blur(11px); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-brightness-\[1\.23\] { - --tw-backdrop-brightness: brightness(1.23); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-contrast-\[0\.87\] { - --tw-backdrop-contrast: contrast(0.87); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-grayscale-\[0\.42\] { - --tw-backdrop-grayscale: grayscale(0.42); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-hue-rotate-\[1\.57rad\] { - --tw-backdrop-hue-rotate: hue-rotate(89.9544deg); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-invert-\[0\.66\] { - --tw-backdrop-invert: invert(0.66); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-opacity-\[22\%\] { - --tw-backdrop-opacity: opacity(22%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-saturate-\[144\%\] { - --tw-backdrop-saturate: saturate(144%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-sepia-\[0\.38\] { - --tw-backdrop-sepia: sepia(0.38); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.transition-\[opacity\,width\] { - transition-property: opacity, width; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.delay-\[var\(--delay\)\] { - transition-delay: var(--delay); -} -.duration-\[2s\] { - transition-duration: 2s; -} -.duration-\[var\(--app-duration\)\] { - transition-duration: var(--app-duration); -} -.will-change-\[top\,left\] { - will-change: top, left; -} -.will-change-\[var\(--will-change\)\] { - will-change: var(--will-change); -} -.content-\[\'\>\'\] { - --tw-content: '>'; - content: var(--tw-content); -} -.content-\[\'hello\'\] { - --tw-content: 'hello'; - content: var(--tw-content); -} -.content-\[attr\(content-before\)\] { - --tw-content: attr(content-before); - content: var(--tw-content); -} -@media (min-width: 1024px) { - .lg\:grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { - grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; - } -} diff --git a/tests/arbitrary-values.test.css b/tests/arbitrary-values.test.css index 6f43a5277..32646e5ff 100644 --- a/tests/arbitrary-values.test.css +++ b/tests/arbitrary-values.test.css @@ -185,7 +185,7 @@ min-width: 3.23rem; } .min-w-\[calc\(1-\(\(12-3\)\*0\.5\)\)\] { - min-width: calc(1 - ((12 - 3) * 0.5)); + min-width: -3.5px; } .min-w-\[calc\(1-\(var\(--something\)\*0\.5\)\)\] { min-width: calc(1 - (var(--something) * 0.5)); diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js index 774b4db40..923607517 100644 --- a/tests/arbitrary-values.test.js +++ b/tests/arbitrary-values.test.js @@ -1,655 +1,612 @@ import fs from 'fs' import path from 'path' -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - test('arbitrary values', () => { - let config = { - content: [path.resolve(__dirname, './arbitrary-values.test.html')], - } +test('arbitrary values', () => { + let config = { + content: [path.resolve(__dirname, './arbitrary-values.test.html')], + } - return run('@tailwind utilities', config).then((result) => { - stable - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './arbitrary-values.test.css'), 'utf8') - ) - oxide - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './arbitrary-values.oxide.test.css'), 'utf8') - ) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss( + fs.readFileSync(path.resolve(__dirname, './arbitrary-values.test.css'), 'utf8') + ) }) +}) - oxide.test.todo( - 'should only detect classes with arbitrary values that are properly terminated after the arbitrary value' - ) - stable.test( - 'should only detect classes with arbitrary values that are properly terminated after the arbitrary value', - () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], +test('should only detect classes with arbitrary values that are properly terminated after the arbitrary value', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css``) + }) +}) + +it('should be possible to differentiate between decoration utilities', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .decoration-\[\#ccc\] { + text-decoration-color: #ccc; } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css``) - }) - } - ) - - it('should be possible to differentiate between decoration utilities', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .decoration-\[\#ccc\] { - text-decoration-color: #ccc; - } - .decoration-\[3px\] { - text-decoration-thickness: 3px; - } - `) - }) + .decoration-\[3px\] { + text-decoration-thickness: 3px; + } + `) }) +}) - it('should support modifiers for arbitrary values that contain the separator', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } +it('should support modifiers for arbitrary values that contain the separator', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .hover\:bg-\[url\(\'https\:\/\/github\.com\/tailwindlabs\.png\'\)\]:hover { - background-image: url('https://github.com/tailwindlabs.png'); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .hover\:bg-\[url\(\'https\:\/\/github\.com\/tailwindlabs\.png\'\)\]:hover { + background-image: url('https://github.com/tailwindlabs.png'); + } + `) }) +}) - it('should support arbitrary values for various background utilities', () => { - let config = { - content: [ - { - raw: html` - -
-
+it('should support arbitrary values for various background utilities', () => { + let config = { + content: [ + { + raw: html` + +
+
- -
-
-
-
+ +
+
+
+
- -
-
- `, - }, - ], - } + +
+
+ `, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-\[\#ff0000\] { - --tw-bg-opacity: 1; - background-color: rgb(255 0 0 / var(--tw-bg-opacity)); - } - .bg-\[color\:var\(--bg-color\)\] { - background-color: var(--bg-color); - } - .bg-\[hsl\(var\(--bg-color\)\)\] { - background-color: hsl(var(--bg-color)); - } - .bg-\[rgb\(var\(--bg-color\)\)\] { - background-color: rgb(var(--bg-color)); - } - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .bg-\[url\(\'\/image-1-0\.png\'\)\] { - background-image: url('/image-1-0.png'); - } - .bg-\[url\:var\(--image-url\)\] { - background-image: var(--image-url); - } - .bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-\[\#ff0000\] { - background-color: red; - } - .bg-\[color\:var\(--bg-color\)\] { - background-color: var(--bg-color); - } - .bg-\[hsl\(var\(--bg-color\)\)\] { - background-color: hsl(var(--bg-color)); - } - .bg-\[rgb\(var\(--bg-color\)\)\] { - background-color: rgb(var(--bg-color)); - } - .bg-red-500 { - background-color: #ef4444; - } - .bg-\[url\(\'\/image-1-0\.png\'\)\] { - background-image: url('/image-1-0.png'); - } - .bg-\[url\:var\(--image-url\)\] { - background-image: var(--image-url); - } - .bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[\#ff0000\] { + --tw-bg-opacity: 1; + background-color: rgb(255 0 0 / var(--tw-bg-opacity)); + } + .bg-\[color\:var\(--bg-color\)\] { + background-color: var(--bg-color); + } + .bg-\[hsl\(var\(--bg-color\)\)\] { + background-color: hsl(var(--bg-color)); + } + .bg-\[rgb\(var\(--bg-color\)\)\] { + background-color: rgb(var(--bg-color)); + } + .bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + } + .bg-\[url\(\'\/image-1-0\.png\'\)\] { + background-image: url('/image-1-0.png'); + } + .bg-\[url\:var\(--image-url\)\] { + background-image: var(--image-url); + } + .bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); + } + `) }) +}) - it('should not generate any css if an unknown typehint is used', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } +it('should not generate any css if an unknown typehint is used', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css``) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css``) }) +}) - it('should handle unknown typehints', () => { - let config = { content: [{ raw: html`
` }] } +it('should handle unknown typehints', () => { + let config = { content: [{ raw: html`
` }] } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .w-\[length\:12px\] { - width: 12px; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .w-\[length\:12px\] { + width: 12px; + } + `) }) +}) - it('should convert _ to spaces', () => { - // Using custom css function here, because otherwise with String.raw, we run - // into an issue with `\2c ` escapes. If we use `\2c ` then JS complains - // about strict mode. But `\\2c ` is not what it expected. - function css(templates) { - return templates.join('') - } +it('should convert _ to spaces', () => { + // Using custom css function here, because otherwise with String.raw, we run + // into an issue with `\2c ` escapes. If we use `\2c ` then JS complains + // about strict mode. But `\\2c ` is not what it expected. + function css(templates) { + return templates.join('') + } - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .col-\\[span_3_\\/_span_8\\] { - grid-column: span 3 / span 8; - } - .row-\\[span_3_\\/_span_8\\] { - grid-row: span 3 / span 8; - } - .m-\\[8px_4px\\] { - margin: 8px 4px; - } - .flex-\\[1_1_100\\%\\] { - flex: 100%; - } - .auto-cols-\\[minmax\\(0\\,_1fr\\)\\] { - grid-auto-columns: minmax(0, 1fr); - } - .grid-cols-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] { - grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; - } - .grid-rows-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] { - grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; - } - .rounded-\\[0px_4px_4px_0px\\] { - border-radius: 0 4px 4px 0; - } - .p-\\[8px_4px\\] { - padding: 8px 4px; - } - .shadow-\\[0px_0px_4px_black\\] { - --tw-shadow: 0px 0px 4px black; - --tw-shadow-colored: 0px 0px 4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .drop-shadow-\\[0px_1px_3px_black\\] { - --tw-drop-shadow: drop-shadow(0px 1px 3px black); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) - var(--tw-drop-shadow); - } - .content-\\[\\'__hello__world__\\'\\] { - --tw-content: ' hello world '; - content: var(--tw-content); - } - .content-\\[___abc____\\] { - --tw-content: abc; - content: var(--tw-content); - } - .content-\\[_hello_world_\\] { - --tw-content: hello world; - content: var(--tw-content); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .col-\\[span_3_\\/_span_8\\] { + grid-column: span 3 / span 8; + } + .row-\\[span_3_\\/_span_8\\] { + grid-row: span 3 / span 8; + } + .m-\\[8px_4px\\] { + margin: 8px 4px; + } + .flex-\\[1_1_100\\%\\] { + flex: 100%; + } + .auto-cols-\\[minmax\\(0\\,_1fr\\)\\] { + grid-auto-columns: minmax(0, 1fr); + } + .grid-cols-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] { + grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; + } + .grid-rows-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] { + grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; + } + .rounded-\\[0px_4px_4px_0px\\] { + border-radius: 0 4px 4px 0; + } + .p-\\[8px_4px\\] { + padding: 8px 4px; + } + .shadow-\\[0px_0px_4px_black\\] { + --tw-shadow: 0px 0px 4px black; + --tw-shadow-colored: 0px 0px 4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .drop-shadow-\\[0px_1px_3px_black\\] { + --tw-drop-shadow: drop-shadow(0px 1px 3px black); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) + var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) + var(--tw-drop-shadow); + } + .content-\\[\\'__hello__world__\\'\\] { + --tw-content: ' hello world '; + content: var(--tw-content); + } + .content-\\[___abc____\\] { + --tw-content: abc; + content: var(--tw-content); + } + .content-\\[_hello_world_\\] { + --tw-content: hello world; + content: var(--tw-content); + } + `) }) +}) - it('should not convert escaped underscores with spaces', () => { - let config = { - content: [{ raw: `
` }], - corePlugins: { preflight: false }, - } +it('should not convert escaped underscores with spaces', () => { + let config = { + content: [{ raw: `
` }], + corePlugins: { preflight: false }, + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .content-\[\'snake\\_case\'\] { - --tw-content: 'snake_case'; - content: var(--tw-content); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .content-\[\'snake\\_case\'\] { + --tw-content: 'snake_case'; + content: var(--tw-content); + } + `) }) +}) - it('should pick the fallback plugin when arbitrary values collide', () => { - let config = { - content: [ - { - raw: html` -
- -
- -
-
- `, - }, - ], - } +it('should pick the fallback plugin when arbitrary values collide', () => { + let config = { + content: [ + { + raw: html` +
+ +
+ +
+
+ `, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .bg-\[var\(--unknown\)\] { - background-color: var(--unknown); - } - .bg-\[200px_100px\] { - background-position: 200px 100px; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .bg-\[var\(--unknown\)\] { + background-color: var(--unknown); + } + .bg-\[200px_100px\] { + background-position: 200px 100px; + } + `) }) +}) - it('should pick the fallback plugin when arbitrary values collide and can not be inferred', () => { - let config = { - content: [{ raw: html`
` }], - } +it('should pick the fallback plugin when arbitrary values collide and can not be inferred', () => { + let config = { + content: [{ raw: html`
` }], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .bg-\[var\(--tw-unknown\)\] { - background-color: var(--tw-unknown); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .bg-\[var\(--tw-unknown\)\] { + background-color: var(--tw-unknown); + } + `) }) +}) - it('should warn and not generate if arbitrary values are ambiguous (without fallback)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [ - function ({ matchUtilities }) { - matchUtilities({ foo: (value) => ({ value }) }, { type: ['position'] }) - matchUtilities({ foo: (value) => ({ value }) }, { type: ['size'] }) - }, - ], - } +it('should warn and not generate if arbitrary values are ambiguous (without fallback)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [ + function ({ matchUtilities }) { + matchUtilities({ foo: (value) => ({ value }) }, { type: ['position'] }) + matchUtilities({ foo: (value) => ({ value }) }, { type: ['size'] }) + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css``) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css``) }) +}) - it('should support colons in URLs', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } +it('should support colons in URLs', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .bg-\[url\(\'https\:\/\/www\.spacejam\.com\/1996\/img\/bg_stars\.gif\'\)\] { - background-image: url('https://www.spacejam.com/1996/img/bg_stars.gif'); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .bg-\[url\(\'https\:\/\/www\.spacejam\.com\/1996\/img\/bg_stars\.gif\'\)\] { + background-image: url('https://www.spacejam.com/1996/img/bg_stars.gif'); + } + `) }) +}) - it('should support unescaped underscores in URLs', () => { - let config = { - content: [ - { raw: html`
` }, - ], - } +it('should support unescaped underscores in URLs', () => { + let config = { + content: [ + { raw: html`
` }, + ], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(` + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(` .bg-\\[url\\(\\'brown_potato\\.jpg\\'\\)\\,_url\\(\\'red_tomato\\.png\\'\\)\\] { background-image: url('brown_potato.jpg'), url('red_tomato.png'); } `) - }) - }) - - it('should be possible to read theme values in arbitrary values (without quotes)', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - spacing: { - 0.5: 'calc(.5 * .25rem)', - 1: 'calc(1 * .25rem)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .w-\[theme\(spacing\.1\)\] { - width: 0.25rem; - } - .w-\[theme\(spacing\[0\.5\]\)\] { - width: 0.125rem; - } - `) - }) - }) - - it('should be possible to read theme values in arbitrary values (with quotes)', () => { - let config = { - content: [ - { raw: html`
` }, - ], - theme: { - spacing: { - 0.5: 'calc(.5 * .25rem)', - 1: 'calc(1 * .25rem)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .w-\[theme\(\'spacing\.1\'\)\] { - width: 0.25rem; - } - .w-\[theme\(\'spacing\[0\.5\]\'\)\] { - width: 0.125rem; - } - `) - }) - }) - - it('should be possible to read theme values in arbitrary values (with quotes) when inside calc or similar functions', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - theme: { - spacing: { - 0.5: 'calc(.5 * .25rem)', - 1: 'calc(1 * .25rem)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .w-\[calc\(100\%-theme\(\'spacing\.1\'\)\)\] { - width: calc(100% - 0.25rem); - } - .w-\[calc\(100\%-theme\(\'spacing\[0\.5\]\'\)\)\] { - width: calc(100% - 0.125rem); - } - `) - }) - }) - - it('should not output unparsable arbitrary CSS values', () => { - let config = { - content: [ - { - raw: 'let classes = `w-[${sizes.width}]`', - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(``) - }) - }) - - // Issue: https://github.com/tailwindlabs/tailwindcss/issues/7997 - // `top_right_50%` was a valid percentage before introducing this change - it('should correctly validate each part when checking for `percentage` data types', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[top_right_50\%\] { - background-position: right 50% top; - } - `) - }) - }) - - it('should correctly validate background size', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[auto_auto\,cover\,_contain\,10px\,10px_10\%\] { - background-size: auto, cover, contain, 10px, 10px 10%; - } - `) - }) - }) - - it('should correctly validate combination of percentage and length', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[50\%_10\%\] { - background-position: 50% 10%; - } - .bg-\[50px_10\%\] { - background-position: 50px 10%; - } - .bg-\[50px_10px\] { - background-position: 50px 10px; - } - `) - }) - }) - - it('can explicitly specify type for percentage and length', () => { - let config = { - content: [ - { raw: html`
` }, - ], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[size\:50px_10\%\] { - background-size: 50px 10%; - } - .bg-\[50px_10px\] { - background-position: 50px 10px; - } - .bg-\[position\:50\%_10\%\] { - background-position: 50% 10%; - } - `) - }) - }) - - it('can use CSS variables as arbitrary values without `var()`', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .w-\[--width-var\] { - width: var(--width-var); - } - .bg-\[--color-var\,\#000\] { - background-color: var(--color-var, #000); - } - .bg-\[--color-var\] { - background-color: var(--color-var); - } - .bg-\[length\:--size-var\] { - background-size: var(--size-var); - } - .text-\[length\:--size-var\,12px\] { - font-size: var(--size-var, 12px); - } - `) - }) - }) - - it('can use CSS variables as arbitrary modifiers without `var()`', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-red-500\/\[--opacity\] { - background-color: rgb(239 68 68 / var(--opacity)); - } - .text-sm\/\[--line-height\] { - font-size: 0.875rem; - line-height: var(--line-height); - } - `) - }) - }) - - it('should support underscores in arbitrary modifiers', () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .text-lg\/\[calc\(50px_\*_2\)\] { - font-size: 1.125rem; - line-height: calc(50px * 2); - } - `) - }) + }) +}) + +it('should be possible to read theme values in arbitrary values (without quotes)', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + spacing: { + 0.5: 'calc(.5 * .25rem)', + 1: 'calc(1 * .25rem)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .w-\[theme\(spacing\.1\)\] { + width: 0.25rem; + } + .w-\[theme\(spacing\[0\.5\]\)\] { + width: 0.125rem; + } + `) + }) +}) + +it('should be possible to read theme values in arbitrary values (with quotes)', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + spacing: { + 0.5: 'calc(.5 * .25rem)', + 1: 'calc(1 * .25rem)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .w-\[theme\(\'spacing\.1\'\)\] { + width: 0.25rem; + } + .w-\[theme\(\'spacing\[0\.5\]\'\)\] { + width: 0.125rem; + } + `) + }) +}) + +it('should be possible to read theme values in arbitrary values (with quotes) when inside calc or similar functions', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + theme: { + spacing: { + 0.5: 'calc(.5 * .25rem)', + 1: 'calc(1 * .25rem)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .w-\[calc\(100\%-theme\(\'spacing\.1\'\)\)\] { + width: calc(100% - 0.25rem); + } + .w-\[calc\(100\%-theme\(\'spacing\[0\.5\]\'\)\)\] { + width: calc(100% - 0.125rem); + } + `) + }) +}) + +it('should not output unparsable arbitrary CSS values', () => { + let config = { + content: [ + { + raw: 'let classes = `w-[${sizes.width}]`', + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(``) + }) +}) + +// Issue: https://github.com/tailwindlabs/tailwindcss/issues/7997 +// `top_right_50%` was a valid percentage before introducing this change +it('should correctly validate each part when checking for `percentage` data types', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[top_right_50\%\] { + background-position: right 50% top; + } + `) + }) +}) + +it('should correctly validate background size', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[auto_auto\,cover\,_contain\,10px\,10px_10\%\] { + background-size: auto, cover, contain, 10px, 10px 10%; + } + `) + }) +}) + +it('should correctly validate combination of percentage and length', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[50\%_10\%\] { + background-position: 50% 10%; + } + .bg-\[50px_10\%\] { + background-position: 50px 10%; + } + .bg-\[50px_10px\] { + background-position: 50px 10px; + } + `) + }) +}) + +it('can explicitly specify type for percentage and length', () => { + let config = { + content: [ + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[size\:50px_10\%\] { + background-size: 50px 10%; + } + .bg-\[50px_10px\] { + background-position: 50px 10px; + } + .bg-\[position\:50\%_10\%\] { + background-position: 50% 10%; + } + `) + }) +}) + +it('can use CSS variables as arbitrary values without `var()`', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .w-\[--width-var\] { + width: var(--width-var); + } + .bg-\[--color-var\,\#000\] { + background-color: var(--color-var, #000); + } + .bg-\[--color-var\] { + background-color: var(--color-var); + } + .bg-\[length\:--size-var\] { + background-size: var(--size-var); + } + .text-\[length\:--size-var\,12px\] { + font-size: var(--size-var, 12px); + } + `) + }) +}) + +it('can use CSS variables as arbitrary modifiers without `var()`', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-500\/\[--opacity\] { + background-color: rgb(239 68 68 / var(--opacity)); + } + .text-sm\/\[--line-height\] { + font-size: 0.875rem; + line-height: var(--line-height); + } + `) + }) +}) + +it('should support underscores in arbitrary modifiers', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .text-lg\/\[calc\(50px_\*_2\)\] { + font-size: 1.125rem; + line-height: calc(50px * 2); + } + `) }) }) diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index b210bdc77..1e37a5047 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -1,377 +1,372 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('basic arbitrary variants', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('basic arbitrary variants', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\>\*\]\:underline > * { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\>\*\]\:underline > * { + text-decoration-line: underline; + } + `) }) +}) - test('spaces in selector (using _)', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +test('spaces in selector (using _)', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .a.b .\[\.a\.b_\&\]\:underline { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .a.b .\[\.a\.b_\&\]\:underline { + text-decoration-line: underline; + } + `) }) +}) - test('arbitrary variants with modifiers', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('arbitrary variants with modifiers', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-color-scheme: dark) { - @media (min-width: 1024px) { - .dark\:lg\:hover\:\[\&\>\*\]\:underline > :hover { - text-decoration-line: underline; - } - } - } - `) - }) - }) - - test('variants without & or an at-rule are ignored', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - `) - }) - }) - - test('arbitrary variants are sorted after other variants', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .underline { - text-decoration-line: underline; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-color-scheme: dark) { @media (min-width: 1024px) { - .lg\:underline { + .dark\:lg\:hover\:\[\&\>\*\]\:underline > :hover { text-decoration-line: underline; } } - .\[\&\>\*\]\:underline > * { + } + `) + }) +}) + +test('variants without & or an at-rule are ignored', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + `) + }) +}) + +test('arbitrary variants are sorted after other variants', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .underline { + text-decoration-line: underline; + } + @media (min-width: 1024px) { + .lg\:underline { text-decoration-line: underline; } - `) - }) + } + .\[\&\>\*\]\:underline > * { + text-decoration-line: underline; + } + `) }) +}) - test('using the important modifier', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('using the important modifier', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\>\*\]\:\!underline > * { - text-decoration-line: underline !important; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\>\*\]\:\!underline > * { + text-decoration-line: underline !important; + } + `) + }) +}) + +test('at-rules', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @supports (what: ever) { + .\[\@supports\(what\:ever\)\]\:underline { + text-decoration-line: underline; } - `) - }) + } + `) }) +}) - test('at-rules', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('nested at-rules', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @supports (what: ever) { - .\[\@supports\(what\:ever\)\]\:underline { - text-decoration-line: underline; - } - } - `) - }) - }) - - test('nested at-rules', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media screen { - @media (hover: hover) { - .\[\@media_screen\{\@media\(hover\:hover\)\}\]\:underline { - text-decoration-line: underline; - } - } - } - `) - }) - }) - - test('at-rules with selector modifications', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media screen { @media (hover: hover) { - .\[\@media\(hover\:hover\)\{\&\:hover\}\]\:underline:hover { + .\[\@media_screen\{\@media\(hover\:hover\)\}\]\:underline { text-decoration-line: underline; } } - `) - }) + } + `) }) +}) - test('nested at-rules with selector modifications', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +test('at-rules with selector modifications', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media screen { - @media (hover: hover) { - .\[\@media_screen\{\@media\(hover\:hover\)\{\&\:hover\}\}\]\:underline:hover { - text-decoration-line: underline; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (hover: hover) { + .\[\@media\(hover\:hover\)\{\&\:hover\}\]\:underline:hover { + text-decoration-line: underline; + } + } + `) + }) +}) + +test('nested at-rules with selector modifications', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media screen { + @media (hover: hover) { + .\[\@media_screen\{\@media\(hover\:hover\)\{\&\:hover\}\}\]\:underline:hover { + text-decoration-line: underline; } } - `) - }) + } + `) }) +}) - test('attribute selectors', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('attribute selectors', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\[data-open\]\]\:underline[data-open] { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\[data-open\]\]\:underline[data-open] { + text-decoration-line: underline; + } + `) }) +}) - test('multiple attribute selectors', () => { - let config = { - content: [ - { raw: html`
` }, - ], - corePlugins: { preflight: false }, - } +test('multiple attribute selectors', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]\:underline[data-foo][data-bar]:not([data-baz]) { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]\:underline[data-foo][data-bar]:not([data-baz]) { + text-decoration-line: underline; + } + `) }) +}) - oxide.test.todo('multiple attribute selectors with custom separator (1)') - stable.test('multiple attribute selectors with custom separator (1)', () => { - let config = { - separator: '__', - content: [ - { raw: html`
` }, - ], - corePlugins: { preflight: false }, - } +test('multiple attribute selectors with custom separator (1)', () => { + let config = { + separator: '__', + content: [ + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]__underline[data-foo][data-bar]:not([data-baz]) { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]__underline[data-foo][data-bar]:not([data-baz]) { + text-decoration-line: underline; + } + `) }) +}) - oxide.test.todo('multiple attribute selectors with custom separator (2)') - stable.test('multiple attribute selectors with custom separator (2)', () => { - let config = { - separator: '_@', - content: [ - { raw: html`
` }, - ], - corePlugins: { preflight: false }, - } +test('multiple attribute selectors with custom separator (2)', () => { + let config = { + separator: '_@', + content: [ + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]_\@underline[data-foo][data-bar]:not([data-baz]) { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]_\@underline[data-foo][data-bar]:not([data-baz]) { + text-decoration-line: underline; + } + `) }) +}) - test('with @apply', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +test('with @apply', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = ` + let input = ` @tailwind base; @tailwind components; @tailwind utilities; @@ -381,138 +376,138 @@ crosscheck(({ stable, oxide }) => { } ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media screen { - @media (hover: hover) { - .foo:hover { - text-decoration-line: underline; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media screen { + @media (hover: hover) { + .foo:hover { + text-decoration-line: underline; } } - `) - }) + } + `) }) +}) - test('keeps escaped underscores', () => { - let config = { - content: [ - { - raw: '
', - }, - ], - corePlugins: { preflight: false }, - } +test('keeps escaped underscores', () => { + let config = { + content: [ + { + raw: '
', + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&_\.foo\\_\\_bar\]\:underline .foo__bar { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&_\.foo\\_\\_bar\]\:underline .foo__bar { + text-decoration-line: underline; + } + `) }) +}) - test('keeps escaped underscores with multiple arbitrary variants', () => { - let config = { - content: [ - { - raw: '
', - }, - ], - corePlugins: { preflight: false }, - } +test('keeps escaped underscores with multiple arbitrary variants', () => { + let config = { + content: [ + { + raw: '
', + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&_\.foo\\_\\_bar\]\:\[\&_\.bar\\_\\_baz\]\:underline .bar__baz .foo__bar { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&_\.foo\\_\\_bar\]\:\[\&_\.bar\\_\\_baz\]\:underline .bar__baz .foo__bar { + text-decoration-line: underline; + } + `) }) +}) - test('keeps escaped underscores in arbitrary variants mixed with normal variants', () => { - let config = { - content: [ - { - raw: ` +test('keeps escaped underscores in arbitrary variants mixed with normal variants', () => { + let config = { + content: [ + { + raw: `
`, - }, - ], - corePlugins: { preflight: false }, - } + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&_\.foo\\_\\_bar\]\:hover\:underline:hover .foo__bar, + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&_\.foo\\_\\_bar\]\:hover\:underline:hover .foo__bar, .hover\:\[\&_\.foo\\_\\_bar\]\:underline .foo__bar:hover { - text-decoration-line: underline; - } - `) - }) + text-decoration-line: underline; + } + `) }) +}) - test('allows attribute variants with quotes', () => { - let config = { - content: [ - { - raw: ` +test('allows attribute variants with quotes', () => { + let config = { + content: [ + { + raw: `
`, - }, - ], - corePlugins: { preflight: false }, - } + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .\[\&\[data-test\=\"2\"\]\]\:underline[data-test='2'], + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\[\&\[data-test\=\"2\"\]\]\:underline[data-test='2'], .\[\&\[data-test\=\'2\'\]\]\:underline[data-test='2'] { - text-decoration-line: underline; - } - `) - }) + text-decoration-line: underline; + } + `) }) +}) - test('classes in arbitrary variants should not be prefixed', () => { - let config = { - prefix: 'tw-', - content: [ - { - raw: ` +test('classes in arbitrary variants should not be prefixed', () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: `
should not be red
should be red
@@ -530,731 +525,662 @@ crosscheck(({ stable, oxide }) => {
should be red
`, - }, - ], - corePlugins: { preflight: false }, - } + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .\[\&_\.foo\]\:tw-text-red-400 .foo, - .\[\&_\.foo\]\:hover\:tw-text-red-400:hover .foo, - .hover\:\[\&_\.foo\]\:tw-text-red-400 .foo:hover, - .foo .\[\.foo_\&\]\:tw-text-red-400 { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .\[\&_\.foo\]\:tw-text-red-400 .foo, - .\[\&_\.foo\]\:hover\:tw-text-red-400:hover .foo, - .hover\:\[\&_\.foo\]\:tw-text-red-400 .foo:hover, - .foo .\[\.foo_\&\]\:tw-text-red-400 { - color: #f87171; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\[\&_\.foo\]\:tw-text-red-400 .foo, + .\[\&_\.foo\]\:hover\:tw-text-red-400:hover .foo, + .hover\:\[\&_\.foo\]\:tw-text-red-400 .foo:hover, + .foo .\[\.foo_\&\]\:tw-text-red-400 { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); + } + `) }) +}) - test('classes in the same arbitrary variant should not be prefixed', () => { - let config = { - prefix: 'tw-', - content: [ - { - raw: ` -
should not be red
+test('classes in the same arbitrary variant should not be prefixed', () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: ` +
should not be red
-
should be red
+
should be red
-
+
should not be red
should be red
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .\[\&_\.foo\]\:tw-bg-white .foo { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - } - .\[\&_\.foo\]\:tw-text-red-400 .foo { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity)); - } - .foo .\[\.foo_\&\]\:tw-bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - } - .foo .\[\.foo_\&\]\:tw-text-red-400 { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .\[\&_\.foo\]\:tw-bg-white .foo { - background-color: #fff; - } - .\[\&_\.foo\]\:tw-text-red-400 .foo { - color: #f87171; - } - .foo .\[\.foo_\&\]\:tw-bg-white { - background-color: #fff; - } - .foo .\[\.foo_\&\]\:tw-text-red-400 { - color: #f87171; - } - `) - }) - }) - - it('should support aria variants', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .underline, - .aria-checked\:underline[aria-checked='true'], - .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'], - .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'], - .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline, - .group[aria-checked='true'] .group-aria-checked\:underline, - .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline, - .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline, - .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline, - .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline, - .peer[aria-checked='true'] ~ .peer-aria-checked\:underline, - .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline, - .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline, - .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline { - text-decoration-line: underline; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .aria-checked\:underline[aria-checked='true'], - .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'], - .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'], - .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline, - .group[aria-checked='true'] .group-aria-checked\:underline, - .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline, - .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline, - .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline, - .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline, - .peer[aria-checked='true'] ~ .peer-aria-checked\:underline, - .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline, - .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline, - .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline { - text-decoration-line: underline; - } - `) - }) - }) - - it('should support data variants', () => { - let config = { - theme: { - data: { - checked: 'ui~="checked"', - }, }, - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .underline, - .data-checked\:underline[data-ui~='checked'], - .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'], - .data-\[position\=top\]\:underline[data-position='top'], - .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline, - .group[data-ui~='checked'] .group-data-checked\:underline, - .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline, - .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline, - .group[data-position='top'] .group-data-\[position\=top\]\:underline, - .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline, - .peer[data-ui~='checked'] ~ .peer-data-checked\:underline, - .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline, - .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline, - .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline { - text-decoration-line: underline; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .data-checked\:underline[data-ui~='checked'], - .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'], - .data-\[position\=top\]\:underline[data-position='top'], - .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline, - .group[data-ui~='checked'] .group-data-checked\:underline, - .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline, - .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline, - .group[data-position='top'] .group-data-\[position\=top\]\:underline, - .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline, - .peer[data-ui~='checked'] ~ .peer-data-checked\:underline, - .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline, - .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline, - .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\[\&_\.foo\]\:tw-font-bold .foo, + .foo .\[\.foo_\&\]\:tw-font-bold { + font-weight: 700; + } + `) }) +}) - it('should support supports', () => { - let config = { - theme: { - supports: { - grid: 'display: grid', - }, +it('should support aria variants', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, }, - content: [ - { - raw: html` -
- -
- -
- -
- -
- -
- -
- -
- -
-
- `, - }, - ], - corePlugins: { preflight: false }, - } + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @supports (display: grid) { - .supports-grid\:underline { - text-decoration-line: underline; - } - .supports-\[display\:grid\]\:grid { - display: grid; - } - } - @supports (foo: bar) and (bar: baz) { - .supports-\[\(foo\:bar\)and\(bar\:baz\)\]\:underline { - text-decoration-line: underline; - } - } - @supports (foo: bar) or (bar: baz) { - .supports-\[\(foo\:bar\)or\(bar\:baz\)\]\:underline { - text-decoration-line: underline; - } - } - @supports (container-type: var(--tw)) { - .supports-\[container-type\]\:underline { - text-decoration-line: underline; - } - } - @supports not (foo: bar) { - .supports-\[not\(foo\:bar\)\]\:underline { - text-decoration-line: underline; - } - } - @supports selector(A > B) { - .supports-\[selector\(A_\>_B\)\]\:underline { - text-decoration-line: underline; - } - } - @supports (transform-origin: 5% 5%) { - .supports-\[transform-origin\:5\%_5\%\]\:underline { - text-decoration-line: underline; - } - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .underline, + .aria-checked\:underline[aria-checked='true'], + .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'], + .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'], + .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline, + .group[aria-checked='true'] .group-aria-checked\:underline, + .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline, + .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline, + .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline, + .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline, + .peer[aria-checked='true'] ~ .peer-aria-checked\:underline, + .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline, + .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline, + .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline { + text-decoration-line: underline; + } + `) }) +}) - it('should be possible to use modifiers and arbitrary groups', () => { - let config = { - content: [ - { - raw: html` -
-
- -
+it('should support data variants', () => { + let config = { + theme: { + data: { + checked: 'ui~="checked"', + }, + }, + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - - -
- -
+ let input = css` + @tailwind utilities; + ` - - -
- -
+ return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .underline, + .data-checked\:underline[data-ui~='checked'], + .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'], + .data-\[position\=top\]\:underline[data-position='top'], + .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline, + .group[data-ui~='checked'] .group-data-checked\:underline, + .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline, + .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline, + .group[data-position='top'] .group-data-\[position\=top\]\:underline, + .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline, + .peer[data-ui~='checked'] ~ .peer-data-checked\:underline, + .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline, + .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline, + .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline { + text-decoration-line: underline; + } + `) + }) +}) - - -
- -
-
+it('should support supports', () => { + let config = { + theme: { + supports: { + grid: 'display: grid', + }, + }, + content: [ + { + raw: html` +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - -
-
+ let input = css` + @tailwind utilities; + ` -
-
- -
-
- -
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .group\/foo:hover .group-hover\/foo\:underline, - .group:hover .group-hover\:underline, - .group\/foo:focus .group-\[\&\:focus\]\/foo\:underline, - .group:focus .group-\[\&\:focus\]\:underline, - .group\/foo[data-open] .group-\[\&\[data-open\]\]\/foo\:underline, - .group[data-open] .group-\[\&\[data-open\]\]\:underline, - .group\/foo.in-foo .group-\[\.in-foo\]\/foo\:underline, - .group.in-foo .group-\[\.in-foo\]\:underline, - .in-foo .group\/foo .group-\[\.in-foo_\&\]\/foo\:underline, - .in-foo .group .group-\[\.in-foo_\&\]\:underline, - .group\/foo:hover .group-\[\:hover\]\/foo\:underline, - .group:hover .group-\[\:hover\]\:underline, - .group\/foo[data-open] .group-\[\[data-open\]\]\/foo\:underline, - .group[data-open] .group-\[\[data-open\]\]\:underline { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @supports (display: grid) { + .supports-grid\:underline { text-decoration-line: underline; } - `) - }) + .supports-\[display\:grid\]\:grid { + display: grid; + } + } + @supports (foo: bar) and (bar: baz) { + .supports-\[\(foo\:bar\)and\(bar\:baz\)\]\:underline { + text-decoration-line: underline; + } + } + @supports (foo: bar) or (bar: baz) { + .supports-\[\(foo\:bar\)or\(bar\:baz\)\]\:underline { + text-decoration-line: underline; + } + } + @supports (container-type: var(--tw)) { + .supports-\[container-type\]\:underline { + text-decoration-line: underline; + } + } + @supports not (foo: bar) { + .supports-\[not\(foo\:bar\)\]\:underline { + text-decoration-line: underline; + } + } + @supports selector(A > B) { + .supports-\[selector\(A_\>_B\)\]\:underline { + text-decoration-line: underline; + } + } + @supports (transform-origin: 5% 5%) { + .supports-\[transform-origin\:5\%_5\%\]\:underline { + text-decoration-line: underline; + } + } + `) }) +}) - it('should be possible to use modifiers and arbitrary peers', () => { - let config = { - content: [ - { - raw: html` -
-
+it('should be possible to use modifiers and arbitrary groups', () => { + let config = { + content: [ + { + raw: html` +
+
+ +
- -
- - + -
+
-
+
- + -
+
-
+
-
+
-
- - -
- -
- -
-
- -
-
- -
-
+
- `, - }, - ], - corePlugins: { preflight: false }, - } - let input = css` - @tailwind utilities; - ` + +
+
- return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .peer\/foo:hover ~ .peer-hover\/foo\:underline, - .peer:hover ~ .peer-hover\:underline, - .peer\/foo:focus ~ .peer-\[\&\:focus\]\/foo\:underline, - .peer:focus ~ .peer-\[\&\:focus\]\:underline, - .peer\/foo[data-open] ~ .peer-\[\&\[data-open\]\]\/foo\:underline, - .peer[data-open] ~ .peer-\[\&\[data-open\]\]\:underline, - .peer\/foo.in-foo ~ .peer-\[\.in-foo\]\/foo\:underline, - .peer.in-foo ~ .peer-\[\.in-foo\]\:underline, - .in-foo .peer\/foo ~ .peer-\[\.in-foo_\&\]\/foo\:underline, - .in-foo .peer ~ .peer-\[\.in-foo_\&\]\:underline, - .peer\/foo:hover ~ .peer-\[\:hover\]\/foo\:underline, - .peer:hover ~ .peer-\[\:hover\]\:underline, - .peer\/foo[data-open] ~ .peer-\[\[data-open\]\]\/foo\:underline, - .peer[data-open] ~ .peer-\[\[data-open\]\]\:underline { - text-decoration-line: underline; - } - `) - }) - }) +
+
- it('Arbitrary variants are ordered alphabetically', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
+
+
+ +
+
- `, - }, - ], - corePlugins: { preflight: false }, - } +
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .\[\&\:\:a\]\:underline::a { - text-decoration-line: underline; - } - .\[\&\:\:b\]\:underline::b { - text-decoration-line: underline; - } - .\[\&\:\:c\]\:underline::c { - text-decoration-line: underline; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .group\/foo:hover .group-hover\/foo\:underline, + .group:hover .group-hover\:underline, + .group\/foo:focus .group-\[\&\:focus\]\/foo\:underline, + .group:focus .group-\[\&\:focus\]\:underline, + .group\/foo[data-open] .group-\[\&\[data-open\]\]\/foo\:underline, + .group[data-open] .group-\[\&\[data-open\]\]\:underline, + .group\/foo.in-foo .group-\[\.in-foo\]\/foo\:underline, + .group.in-foo .group-\[\.in-foo\]\:underline, + .in-foo .group\/foo .group-\[\.in-foo_\&\]\/foo\:underline, + .in-foo .group .group-\[\.in-foo_\&\]\:underline, + .group\/foo:hover .group-\[\:hover\]\/foo\:underline, + .group:hover .group-\[\:hover\]\:underline, + .group\/foo[data-open] .group-\[\[data-open\]\]\/foo\:underline, + .group[data-open] .group-\[\[data-open\]\]\:underline { + text-decoration-line: underline; + } + `) }) +}) - it('Arbitrary variants support multiple attribute selectors', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +it('should be possible to use modifiers and arbitrary peers', () => { + let config = { + content: [ + { + raw: html` +
+
- let input = css` - @tailwind utilities; - ` + +
- return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - [data-foo='bar'][data-baz] .\[\[data-foo\=\'bar\'\]\[data-baz\]_\&\]\:underline { - text-decoration-line: underline; - } - `) - }) + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
+ +
+ +
+
+ +
+
+ +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .peer\/foo:hover ~ .peer-hover\/foo\:underline, + .peer:hover ~ .peer-hover\:underline, + .peer\/foo:focus ~ .peer-\[\&\:focus\]\/foo\:underline, + .peer:focus ~ .peer-\[\&\:focus\]\:underline, + .peer\/foo[data-open] ~ .peer-\[\&\[data-open\]\]\/foo\:underline, + .peer[data-open] ~ .peer-\[\&\[data-open\]\]\:underline, + .peer\/foo.in-foo ~ .peer-\[\.in-foo\]\/foo\:underline, + .peer.in-foo ~ .peer-\[\.in-foo\]\:underline, + .in-foo .peer\/foo ~ .peer-\[\.in-foo_\&\]\/foo\:underline, + .in-foo .peer ~ .peer-\[\.in-foo_\&\]\:underline, + .peer\/foo:hover ~ .peer-\[\:hover\]\/foo\:underline, + .peer:hover ~ .peer-\[\:hover\]\:underline, + .peer\/foo[data-open] ~ .peer-\[\[data-open\]\]\/foo\:underline, + .peer[data-open] ~ .peer-\[\[data-open\]\]\:underline { + text-decoration-line: underline; + } + `) }) +}) - it('Invalid arbitrary variants selectors should produce nothing instead of failing', () => { - let config = { - content: [ - { - raw: html` +it('Arbitrary variants are ordered alphabetically', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\[\&\:\:a\]\:underline::a { + text-decoration-line: underline; + } + .\[\&\:\:b\]\:underline::b { + text-decoration-line: underline; + } + .\[\&\:\:c\]\:underline::c { + text-decoration-line: underline; + } + `) + }) +}) + +it('Arbitrary variants support multiple attribute selectors', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + [data-foo='bar'][data-baz] .\[\[data-foo\=\'bar\'\]\[data-baz\]_\&\]\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('Invalid arbitrary variants selectors should produce nothing instead of failing', () => { + let config = { + content: [ + { + raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css``) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css``) }) +}) - it('should output responsive variants + stacked variants in the right order', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } +it('should output responsive variants + stacked variants in the right order', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 1280px) { - .xl\:p-1 { - padding: 0.25rem; - } - } - .\[\&_ul\]\:flex ul { - display: flex; - } - .\[\&_ul\]\:flex-col ul { - flex-direction: column; - } - @media (min-width: 768px) { - .md\:\[\&_ul\]\:flex-row ul { - flex-direction: row; - } - } - `) - }) - }) - - it('it should discard arbitrary variants with multiple selectors', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - { - // escaped commas are a-ok - // This is separate because prettier complains about `\,` in the template string - raw: '
', - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .p-1, - .span\,div .hover\:\[\.span\\\,div_\&\]\:p-1:hover, - :is(span, div) .hover\:\[\:is\(span\,div\)_\&\]\:p-1:hover, - div .\[div_\&\]\:p-1, - div .hover\:\[div_\&\]\:p-1:hover { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 1280px) { + .xl\:p-1 { padding: 0.25rem; } - `) - }) + } + .\[\&_ul\]\:flex ul { + display: flex; + } + .\[\&_ul\]\:flex-col ul { + flex-direction: column; + } + @media (min-width: 768px) { + .md\:\[\&_ul\]\:flex-row ul { + flex-direction: row; + } + } + `) }) +}) - it('should sort multiple variant fns with normal variants between them', () => { - /** @type {string[]} */ - let lines = [] +it('it should discard arbitrary variants with multiple selectors', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + { + // escaped commas are a-ok + // This is separate because prettier complains about `\,` in the template string + raw: '
', + }, + ], + corePlugins: { preflight: false }, + } - for (let a of [1, 2]) { - for (let b of [2, 1]) { - for (let c of [1, 2]) { - for (let d of [2, 1]) { - for (let e of [1, 2]) { - lines.push(`
`) - } + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .p-1, + .span\,div .hover\:\[\.span\\\,div_\&\]\:p-1:hover, + :is(span, div) .hover\:\[\:is\(span\,div\)_\&\]\:p-1:hover, + div .\[div_\&\]\:p-1, + div .hover\:\[div_\&\]\:p-1:hover { + padding: 0.25rem; + } + `) + }) +}) + +it('should sort multiple variant fns with normal variants between them', () => { + /** @type {string[]} */ + let lines = [] + + for (let a of [1, 2]) { + for (let b of [2, 1]) { + for (let c of [1, 2]) { + for (let d of [2, 1]) { + for (let e of [1, 2]) { + lines.push(`
`) } } } } + } - // Fisher-Yates shuffle - for (let i = lines.length - 1; i > 0; i--) { - let j = Math.floor(Math.random() * i) - ;[lines[i], lines[j]] = [lines[j], lines[i]] - } + // Fisher-Yates shuffle + for (let i = lines.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * i) + ;[lines[i], lines[j]] = [lines[j], lines[i]] + } - let config = { - content: [ - { - raw: lines.join('\n'), - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ addVariant, matchVariant }) { - addVariant('foo1', "&[data-foo='1']") - addVariant('foo2', "&[data-foo='2']") + let config = { + content: [ + { + raw: lines.join('\n'), + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addVariant, matchVariant }) { + addVariant('foo1', "&[data-foo='1']") + addVariant('foo2', "&[data-foo='2']") - matchVariant('bar', (value) => `&[data-bar='${value}']`, { - sort: (a, b) => b.value - a.value, - }) + matchVariant('bar', (value) => `&[data-bar='${value}']`, { + sort: (a, b) => b.value - a.value, + }) - addVariant('baz1', "&[data-baz='1']") - addVariant('baz2', "&[data-baz='2']") + addVariant('baz1', "&[data-baz='1']") + addVariant('baz2', "&[data-baz='2']") - matchVariant('qux', (value) => `&[data-qux='${value}']`, { - sort: (a, b) => b.value - a.value, - }) + matchVariant('qux', (value) => `&[data-qux='${value}']`, { + sort: (a, b) => b.value - a.value, + }) - addVariant('fred1', "&[data-fred='1']") - addVariant('fred2', "&[data-fred='2']") - }, - ], - } + addVariant('fred1', "&[data-fred='1']") + addVariant('fred2', "&[data-fred='2']") + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], - .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], - .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], - .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], - .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'], - .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + `) }) }) diff --git a/tests/basic-usage.oxide.test.css b/tests/basic-usage.oxide.test.css deleted file mode 100644 index 6a01dc045..000000000 --- a/tests/basic-usage.oxide.test.css +++ /dev/null @@ -1,1028 +0,0 @@ -*, -:before, -:after, -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #3b82f680; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} -.container { - width: 100%; -} -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} -.sr-only { - width: 1px; - height: 1px; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - margin: -1px; - padding: 0; - position: absolute; - overflow: hidden; -} -.pointer-events-none { - pointer-events: none; -} -.invisible { - visibility: hidden; -} -.collapse { - visibility: collapse; -} -.absolute { - position: absolute; -} -.inset-0 { - inset: 0; -} -.inset-x-2 { - left: 0.5rem; - right: 0.5rem; -} -.inset-y-4 { - top: 1rem; - bottom: 1rem; -} -.bottom-12 { - bottom: 3rem; -} -.left-16 { - left: 4rem; -} -.right-8 { - right: 2rem; -} -.top-6 { - top: 1.5rem; -} -.isolate { - isolation: isolate; -} -.isolation-auto { - isolation: auto; -} -.z-30 { - z-index: 30; -} -.order-2 { - order: 2; -} -.order-last { - order: 9999; -} -.col-span-3 { - grid-column: span 3 / span 3; -} -.col-start-1 { - grid-column-start: 1; -} -.col-end-4 { - grid-column-end: 4; -} -.row-span-2 { - grid-row: span 2 / span 2; -} -.row-start-3 { - grid-row-start: 3; -} -.row-end-5 { - grid-row-end: 5; -} -.float-right { - float: right; -} -.clear-left { - clear: left; -} -.m-4 { - margin: 1rem; -} -.mx-auto { - margin-left: auto; - margin-right: auto; -} -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} -.mb-3 { - margin-bottom: 0.75rem; -} -.ml-4 { - margin-left: 1rem; -} -.mr-1 { - margin-right: 0.25rem; -} -.mt-0 { - margin-top: 0; -} -.box-border { - box-sizing: border-box; -} -.inline-grid { - display: inline-grid; -} -.hidden { - display: none; -} -.aspect-square { - aspect-ratio: 1 / 1; -} -.aspect-video { - aspect-ratio: 16 / 9; -} -.h-16 { - height: 4rem; -} -.max-h-screen { - max-height: 100vh; -} -.min-h-0 { - min-height: 0; -} -.w-12 { - width: 3rem; -} -.min-w-min { - min-width: min-content; -} -.max-w-full { - max-width: 100%; -} -.flex-1 { - flex: 1; -} -.flex-shrink { - flex-shrink: 1; -} -.flex-shrink-0 { - flex-shrink: 0; -} -.shrink { - flex-shrink: 1; -} -.shrink-0 { - flex-shrink: 0; -} -.flex-grow { - flex-grow: 1; -} -.flex-grow-0 { - flex-grow: 0; -} -.grow { - flex-grow: 1; -} -.grow-0 { - flex-grow: 0; -} -.basis-7 { - flex-basis: 1.75rem; -} -.basis-auto { - flex-basis: auto; -} -.table-fixed { - table-layout: fixed; -} -.caption-top { - caption-side: top; -} -.caption-bottom { - caption-side: bottom; -} -.border-collapse { - border-collapse: collapse; -} -.border-spacing-4 { - --tw-border-spacing-x: 1rem; - --tw-border-spacing-y: 1rem; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); -} -.border-spacing-x-6 { - --tw-border-spacing-x: 1.5rem; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); -} -.border-spacing-y-8 { - --tw-border-spacing-y: 2rem; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); -} -.origin-top-right { - transform-origin: 100% 0; -} -.-translate-x-3 { - --tw-translate-x: -0.75rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.-translate-x-4 { - --tw-translate-x: -1rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-x-5 { - --tw-translate-x: 1.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-y-6 { - --tw-translate-y: 1.5rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-3 { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-x-12 { - --tw-skew-x: 12deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-y-12 { - --tw-skew-y: 12deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-95 { - --tw-scale-x: 0.95; - --tw-scale-y: 0.95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.-scale-x-100 { - --tw-scale-x: -1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.transform-gpu { - transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.transform-none { - transform: none; -} -.animate-none { - animation: none; -} -@keyframes spin { - to { - transform: rotate(360deg); - } -} -.animate-spin { - animation: 1s linear infinite spin; -} -.cursor-pointer { - cursor: pointer; -} -.touch-pan-x { - --tw-pan-x: pan-x; - touch-action: var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom); -} -.touch-pan-y { - --tw-pan-y: pan-y; - touch-action: var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom); -} -.touch-pan-up { - --tw-pan-y: pan-up; - touch-action: var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom); -} -.touch-pinch-zoom { - --tw-pinch-zoom: pinch-zoom; - touch-action: var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom); -} -.touch-manipulation { - touch-action: manipulation; -} -.select-none { - user-select: none; -} -.resize-none { - resize: none; -} -.snap-x { - scroll-snap-type: x var(--tw-scroll-snap-strictness); -} -.snap-mandatory { - --tw-scroll-snap-strictness: mandatory; -} -.snap-center { - scroll-snap-align: center; -} -.snap-always { - scroll-snap-stop: always; -} -.scroll-mt-6 { - scroll-margin-top: 1.5rem; -} -.scroll-p-6 { - scroll-padding: 1.5rem; -} -.list-inside { - list-style-position: inside; -} -.list-disc { - list-style-type: disc; -} -.list-image-none { - list-style-image: none; -} -.appearance-none { - appearance: none; -} -.columns-1 { - columns: 1; -} -.columns-md { - columns: 28rem; -} -.break-before-page { - break-before: page; -} -.break-inside-avoid-column { - break-inside: avoid-column; -} -.break-after-auto { - break-after: auto; -} -.auto-cols-min { - grid-auto-columns: min-content; -} -.grid-flow-row { - grid-auto-flow: row; -} -.auto-rows-max { - grid-auto-rows: max-content; -} -.grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} -.grid-rows-3 { - grid-template-rows: repeat(3, minmax(0, 1fr)); -} -.flex-row-reverse { - flex-direction: row-reverse; -} -.flex-wrap { - flex-wrap: wrap; -} -.place-content-start { - place-content: start; -} -.place-items-end { - place-items: end; -} -.content-center { - align-content: center; -} -.items-start { - align-items: flex-start; -} -.justify-center { - justify-content: center; -} -.justify-items-end { - justify-items: end; -} -.gap-4 { - gap: 1rem; -} -.gap-x-2 { - column-gap: 0.5rem; -} -.gap-y-3 { - row-gap: 0.75rem; -} -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); -} -.space-y-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); -} -.space-y-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 1; -} -.space-x-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 1; -} -.divide-x-0 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(0px * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(2px * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(2px * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); -} -.divide-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(4px * var(--tw-divide-y-reverse)); -} -.divide-dotted > :not([hidden]) ~ :not([hidden]) { - border-style: dotted; -} -.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { - border-color: #e5e7eb; -} -.place-self-center { - place-self: center; -} -.self-end { - align-self: flex-end; -} -.justify-self-start { - justify-self: start; -} -.overflow-hidden { - overflow: hidden; -} -.overscroll-contain { - overscroll-behavior: contain; -} -.truncate { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} -.overflow-ellipsis { - text-overflow: ellipsis; -} -.whitespace-nowrap { - white-space: nowrap; -} -.break-words { - overflow-wrap: break-word; -} -.rounded-md { - border-radius: 0.375rem; -} -.border { - border-width: 1px; -} -.border-2 { - border-width: 2px; -} -.border-x-4 { - border-left-width: 4px; - border-right-width: 4px; -} -.border-y-4 { - border-top-width: 4px; - border-bottom-width: 4px; -} -.border-b-4 { - border-bottom-width: 4px; -} -.border-t { - border-top-width: 1px; -} -.border-solid { - border-style: solid; -} -.border-hidden { - border-style: hidden; -} -.border-black { - border-color: #000; -} -.border-x-black { - border-left-color: #000; - border-right-color: #000; -} -.border-y-black { - border-top-color: #000; - border-bottom-color: #000; -} -.border-b-black { - border-bottom-color: #000; -} -.border-l-black { - border-left-color: #000; -} -.border-r-black { - border-right-color: #000; -} -.border-t-black { - border-top-color: #000; -} -.bg-green-500 { - background-color: #22c55e; -} -.bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); -} -.from-red-300 { - --tw-gradient-from: #fca5a5 var(--tw-gradient-from-position); - --tw-gradient-to: #fca5a500 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} -.via-purple-200 { - --tw-gradient-to: #e9d5ff00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #e9d5ff var(--tw-gradient-via-position), - var(--tw-gradient-to); -} -.to-blue-400 { - --tw-gradient-to: #60a5fa var(--tw-gradient-to-position); -} -.decoration-slice { - -webkit-box-decoration-break: slice; - box-decoration-break: slice; -} -.decoration-clone { - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -.box-decoration-slice { - -webkit-box-decoration-break: slice; - box-decoration-break: slice; -} -.box-decoration-clone { - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -.bg-cover { - background-size: cover; -} -.bg-local { - background-attachment: local; -} -.bg-clip-border { - background-clip: border-box; -} -.bg-top { - background-position: top; -} -.bg-no-repeat { - background-repeat: no-repeat; -} -.bg-origin-border { - background-origin: border-box; -} -.bg-origin-padding { - background-origin: padding-box; -} -.bg-origin-content { - background-origin: content-box; -} -.fill-current { - fill: currentColor; -} -.stroke-current { - stroke: currentColor; -} -.stroke-2 { - stroke-width: 2px; -} -.object-cover { - object-fit: cover; -} -.object-bottom { - object-position: bottom; -} -.p-4 { - padding: 1rem; -} -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} -.pb-3 { - padding-bottom: 0.75rem; -} -.pl-4 { - padding-left: 1rem; -} -.pr-2 { - padding-right: 0.5rem; -} -.pt-1 { - padding-top: 0.25rem; -} -.text-center { - text-align: center; -} -.-indent-12 { - text-indent: -3rem; -} -.indent-6 { - text-indent: 1.5rem; -} -.align-middle { - vertical-align: middle; -} -.font-sans { - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, - Noto Color Emoji; -} -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} -.font-medium { - font-weight: 500; -} -.uppercase { - text-transform: uppercase; -} -.not-italic { - font-style: normal; -} -.ordinal { - --tw-ordinal: ordinal; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.diagonal-fractions { - --tw-numeric-fraction: diagonal-fractions; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.leading-5 { - line-height: 1.25rem; -} -.leading-relaxed { - line-height: 1.625; -} -.tracking-tight { - letter-spacing: -0.025em; -} -.text-indigo-500 { - color: #6366f1; -} -.underline { - text-decoration-line: underline; -} -.decoration-red-600 { - text-decoration-color: #dc2626; -} -.decoration-solid { - text-decoration-style: solid; -} -.decoration-1 { - text-decoration-thickness: 1px; -} -.decoration-2 { - text-decoration-thickness: 2px; -} -.underline-offset-1 { - text-underline-offset: 1px; -} -.underline-offset-2 { - text-underline-offset: 2px; -} -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.placeholder-green-300::placeholder { - color: #86efac; -} -.caret-red-600 { - caret-color: #dc2626; -} -.accent-red-600 { - accent-color: #dc2626; -} -.opacity-90 { - opacity: 0.9; -} -.bg-blend-darken { - background-blend-mode: darken; -} -.bg-blend-difference { - background-blend-mode: difference; -} -.mix-blend-multiply { - mix-blend-mode: multiply; -} -.mix-blend-saturation { - mix-blend-mode: saturation; -} -.shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-lg { - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-black { - --tw-shadow-color: #000; - --tw-shadow: var(--tw-shadow-colored); -} -.shadow-blue-100\/10 { - --tw-shadow-color: #dbeafe1a; - --tw-shadow: var(--tw-shadow-colored); -} -.shadow-red-500\/25 { - --tw-shadow-color: #ef444440; - --tw-shadow: var(--tw-shadow-colored); -} -.outline-none { - outline-offset: 2px; - outline: 2px solid #0000; -} -.outline { - outline-style: solid; -} -.outline-dashed { - outline-style: dashed; -} -.outline-4 { - outline-width: 4px; -} -.-outline-offset-2 { - outline-offset: -2px; -} -.outline-offset-2 { - outline-offset: 2px; -} -.outline-black { - outline-color: #000; -} -.ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-4 { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-white { - --tw-ring-color: #fff; -} -.ring-offset-2 { - --tw-ring-offset-width: 2px; -} -.ring-offset-blue-300 { - --tw-ring-offset-color: #93c5fd; -} -.blur-md { - --tw-blur: blur(12px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.brightness-150 { - --tw-brightness: brightness(1.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.contrast-50 { - --tw-contrast: contrast(0.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.drop-shadow-md { - --tw-drop-shadow: drop-shadow(0 4px 3px #00000012) drop-shadow(0 2px 2px #0000000f); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.grayscale { - --tw-grayscale: grayscale(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.hue-rotate-60 { - --tw-hue-rotate: hue-rotate(60deg); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.invert { - --tw-invert: invert(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.saturate-200 { - --tw-saturate: saturate(2); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.sepia { - --tw-sepia: sepia(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.filter-none { - filter: none; -} -.backdrop-blur-lg { - --tw-backdrop-blur: blur(16px); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-brightness-50 { - --tw-backdrop-brightness: brightness(0.5); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-contrast-0 { - --tw-backdrop-contrast: contrast(0); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-grayscale { - --tw-backdrop-grayscale: grayscale(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-hue-rotate-90 { - --tw-backdrop-hue-rotate: hue-rotate(90deg); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-invert { - --tw-backdrop-invert: invert(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-opacity-75 { - --tw-backdrop-opacity: opacity(0.75); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-saturate-150 { - --tw-backdrop-saturate: saturate(1.5); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-sepia { - --tw-backdrop-sepia: sepia(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-filter { - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-filter-none { - backdrop-filter: none; -} -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.transition-all { - transition-property: all; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.delay-300 { - transition-delay: 0.3s; -} -.duration-200 { - transition-duration: 0.2s; -} -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.will-change-scroll { - will-change: scroll-position; -} -.will-change-transform { - will-change: transform; -} -.content-none { - --tw-content: none; - content: var(--tw-content); -} diff --git a/tests/basic-usage.test.js b/tests/basic-usage.test.js index 53c2a4418..10cdac86d 100644 --- a/tests/basic-usage.test.js +++ b/tests/basic-usage.test.js @@ -1,133 +1,132 @@ import fs from 'fs' import path from 'path' -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('basic usage', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
{ backdrop-saturate-150 backdrop-sepia " - >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } + >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './basic-usage.test.css'), 'utf8') - ) - oxide - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './basic-usage.oxide.test.css'), 'utf8') - ) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss( + fs.readFileSync(path.resolve(__dirname, './basic-usage.test.css'), 'utf8') + ) }) +}) - test('all plugins are executed that match a candidate', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - colors: { - green: { - light: 'green', - }, +test('all plugins are executed that match a candidate', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + colors: { + green: { + light: 'green', }, }, - corePlugins: { preflight: false }, + }, + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + + .bg-green { + /* Empty on purpose */ } + ` - let input = css` - @tailwind utilities; - - .bg-green { - /* Empty on purpose */ - } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-green-light { - --tw-bg-opacity: 1; - background-color: rgb(0 128 0 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-green-light { - background-color: green; - } - `) - }) - }) - - test('per-plugin colors with the same key can differ when using a custom colors object', () => { - let config = { - content: [ - { - raw: html` -
This should be green text on red background.
- `, - }, - ], - theme: { - // colors & theme MUST be plain objects - // If they're functions here the test passes regardless - colors: { - theme: { - bg: 'red', - text: 'green', - }, - }, - extend: { - textColor: { - theme: { - DEFAULT: 'green', - }, - }, - backgroundColor: { - theme: { - DEFAULT: 'red', - }, - }, - }, - }, - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-theme { - --tw-bg-opacity: 1; - background-color: rgb(255 0 0 / var(--tw-bg-opacity)); - } - .text-theme { - --tw-text-opacity: 1; - color: rgb(0 128 0 / var(--tw-text-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-theme { - background-color: red; - } - .text-theme { - color: green; - } - `) - }) - }) - - test('default ring color can be a function', () => { - function color(variable) { - return function ({ opacityVariable, opacityValue }) { - if (opacityValue !== undefined) { - return `rgba(${variable}, ${opacityValue})` - } - if (opacityVariable !== undefined) { - return `rgba(${variable}, var(${opacityVariable}, 1))` - } - return `rgb(${variable})` - } - } - - let config = { - content: [ - { - raw: html`
`, - }, - ], - - theme: { - extend: { - ringColor: { - DEFAULT: color('var(--red)'), - }, - }, - }, - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: 'rgba(var(--red), 0.5)' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('falsy config values still work', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - inset: { - 0: 0, - }, - }, - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .inset-0 { - inset: 0; - } - `) - }) - }) - - it('shadows support values without a leading zero', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - boxShadow: { - one: '0.5rem 0.5rem 0.5rem #0005', - two: '.5rem .5rem .5rem #0005', - }, - }, - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .shadow-one, - .shadow-two { - --tw-shadow: 0.5rem 0.5rem 0.5rem #0005; - --tw-shadow-colored: 0.5rem 0.5rem 0.5rem var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) - }) - - it('can scan extremely long classes without crashing', () => { - let val = 'cols-' + '-a'.repeat(65536) - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css``) - }) - }) - - it('does not produce duplicate output when seeing variants preceding a wildcard (*)', () => { - let config = { - content: [{ raw: html`underline focus:*` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - - * { - color: red; - } - - .combined, - * { - text-align: center; - } - - @layer base { - * { - color: blue; - } - - .combined, - * { - color: red; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - * { - color: #00f; - } - .combined, - * { - color: red; - } - ${defaults} - .underline { - text-decoration-line: underline; - } - * { - color: red; - } - .combined, - * { - text-align: center; - } - `) - }) - }) - - it('can parse box shadows with variables', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - boxShadow: { - lg: 'var(--a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0)', - }, - }, - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .shadow-lg { - --tw-shadow: var(--a, 0 35px 60px -15px #000), 0 0 1px #000; - --tw-shadow-colored: 0 35px 60px -15px var(--tw-shadow-color), - 0 0 1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) - }) - - it('should generate styles using :not(.unknown-class) even if `.unknown-class` does not exist', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind components; - - @layer components { - div:not(.unknown-class) { - color: red; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - div:not(.unknown-class) { - color: red; - } - `) - }) - }) - - it('supports multiple backgrounds as arbitrary values even if only some are quoted', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[url\(\'\/images\/one-two-three\.png\'\)\,linear-gradient\(to_right\,_\#eeeeee\,_\#000000\)\] { - background-image: url('/images/one-two-three.png'), linear-gradient(to right, #eee, #000); - } - `) - }) - }) - - it('The "default" ring opacity is used by the default ring color when not using respectDefaultRingColorOpacity (1)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#3b82f680' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('The "default" ring opacity is used by the default ring color when not using respectDefaultRingColorOpacity (2)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringOpacity: { - DEFAULT: 0.75, - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#3b82f6bf' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('Customizing the default ring color uses the "default" opacity when not using respectDefaultRingColorOpacity (1)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringColor: { - DEFAULT: '#ff7f7f', - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#ff7f7f80' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('Customizing the default ring color uses the "default" opacity when not using respectDefaultRingColorOpacity (2)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringColor: { - DEFAULT: '#ff7f7f00', - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#ff7f7f80' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('The "default" ring color ignores the default opacity when using respectDefaultRingColorOpacity (1)', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#3b82f67f' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('The "default" ring color ignores the default opacity when using respectDefaultRingColorOpacity (2)', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringOpacity: { - DEFAULT: 0.75, - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#3b82f67f' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('Customizing the default ring color preserves its opacity when using respectDefaultRingColorOpacity (1)', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringColor: { - DEFAULT: '#ff7f7f', - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#ff7f7f' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('Customizing the default ring color preserves its opacity when using respectDefaultRingColorOpacity (2)', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringColor: { - DEFAULT: '#ff7f7f00', - }, - }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults({ defaultRingColor: '#ff7f7f00' })} - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - `) - }) - }) - - it('A bare ring-opacity utility is not supported when not using respectDefaultRingColorOpacity', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringOpacity: { - DEFAULT: '0.33', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css``) - }) - }) - - test('A bare ring-opacity utility is supported when using respectDefaultRingColorOpacity', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - theme: { - ringOpacity: { - DEFAULT: '0.33', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .ring-opacity { - --tw-ring-opacity: 0.33; - } - `) - // The opacity plugins are disabled by default in the `oxide` engine - oxide.expect(result.css).toMatchFormattedCss(css``) - }) - }) - - it('Ring color utilities are generated when using respectDefaultRingColorOpacity', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - .ring-blue-500 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - .ring-blue-500 { - --tw-ring-color: #3b82f6; - } - `) - }) - }) - - oxide.test.todo('should not crash when group names contain special characters') - stable.test('should not crash when group names contain special characters', () => { - let config = { - future: { respectDefaultRingColorOpacity: true }, - content: [ - { - raw: '
', - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .group\/\$\{id\}:hover .group-hover\/\$\{id\}\:visible { - visibility: visible; - } - `) - }) - }) - - it('should not crash when matching variants where utility classes are doubled up', () => { - let config = { - content: [ - { - raw: '
', - }, - ], - } - - let input = css` - @tailwind utilities; - @layer utilities { - .foo.foo { - text-decoration-line: underline; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .hover\:foo:hover.hover\:foo:hover { - text-decoration-line: underline; - } - `) - }) - }) - - test('detects quoted arbitrary values containing a slash', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - oxide.expect(result.css).toMatchFormattedCss(css` - .group[href^='/'] .group-\[\[href\^\=\'\/\'\]\]\:hidden { - display: none; - } - `) - - stable.expect(result.css).toMatchFormattedCss(css` - .hidden, - .group[href^='/'] .group-\[\[href\^\=\'\/\'\]\]\:hidden { - display: none; - } - `) - }) - - test('handled quoted arbitrary values containing escaped spaces', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - oxide.expect(result.css).toMatchFormattedCss(css` - .group[href^=' bar'] .group-\[\[href\^\=\'_bar\'\]\]\:hidden { - display: none; - } - `) - - stable.expect(result.css).toMatchFormattedCss(css` - .hidden, - .group[href^=' bar'] .group-\[\[href\^\=\'_bar\'\]\]\:hidden { - display: none; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-green-light { + --tw-bg-opacity: 1; + background-color: rgb(0 128 0 / var(--tw-bg-opacity)); } `) }) }) + +test('per-plugin colors with the same key can differ when using a custom colors object', () => { + let config = { + content: [ + { + raw: html` +
This should be green text on red background.
+ `, + }, + ], + theme: { + // colors & theme MUST be plain objects + // If they're functions here the test passes regardless + colors: { + theme: { + bg: 'red', + text: 'green', + }, + }, + extend: { + textColor: { + theme: { + DEFAULT: 'green', + }, + }, + backgroundColor: { + theme: { + DEFAULT: 'red', + }, + }, + }, + }, + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-theme { + --tw-bg-opacity: 1; + background-color: rgb(255 0 0 / var(--tw-bg-opacity)); + } + .text-theme { + --tw-text-opacity: 1; + color: rgb(0 128 0 / var(--tw-text-opacity)); + } + `) + }) +}) + +test('default ring color can be a function', () => { + function color(variable) { + return function ({ opacityVariable, opacityValue }) { + if (opacityValue !== undefined) { + return `rgba(${variable}, ${opacityValue})` + } + if (opacityVariable !== undefined) { + return `rgba(${variable}, var(${opacityVariable}, 1))` + } + return `rgb(${variable})` + } + } + + let config = { + content: [ + { + raw: html`
`, + }, + ], + + theme: { + extend: { + ringColor: { + DEFAULT: color('var(--red)'), + }, + }, + }, + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: 'rgba(var(--red), 0.5)' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('falsy config values still work', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + inset: { + 0: 0, + }, + }, + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .inset-0 { + inset: 0; + } + `) + }) +}) + +it('shadows support values without a leading zero', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + boxShadow: { + one: '0.5rem 0.5rem 0.5rem #0005', + two: '.5rem .5rem .5rem #0005', + }, + }, + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .shadow-one, + .shadow-two { + --tw-shadow: 0.5rem 0.5rem 0.5rem #0005; + --tw-shadow-colored: 0.5rem 0.5rem 0.5rem var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) + }) +}) + +it('can scan extremely long classes without crashing', () => { + let val = 'cols-' + '-a'.repeat(65536) + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css``) + }) +}) + +it('does not produce duplicate output when seeing variants preceding a wildcard (*)', () => { + let config = { + content: [{ raw: html`underline focus:*` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + * { + color: red; + } + + .combined, + * { + text-align: center; + } + + @layer base { + * { + color: blue; + } + + .combined, + * { + color: red; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + * { + color: #00f; + } + .combined, + * { + color: red; + } + ${defaults} + .underline { + text-decoration-line: underline; + } + * { + color: red; + } + .combined, + * { + text-align: center; + } + `) + }) +}) + +it('can parse box shadows with variables', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + boxShadow: { + lg: 'var(--a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0)', + }, + }, + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .shadow-lg { + --tw-shadow: var(--a, 0 35px 60px -15px #000), 0 0 1px #000; + --tw-shadow-colored: 0 35px 60px -15px var(--tw-shadow-color), + 0 0 1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) + }) +}) + +it('should generate styles using :not(.unknown-class) even if `.unknown-class` does not exist', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind components; + + @layer components { + div:not(.unknown-class) { + color: red; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + div:not(.unknown-class) { + color: red; + } + `) + }) +}) + +it('supports multiple backgrounds as arbitrary values even if only some are quoted', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[url\(\'\/images\/one-two-three\.png\'\)\,linear-gradient\(to_right\,_\#eeeeee\,_\#000000\)\] { + background-image: url('/images/one-two-three.png'), linear-gradient(to right, #eee, #000); + } + `) + }) +}) + +it('The "default" ring opacity is used by the default ring color when not using respectDefaultRingColorOpacity (1)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#3b82f680' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('The "default" ring opacity is used by the default ring color when not using respectDefaultRingColorOpacity (2)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringOpacity: { + DEFAULT: 0.75, + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#3b82f6bf' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('Customizing the default ring color uses the "default" opacity when not using respectDefaultRingColorOpacity (1)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringColor: { + DEFAULT: '#ff7f7f', + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#ff7f7f80' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('Customizing the default ring color uses the "default" opacity when not using respectDefaultRingColorOpacity (2)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringColor: { + DEFAULT: '#ff7f7f00', + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#ff7f7f80' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('The "default" ring color ignores the default opacity when using respectDefaultRingColorOpacity (1)', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#3b82f67f' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('The "default" ring color ignores the default opacity when using respectDefaultRingColorOpacity (2)', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringOpacity: { + DEFAULT: 0.75, + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#3b82f67f' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('Customizing the default ring color preserves its opacity when using respectDefaultRingColorOpacity (1)', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringColor: { + DEFAULT: '#ff7f7f', + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#ff7f7f' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('Customizing the default ring color preserves its opacity when using respectDefaultRingColorOpacity (2)', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringColor: { + DEFAULT: '#ff7f7f00', + }, + }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults({ defaultRingColor: '#ff7f7f00' })} + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + `) + }) +}) + +it('A bare ring-opacity utility is not supported when not using respectDefaultRingColorOpacity', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringOpacity: { + DEFAULT: '0.33', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css``) + }) +}) + +test('A bare ring-opacity utility is supported when using respectDefaultRingColorOpacity', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + theme: { + ringOpacity: { + DEFAULT: '0.33', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .ring-opacity { + --tw-ring-opacity: 0.33; + } + `) + }) +}) + +it('Ring color utilities are generated when using respectDefaultRingColorOpacity', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + .ring-blue-500 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); + } + `) + }) +}) + +test('should not crash when group names contain special characters', () => { + let config = { + future: { respectDefaultRingColorOpacity: true }, + content: [ + { + raw: '
', + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .group\/\$\{id\}:hover .group-hover\/\$\{id\}\:visible { + visibility: visible; + } + `) + }) +}) + +it('should not crash when matching variants where utility classes are doubled up', () => { + let config = { + content: [ + { + raw: '
', + }, + ], + } + + let input = css` + @tailwind utilities; + @layer utilities { + .foo.foo { + text-decoration-line: underline; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .hover\:foo:hover.hover\:foo:hover { + text-decoration-line: underline; + } + `) + }) +}) + +test('detects quoted arbitrary values containing a slash', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .hidden, + .group[href^='/'] .group-\[\[href\^\=\'\/\'\]\]\:hidden { + display: none; + } + `) +}) + +test('handled quoted arbitrary values containing escaped spaces', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .hidden, + .group[href^=' bar'] .group-\[\[href\^\=\'_bar\'\]\]\:hidden { + display: none; + } + `) +}) diff --git a/tests/blocklist.test.js b/tests/blocklist.test.js index 3642aa376..e9915350f 100644 --- a/tests/blocklist.test.js +++ b/tests/blocklist.test.js @@ -1,126 +1,107 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - it('can block classes matched literally', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - blocklist: ['font', 'uppercase', 'hover:text-sm', 'bg-red-500/50', 'my-custom-class'], +it('can block classes matched literally', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + blocklist: ['font', 'uppercase', 'hover:text-sm', 'bg-red-500/50', 'my-custom-class'], + } + + let input = css` + @tailwind utilities; + .my-custom-class { + color: red; } + ` - let input = css` - @tailwind utilities; + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } .my-custom-class { color: red; } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; + @media (min-width: 640px) { + .sm\:hover\:text-sm:hover { + font-size: 0.875rem; + line-height: 1.25rem; } - .my-custom-class { - color: red; - } - @media (min-width: 640px) { - .sm\:hover\:text-sm:hover { - font-size: 0.875rem; - line-height: 1.25rem; - } - } - `) - }) - }) - - it('can block classes inside @layer', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - blocklist: ['my-custom-class'], - } - - let input = css` - @tailwind utilities; - @layer utilities { - .my-custom-class { - color: red; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - `) - }) - }) - - it('blocklists do NOT support regexes', async () => { - let config = { - content: [{ raw: html`
` }], - blocklist: [/^bg-\[[^]+\]$/], - } - - let result = await run('@tailwind utilities', config) - - stable.expect(result.css).toMatchFormattedCss(css` - .bg-\[\#f00d1e\] { - --tw-bg-opacity: 1; - background-color: rgb(240 13 30 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; } `) - - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-\[\#f00d1e\] { - background-color: #f00d1e; - } - .font-bold { - font-weight: 700; - } - `) - - expect().toHaveBeenWarnedWith(['blocklist-invalid']) - }) - - it('can block classes generated by the safelist', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [{ pattern: /^bg-red-(400|500)$/ }], - blocklist: ['bg-red-500'], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-400 { - --tw-bg-opacity: 1; - background-color: rgb(248 113 113 / var(--tw-bg-opacity)); - } - .font-bold { - font-weight: 700; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-red-400 { - background-color: #f87171; - } - .font-bold { - font-weight: 700; - } - `) - }) + }) +}) + +it('can block classes inside @layer', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + blocklist: ['my-custom-class'], + } + + let input = css` + @tailwind utilities; + @layer utilities { + .my-custom-class { + color: red; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + `) + }) +}) + +it('blocklists do NOT support regexes', async () => { + let config = { + content: [{ raw: html`
` }], + blocklist: [/^bg-\[[^]+\]$/], + } + + let result = await run('@tailwind utilities', config) + + expect(result.css).toMatchFormattedCss(css` + .bg-\[\#f00d1e\] { + --tw-bg-opacity: 1; + background-color: rgb(240 13 30 / var(--tw-bg-opacity)); + } + .font-bold { + font-weight: 700; + } + `) + + expect().toHaveBeenWarnedWith(['blocklist-invalid']) +}) + +it('can block classes generated by the safelist', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [{ pattern: /^bg-red-(400|500)$/ }], + blocklist: ['bg-red-500'], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-400 { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); + } + .font-bold { + font-weight: 700; + } + `) }) }) diff --git a/tests/collapse-adjacent-rules.test.js b/tests/collapse-adjacent-rules.test.js index 4dbaab769..69c471686 100644 --- a/tests/collapse-adjacent-rules.test.js +++ b/tests/collapse-adjacent-rules.test.js @@ -1,114 +1,119 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('collapse adjacent rules', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [ - function ({ addVariant }) { - addVariant('foo-bar', '@supports (foo: bar)') - }, - ], +test('collapse adjacent rules', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [ + function ({ addVariant }) { + addVariant('foo-bar', '@supports (foo: bar)') + }, + ], + } + + let input = css` + @tailwind base; + @font-face { + font-family: 'Inter'; + src: url('/fonts/Inter.woff2') format('woff2'), url('/fonts/Inter.woff') format('woff'); } - - let input = css` - @tailwind base; + @font-face { + font-family: 'Gilroy'; + src: url('/fonts/Gilroy.woff2') format('woff2'), url('/fonts/Gilroy.woff') format('woff'); + } + @page { + margin: 1cm; + } + @tailwind components; + @tailwind utilities; + @layer base { @font-face { - font-family: 'Inter'; + font-family: 'Poppins'; + src: url('/fonts/Poppins.woff2') format('woff2'), url('/fonts/Poppins.woff') format('woff'); + } + @font-face { + font-family: 'Proxima Nova'; + src: url('/fonts/ProximaNova.woff2') format('woff2'), + url('/fonts/ProximaNova.woff') format('woff'); + } + } + .foo, + .bar { + color: black; + } + .foo, + .bar { + font-weight: 700; + } + .some-apply-thing { + @apply foo-bar:md:text-black foo-bar:md:font-bold foo-bar:text-black foo-bar:font-bold md:font-bold md:text-black; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @font-face { + font-family: Poppins; + src: url('/fonts/Poppins.woff2') format('woff2'), url('/fonts/Poppins.woff') format('woff'); + } + @font-face { + font-family: Proxima Nova; + src: url('/fonts/ProximaNova.woff2') format('woff2'), + url('/fonts/ProximaNova.woff') format('woff'); + } + ${defaults} + @font-face { + font-family: Inter; src: url('/fonts/Inter.woff2') format('woff2'), url('/fonts/Inter.woff') format('woff'); } @font-face { - font-family: 'Gilroy'; + font-family: Gilroy; src: url('/fonts/Gilroy.woff2') format('woff2'), url('/fonts/Gilroy.woff') format('woff'); } @page { margin: 1cm; } - @tailwind components; - @tailwind utilities; - @layer base { - @font-face { - font-family: 'Poppins'; - src: url('/fonts/Poppins.woff2') format('woff2'), - url('/fonts/Poppins.woff') format('woff'); - } - @font-face { - font-family: 'Proxima Nova'; - src: url('/fonts/ProximaNova.woff2') format('woff2'), - url('/fonts/ProximaNova.woff') format('woff'); - } - } - .foo, - .bar { - color: black; - } - .foo, - .bar { + .font-bold { font-weight: 700; } - .some-apply-thing { - @apply foo-bar:md:text-black foo-bar:md:font-bold foo-bar:text-black foo-bar:font-bold md:font-bold md:text-black; + .foo, + .bar { + color: #000; + font-weight: 700; } - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - @font-face { - font-family: Poppins; - src: url('/fonts/Poppins.woff2') format('woff2'), - url('/fonts/Poppins.woff') format('woff'); - } - @font-face { - font-family: Proxima Nova; - src: url('/fonts/ProximaNova.woff2') format('woff2'), - url('/fonts/ProximaNova.woff') format('woff'); - } - ${defaults} - @font-face { - font-family: Inter; - src: url('/fonts/Inter.woff2') format('woff2'), url('/fonts/Inter.woff') format('woff'); - } - @font-face { - font-family: Gilroy; - src: url('/fonts/Gilroy.woff2') format('woff2'), url('/fonts/Gilroy.woff') format('woff'); - } - @page { - margin: 1cm; - } - .font-bold { + @supports (foo: bar) { + .some-apply-thing { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); font-weight: 700; } - .foo, - .bar { - color: #000; + } + @media (min-width: 768px) { + .some-apply-thing { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); font-weight: 700; } - @supports (foo: bar) { - .some-apply-thing { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); - font-weight: 700; - } - } + } + @supports (foo: bar) { @media (min-width: 768px) { .some-apply-thing { --tw-text-opacity: 1; @@ -116,134 +121,49 @@ crosscheck(({ stable, oxide }) => { font-weight: 700; } } - @supports (foo: bar) { - @media (min-width: 768px) { - .some-apply-thing { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); - font-weight: 700; - } - } + } + @media (min-width: 640px) { + .sm\:text-center { + text-align: center; } - @media (min-width: 640px) { - .sm\:text-center { - text-align: center; - } - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:text-center { - text-align: center; - } - .md\:font-bold { - font-weight: 700; - } - } - @media (min-width: 1024px) { - .lg\:text-center { - text-align: center; - } - .lg\:font-bold { - font-weight: 700; - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - @font-face { - font-family: Poppins; - src: url('/fonts/Poppins.woff2') format('woff2'), - url('/fonts/Poppins.woff') format('woff'); - } - @font-face { - font-family: Proxima Nova; - src: url('/fonts/ProximaNova.woff2') format('woff2'), - url('/fonts/ProximaNova.woff') format('woff'); - } - ${defaults} - @font-face { - font-family: Inter; - src: url('/fonts/Inter.woff2') format('woff2'), url('/fonts/Inter.woff') format('woff'); - } - @font-face { - font-family: Gilroy; - src: url('/fonts/Gilroy.woff2') format('woff2'), url('/fonts/Gilroy.woff') format('woff'); - } - @page { - margin: 1cm; - } - .font-bold { + .sm\:font-bold { font-weight: 700; } - .foo, - .bar { - color: #000; + } + @media (min-width: 768px) { + .md\:text-center { + text-align: center; + } + .md\:font-bold { font-weight: 700; } - @supports (foo: bar) { - .some-apply-thing { - color: #000; - font-weight: 700; - } + } + @media (min-width: 1024px) { + .lg\:text-center { + text-align: center; } - @media (min-width: 768px) { - .some-apply-thing { - color: #000; - font-weight: 700; - } + .lg\:font-bold { + font-weight: 700; } - @supports (foo: bar) { - @media (min-width: 768px) { - .some-apply-thing { - color: #000; - font-weight: 700; - } - } - } - @media (min-width: 640px) { - .sm\:text-center { - text-align: center; - } - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:text-center { - text-align: center; - } - .md\:font-bold { - font-weight: 700; - } - } - @media (min-width: 1024px) { - .lg\:text-center { - text-align: center; - } - .lg\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - test('duplicate url imports does not break rule collapsing', () => { - let config = { - content: [{ raw: html`` }], - corePlugins: { preflight: false }, - } - - let input = css` - @import url('https://example.com'); - @import url('https://example.com'); - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @import 'https://example.com'; - `) - }) + } + `) + }) +}) + +test('duplicate url imports does not break rule collapsing', () => { + let config = { + content: [{ raw: html`` }], + corePlugins: { preflight: false }, + } + + let input = css` + @import url('https://example.com'); + @import url('https://example.com'); + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @import 'https://example.com'; + `) }) }) diff --git a/tests/collapse-duplicate-declarations.test.js b/tests/collapse-duplicate-declarations.test.js index 128eb9bef..dc8834b74 100644 --- a/tests/collapse-duplicate-declarations.test.js +++ b/tests/collapse-duplicate-declarations.test.js @@ -1,177 +1,175 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - it('should collapse duplicate declarations with the same units (px)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } +it('should collapse duplicate declarations with the same units (px)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } - let input = css` - @tailwind utilities; + let input = css` + @tailwind utilities; - @layer utilities { - .example { - height: 100px; - height: 200px; - } + @layer utilities { + .example { + height: 100px; + height: 200px; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .example { - height: 200px; - } - `) - }) - }) - - it('should collapse duplicate declarations with the same units (no unit)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], } + ` - let input = css` - @tailwind utilities; - - @layer utilities { - .example { - line-height: 3; - line-height: 2; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .example { + height: 200px; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .example { - line-height: 2; - } - `) - }) - }) - - it('should not collapse duplicate declarations with the different units', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - .example { - height: 100px; - height: 50%; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .example { - height: 100px; - height: 50%; - } - `) - }) - }) - - it('should collapse the duplicate declarations with the same unit, but leave the ones with different units', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - .example { - height: 100px; - height: 50%; - height: 20vh; - height: 200px; - height: 100%; - height: 30vh; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .example { - height: 200px; - height: 100%; - height: 30vh; - } - `) - }) - }) - - it('should collapse the duplicate declarations with the exact same value', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - .example { - height: var(--value); - color: blue; - height: var(--value); - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .example { - color: #00f; - height: var(--value); - } - `) - }) - }) - - it('should work on a real world example', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - .h-available { - height: 100%; - height: 100vh; - height: -webkit-fill-available; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .h-available { - height: 100%; - height: 100vh; - height: -webkit-fill-available; - } - `) - }) + `) + }) +}) + +it('should collapse duplicate declarations with the same units (no unit)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .example { + line-height: 3; + line-height: 2; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .example { + line-height: 2; + } + `) + }) +}) + +it('should not collapse duplicate declarations with the different units', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .example { + height: 100px; + height: 50%; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .example { + height: 100px; + height: 50%; + } + `) + }) +}) + +it('should collapse the duplicate declarations with the same unit, but leave the ones with different units', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .example { + height: 100px; + height: 50%; + height: 20vh; + height: 200px; + height: 100%; + height: 30vh; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .example { + height: 200px; + height: 100%; + height: 30vh; + } + `) + }) +}) + +it('should collapse the duplicate declarations with the exact same value', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .example { + height: var(--value); + color: blue; + height: var(--value); + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .example { + color: #00f; + height: var(--value); + } + `) + }) +}) + +it('should work on a real world example', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .h-available { + height: 100%; + height: 100vh; + height: -webkit-fill-available; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .h-available { + height: 100%; + height: 100vh; + height: -webkit-fill-available; + } + `) }) }) diff --git a/tests/color-opacity-modifiers.test.js b/tests/color-opacity-modifiers.test.js index 8da4d38c2..a4600a495 100644 --- a/tests/color-opacity-modifiers.test.js +++ b/tests/color-opacity-modifiers.test.js @@ -1,264 +1,234 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - test('basic color opacity modifier', async () => { - let config = { - content: [{ raw: html`
` }], - } +test('basic color opacity modifier', async () => { + let config = { + content: [{ raw: html`
` }], + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-red-500\/50 { - background-color: #ef444480; - } - `) - }) - }) - - test('colors with slashes are matched first', async () => { - let config = { - content: [{ raw: html`
` }], - theme: { - extend: { - colors: { - 'red-500/50': '#ff0000', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-500\/50 { - --tw-bg-opacity: 1; - background-color: rgb(255 0 0 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-red-500\/50 { - background-color: red; - } - `) - }) - }) - - test('arbitrary color opacity modifier', async () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-red-500\/\[var\(--opacity\)\] { - background-color: rgb(239 68 68 / var(--opacity)); - } - `) - }) - }) - - test('missing alpha generates nothing', async () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(``) - }) - }) - - test('arbitrary color with opacity from scale', async () => { - let config = { - content: [{ raw: 'bg-[wheat]/50' }], - theme: {}, - plugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[wheat\]\/50 { - background-color: #f5deb380; - } - `) - }) - }) - - test('arbitrary color with arbitrary opacity', async () => { - let config = { - content: [{ raw: 'bg-[#bada55]/[0.2]' }], - theme: {}, - plugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[\#bada55\]\/\[0\.2\] { - background-color: #bada5533; - } - `) - }) - }) - - test('undefined theme color with opacity from scale', async () => { - let config = { - content: [{ raw: 'bg-garbage/50' }], - theme: {}, - plugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(``) - }) - }) - - test('values not in the opacity config are ignored', async () => { - let config = { - content: [{ raw: html`
` }], - theme: { - opacity: { - 0: '0', - 25: '0.25', - 5: '0.5', - 75: '0.75', - 100: '1', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(``) - }) - }) - - test('function colors are supported', async () => { - let config = { - content: [{ raw: html`
` }], - theme: { - colors: { - blue: ({ opacityValue }) => { - return `rgba(var(--colors-blue), ${opacityValue})` - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-blue\/50 { - background-color: rgba(var(--colors-blue), 0.5); - } - `) - }) - }) - - test('utilities that support any type are supported', async () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - theme: { - extend: { - fill: (theme) => theme('colors'), - }, - }, - plugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .from-red-500\/50 { - --tw-gradient-from: #ef444480 var(--tw-gradient-from-position); - --tw-gradient-to: #ef444400 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .fill-red-500\/25 { - fill: #ef444440; - } - .placeholder-red-500\/75::placeholder { - color: #ef4444bf; - } - `) - }) - }) - - test('opacity modifier in combination with partial custom properties', async () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\] { - --tw-bg-opacity: 1; - background-color: hsl(123 50% var(--foo) / var(--tw-bg-opacity)); - } - .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\]\/50 { - background-color: hsl(123 50% var(--foo) / 0.5); - } - .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\] { - --tw-bg-opacity: 1; - background-color: hsl(123 var(--foo) 50% / var(--tw-bg-opacity)); - } - .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\]\/50 { - background-color: hsl(123 var(--foo) 50% / 0.5); - } - .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\] { - --tw-bg-opacity: 1; - background-color: hsl(var(--foo) 50% 50% / var(--tw-bg-opacity)); - } - .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\]\/50 { - background-color: hsl(var(--foo) 50% 50% / 0.5); - } - .bg-\[hsl\(var\(--foo\)\,var\(--bar\)\,var\(--baz\)\)\]\/50 { - background-color: hsl(var(--foo) var(--bar) var(--baz) / 0.5); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\] { - background-color: hsl(123, 50%, var(--foo)); - } - .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\]\/50 { - background-color: hsl(123 50% var(--foo) / 0.5); - } - .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\] { - background-color: hsl(123, var(--foo), 50%); - } - .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\]\/50 { - background-color: hsl(123 var(--foo) 50% / 0.5); - } - .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\] { - background-color: hsl(var(--foo), 50%, 50%); - } - .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\]\/50 { - background-color: hsl(var(--foo) 50% 50% / 0.5); - } - .bg-\[hsl\(var\(--foo\)\,var\(--bar\)\,var\(--baz\)\)\]\/50 { - background-color: hsl(var(--foo) var(--bar) var(--baz) / 0.5); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-500\/50 { + background-color: #ef444480; + } + `) + }) +}) + +test('colors with slashes are matched first', async () => { + let config = { + content: [{ raw: html`
` }], + theme: { + extend: { + colors: { + 'red-500/50': '#ff0000', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-500\/50 { + --tw-bg-opacity: 1; + background-color: rgb(255 0 0 / var(--tw-bg-opacity)); + } + `) + }) +}) + +test('arbitrary color opacity modifier', async () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-500\/\[var\(--opacity\)\] { + background-color: rgb(239 68 68 / var(--opacity)); + } + `) + }) +}) + +test('missing alpha generates nothing', async () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(``) + }) +}) + +test('arbitrary color with opacity from scale', async () => { + let config = { + content: [{ raw: 'bg-[wheat]/50' }], + theme: {}, + plugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[wheat\]\/50 { + background-color: #f5deb380; + } + `) + }) +}) + +test('arbitrary color with arbitrary opacity', async () => { + let config = { + content: [{ raw: 'bg-[#bada55]/[0.2]' }], + theme: {}, + plugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[\#bada55\]\/\[0\.2\] { + background-color: #bada5533; + } + `) + }) +}) + +test('undefined theme color with opacity from scale', async () => { + let config = { + content: [{ raw: 'bg-garbage/50' }], + theme: {}, + plugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(``) + }) +}) + +test('values not in the opacity config are ignored', async () => { + let config = { + content: [{ raw: html`
` }], + theme: { + opacity: { + 0: '0', + 25: '0.25', + 5: '0.5', + 75: '0.75', + 100: '1', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(``) + }) +}) + +test('function colors are supported', async () => { + let config = { + content: [{ raw: html`
` }], + theme: { + colors: { + blue: ({ opacityValue }) => { + return `rgba(var(--colors-blue), ${opacityValue})` + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-blue\/50 { + background-color: rgba(var(--colors-blue), 0.5); + } + `) + }) +}) + +test('utilities that support any type are supported', async () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + theme: { + extend: { + fill: (theme) => theme('colors'), + }, + }, + plugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .from-red-500\/50 { + --tw-gradient-from: #ef444480 var(--tw-gradient-from-position); + --tw-gradient-to: #ef444400 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .fill-red-500\/25 { + fill: #ef444440; + } + .placeholder-red-500\/75::placeholder { + color: #ef4444bf; + } + `) + }) +}) + +test('opacity modifier in combination with partial custom properties', async () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\] { + --tw-bg-opacity: 1; + background-color: hsl(123 50% var(--foo) / var(--tw-bg-opacity)); + } + .bg-\[hsl\(123\,50\%\,var\(--foo\)\)\]\/50 { + background-color: hsl(123 50% var(--foo) / 0.5); + } + .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\] { + --tw-bg-opacity: 1; + background-color: hsl(123 var(--foo) 50% / var(--tw-bg-opacity)); + } + .bg-\[hsl\(123\,var\(--foo\)\,50\%\)\]\/50 { + background-color: hsl(123 var(--foo) 50% / 0.5); + } + .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\] { + --tw-bg-opacity: 1; + background-color: hsl(var(--foo) 50% 50% / var(--tw-bg-opacity)); + } + .bg-\[hsl\(var\(--foo\)\,50\%\,50\%\)\]\/50 { + background-color: hsl(var(--foo) 50% 50% / 0.5); + } + .bg-\[hsl\(var\(--foo\)\,var\(--bar\)\,var\(--baz\)\)\]\/50 { + background-color: hsl(var(--foo) var(--bar) var(--baz) / 0.5); + } + `) }) }) diff --git a/tests/color.test.js b/tests/color.test.js index be1d85924..659a2d045 100644 --- a/tests/color.test.js +++ b/tests/color.test.js @@ -1,96 +1,93 @@ import { parseColor, formatColor } from '../src/util/color' -import { crosscheck } from './util/run' -crosscheck(() => { - describe('parseColor', () => { - it.each` - color | output - ${'black'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }} - ${'#0088cc'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} - ${'#08c'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} - ${'#0088cc99'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} - ${'#08c9'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} - ${'rgb(0, 30, 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }} - ${'rgba(0, 30, 60, 0.5)'} | ${{ mode: 'rgba', color: ['0', '30', '60'], alpha: '0.5' }} - ${'rgb(0 30 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }} - ${'rgb(0 30 60 / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }} - ${'rgb(var(--foo), 30, 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }} - ${'rgb(0, var(--foo), 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }} - ${'rgb(0, 30, var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }} - ${'rgb(0, 30, var(--foo), 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }} - ${'rgb(var(--foo), 30, var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }} - ${'rgb(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} - ${'rgb(var(--foo) 30 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }} - ${'rgb(0 var(--foo) 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }} - ${'rgb(0 30 var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }} - ${'rgb(0 30 var(--foo) / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }} - ${'rgb(var(--foo) 30 var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }} - ${'rgb(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} - ${'hsl(0, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }} - ${'hsl(0deg, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }} - ${'hsl(0rad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }} - ${'hsl(0grad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }} - ${'hsl(0turn, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }} - ${'hsla(0, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0', '30%', '60%'], alpha: '0.5' }} - ${'hsla(0deg, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0deg', '30%', '60%'], alpha: '0.5' }} - ${'hsla(0rad, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0rad', '30%', '60%'], alpha: '0.5' }} - ${'hsla(0grad, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0grad', '30%', '60%'], alpha: '0.5' }} - ${'hsla(0turn, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0turn', '30%', '60%'], alpha: '0.5' }} - ${'hsl(0 30% 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }} - ${'hsl(0deg 30% 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }} - ${'hsl(0rad 30% 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }} - ${'hsl(0grad 30% 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }} - ${'hsl(0turn 30% 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }} - ${'hsl(0 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }} - ${'hsl(0deg 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }} - ${'hsl(0rad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }} - ${'hsl(0grad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }} - ${'hsl(0turn 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }} - ${'hsl(var(--foo), 30%, 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }} - ${'hsl(0, var(--foo), 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }} - ${'hsl(0, 30%, var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }} - ${'hsl(0, 30%, var(--foo), 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }} - ${'hsl(var(--foo), 30%, var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }} - ${'hsl(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} - ${'hsl(var(--foo) 30% 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }} - ${'hsl(0 var(--foo) 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }} - ${'hsl(0 30% var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }} - ${'hsl(0 30% var(--foo) / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }} - ${'hsl(var(--foo) 30% var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }} - ${'hsl(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} - ${'rgba(var(--foo), 0.1)'} | ${{ mode: 'rgba', color: ['var(--foo)'], alpha: '0.1' }} - ${'rgba(var(--foo), var(--alpha))'} | ${{ mode: 'rgba', color: ['var(--foo)'], alpha: 'var(--alpha)' }} - ${'hsla(var(--foo), 0.1)'} | ${{ mode: 'hsla', color: ['var(--foo)'], alpha: '0.1' }} - ${'hsla(var(--foo), var(--alpha))'} | ${{ mode: 'hsla', color: ['var(--foo)'], alpha: 'var(--alpha)' }} - ${'transparent'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }} - `('should parse "$color" to the correct value', ({ color, output }) => { - expect(parseColor(color)).toEqual(output) - }) - - it.each` - color - ${'var(--my-color)'} - ${'currentColor'} - ${'inherit'} - ${'initial'} - ${'revert'} - ${'unset'} - `('should return `null` for unparseable color "$color"', ({ color }) => { - expect(parseColor(color)).toBe(null) - }) +describe('parseColor', () => { + it.each` + color | output + ${'black'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }} + ${'#0088cc'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} + ${'#08c'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} + ${'#0088cc99'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} + ${'#08c9'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} + ${'rgb(0, 30, 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }} + ${'rgba(0, 30, 60, 0.5)'} | ${{ mode: 'rgba', color: ['0', '30', '60'], alpha: '0.5' }} + ${'rgb(0 30 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }} + ${'rgb(0 30 60 / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }} + ${'rgb(var(--foo), 30, 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }} + ${'rgb(0, var(--foo), 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }} + ${'rgb(0, 30, var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }} + ${'rgb(0, 30, var(--foo), 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }} + ${'rgb(var(--foo), 30, var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }} + ${'rgb(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} + ${'rgb(var(--foo) 30 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }} + ${'rgb(0 var(--foo) 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }} + ${'rgb(0 30 var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }} + ${'rgb(0 30 var(--foo) / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }} + ${'rgb(var(--foo) 30 var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }} + ${'rgb(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} + ${'hsl(0, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }} + ${'hsl(0deg, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }} + ${'hsl(0rad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }} + ${'hsl(0grad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }} + ${'hsl(0turn, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }} + ${'hsla(0, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0', '30%', '60%'], alpha: '0.5' }} + ${'hsla(0deg, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0deg', '30%', '60%'], alpha: '0.5' }} + ${'hsla(0rad, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0rad', '30%', '60%'], alpha: '0.5' }} + ${'hsla(0grad, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0grad', '30%', '60%'], alpha: '0.5' }} + ${'hsla(0turn, 30%, 60%, 0.5)'} | ${{ mode: 'hsla', color: ['0turn', '30%', '60%'], alpha: '0.5' }} + ${'hsl(0 30% 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }} + ${'hsl(0deg 30% 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }} + ${'hsl(0rad 30% 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }} + ${'hsl(0grad 30% 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }} + ${'hsl(0turn 30% 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }} + ${'hsl(0 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }} + ${'hsl(0deg 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }} + ${'hsl(0rad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }} + ${'hsl(0grad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }} + ${'hsl(0turn 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }} + ${'hsl(var(--foo), 30%, 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }} + ${'hsl(0, var(--foo), 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }} + ${'hsl(0, 30%, var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }} + ${'hsl(0, 30%, var(--foo), 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }} + ${'hsl(var(--foo), 30%, var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }} + ${'hsl(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} + ${'hsl(var(--foo) 30% 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }} + ${'hsl(0 var(--foo) 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }} + ${'hsl(0 30% var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }} + ${'hsl(0 30% var(--foo) / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }} + ${'hsl(var(--foo) 30% var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }} + ${'hsl(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }} + ${'rgba(var(--foo), 0.1)'} | ${{ mode: 'rgba', color: ['var(--foo)'], alpha: '0.1' }} + ${'rgba(var(--foo), var(--alpha))'} | ${{ mode: 'rgba', color: ['var(--foo)'], alpha: 'var(--alpha)' }} + ${'hsla(var(--foo), 0.1)'} | ${{ mode: 'hsla', color: ['var(--foo)'], alpha: '0.1' }} + ${'hsla(var(--foo), var(--alpha))'} | ${{ mode: 'hsla', color: ['var(--foo)'], alpha: 'var(--alpha)' }} + ${'transparent'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }} + `('should parse "$color" to the correct value', ({ color, output }) => { + expect(parseColor(color)).toEqual(output) }) - describe('formatColor', () => { - it.each` - color | output - ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }} | ${'rgb(0 0 0)'} - ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} | ${'rgb(0 136 204)'} - ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} | ${'rgb(0 136 204 / 0.6)'} - ${{ mode: 'hsl', color: ['0', '0%', '0%'], alpha: undefined }} | ${'hsl(0 0% 0%)'} - ${{ mode: 'hsl', color: ['0', '136%', '204%'], alpha: undefined }} | ${'hsl(0 136% 204%)'} - ${{ mode: 'hsl', color: ['0', '136%', '204%'], alpha: '0.6' }} | ${'hsl(0 136% 204% / 0.6)'} - `('should format the color pieces into a proper "$output"', ({ color, output }) => { - expect(formatColor(color)).toEqual(output) - }) + it.each` + color + ${'var(--my-color)'} + ${'currentColor'} + ${'inherit'} + ${'initial'} + ${'revert'} + ${'unset'} + `('should return `null` for unparseable color "$color"', ({ color }) => { + expect(parseColor(color)).toBe(null) + }) +}) + +describe('formatColor', () => { + it.each` + color | output + ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }} | ${'rgb(0 0 0)'} + ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }} | ${'rgb(0 136 204)'} + ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }} | ${'rgb(0 136 204 / 0.6)'} + ${{ mode: 'hsl', color: ['0', '0%', '0%'], alpha: undefined }} | ${'hsl(0 0% 0%)'} + ${{ mode: 'hsl', color: ['0', '136%', '204%'], alpha: undefined }} | ${'hsl(0 136% 204%)'} + ${{ mode: 'hsl', color: ['0', '136%', '204%'], alpha: '0.6' }} | ${'hsl(0 136% 204% / 0.6)'} + `('should format the color pieces into a proper "$output"', ({ color, output }) => { + expect(formatColor(color)).toEqual(output) }) }) diff --git a/tests/combined-selectors.test.js b/tests/combined-selectors.test.js index 51aa0a8ce..b3e8a333f 100644 --- a/tests/combined-selectors.test.js +++ b/tests/combined-selectors.test.js @@ -1,76 +1,74 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - it('should generate the partial selector, if only a partial is used (base layer)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +it('should generate the partial selector, if only a partial is used (base layer)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; + let input = css` + @tailwind base; - @layer base { - :root { - font-weight: bold; - } - - /* --- */ - - :root, - .a { - color: black; - } + @layer base { + :root { + font-weight: bold; } - ` - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - :root { - font-weight: bold; - } - :root, - .a { - color: #000; - } - ${defaults} - `) - }) - }) + /* --- */ - it('should generate the partial selector, if only a partial is used (utilities layer)', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - - @layer utilities { - :root { - font-weight: bold; - } - - /* --- */ - - :root, - .a { - color: black; - } + :root, + .a { + color: black; } - ` + } + ` - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - :root { - font-weight: bold; - } - :root, - .a { - color: #000; - } - `) - }) + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + :root { + font-weight: bold; + } + :root, + .a { + color: #000; + } + ${defaults} + `) + }) +}) + +it('should generate the partial selector, if only a partial is used (utilities layer)', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + + @layer utilities { + :root { + font-weight: bold; + } + + /* --- */ + + :root, + .a { + color: black; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + :root { + font-weight: bold; + } + :root, + .a { + color: #000; + } + `) }) }) diff --git a/tests/configurePlugins.test.js b/tests/configurePlugins.test.js index 367ab223f..490cddc03 100644 --- a/tests/configurePlugins.test.js +++ b/tests/configurePlugins.test.js @@ -1,28 +1,25 @@ import configurePlugins from '../src/util/configurePlugins' -import { crosscheck } from './util/run' -crosscheck(() => { - test('setting a plugin to false removes it', () => { - const plugins = ['fontSize', 'display', 'backgroundPosition'] +test('setting a plugin to false removes it', () => { + const plugins = ['fontSize', 'display', 'backgroundPosition'] - const configuredPlugins = configurePlugins({ display: false }, plugins) + const configuredPlugins = configurePlugins({ display: false }, plugins) - expect(configuredPlugins).toEqual(['fontSize', 'backgroundPosition']) - }) - - test('passing only false removes all plugins', () => { - const plugins = ['fontSize', 'display', 'backgroundPosition'] - - const configuredPlugins = configurePlugins(false, plugins) - - expect(configuredPlugins).toEqual([]) - }) - - test('passing an array safelists plugins', () => { - const plugins = ['fontSize', 'display', 'backgroundPosition'] - - const configuredPlugins = configurePlugins(['display'], plugins) - - expect(configuredPlugins).toEqual(['display']) - }) + expect(configuredPlugins).toEqual(['fontSize', 'backgroundPosition']) +}) + +test('passing only false removes all plugins', () => { + const plugins = ['fontSize', 'display', 'backgroundPosition'] + + const configuredPlugins = configurePlugins(false, plugins) + + expect(configuredPlugins).toEqual([]) +}) + +test('passing an array safelists plugins', () => { + const plugins = ['fontSize', 'display', 'backgroundPosition'] + + const configuredPlugins = configurePlugins(['display'], plugins) + + expect(configuredPlugins).toEqual(['display']) }) diff --git a/tests/containerPlugin.test.js b/tests/containerPlugin.test.js index 428a807d0..4b334e90c 100644 --- a/tests/containerPlugin.test.js +++ b/tests/containerPlugin.test.js @@ -1,347 +1,345 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({}) => { - test('options are not required', () => { - let config = { content: [{ raw: html`
` }] } +test('options are not required', () => { + let config = { content: [{ raw: html`
` }] } - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + } + @media (min-width: 640px) { .container { - width: 100%; + max-width: 640px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - `) - }) - }) - - test('screens can be passed explicitly', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - screens: ['400px', '500px'], - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + } + @media (min-width: 768px) { .container { - width: 100%; + max-width: 768px; } - @media (min-width: 400px) { - .container { - max-width: 400px; - } - } - @media (min-width: 500px) { - .container { - max-width: 500px; - } - } - `) - }) - }) - - test('screens are ordered ascending by min-width', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - screens: ['500px', '400px'], - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + } + @media (min-width: 1024px) { .container { - width: 100%; + max-width: 1024px; } - @media (min-width: 400px) { - .container { - max-width: 400px; - } - } - @media (min-width: 500px) { - .container { - max-width: 500px; - } - } - `) - }) - }) - - test('screens are deduplicated by min-width', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - screens: { - sm: '576px', - md: '768px', - 'sm-only': { min: '576px', max: '767px' }, - }, - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + } + @media (min-width: 1280px) { .container { - width: 100%; + max-width: 1280px; } - @media (min-width: 576px) { - .container { - max-width: 576px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - `) - }) - }) - - test('the container can be centered by default', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - center: true, - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + } + @media (min-width: 1536px) { .container { - width: 100%; - margin-left: auto; - margin-right: auto; + max-width: 1536px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - `) - }) - }) - - test('horizontal padding can be included by default', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - padding: '2rem', - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .container { - width: 100%; - padding-left: 2rem; - padding-right: 2rem; - } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - `) - }) - }) - - test('responsive horizontal padding can be included by default', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - screens: { - sm: '576px', - md: { min: '768px' }, - lg: { 'min-width': '992px' }, - xl: { min: '1200px', max: '1600px' }, - }, - container: { - padding: { - DEFAULT: '1rem', - sm: '2rem', - lg: '4rem', - xl: '5rem', - }, - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .container { - width: 100%; - padding-left: 1rem; - padding-right: 1rem; - } - @media (min-width: 576px) { - .container { - max-width: 576px; - padding-left: 2rem; - padding-right: 2rem; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 992px) { - .container { - max-width: 992px; - padding-left: 4rem; - padding-right: 4rem; - } - } - @media (min-width: 1200px) { - .container { - max-width: 1200px; - padding-left: 5rem; - padding-right: 5rem; - } - } - `) - }) - }) - - test('setting all options at once', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - screens: ['400px', '500px'], - center: true, - padding: '2rem', - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .container { - width: 100%; - margin-left: auto; - margin-right: auto; - padding-left: 2rem; - padding-right: 2rem; - } - @media (min-width: 400px) { - .container { - max-width: 400px; - } - } - @media (min-width: 500px) { - .container { - max-width: 500px; - } - } - `) - }) - }) - - test('container can use variants', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - container: { - screens: ['400px', '500px'], - }, - }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 1024px) { - .lg\:hover\:container:hover { - width: 100%; - } - @media (min-width: 400px) { - .lg\:hover\:container:hover { - max-width: 400px; - } - } - @media (min-width: 500px) { - .lg\:hover\:container:hover { - max-width: 500px; - } - } - } - `) - }) + } + `) + }) +}) + +test('screens can be passed explicitly', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + screens: ['400px', '500px'], + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + } + @media (min-width: 400px) { + .container { + max-width: 400px; + } + } + @media (min-width: 500px) { + .container { + max-width: 500px; + } + } + `) + }) +}) + +test('screens are ordered ascending by min-width', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + screens: ['500px', '400px'], + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + } + @media (min-width: 400px) { + .container { + max-width: 400px; + } + } + @media (min-width: 500px) { + .container { + max-width: 500px; + } + } + `) + }) +}) + +test('screens are deduplicated by min-width', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + screens: { + sm: '576px', + md: '768px', + 'sm-only': { min: '576px', max: '767px' }, + }, + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + } + @media (min-width: 576px) { + .container { + max-width: 576px; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + `) + }) +}) + +test('the container can be centered by default', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + center: true, + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + margin-left: auto; + margin-right: auto; + } + @media (min-width: 640px) { + .container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; + } + } + `) + }) +}) + +test('horizontal padding can be included by default', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + padding: '2rem', + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + padding-left: 2rem; + padding-right: 2rem; + } + @media (min-width: 640px) { + .container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; + } + } + `) + }) +}) + +test('responsive horizontal padding can be included by default', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + screens: { + sm: '576px', + md: { min: '768px' }, + lg: { 'min-width': '992px' }, + xl: { min: '1200px', max: '1600px' }, + }, + container: { + padding: { + DEFAULT: '1rem', + sm: '2rem', + lg: '4rem', + xl: '5rem', + }, + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + padding-left: 1rem; + padding-right: 1rem; + } + @media (min-width: 576px) { + .container { + max-width: 576px; + padding-left: 2rem; + padding-right: 2rem; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 992px) { + .container { + max-width: 992px; + padding-left: 4rem; + padding-right: 4rem; + } + } + @media (min-width: 1200px) { + .container { + max-width: 1200px; + padding-left: 5rem; + padding-right: 5rem; + } + } + `) + }) +}) + +test('setting all options at once', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + screens: ['400px', '500px'], + center: true, + padding: '2rem', + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 2rem; + padding-right: 2rem; + } + @media (min-width: 400px) { + .container { + max-width: 400px; + } + } + @media (min-width: 500px) { + .container { + max-width: 500px; + } + } + `) + }) +}) + +test('container can use variants', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + container: { + screens: ['400px', '500px'], + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 1024px) { + .lg\:hover\:container:hover { + width: 100%; + } + @media (min-width: 400px) { + .lg\:hover\:container:hover { + max-width: 400px; + } + } + @media (min-width: 500px) { + .lg\:hover\:container:hover { + max-width: 500px; + } + } + } + `) }) }) diff --git a/tests/context-reuse.test.js b/tests/context-reuse.test.js index 4ead43fd4..7cd006cde 100644 --- a/tests/context-reuse.test.js +++ b/tests/context-reuse.test.js @@ -1,4 +1,4 @@ -import { crosscheck, css } from './util/run' +import { css } from './util/run' const fs = require('fs') const path = require('path') @@ -7,126 +7,122 @@ const tailwind = require('../src/index.js') const sharedState = require('../src/lib/sharedState.js') const configPath = path.resolve(__dirname, './context-reuse.tailwind.config.js') -crosscheck(({ stable, oxide }) => { - function run(input, config = {}, from = null) { - let { currentTestName } = expect.getState() +function run(input, config = {}, from = null) { + let { currentTestName } = expect.getState() - from = `${path.resolve(__filename)}?test=${currentTestName}&${from}` + from = `${path.resolve(__filename)}?test=${currentTestName}&${from}` - return postcss(tailwind(config)).process(input, { from }) + return postcss(tailwind(config)).process(input, { from }) +} + +beforeEach(async () => { + let config = { + content: [path.resolve(__dirname, './context-reuse.test.html')], + corePlugins: { preflight: false }, } - beforeEach(async () => { - let config = { - content: [path.resolve(__dirname, './context-reuse.test.html')], - corePlugins: { preflight: false }, + await fs.promises.writeFile(configPath, `module.exports = ${JSON.stringify(config)};`) +}) + +afterEach(async () => { + await fs.promises.unlink(configPath) +}) + +test('re-uses the context across multiple files with the same config', async () => { + let results = [ + await run(`@tailwind utilities;`, configPath, `id=1`), + + // Using @apply directives should still re-use the context + // They depend on the config but do not the other way around + await run(`body { @apply bg-blue-400; }`, configPath, `id=2`), + await run(`body { @apply text-red-400; }`, configPath, `id=3`), + await run(`body { @apply mb-4; }`, configPath, `id=4`), + ] + + let dependencies = results.map((result) => { + return result.messages + .filter((message) => message.type === 'dependency') + .map((message) => message.file) + }) + + // The content files don't have any utilities in them so this should be empty + expect(results[0].css).toMatchFormattedCss(css``) + + // However, @apply is being used so we want to verify that they're being inlined into the CSS rules + expect(results[1].css).toMatchFormattedCss(css` + body { + --tw-bg-opacity: 1; + background-color: rgb(96 165 250 / var(--tw-bg-opacity)); } + `) - await fs.promises.writeFile(configPath, `module.exports = ${JSON.stringify(config)};`) - }) + expect(results[2].css).toMatchFormattedCss(css` + body { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); + } + `) - afterEach(async () => { - await fs.promises.unlink(configPath) - }) + expect(results[3].css).toMatchFormattedCss(css` + body { + margin-bottom: 1rem; + } + `) - oxide.test.todo('re-uses the context across multiple files with the same config') - stable.test('re-uses the context across multiple files with the same config', async () => { - let results = [ - await run(`@tailwind utilities;`, configPath, `id=1`), + // Files with @tailwind directives depends on the PostCSS tree, config, AND any content files + expect(dependencies[0]).toEqual([ + path.resolve(__dirname, 'context-reuse.test.html'), + path.resolve(__dirname, 'context-reuse.tailwind.config.js'), + ]) - // Using @apply directives should still re-use the context - // They depend on the config but do not the other way around - await run(`body { @apply bg-blue-400; }`, configPath, `id=2`), - await run(`body { @apply text-red-400; }`, configPath, `id=3`), - await run(`body { @apply mb-4; }`, configPath, `id=4`), - ] + // @apply depends only on the containing PostCSS tree *and* the config file but no content files + // as they cannot affect the outcome of the @apply directives + expect(dependencies[1]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - let dependencies = results.map((result) => { - return result.messages - .filter((message) => message.type === 'dependency') - .map((message) => message.file) - }) + expect(dependencies[2]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - // The content files don't have any utilities in them so this should be empty - expect(results[0].css).toMatchFormattedCss(css``) + expect(dependencies[3]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - // However, @apply is being used so we want to verify that they're being inlined into the CSS rules - expect(results[1].css).toMatchFormattedCss(css` - body { - --tw-bg-opacity: 1; - background-color: rgb(96 165 250 / var(--tw-bg-opacity)); - } - `) + // And none of this should have resulted in multiple contexts being created + expect(sharedState.contextSourcesMap.size).toBe(1) +}) - expect(results[2].css).toMatchFormattedCss(css` - body { - --tw-text-opacity: 1; - color: rgb(248 113 113 / var(--tw-text-opacity)); - } - `) +test('updates layers when any CSS containing @tailwind directives changes', async () => { + let result - expect(results[3].css).toMatchFormattedCss(css` - body { - margin-bottom: 1rem; - } - `) - - // Files with @tailwind directives depends on the PostCSS tree, config, AND any content files - expect(dependencies[0]).toEqual([ - path.resolve(__dirname, 'context-reuse.test.html'), - path.resolve(__dirname, 'context-reuse.tailwind.config.js'), - ]) - - // @apply depends only on the containing PostCSS tree *and* the config file but no content files - // as they cannot affect the outcome of the @apply directives - expect(dependencies[1]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - - expect(dependencies[2]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - - expect(dependencies[3]).toEqual([path.resolve(__dirname, 'context-reuse.tailwind.config.js')]) - - // And none of this should have resulted in multiple contexts being created - expect(sharedState.contextSourcesMap.size).toBe(1) - }) - - oxide.test.todo('updates layers when any CSS containing @tailwind directives changes') - stable.test('updates layers when any CSS containing @tailwind directives changes', async () => { - let result - - // Compile the initial version once - let input = css` - @tailwind utilities; - @layer utilities { - .custom-utility { - color: orange; - } - } - ` - - result = await run(input, configPath, `id=1`) - - expect(result.css).toMatchFormattedCss(css` - .only\:custom-utility:only-child { + // Compile the initial version once + let input = css` + @tailwind utilities; + @layer utilities { + .custom-utility { color: orange; } - `) + } + ` - // Save the file with a change - input = css` - @tailwind utilities; - @layer utilities { - .custom-utility { - color: blue; - } + result = await run(input, configPath, `id=1`) + + expect(result.css).toMatchFormattedCss(css` + .only\:custom-utility:only-child { + color: orange; + } + `) + + // Save the file with a change + input = css` + @tailwind utilities; + @layer utilities { + .custom-utility { + color: blue; } - ` + } + ` - result = await run(input, configPath, `id=1`) + result = await run(input, configPath, `id=1`) - expect(result.css).toMatchFormattedCss(css` - .only\:custom-utility:only-child { - color: #00f; - } - `) - }) + expect(result.css).toMatchFormattedCss(css` + .only\:custom-utility:only-child { + color: #00f; + } + `) }) diff --git a/tests/custom-extractors.test.js b/tests/custom-extractors.test.js index 089084e77..f5933b7a5 100644 --- a/tests/custom-extractors.test.js +++ b/tests/custom-extractors.test.js @@ -1,4 +1,4 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' function customExtractor(content) { let matches = content.match(/class="([^"]+)"/) @@ -21,117 +21,109 @@ let expected = css` } ` -crosscheck(({ stable, oxide }) => { - describe('modern', () => { - oxide.test.todo('extract.DEFAULT') - stable.test('extract.DEFAULT', () => { - let config = { - content: { - files: [{ raw: sharedHtml }], - extract: { - DEFAULT: customExtractor, - }, +describe('modern', () => { + test('extract.DEFAULT', () => { + let config = { + content: { + files: [{ raw: sharedHtml }], + extract: { + DEFAULT: customExtractor, }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(expected) - }) - }) - - oxide.test.todo('extract.{extension}') - stable.test('extract.{extension}', () => { - let config = { - content: { - files: [{ raw: sharedHtml }], - extract: { - html: customExtractor, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(expected) - }) - }) - - oxide.test.todo('extract function') - stable.test('extract function', () => { - let config = { - content: { - files: [{ raw: sharedHtml }], - extract: customExtractor, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(expected) - }) - }) - - oxide.test.todo('raw content with extension') - stable.test('raw content with extension', () => { - let config = { - content: { - files: [ - { - raw: sharedHtml, - extension: 'html', - }, - ], - extract: { - html: () => ['invisible'], - }, - }, - corePlugins: { preflight: false }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .invisible { - visibility: hidden; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(expected) }) }) - describe('legacy', () => { - oxide.test.todo('defaultExtractor') - stable.test('defaultExtractor', () => { - let config = { - content: { - files: [{ raw: sharedHtml }], - options: { - defaultExtractor: customExtractor, - }, + test('extract.{extension}', () => { + let config = { + content: { + files: [{ raw: sharedHtml }], + extract: { + html: customExtractor, }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(expected) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(expected) }) + }) - oxide.test.todo('extractors array') - stable.test('extractors array', () => { - let config = { - content: { - files: [{ raw: sharedHtml }], - options: { - extractors: [ - { - extractor: customExtractor, - extensions: ['html'], - }, - ], + test('extract function', () => { + let config = { + content: { + files: [{ raw: sharedHtml }], + extract: customExtractor, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(expected) + }) + }) + + test('raw content with extension', () => { + let config = { + content: { + files: [ + { + raw: sharedHtml, + extension: 'html', }, + ], + extract: { + html: () => ['invisible'], }, - } + }, + corePlugins: { preflight: false }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(expected) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .invisible { + visibility: hidden; + } + `) + }) + }) +}) + +describe('legacy', () => { + test('defaultExtractor', () => { + let config = { + content: { + files: [{ raw: sharedHtml }], + options: { + defaultExtractor: customExtractor, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(expected) + }) + }) + + test('extractors array', () => { + let config = { + content: { + files: [{ raw: sharedHtml }], + options: { + extractors: [ + { + extractor: customExtractor, + extensions: ['html'], + }, + ], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(expected) }) }) }) diff --git a/tests/custom-plugins.test.js b/tests/custom-plugins.test.js index d91843199..0f45e9823 100644 --- a/tests/custom-plugins.test.js +++ b/tests/custom-plugins.test.js @@ -1,18 +1,1132 @@ import createPlugin from '../src/public/create-plugin' -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - test('plugins can create utilities with object syntax', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addUtilities }) { +test('plugins can create utilities with object syntax', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addUtilities }) { + addUtilities({ + '.custom-object-fill': { + 'object-fit': 'fill', + }, + '.custom-object-contain': { + 'object-fit': 'contain', + }, + '.custom-object-cover': { + 'object-fit': 'cover', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-object-fill { + object-fit: fill; + } + .custom-object-contain { + object-fit: contain; + } + .custom-object-cover { + object-fit: cover; + } + `) + }) +}) + +test('plugins can create utilities with arrays of objects', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addUtilities }) { + addUtilities([ + { + '.custom-object-fill': { + 'object-fit': 'fill', + }, + '.custom-object-contain': { + 'object-fit': 'contain', + }, + '.custom-object-cover': { + 'object-fit': 'cover', + }, + }, + ]) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-object-fill { + object-fit: fill; + } + .custom-object-contain { + object-fit: contain; + } + .custom-object-cover { + object-fit: cover; + } + `) + }) +}) + +test('plugins can create utilities with raw PostCSS nodes', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addUtilities, postcss }) { + addUtilities([ + postcss.rule({ selector: '.custom-object-fill' }).append([ + postcss.decl({ + prop: 'object-fit', + value: 'fill', + }), + ]), + postcss.rule({ selector: '.custom-object-contain' }).append([ + postcss.decl({ + prop: 'object-fit', + value: 'contain', + }), + ]), + postcss.rule({ selector: '.custom-object-cover' }).append([ + postcss.decl({ + prop: 'object-fit', + value: 'cover', + }), + ]), + ]) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-object-fill { + object-fit: fill; + } + .custom-object-contain { + object-fit: contain; + } + .custom-object-cover { + object-fit: cover; + } + `) + }) +}) + +test('plugins can create utilities with mixed object styles and PostCSS nodes', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addUtilities, postcss }) { + addUtilities([ + { + '.custom-object-fill': { + objectFit: 'fill', + }, + }, + postcss.rule({ selector: '.custom-object-contain' }).append([ + postcss.decl({ + prop: 'object-fit', + value: 'contain', + }), + ]), + postcss.rule({ selector: '.custom-object-cover' }).append([ + postcss.decl({ + prop: 'object-fit', + value: 'cover', + }), + ]), + ]) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-object-fill { + object-fit: fill; + } + .custom-object-contain { + object-fit: contain; + } + .custom-object-cover { + object-fit: cover; + } + `) + }) +}) + +test('plugins can create components with object syntax', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + plugins: [ + function ({ addComponents }) { + addComponents({ + '.btn-blue': { + backgroundColor: 'blue', + color: 'white', + padding: '.5rem 1rem', + borderRadius: '.25rem', + }, + '.btn-blue:hover': { + backgroundColor: 'darkblue', + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + color: #fff; + background-color: #00f; + border-radius: 0.25rem; + padding: 0.5rem 1rem; + } + .btn-blue:hover { + background-color: #00008b; + } + `) + }) +}) + +test('plugins can add base styles with object syntax', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + plugins: [ + function ({ addBase }) { + addBase({ + img: { + maxWidth: '100%', + }, + button: { + fontFamily: 'inherit', + }, + }) + }, + ], + corePlugins: { preflight: false }, + } + + return run('@tailwind base', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + img { + max-width: 100%; + } + button { + font-family: inherit; + } + ${defaults} + `) + }) +}) + +test('plugins can add base styles with raw PostCSS nodes', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + plugins: [ + function ({ addBase, postcss }) { + addBase([ + postcss.rule({ selector: 'img' }).append([ + postcss.decl({ + prop: 'max-width', + value: '100%', + }), + ]), + postcss.rule({ selector: 'button' }).append([ + postcss.decl({ + prop: 'font-family', + value: 'inherit', + }), + ]), + ]) + }, + ], + corePlugins: { preflight: false }, + } + + return run('@tailwind base', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + img { + max-width: 100%; + } + button { + font-family: inherit; + } + ${defaults} + `) + }) +}) + +test('plugins can create components with raw PostCSS nodes', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + plugins: [ + function ({ addComponents, postcss }) { + addComponents([ + postcss.rule({ selector: '.btn-blue' }).append([ + postcss.decl({ + prop: 'background-color', + value: 'blue', + }), + postcss.decl({ + prop: 'color', + value: 'white', + }), + postcss.decl({ + prop: 'padding', + value: '.5rem 1rem', + }), + postcss.decl({ + prop: 'border-radius', + value: '.25rem', + }), + ]), + postcss.rule({ selector: '.btn-blue:hover' }).append([ + postcss.decl({ + prop: 'background-color', + value: 'darkblue', + }), + ]), + ]) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + color: #fff; + background-color: #00f; + border-radius: 0.25rem; + padding: 0.5rem 1rem; + } + .btn-blue:hover { + background-color: #00008b; + } + `) + }) +}) + +test('plugins can create components with mixed object styles and raw PostCSS nodes', () => { + let config = { + content: [ + { + raw: html``, + }, + ], + plugins: [ + function ({ addComponents, postcss }) { + addComponents([ + postcss.rule({ selector: '.btn-blue' }).append([ + postcss.decl({ + prop: 'background-color', + value: 'blue', + }), + postcss.decl({ + prop: 'color', + value: 'white', + }), + postcss.decl({ + prop: 'padding', + value: '.5rem 1rem', + }), + postcss.decl({ + prop: 'border-radius', + value: '.25rem', + }), + ]), + { + '.btn-blue:hover': { + backgroundColor: 'darkblue', + }, + }, + ]) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + color: #fff; + background-color: #00f; + border-radius: 0.25rem; + padding: 0.5rem 1rem; + } + .btn-blue:hover { + background-color: #00008b; + } + `) + }) +}) + +test('plugins can create components with media queries with object syntax', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addComponents }) { + addComponents({ + '.custom-container': { + width: '100%', + }, + '@media (min-width: 100px)': { + '.custom-container': { + maxWidth: '100px', + }, + }, + '@media (min-width: 200px)': { + '.custom-container': { + maxWidth: '200px', + }, + }, + '@media (min-width: 300px)': { + '.custom-container': { + maxWidth: '300px', + }, + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-container { + width: 100%; + } + @media (min-width: 100px) { + .custom-container { + max-width: 100px; + } + } + @media (min-width: 200px) { + .custom-container { + max-width: 200px; + } + } + @media (min-width: 300px) { + .custom-container { + max-width: 300px; + } + } + `) + }) +}) + +test('media queries can be defined multiple times using objects-in-array syntax', () => { + let config = { + content: [ + { + raw: html`
+ `, + }, + ], + plugins: [ + function ({ addComponents }) { + addComponents([ + { + '.custom-container': { + width: '100%', + }, + '@media (min-width: 100px)': { + '.custom-container': { + maxWidth: '100px', + }, + }, + }, + { + '.btn': { + padding: '1rem .5rem', + display: 'block', + }, + '@media (min-width: 100px)': { + '.btn': { + display: 'inline-block', + }, + }, + }, + ]) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-container { + width: 100%; + } + @media (min-width: 100px) { + .custom-container { + max-width: 100px; + } + } + .btn { + padding: 1rem 0.5rem; + display: block; + } + @media (min-width: 100px) { + .btn { + display: inline-block; + } + } + `) + }) +}) + +test('plugins can create nested rules', () => { + let config = { + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents }) { + addComponents({ + '.btn-blue': { + backgroundColor: 'blue', + color: 'white', + padding: '.5rem 1rem', + borderRadius: '.25rem', + '&:hover': { + backgroundColor: 'darkblue', + }, + '@media (min-width: 500px)': { + '&:hover': { + backgroundColor: 'orange', + }, + }, + '> a': { + color: 'red', + }, + 'h1 &': { + color: 'purple', + }, + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + color: #fff; + background-color: #00f; + border-radius: 0.25rem; + padding: 0.5rem 1rem; + } + .btn-blue:hover { + background-color: #00008b; + } + @media (min-width: 500px) { + .btn-blue:hover { + background-color: orange; + } + } + .btn-blue > a { + color: red; + } + h1 .btn-blue { + color: purple; + } + `) + }) +}) + +test('plugins can create rules with escaped selectors', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [ + function ({ e, addUtilities }) { + addUtilities({ + [`.${e('custom-top-1/4')}`]: { + top: '25%', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-top-1\/4 { + top: 25%; + } + `) + }) +}) + +test('plugins can access the current config', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [ + function ({ addComponents, config }) { + let containerClasses = [ + { + '.custom-container': { + width: '100%', + }, + }, + ] + + for (let maxWidth of Object.values(config('theme.customScreens'))) { + containerClasses.push({ + [`@media (min-width: ${maxWidth})`]: { + '.custom-container': { maxWidth }, + }, + }) + } + + addComponents(containerClasses) + }, + ], + theme: { + customScreens: { + sm: '576px', + md: '768px', + lg: '992px', + xl: '1200px', + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-container { + width: 100%; + } + @media (min-width: 576px) { + .custom-container { + max-width: 576px; + } + } + @media (min-width: 768px) { + .custom-container { + max-width: 768px; + } + } + @media (min-width: 992px) { + .custom-container { + max-width: 992px; + } + } + @media (min-width: 1200px) { + .custom-container { + max-width: 1200px; + } + } + `) + }) +}) + +test('plugins can check if corePlugins are enabled', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities, corePlugins }) { + addUtilities({ + '.test': { + 'text-color': corePlugins('textColor') ? 'true' : 'false', + opacity: corePlugins('opacity') ? 'true' : 'false', + }, + }) + }, + ], + corePlugins: { textColor: false }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .test { + text-color: false; + opacity: true; + } + `) + }) +}) + +test('plugins can check if corePlugins are enabled when using array white-listing', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities, corePlugins }) { + addUtilities({ + '.test': { + 'text-color': corePlugins('textColor') ? 'true' : 'false', + opacity: corePlugins('opacity') ? 'true' : 'false', + }, + }) + }, + ], + corePlugins: ['textColor'], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .test { + text-color: true; + opacity: false; + } + `) + }) +}) + +test('plugins can provide fallbacks to keys missing from the config', () => { + let config = { + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents, config }) { + addComponents({ + '.btn': { + borderRadius: config('borderRadius.default', '.25rem'), + }, + }) + }, + ], + theme: { + borderRadius: { + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + }, + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn { + border-radius: 0.25rem; + } + `) + }) +}) + +test('plugins can add multiple sets of utilities and components', () => { + let config = { + content: [ + { + raw: html` +
`, + }, + ], + plugins: [ + function ({ addUtilities, addComponents }) { + addComponents({ + '.card': { + padding: '1rem', + borderRadius: '.25rem', + }, + }) + + addUtilities({ + '.custom-skew-12deg': { + transform: 'skewY(-12deg)', + }, + }) + + addComponents({ + '.btn': { + padding: '1rem .5rem', + display: 'inline-block', + }, + }) + + addUtilities({ + '.custom-border-collapse': { + borderCollapse: 'collapse', + }, + }) + }, + ], + } + + return run('@tailwind components; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .card { + border-radius: 0.25rem; + padding: 1rem; + } + .btn { + padding: 1rem 0.5rem; + display: inline-block; + } + .custom-skew-12deg { + transform: skewY(-12deg); + } + .custom-border-collapse { + border-collapse: collapse; + } + `) + }) +}) + +test('plugins respect prefix and important options by default when adding utilities', () => { + let config = { + prefix: 'tw-', + important: true, + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities({ + '.custom-rotate-90': { + transform: 'rotate(90deg)', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-custom-rotate-90 { + transform: rotate(90deg) !important; + } + `) + }) +}) + +test('when important is a selector it is used to scope utilities instead of adding !important', () => { + let config = { + prefix: 'tw-', + important: '#app', + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities({ + '.custom-rotate-90': { + transform: 'rotate(90deg)', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + #app .tw-custom-rotate-90 { + transform: rotate(90deg); + } + `) + }) +}) + +test('when important is a selector it scopes all selectors in a rule, even though defining utilities like this is stupid', () => { + let config = { + important: '#app', + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities({ + '.custom-rotate-90, .custom-rotate-1\\/4': { + transform: 'rotate(90deg)', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + #app .custom-rotate-90, + #app .custom-rotate-1\/4 { + transform: rotate(90deg); + } + `) + }) +}) + +test('important utilities are not made double important when important option is used', () => { + let config = { + important: true, + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities({ + '.custom-rotate-90': { + transform: 'rotate(90deg) !important', + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-rotate-90 { + transform: rotate(90deg) !important; + } + `) + }) +}) + +test("component declarations respect the 'prefix' option by default", () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents }) { + addComponents({ + '.btn-blue': { + backgroundColor: 'blue', + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-btn-blue { + background-color: #00f; + } + `) + }) +}) + +test('all selectors in a rule are prefixed', () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: html` +
`, + }, + ], + plugins: [ + function ({ addUtilities, addComponents }) { + addUtilities({ + '.custom-rotate-90, .custom-rotate-1\\/4': { + transform: 'rotate(90deg)', + }, + }) + addComponents({ + '.btn-blue, .btn-red': { + padding: '10px', + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-btn-blue, + .tw-btn-red { + padding: 10px; + } + `) + }) +}) + +test("component declarations can optionally ignore 'prefix' option", () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents }) { + addComponents( + { + '.btn-blue': { + backgroundColor: 'blue', + }, + }, + { respectPrefix: false } + ) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + background-color: #00f; + } + `) + }) +}) + +test("component declarations are not affected by the 'important' option", () => { + let config = { + important: true, + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents }) { + addComponents({ + '.btn-blue': { + backgroundColor: 'blue', + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .btn-blue { + background-color: #00f; + } + `) + }) +}) + +test("plugins can apply the user's chosen prefix to components manually", () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`` }], + plugins: [ + function ({ addComponents, prefix }) { + addComponents( + { + [prefix('.btn-blue')]: { + backgroundColor: 'blue', + }, + }, + { respectPrefix: false } + ) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-btn-blue { + background-color: #00f; + } + `) + }) +}) + +test('prefix can optionally be ignored for utilities', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities( + { + '.custom-rotate-90': { + transform: 'rotate(90deg)', + }, + }, + { respectPrefix: false } + ) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-rotate-90 { + transform: rotate(90deg); + } + `) + }) +}) + +test('important can optionally be ignored for utilities', () => { + let config = { + important: true, + content: [{ raw: html`
` }], + plugins: [ + function ({ addUtilities }) { + addUtilities( + { + '.custom-rotate-90': { + transform: 'rotate(90deg)', + }, + }, + { respectImportant: false } + ) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-rotate-90 { + transform: rotate(90deg); + } + `) + }) +}) + +test('prefix will prefix all classes in a selector', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + plugins: [ + function ({ addComponents, prefix }) { + addComponents( + { + [prefix('.btn-blue .w-1\\/4 > h1.text-xl + a .bar')]: { + backgroundColor: 'blue', + }, + }, + { respectPrefix: false } + ) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-btn-blue .tw-w-1\/4 > h1.tw-text-xl + a .tw-bar { + background-color: #00f; + } + `) + }) +}) + +test('plugins can be provided as an object with a handler function', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + { + handler({ addUtilities }) { addUtilities({ '.custom-object-fill': { 'object-fit': 'fill', @@ -25,1273 +1139,95 @@ crosscheck(() => { }, }) }, - ], - } + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-object-fill { - object-fit: fill; - } - .custom-object-contain { - object-fit: contain; - } - .custom-object-cover { - object-fit: cover; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .custom-object-fill { + object-fit: fill; + } + .custom-object-contain { + object-fit: contain; + } + .custom-object-cover { + object-fit: cover; + } + `) }) +}) - test('plugins can create utilities with arrays of objects', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addUtilities }) { - addUtilities([ - { - '.custom-object-fill': { - 'object-fit': 'fill', - }, - '.custom-object-contain': { - 'object-fit': 'contain', - }, - '.custom-object-cover': { - 'object-fit': 'cover', - }, - }, - ]) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-object-fill { - object-fit: fill; - } - .custom-object-contain { - object-fit: contain; - } - .custom-object-cover { - object-fit: cover; - } - `) - }) - }) - - test('plugins can create utilities with raw PostCSS nodes', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addUtilities, postcss }) { - addUtilities([ - postcss.rule({ selector: '.custom-object-fill' }).append([ - postcss.decl({ - prop: 'object-fit', - value: 'fill', - }), - ]), - postcss.rule({ selector: '.custom-object-contain' }).append([ - postcss.decl({ - prop: 'object-fit', - value: 'contain', - }), - ]), - postcss.rule({ selector: '.custom-object-cover' }).append([ - postcss.decl({ - prop: 'object-fit', - value: 'cover', - }), - ]), - ]) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-object-fill { - object-fit: fill; - } - .custom-object-contain { - object-fit: contain; - } - .custom-object-cover { - object-fit: cover; - } - `) - }) - }) - - test('plugins can create utilities with mixed object styles and PostCSS nodes', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addUtilities, postcss }) { - addUtilities([ - { - '.custom-object-fill': { - objectFit: 'fill', - }, - }, - postcss.rule({ selector: '.custom-object-contain' }).append([ - postcss.decl({ - prop: 'object-fit', - value: 'contain', - }), - ]), - postcss.rule({ selector: '.custom-object-cover' }).append([ - postcss.decl({ - prop: 'object-fit', - value: 'cover', - }), - ]), - ]) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-object-fill { - object-fit: fill; - } - .custom-object-contain { - object-fit: contain; - } - .custom-object-cover { - object-fit: cover; - } - `) - }) - }) - - test('plugins can create components with object syntax', () => { - let config = { - content: [ - { - raw: html``, - }, - ], - plugins: [ - function ({ addComponents }) { - addComponents({ - '.btn-blue': { - backgroundColor: 'blue', - color: 'white', - padding: '.5rem 1rem', - borderRadius: '.25rem', - }, - '.btn-blue:hover': { - backgroundColor: 'darkblue', - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - color: #fff; - background-color: #00f; - border-radius: 0.25rem; - padding: 0.5rem 1rem; - } - .btn-blue:hover { - background-color: #00008b; - } - `) - }) - }) - - test('plugins can add base styles with object syntax', () => { - let config = { - content: [ - { - raw: html``, - }, - ], - plugins: [ - function ({ addBase }) { - addBase({ - img: { - maxWidth: '100%', - }, - button: { - fontFamily: 'inherit', - }, - }) - }, - ], - corePlugins: { preflight: false }, - } - - return run('@tailwind base', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - img { - max-width: 100%; - } - button { - font-family: inherit; - } - ${defaults} - `) - }) - }) - - test('plugins can add base styles with raw PostCSS nodes', () => { - let config = { - content: [ - { - raw: html``, - }, - ], - plugins: [ - function ({ addBase, postcss }) { - addBase([ - postcss.rule({ selector: 'img' }).append([ - postcss.decl({ - prop: 'max-width', - value: '100%', - }), - ]), - postcss.rule({ selector: 'button' }).append([ - postcss.decl({ - prop: 'font-family', - value: 'inherit', - }), - ]), - ]) - }, - ], - corePlugins: { preflight: false }, - } - - return run('@tailwind base', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - img { - max-width: 100%; - } - button { - font-family: inherit; - } - ${defaults} - `) - }) - }) - - test('plugins can create components with raw PostCSS nodes', () => { - let config = { - content: [ - { - raw: html``, - }, - ], - plugins: [ - function ({ addComponents, postcss }) { - addComponents([ - postcss.rule({ selector: '.btn-blue' }).append([ - postcss.decl({ - prop: 'background-color', - value: 'blue', - }), - postcss.decl({ - prop: 'color', - value: 'white', - }), - postcss.decl({ - prop: 'padding', - value: '.5rem 1rem', - }), - postcss.decl({ - prop: 'border-radius', - value: '.25rem', - }), - ]), - postcss.rule({ selector: '.btn-blue:hover' }).append([ - postcss.decl({ - prop: 'background-color', - value: 'darkblue', - }), - ]), - ]) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - color: #fff; - background-color: #00f; - border-radius: 0.25rem; - padding: 0.5rem 1rem; - } - .btn-blue:hover { - background-color: #00008b; - } - `) - }) - }) - - test('plugins can create components with mixed object styles and raw PostCSS nodes', () => { - let config = { - content: [ - { - raw: html``, - }, - ], - plugins: [ - function ({ addComponents, postcss }) { - addComponents([ - postcss.rule({ selector: '.btn-blue' }).append([ - postcss.decl({ - prop: 'background-color', - value: 'blue', - }), - postcss.decl({ - prop: 'color', - value: 'white', - }), - postcss.decl({ - prop: 'padding', - value: '.5rem 1rem', - }), - postcss.decl({ - prop: 'border-radius', - value: '.25rem', - }), - ]), - { - '.btn-blue:hover': { - backgroundColor: 'darkblue', - }, - }, - ]) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - color: #fff; - background-color: #00f; - border-radius: 0.25rem; - padding: 0.5rem 1rem; - } - .btn-blue:hover { - background-color: #00008b; - } - `) - }) - }) - - test('plugins can create components with media queries with object syntax', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addComponents }) { - addComponents({ - '.custom-container': { - width: '100%', - }, - '@media (min-width: 100px)': { - '.custom-container': { - maxWidth: '100px', - }, - }, - '@media (min-width: 200px)': { - '.custom-container': { - maxWidth: '200px', - }, - }, - '@media (min-width: 300px)': { - '.custom-container': { - maxWidth: '300px', - }, - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-container { - width: 100%; - } - @media (min-width: 100px) { - .custom-container { - max-width: 100px; - } - } - @media (min-width: 200px) { - .custom-container { - max-width: 200px; - } - } - @media (min-width: 300px) { - .custom-container { - max-width: 300px; - } - } - `) - }) - }) - - test('media queries can be defined multiple times using objects-in-array syntax', () => { - let config = { - content: [ - { - raw: html`
- `, - }, - ], - plugins: [ - function ({ addComponents }) { - addComponents([ - { - '.custom-container': { - width: '100%', - }, - '@media (min-width: 100px)': { - '.custom-container': { - maxWidth: '100px', - }, - }, - }, - { - '.btn': { - padding: '1rem .5rem', - display: 'block', - }, - '@media (min-width: 100px)': { - '.btn': { - display: 'inline-block', - }, - }, - }, - ]) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-container { - width: 100%; - } - @media (min-width: 100px) { - .custom-container { - max-width: 100px; - } - } - .btn { - padding: 1rem 0.5rem; - display: block; - } - @media (min-width: 100px) { - .btn { - display: inline-block; - } - } - `) - }) - }) - - test('plugins can create nested rules', () => { - let config = { - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents }) { - addComponents({ - '.btn-blue': { - backgroundColor: 'blue', - color: 'white', - padding: '.5rem 1rem', - borderRadius: '.25rem', - '&:hover': { - backgroundColor: 'darkblue', - }, - '@media (min-width: 500px)': { - '&:hover': { - backgroundColor: 'orange', - }, - }, - '> a': { - color: 'red', - }, - 'h1 &': { - color: 'purple', - }, - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - color: #fff; - background-color: #00f; - border-radius: 0.25rem; - padding: 0.5rem 1rem; - } - .btn-blue:hover { - background-color: #00008b; - } - @media (min-width: 500px) { - .btn-blue:hover { - background-color: orange; - } - } - .btn-blue > a { - color: red; - } - h1 .btn-blue { - color: purple; - } - `) - }) - }) - - test('plugins can create rules with escaped selectors', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [ - function ({ e, addUtilities }) { - addUtilities({ - [`.${e('custom-top-1/4')}`]: { - top: '25%', - }, - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-top-1\/4 { - top: 25%; - } - `) - }) - }) - - test('plugins can access the current config', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [ - function ({ addComponents, config }) { - let containerClasses = [ - { - '.custom-container': { - width: '100%', - }, - }, - ] - - for (let maxWidth of Object.values(config('theme.customScreens'))) { - containerClasses.push({ - [`@media (min-width: ${maxWidth})`]: { - '.custom-container': { maxWidth }, - }, - }) - } - - addComponents(containerClasses) - }, - ], - theme: { - customScreens: { - sm: '576px', - md: '768px', - lg: '992px', - xl: '1200px', +test('plugins can provide a config but no handler', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + { + config: { + prefix: 'tw-', }, }, - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-container { - width: 100%; - } - @media (min-width: 576px) { - .custom-container { - max-width: 576px; - } - } - @media (min-width: 768px) { - .custom-container { - max-width: 768px; - } - } - @media (min-width: 992px) { - .custom-container { - max-width: 992px; - } - } - @media (min-width: 1200px) { - .custom-container { - max-width: 1200px; - } - } - `) - }) - }) - - test('plugins can check if corePlugins are enabled', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities, corePlugins }) { + { + handler({ addUtilities }) { addUtilities({ - '.test': { - 'text-color': corePlugins('textColor') ? 'true' : 'false', - opacity: corePlugins('opacity') ? 'true' : 'false', + '.custom-object-fill': { + 'object-fit': 'fill', + }, + '.custom-object-contain': { + 'object-fit': 'contain', + }, + '.custom-object-cover': { + 'object-fit': 'cover', }, }) }, - ], - corePlugins: { textColor: false }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .test { - text-color: false; - opacity: true; - } - `) - }) - }) - - test('plugins can check if corePlugins are enabled when using array white-listing', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities, corePlugins }) { - addUtilities({ - '.test': { - 'text-color': corePlugins('textColor') ? 'true' : 'false', - opacity: corePlugins('opacity') ? 'true' : 'false', - }, - }) - }, - ], - corePlugins: ['textColor'], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .test { - text-color: true; - opacity: false; - } - `) - }) - }) - - test('plugins can provide fallbacks to keys missing from the config', () => { - let config = { - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents, config }) { - addComponents({ - '.btn': { - borderRadius: config('borderRadius.default', '.25rem'), - }, - }) - }, - ], - theme: { - borderRadius: { - 1: '1px', - 2: '2px', - 4: '4px', - 8: '8px', - }, }, - } + ], + } - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn { - border-radius: 0.25rem; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-custom-object-fill { + object-fit: fill; + } + .tw-custom-object-contain { + object-fit: contain; + } + .tw-custom-object-cover { + object-fit: cover; + } + `) }) +}) - test('plugins can add multiple sets of utilities and components', () => { - let config = { - content: [ - { - raw: html` -
`, - }, - ], - plugins: [ - function ({ addUtilities, addComponents }) { - addComponents({ - '.card': { - padding: '1rem', - borderRadius: '.25rem', - }, - }) - - addUtilities({ - '.custom-skew-12deg': { - transform: 'skewY(-12deg)', - }, - }) - - addComponents({ - '.btn': { - padding: '1rem .5rem', - display: 'inline-block', - }, - }) - - addUtilities({ - '.custom-border-collapse': { - borderCollapse: 'collapse', - }, - }) - }, - ], - } - - return run('@tailwind components; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .card { - border-radius: 0.25rem; - padding: 1rem; - } - .btn { - padding: 1rem 0.5rem; - display: inline-block; - } - .custom-skew-12deg { - transform: skewY(-12deg); - } - .custom-border-collapse { - border-collapse: collapse; - } - `) - }) - }) - - test('plugins respect prefix and important options by default when adding utilities', () => { - let config = { - prefix: 'tw-', - important: true, - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities({ - '.custom-rotate-90': { - transform: 'rotate(90deg)', - }, - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-custom-rotate-90 { - transform: rotate(90deg) !important; - } - `) - }) - }) - - test('when important is a selector it is used to scope utilities instead of adding !important', () => { - let config = { - prefix: 'tw-', - important: '#app', - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities({ - '.custom-rotate-90': { - transform: 'rotate(90deg)', - }, - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - #app .tw-custom-rotate-90 { - transform: rotate(90deg); - } - `) - }) - }) - - test('when important is a selector it scopes all selectors in a rule, even though defining utilities like this is stupid', () => { - let config = { - important: '#app', - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities({ - '.custom-rotate-90, .custom-rotate-1\\/4': { - transform: 'rotate(90deg)', - }, - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - #app .custom-rotate-90, - #app .custom-rotate-1\/4 { - transform: rotate(90deg); - } - `) - }) - }) - - test('important utilities are not made double important when important option is used', () => { - let config = { - important: true, - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities({ - '.custom-rotate-90': { - transform: 'rotate(90deg) !important', - }, - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-rotate-90 { - transform: rotate(90deg) !important; - } - `) - }) - }) - - test("component declarations respect the 'prefix' option by default", () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents }) { - addComponents({ - '.btn-blue': { - backgroundColor: 'blue', - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-btn-blue { - background-color: #00f; - } - `) - }) - }) - - test('all selectors in a rule are prefixed', () => { - let config = { - prefix: 'tw-', - content: [ - { - raw: html` -
`, - }, - ], - plugins: [ - function ({ addUtilities, addComponents }) { - addUtilities({ - '.custom-rotate-90, .custom-rotate-1\\/4': { - transform: 'rotate(90deg)', - }, - }) - addComponents({ - '.btn-blue, .btn-red': { - padding: '10px', - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-btn-blue, - .tw-btn-red { - padding: 10px; - } - `) - }) - }) - - test("component declarations can optionally ignore 'prefix' option", () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents }) { - addComponents( - { - '.btn-blue': { - backgroundColor: 'blue', - }, - }, - { respectPrefix: false } - ) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - background-color: #00f; - } - `) - }) - }) - - test("component declarations are not affected by the 'important' option", () => { - let config = { - important: true, - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents }) { - addComponents({ - '.btn-blue': { - backgroundColor: 'blue', - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .btn-blue { - background-color: #00f; - } - `) - }) - }) - - test("plugins can apply the user's chosen prefix to components manually", () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`` }], - plugins: [ - function ({ addComponents, prefix }) { - addComponents( - { - [prefix('.btn-blue')]: { - backgroundColor: 'blue', - }, - }, - { respectPrefix: false } - ) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-btn-blue { - background-color: #00f; - } - `) - }) - }) - - test('prefix can optionally be ignored for utilities', () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities( - { - '.custom-rotate-90': { - transform: 'rotate(90deg)', - }, - }, - { respectPrefix: false } - ) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-rotate-90 { - transform: rotate(90deg); - } - `) - }) - }) - - test('important can optionally be ignored for utilities', () => { - let config = { - important: true, - content: [{ raw: html`
` }], - plugins: [ - function ({ addUtilities }) { - addUtilities( - { - '.custom-rotate-90': { - transform: 'rotate(90deg)', - }, - }, - { respectImportant: false } - ) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-rotate-90 { - transform: rotate(90deg); - } - `) - }) - }) - - test('prefix will prefix all classes in a selector', () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - plugins: [ - function ({ addComponents, prefix }) { - addComponents( - { - [prefix('.btn-blue .w-1\\/4 > h1.text-xl + a .bar')]: { - backgroundColor: 'blue', - }, - }, - { respectPrefix: false } - ) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-btn-blue .tw-w-1\/4 > h1.tw-text-xl + a .tw-bar { - background-color: #00f; - } - `) - }) - }) - - test('plugins can be provided as an object with a handler function', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - { - handler({ addUtilities }) { - addUtilities({ - '.custom-object-fill': { - 'object-fit': 'fill', - }, - '.custom-object-contain': { - 'object-fit': 'contain', - }, - '.custom-object-cover': { - 'object-fit': 'cover', - }, - }) - }, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .custom-object-fill { - object-fit: fill; - } - .custom-object-contain { - object-fit: contain; - } - .custom-object-cover { - object-fit: cover; - } - `) - }) - }) - - test('plugins can provide a config but no handler', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - { - config: { - prefix: 'tw-', - }, - }, - { - handler({ addUtilities }) { - addUtilities({ - '.custom-object-fill': { - 'object-fit': 'fill', - }, - '.custom-object-contain': { - 'object-fit': 'contain', - }, - '.custom-object-cover': { - 'object-fit': 'cover', - }, - }) - }, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tw-custom-object-fill { - object-fit: fill; - } - .tw-custom-object-contain { - object-fit: contain; - } - .tw-custom-object-cover { - object-fit: cover; - } - `) - }) - }) - - test('plugins can be created using the `createPlugin` function', () => { - let config = { - content: [ - { raw: html`
` }, - ], - corePlugins: [], - theme: { - screens: { - sm: '400px', - }, +test('plugins can be created using the `createPlugin` function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + screens: { + sm: '400px', }, - plugins: [ - createPlugin( - function ({ addUtilities, theme }) { - addUtilities( - Object.fromEntries( - Object.entries(theme('testPlugin')).map(([k, v]) => [ - `.test-${k}`, - { testProperty: v }, - ]) - ) - ) - }, - { - theme: { - testPlugin: { - sm: '1rem', - md: '2rem', - lg: '3rem', - }, - }, - } - ), - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .test-sm { - test-property: 1rem; - } - .test-md { - test-property: 2rem; - } - .test-lg { - test-property: 3rem; - } - .hover\:test-sm:hover { - test-property: 1rem; - } - @media (min-width: 400px) { - .sm\:test-sm { - test-property: 1rem; - } - } - `) - }) - }) - - test('plugins with extra options can be created using the `createPlugin.withOptions` function', () => { - let plugin = createPlugin.withOptions( - function ({ className }) { - return function ({ addUtilities, theme }) { + }, + plugins: [ + createPlugin( + function ({ addUtilities, theme }) { addUtilities( Object.fromEntries( Object.entries(theme('testPlugin')).map(([k, v]) => [ - `.${className}-${k}`, + `.test-${k}`, { testProperty: v }, ]) ) ) - } - }, - function () { - return { + }, + { theme: { testPlugin: { sm: '1rem', @@ -1300,169 +1236,36 @@ crosscheck(() => { }, }, } + ), + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .test-sm { + test-property: 1rem; } - ) - - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: [], - theme: { - screens: { - sm: '400px', - }, - }, - plugins: [plugin({ className: 'banana' })], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .banana-sm { - test-property: 1rem; - } - .banana-md { - test-property: 2rem; - } - .banana-lg { - test-property: 3rem; - } - .hover\:banana-sm:hover { - test-property: 1rem; - } - @media (min-width: 400px) { - .sm\:banana-sm { - test-property: 1rem; - } - } - `) - }) - }) - - test('plugins should cache correctly', () => { - let plugin = createPlugin.withOptions( - ({ className = 'banana' } = {}) => - ({ addComponents }) => { - addComponents({ [`.${className}`]: { position: 'absolute' } }) - } - ) - - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - screens: { - sm: '400px', - }, - }, - } - - function internalRun(options = {}) { - return run('@tailwind components', { - ...config, - plugins: [plugin(options)], - }) - } - - return Promise.all([internalRun(), internalRun({ className: 'apple' })]).then( - ([result1, result2]) => { - let expected1 = css` - .banana { - position: absolute; - } - @media (min-width: 400px) { - .sm\:banana { - position: absolute; - } - } - ` - - let expected2 = css` - .apple { - position: absolute; - } - @media (min-width: 400px) { - .sm\:apple { - position: absolute; - } - } - ` - - expect(result1.css).toMatchCss(expected1) - expect(result2.css).toMatchCss(expected2) + .test-md { + test-property: 2rem; } - ) - }) - - test('plugins created using `createPlugin.withOptions` do not need to be invoked if the user wants to use the default options', () => { - let plugin = createPlugin.withOptions( - function ({ className } = { className: 'banana' }) { - return function ({ addUtilities, theme }) { - addUtilities( - Object.fromEntries( - Object.entries(theme('testPlugin')).map(([k, v]) => [ - `.${className}-${k}`, - { testProperty: v }, - ]) - ) - ) - } - }, - function () { - return { - theme: { - testPlugin: { - sm: '1rem', - md: '2rem', - lg: '3rem', - }, - }, + .test-lg { + test-property: 3rem; + } + .hover\:test-sm:hover { + test-property: 1rem; + } + @media (min-width: 400px) { + .sm\:test-sm { + test-property: 1rem; } } - ) - - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: [], - theme: { - screens: { - sm: '400px', - }, - }, - plugins: [plugin], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .banana-sm { - test-property: 1rem; - } - .banana-md { - test-property: 2rem; - } - .banana-lg { - test-property: 3rem; - } - .hover\:banana-sm:hover { - test-property: 1rem; - } - @media (min-width: 400px) { - .sm\:banana-sm { - test-property: 1rem; - } - } - `) - }) + `) }) +}) - test('the configFunction parameter is optional when using the `createPlugin.withOptions` function', () => { - let plugin = createPlugin.withOptions(function ({ className }) { +test('plugins with extra options can be created using the `createPlugin.withOptions` function', () => { + let plugin = createPlugin.withOptions( + function ({ className }) { return function ({ addUtilities, theme }) { addUtilities( Object.fromEntries( @@ -1473,461 +1276,641 @@ crosscheck(() => { ) ) } - }) - - let config = { - content: [ - { - raw: html`
`, + }, + function () { + return { + theme: { + testPlugin: { + sm: '1rem', + md: '2rem', + lg: '3rem', + }, }, - ], - corePlugins: [], - theme: { - screens: { - sm: '400px', - }, - testPlugin: { - sm: '1px', - md: '2px', - lg: '3px', - }, - }, - plugins: [plugin({ className: 'banana' })], + } } + ) - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .banana-sm { - test-property: 1px; + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: [], + theme: { + screens: { + sm: '400px', + }, + }, + plugins: [plugin({ className: 'banana' })], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .banana-sm { + test-property: 1rem; + } + .banana-md { + test-property: 2rem; + } + .banana-lg { + test-property: 3rem; + } + .hover\:banana-sm:hover { + test-property: 1rem; + } + @media (min-width: 400px) { + .sm\:banana-sm { + test-property: 1rem; } - .banana-md { - test-property: 2px; - } - .banana-lg { - test-property: 3px; - } - .hover\:banana-sm:hover { - test-property: 1px; - } - @media (min-width: 400px) { - .sm\:banana-sm { - test-property: 1px; - } - } - `) - }) + } + `) + }) +}) + +test('plugins should cache correctly', () => { + let plugin = createPlugin.withOptions(({ className = 'banana' } = {}) => ({ addComponents }) => { + addComponents({ [`.${className}`]: { position: 'absolute' } }) }) - test('keyframes are not escaped', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - plugins: [ - function ({ matchUtilities }) { - matchUtilities({ - foo: (value) => { - return { - [`@keyframes ${value}`]: { - '25.001%': { - color: 'black', - }, - }, - animation: `${value} 1s infinite`, - } - }, - }) - }, - ], - } + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + screens: { + sm: '400px', + }, + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @keyframes abc { + function internalRun(options = {}) { + return run('@tailwind components', { + ...config, + plugins: [plugin(options)], + }) + } + + return Promise.all([internalRun(), internalRun({ className: 'apple' })]).then( + ([result1, result2]) => { + let expected1 = css` + .banana { + position: absolute; + } + @media (min-width: 400px) { + .sm\:banana { + position: absolute; + } + } + ` + + let expected2 = css` + .apple { + position: absolute; + } + @media (min-width: 400px) { + .sm\:apple { + position: absolute; + } + } + ` + + expect(result1.css).toMatchCss(expected1) + expect(result2.css).toMatchCss(expected2) + } + ) +}) + +test('plugins created using `createPlugin.withOptions` do not need to be invoked if the user wants to use the default options', () => { + let plugin = createPlugin.withOptions( + function ({ className } = { className: 'banana' }) { + return function ({ addUtilities, theme }) { + addUtilities( + Object.fromEntries( + Object.entries(theme('testPlugin')).map(([k, v]) => [ + `.${className}-${k}`, + { testProperty: v }, + ]) + ) + ) + } + }, + function () { + return { + theme: { + testPlugin: { + sm: '1rem', + md: '2rem', + lg: '3rem', + }, + }, + } + } + ) + + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: [], + theme: { + screens: { + sm: '400px', + }, + }, + plugins: [plugin], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .banana-sm { + test-property: 1rem; + } + .banana-md { + test-property: 2rem; + } + .banana-lg { + test-property: 3rem; + } + .hover\:banana-sm:hover { + test-property: 1rem; + } + @media (min-width: 400px) { + .sm\:banana-sm { + test-property: 1rem; + } + } + `) + }) +}) + +test('the configFunction parameter is optional when using the `createPlugin.withOptions` function', () => { + let plugin = createPlugin.withOptions(function ({ className }) { + return function ({ addUtilities, theme }) { + addUtilities( + Object.fromEntries( + Object.entries(theme('testPlugin')).map(([k, v]) => [ + `.${className}-${k}`, + { testProperty: v }, + ]) + ) + ) + } + }) + + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: [], + theme: { + screens: { + sm: '400px', + }, + testPlugin: { + sm: '1px', + md: '2px', + lg: '3px', + }, + }, + plugins: [plugin({ className: 'banana' })], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .banana-sm { + test-property: 1px; + } + .banana-md { + test-property: 2px; + } + .banana-lg { + test-property: 3px; + } + .hover\:banana-sm:hover { + test-property: 1px; + } + @media (min-width: 400px) { + .sm\:banana-sm { + test-property: 1px; + } + } + `) + }) +}) + +test('keyframes are not escaped', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [ + function ({ matchUtilities }) { + matchUtilities({ + foo: (value) => { + return { + [`@keyframes ${value}`]: { + '25.001%': { + color: 'black', + }, + }, + animation: `${value} 1s infinite`, + } + }, + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes abc { + 25.001% { + color: #000; + } + } + .foo-\[abc\] { + animation: 1s infinite abc; + } + @media (min-width: 768px) { + @keyframes def { 25.001% { color: #000; } } - .foo-\[abc\] { - animation: 1s infinite abc; + .md\:foo-\[def\] { + animation: 1s infinite def; } - @media (min-width: 768px) { - @keyframes def { - 25.001% { - color: #000; - } - } - .md\:foo-\[def\] { - animation: 1s infinite def; - } - } - `) - }) - }) - - test('font sizes are retrieved without default line-heights or letter-spacing using theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - fontSize: { - sm: ['14px', '20px'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - fontSize: theme('fontSize.sm'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - font-size: 14px; - } - `) - }) - }) - - test('outlines are retrieved without outline-offset using theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - outline: { - black: ['2px dotted black', '4px'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - outline: theme('outline.black'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - outline: 2px dotted #000; - } - `) - }) - }) - - test('box-shadow values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - boxShadow: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - boxShadow: theme('boxShadow.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - box-shadow: width, height; - } - `) - }) - }) - - test('transition-property values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - transitionProperty: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - transitionProperty: theme('transitionProperty.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - transition-property: width, height; - } - `) - }) - }) - - test('transition-duration values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - transitionDuration: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - transitionDuration: theme('transitionDuration.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - transition-duration: width, height; - } - `) - }) - }) - - test('transition-delay values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - transitionDuration: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - transitionDuration: theme('transitionDuration.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - transition-duration: width, height; - } - `) - }) - }) - - test('transition-timing-function values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - transitionTimingFunction: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - transitionTimingFunction: theme('transitionTimingFunction.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - transition-timing-function: width, height; - } - `) - }) - }) - - test('background-image values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - backgroundImage: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - backgroundImage: theme('backgroundImage.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - background-image: width, height; - } - `) - }) - }) - - test('background-size values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - backgroundSize: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - backgroundSize: theme('backgroundSize.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - background-size: width, height; - } - `) - }) - }) - - test('background-color values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - backgroundColor: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - backgroundColor: theme('backgroundColor.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - background-color: width, height; - } - `) - }) - }) - - test('cursor values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - cursor: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - cursor: theme('cursor.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - cursor: width, height; - } - `) - }) - }) - - test('animation values are joined when retrieved using the theme function', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: [], - theme: { - animation: { - lol: ['width', 'height'], - }, - }, - plugins: [ - function ({ addComponents, theme }) { - addComponents({ - '.foo': { - animation: theme('animation.lol'), - }, - }) - }, - ], - } - - return run('@tailwind components', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo { - animation: width, height; - } - `) - }) - }) - - test('custom properties are not converted to kebab-case when added to base layer', () => { - let config = { - content: [], - plugins: [ - function ({ addBase }) { - addBase({ - ':root': { - '--colors-primaryThing-500': '0, 0, 255', - }, - }) - }, - ], - } - - return run('@tailwind base', config).then((result) => { - expect(result.css).toContain(`--colors-primaryThing-500: 0, 0, 255;`) - }) + } + `) + }) +}) + +test('font sizes are retrieved without default line-heights or letter-spacing using theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + fontSize: { + sm: ['14px', '20px'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + fontSize: theme('fontSize.sm'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + font-size: 14px; + } + `) + }) +}) + +test('outlines are retrieved without outline-offset using theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + outline: { + black: ['2px dotted black', '4px'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + outline: theme('outline.black'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + outline: 2px dotted #000; + } + `) + }) +}) + +test('box-shadow values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + boxShadow: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + boxShadow: theme('boxShadow.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + box-shadow: width, height; + } + `) + }) +}) + +test('transition-property values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + transitionProperty: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + transitionProperty: theme('transitionProperty.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + transition-property: width, height; + } + `) + }) +}) + +test('transition-duration values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + transitionDuration: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + transitionDuration: theme('transitionDuration.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + transition-duration: width, height; + } + `) + }) +}) + +test('transition-delay values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + transitionDuration: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + transitionDuration: theme('transitionDuration.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + transition-duration: width, height; + } + `) + }) +}) + +test('transition-timing-function values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + transitionTimingFunction: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + transitionTimingFunction: theme('transitionTimingFunction.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + transition-timing-function: width, height; + } + `) + }) +}) + +test('background-image values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + backgroundImage: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + backgroundImage: theme('backgroundImage.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + background-image: width, height; + } + `) + }) +}) + +test('background-size values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + backgroundSize: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + backgroundSize: theme('backgroundSize.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + background-size: width, height; + } + `) + }) +}) + +test('background-color values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + backgroundColor: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + backgroundColor: theme('backgroundColor.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + background-color: width, height; + } + `) + }) +}) + +test('cursor values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + cursor: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + cursor: theme('cursor.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + cursor: width, height; + } + `) + }) +}) + +test('animation values are joined when retrieved using the theme function', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: [], + theme: { + animation: { + lol: ['width', 'height'], + }, + }, + plugins: [ + function ({ addComponents, theme }) { + addComponents({ + '.foo': { + animation: theme('animation.lol'), + }, + }) + }, + ], + } + + return run('@tailwind components', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo { + animation: width, height; + } + `) + }) +}) + +test('custom properties are not converted to kebab-case when added to base layer', () => { + let config = { + content: [], + plugins: [ + function ({ addBase }) { + addBase({ + ':root': { + '--colors-primaryThing-500': '0, 0, 255', + }, + }) + }, + ], + } + + return run('@tailwind base', config).then((result) => { + expect(result.css).toContain(`--colors-primaryThing-500: 0, 0, 255;`) }) }) diff --git a/tests/custom-separator.test.js b/tests/custom-separator.test.js index adc1d55af..3b1708dfb 100644 --- a/tests/custom-separator.test.js +++ b/tests/custom-separator.test.js @@ -1,57 +1,55 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('custom separator', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
- `, - }, - ], - separator: '_', - } +test('custom separator', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+ `, + }, + ], + separator: '_', + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .group:hover .group-hover_focus-within_text-left:focus-within { - text-align: left; - } - :is([dir='rtl'] .rtl_active_text-center:active) { + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .group:hover .group-hover_focus-within_text-left:focus-within { + text-align: left; + } + :is([dir='rtl'] .rtl_active_text-center:active) { + text-align: center; + } + @media (prefers-reduced-motion: no-preference) { + .motion-safe_hover_text-center:hover { text-align: center; } - @media (prefers-reduced-motion: no-preference) { - .motion-safe_hover_text-center:hover { - text-align: center; - } + } + :is(.dark .dark_focus_text-left:focus) { + text-align: left; + } + @media (min-width: 768px) { + .md_hover_text-right:hover { + text-align: right; } - :is(.dark .dark_focus_text-left:focus) { - text-align: left; - } - @media (min-width: 768px) { - .md_hover_text-right:hover { - text-align: right; - } - } - `) - }) - }) - - test('dash is not supported', () => { - let config = { - darkMode: 'class', - content: [{ raw: 'lg-hover-font-bold' }], - separator: '-', - } - - return expect(run('@tailwind utilities', config)).rejects.toThrowError( - "The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead." - ) + } + `) }) }) + +test('dash is not supported', () => { + let config = { + darkMode: 'class', + content: [{ raw: 'lg-hover-font-bold' }], + separator: '-', + } + + return expect(run('@tailwind utilities', config)).rejects.toThrowError( + "The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead." + ) +}) diff --git a/tests/custom-transformers.test.js b/tests/custom-transformers.test.js index 14e3abac0..2715a7155 100644 --- a/tests/custom-transformers.test.js +++ b/tests/custom-transformers.test.js @@ -1,72 +1,67 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' function customTransformer(content) { return content.replace(/uppercase/g, 'lowercase') } -crosscheck(({ stable, oxide }) => { - oxide.test.todo('transform function') - stable.test('transform function', () => { - let config = { - content: { - files: [{ raw: html`
` }], - transform: customTransformer, - }, - } +test('transform function', () => { + let config = { + content: { + files: [{ raw: html`
` }], + transform: customTransformer, + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .lowercase { - text-transform: lowercase; - } - `) - }) - }) - - oxide.test.todo('transform.DEFAULT') - stable.test('transform.DEFAULT', () => { - let config = { - content: { - files: [{ raw: html`
` }], - transform: { - DEFAULT: customTransformer, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .lowercase { - text-transform: lowercase; - } - `) - }) - }) - - oxide.test.todo('transform.{extension}') - stable.test('transform.{extension}', () => { - let config = { - content: { - files: [ - { raw: 'blah blah blah', extension: 'html' }, - { raw: 'blah blah blah', extension: 'php' }, - ], - transform: { - html: () => 'uppercase', - php: () => 'lowercase', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .uppercase { - text-transform: uppercase; - } - .lowercase { - text-transform: lowercase; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .lowercase { + text-transform: lowercase; + } + `) + }) +}) + +test('transform.DEFAULT', () => { + let config = { + content: { + files: [{ raw: html`
` }], + transform: { + DEFAULT: customTransformer, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .lowercase { + text-transform: lowercase; + } + `) + }) +}) + +test('transform.{extension}', () => { + let config = { + content: { + files: [ + { raw: 'blah blah blah', extension: 'html' }, + { raw: 'blah blah blah', extension: 'php' }, + ], + transform: { + html: () => 'uppercase', + php: () => 'lowercase', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .uppercase { + text-transform: uppercase; + } + .lowercase { + text-transform: lowercase; + } + `) }) }) diff --git a/tests/customConfig.test.js b/tests/customConfig.test.js index 5b83f9f2e..b8f459144 100644 --- a/tests/customConfig.test.js +++ b/tests/customConfig.test.js @@ -2,37 +2,100 @@ import fs from 'fs' import path from 'path' import inTempDirectory from '../jest/runInTempDirectory' -import { crosscheck, run, html, css, javascript } from './util/run' +import { run, html, css, javascript } from './util/run' const defaultConfigFile = 'tailwind.config.js' const cjsConfigFile = 'tailwind.config.cjs' -crosscheck(() => { - test('it uses the values from the custom config file', () => { - let config = require(path.resolve(`${__dirname}/fixtures/custom-config.js`)) +test('it uses the values from the custom config file', () => { + let config = require(path.resolve(`${__dirname}/fixtures/custom-config.js`)) - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; } - `) - }) + } + `) }) +}) - test('custom config can be passed as an object', () => { - let config = { +test('custom config can be passed as an object', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + screens: { + mobile: '400px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +test('custom config path can be passed using `config` property in an object', () => { + let config = { + config: path.resolve(`${__dirname}/fixtures/custom-config.js`), + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +test('custom config can be passed under the `config` property', () => { + let config = { + config: { content: [{ raw: html`
` }], theme: { screens: { mobile: '400px', }, }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +test('tailwind.config.cjs is picked up by default', () => { + return inTempDirectory(() => { + fs.writeFileSync( + path.resolve(cjsConfigFile), + javascript`module.exports = { + content: [{ raw: '
' }], + theme: { + screens: { + mobile: '400px', + }, + }, + }` + ) + + return run('@tailwind utilities').then((result) => { expect(result.css).toMatchFormattedCss(css` @media (min-width: 400px) { .mobile\:font-bold { @@ -42,349 +105,284 @@ crosscheck(() => { `) }) }) - - test('custom config path can be passed using `config` property in an object', () => { - let config = { - config: path.resolve(`${__dirname}/fixtures/custom-config.js`), - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - test('custom config can be passed under the `config` property', () => { - let config = { - config: { - content: [{ raw: html`
` }], - theme: { - screens: { - mobile: '400px', - }, - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - test('tailwind.config.cjs is picked up by default', () => { - return inTempDirectory(() => { - fs.writeFileSync( - path.resolve(cjsConfigFile), - javascript`module.exports = { - content: [{ raw: '
' }], - theme: { - screens: { - mobile: '400px', - }, - }, - }` - ) - - return run('@tailwind utilities').then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - }) - - test('tailwind.config.js is picked up by default', () => { - return inTempDirectory(() => { - fs.writeFileSync( - path.resolve(defaultConfigFile), - javascript`module.exports = { - content: [{ raw: '
' }], - theme: { - screens: { - mobile: '400px', - }, - }, - }` - ) - - return run('@tailwind utilities').then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - }) - - test('tailwind.config.cjs is picked up by default when passing an empty object', () => { - return inTempDirectory(() => { - fs.writeFileSync( - path.resolve(cjsConfigFile), - javascript`module.exports = { - content: [{ raw: '
' }], - theme: { - screens: { - mobile: '400px', - }, - }, - }` - ) - - return run('@tailwind utilities', {}).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - }) - - test('tailwind.config.js is picked up by default when passing an empty object', () => { - return inTempDirectory(() => { - fs.writeFileSync( - path.resolve(defaultConfigFile), - javascript`module.exports = { - content: [{ raw: '
' }], - theme: { - screens: { - mobile: '400px', - }, - }, - }` - ) - - return run('@tailwind utilities', {}).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 400px) { - .mobile\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - }) - - test('the default config can be overridden using the presets key', () => { - let config = { - content: [{ raw: html`
` }], - presets: [ - { - theme: { - extend: { minHeight: { secondary: '24px' } }, - }, - }, - ], - theme: { - extend: { minHeight: { primary: '48px' } }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .min-h-0 { - min-height: 0; - } - .min-h-primary { - min-height: 48px; - } - .min-h-secondary { - min-height: 24px; - } - `) - }) - }) - - test('presets can be functions', () => { - let config = { - content: [{ raw: html`
` }], - presets: [ - () => ({ - theme: { - extend: { minHeight: { secondary: '24px' } }, - }, - }), - ], - theme: { - extend: { minHeight: { primary: '48px' } }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .min-h-0 { - min-height: 0; - } - .min-h-primary { - min-height: 48px; - } - .min-h-secondary { - min-height: 24px; - } - `) - }) - }) - - test('the default config can be removed by using an empty presets key in a preset', () => { - let config = { - content: [{ raw: html`
` }], - presets: [ - { - presets: [], - theme: { - extend: { minHeight: { secondary: '24px' } }, - }, - }, - ], - theme: { - extend: { minHeight: { primary: '48px' } }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .min-h-primary { - min-height: 48px; - } - .min-h-secondary { - min-height: 24px; - } - `) - }) - }) - - test('presets can have their own presets', () => { - let config = { - content: [{ raw: html`
` }], - presets: [ - { - presets: [], - theme: { - colors: { red: '#dd0000' }, - }, - }, - { - presets: [ - { - presets: [], - theme: { - colors: { - transparent: 'transparent', - red: '#ff0000', - }, - }, - }, - ], - theme: { - extend: { - colors: { - black: 'black', - red: '#ee0000', - }, - backgroundColor: (theme) => theme('colors'), - }, - }, - corePlugins: ['backgroundColor'], - }, - ], - theme: { - extend: { colors: { white: 'white' } }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-black { - background-color: #000; - } - .bg-red { - background-color: #e00; - } - .bg-transparent { - background-color: #0000; - } - .bg-white { - background-color: #fff; - } - `) - }) - }) - - test('function presets can be mixed with object presets', () => { - let config = { - content: [{ raw: html`
` }], - presets: [ - () => ({ - presets: [], - theme: { - colors: { red: '#dd0000' }, - }, - }), - { - presets: [ - () => ({ - presets: [], - theme: { - colors: { - transparent: 'transparent', - red: '#ff0000', - }, - }, - }), - ], - theme: { - extend: { - colors: { - black: 'black', - red: '#ee0000', - }, - backgroundColor: (theme) => theme('colors'), - }, - }, - corePlugins: ['backgroundColor'], - }, - ], - theme: { - extend: { colors: { white: 'white' } }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-black { - background-color: #000; - } - .bg-red { - background-color: #e00; - } - .bg-transparent { - background-color: #0000; - } - .bg-white { - background-color: #fff; - } - `) - }) - }) +}) + +test('tailwind.config.js is picked up by default', () => { + return inTempDirectory(() => { + fs.writeFileSync( + path.resolve(defaultConfigFile), + javascript`module.exports = { + content: [{ raw: '
' }], + theme: { + screens: { + mobile: '400px', + }, + }, + }` + ) + + return run('@tailwind utilities').then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) + }) +}) + +test('tailwind.config.cjs is picked up by default when passing an empty object', () => { + return inTempDirectory(() => { + fs.writeFileSync( + path.resolve(cjsConfigFile), + javascript`module.exports = { + content: [{ raw: '
' }], + theme: { + screens: { + mobile: '400px', + }, + }, + }` + ) + + return run('@tailwind utilities', {}).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) + }) +}) + +test('tailwind.config.js is picked up by default when passing an empty object', () => { + return inTempDirectory(() => { + fs.writeFileSync( + path.resolve(defaultConfigFile), + javascript`module.exports = { + content: [{ raw: '
' }], + theme: { + screens: { + mobile: '400px', + }, + }, + }` + ) + + return run('@tailwind utilities', {}).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 400px) { + .mobile\:font-bold { + font-weight: 700; + } + } + `) + }) + }) +}) + +test('the default config can be overridden using the presets key', () => { + let config = { + content: [{ raw: html`
` }], + presets: [ + { + theme: { + extend: { minHeight: { secondary: '24px' } }, + }, + }, + ], + theme: { + extend: { minHeight: { primary: '48px' } }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .min-h-0 { + min-height: 0; + } + .min-h-primary { + min-height: 48px; + } + .min-h-secondary { + min-height: 24px; + } + `) + }) +}) + +test('presets can be functions', () => { + let config = { + content: [{ raw: html`
` }], + presets: [ + () => ({ + theme: { + extend: { minHeight: { secondary: '24px' } }, + }, + }), + ], + theme: { + extend: { minHeight: { primary: '48px' } }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .min-h-0 { + min-height: 0; + } + .min-h-primary { + min-height: 48px; + } + .min-h-secondary { + min-height: 24px; + } + `) + }) +}) + +test('the default config can be removed by using an empty presets key in a preset', () => { + let config = { + content: [{ raw: html`
` }], + presets: [ + { + presets: [], + theme: { + extend: { minHeight: { secondary: '24px' } }, + }, + }, + ], + theme: { + extend: { minHeight: { primary: '48px' } }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .min-h-primary { + min-height: 48px; + } + .min-h-secondary { + min-height: 24px; + } + `) + }) +}) + +test('presets can have their own presets', () => { + let config = { + content: [{ raw: html`
` }], + presets: [ + { + presets: [], + theme: { + colors: { red: '#dd0000' }, + }, + }, + { + presets: [ + { + presets: [], + theme: { + colors: { + transparent: 'transparent', + red: '#ff0000', + }, + }, + }, + ], + theme: { + extend: { + colors: { + black: 'black', + red: '#ee0000', + }, + backgroundColor: (theme) => theme('colors'), + }, + }, + corePlugins: ['backgroundColor'], + }, + ], + theme: { + extend: { colors: { white: 'white' } }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-black { + background-color: #000; + } + .bg-red { + background-color: #e00; + } + .bg-transparent { + background-color: #0000; + } + .bg-white { + background-color: #fff; + } + `) + }) +}) + +test('function presets can be mixed with object presets', () => { + let config = { + content: [{ raw: html`
` }], + presets: [ + () => ({ + presets: [], + theme: { + colors: { red: '#dd0000' }, + }, + }), + { + presets: [ + () => ({ + presets: [], + theme: { + colors: { + transparent: 'transparent', + red: '#ff0000', + }, + }, + }), + ], + theme: { + extend: { + colors: { + black: 'black', + red: '#ee0000', + }, + backgroundColor: (theme) => theme('colors'), + }, + }, + corePlugins: ['backgroundColor'], + }, + ], + theme: { + extend: { colors: { white: 'white' } }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-black { + background-color: #000; + } + .bg-red { + background-color: #e00; + } + .bg-transparent { + background-color: #0000; + } + .bg-white { + background-color: #fff; + } + `) + }) }) diff --git a/tests/dark-mode.test.js b/tests/dark-mode.test.js index ce06496b2..0c5273991 100644 --- a/tests/dark-mode.test.js +++ b/tests/dark-mode.test.js @@ -1,123 +1,121 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - it('should be possible to use the darkMode "class" mode', () => { - let config = { - darkMode: 'class', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +it('should be possible to use the darkMode "class" mode', () => { + let config = { + darkMode: 'class', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - :is(.dark .dark\:font-bold) { - font-weight: 700; - } - `) - }) - }) - - it('should be possible to change the class name', () => { - let config = { - darkMode: ['class', '.test-dark'], - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - :is(.test-dark .dark\:font-bold) { - font-weight: 700; - } - `) - }) - }) - - it('should be possible to use the darkMode "media" mode', () => { - let config = { - darkMode: 'media', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-color-scheme: dark) { - .dark\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('should default to the `media` mode when no mode is provided', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-color-scheme: dark) { - .dark\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('should default to the `media` mode when mode is set to `false`', () => { - let config = { - darkMode: false, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-color-scheme: dark) { - .dark\:font-bold { - font-weight: 700; - } - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + :is(.dark .dark\:font-bold) { + font-weight: 700; + } + `) + }) +}) + +it('should be possible to change the class name', () => { + let config = { + darkMode: ['class', '.test-dark'], + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + :is(.test-dark .dark\:font-bold) { + font-weight: 700; + } + `) + }) +}) + +it('should be possible to use the darkMode "media" mode', () => { + let config = { + darkMode: 'media', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-color-scheme: dark) { + .dark\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +it('should default to the `media` mode when no mode is provided', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-color-scheme: dark) { + .dark\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +it('should default to the `media` mode when mode is set to `false`', () => { + let config = { + darkMode: false, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-color-scheme: dark) { + .dark\:font-bold { + font-weight: 700; + } + } + `) }) }) diff --git a/tests/detect-nesting.test.js b/tests/detect-nesting.test.js index 39af6d422..08eeacf98 100644 --- a/tests/detect-nesting.test.js +++ b/tests/detect-nesting.test.js @@ -1,130 +1,128 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - it('should warn when we detect nested css', () => { - let config = { - content: [{ raw: html`
` }], - } +it('should warn when we detect nested css', () => { + let config = { + content: [{ raw: html`
` }], + } - let input = css` - @tailwind utilities; + let input = css` + @tailwind utilities; - .nested { - .example { - } + .nested { + .example { } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - expect(result.messages).toMatchObject([ - { - type: 'warning', - text: [ - 'Nested CSS was detected, but CSS nesting has not been configured correctly.', - 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', - 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', - ].join('\n'), - }, - ]) - }) - }) - - it('should not warn when we detect nested css inside css @layer rules', () => { - let config = { - content: [{ raw: html`
` }], } + ` - let input = css` + return run(input, config).then((result) => { + expect(result.messages).toHaveLength(1) + expect(result.messages).toMatchObject([ + { + type: 'warning', + text: [ + 'Nested CSS was detected, but CSS nesting has not been configured correctly.', + 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', + 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', + ].join('\n'), + }, + ]) + }) +}) + +it('should not warn when we detect nested css inside css @layer rules', () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @layer tw-base, tw-components, tw-utilities; + @layer tw-utilities { + @tailwind utilities; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` @layer tw-base, tw-components, tw-utilities; @layer tw-utilities { - @tailwind utilities; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @layer tw-base, tw-components, tw-utilities; - @layer tw-utilities { - .underline { - text-decoration-line: underline; - } - } - `) - expect(result.messages).toHaveLength(0) - }) - }) - - it('should warn when we detect namespaced @tailwind at rules', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - .namespace { - @tailwind utilities; - } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - expect(result.messages).toMatchObject([ - { - type: 'warning', - text: [ - 'Nested @tailwind rules were detected, but are not supported.', - "Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix", - 'Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy', - ].join('\n'), - }, - ]) - }) - }) - - it('should not warn when nesting a single rule inside a media query', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind utilities; - - @media (min-width: 768px) { - .nested { + .underline { + text-decoration-line: underline; } } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(0) - expect(result.messages).toEqual([]) - }) + `) + expect(result.messages).toHaveLength(0) }) +}) - it('should only warn for the first detected nesting ', () => { - let config = { - content: [{ raw: html`
` }], - } +it('should warn when we detect namespaced @tailwind at rules', () => { + let config = { + content: [{ raw: html`
` }], + } - let input = css` + let input = css` + .namespace { @tailwind utilities; + } + ` + return run(input, config).then((result) => { + expect(result.messages).toHaveLength(1) + expect(result.messages).toMatchObject([ + { + type: 'warning', + text: [ + 'Nested @tailwind rules were detected, but are not supported.', + "Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix", + 'Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy', + ].join('\n'), + }, + ]) + }) +}) + +it('should not warn when nesting a single rule inside a media query', () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind utilities; + + @media (min-width: 768px) { .nested { - .example { - } + } + } + ` - .other { - } + return run(input, config).then((result) => { + expect(result.messages).toHaveLength(0) + expect(result.messages).toEqual([]) + }) +}) + +it('should only warn for the first detected nesting ', () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind utilities; + + .nested { + .example { } .other { - .example { - } } - ` + } - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - }) + .other { + .example { + } + } + ` + + return run(input, config).then((result) => { + expect(result.messages).toHaveLength(1) }) }) diff --git a/tests/escapeClassName.test.js b/tests/escapeClassName.test.js index cdd834a79..7a8cd7b7b 100644 --- a/tests/escapeClassName.test.js +++ b/tests/escapeClassName.test.js @@ -1,8 +1,5 @@ import escapeClassName from '../src/util/escapeClassName' -import { crosscheck } from './util/run' -crosscheck(() => { - test('invalid characters are escaped', () => { - expect(escapeClassName('w:_$-1/2')).toEqual('w\\:_\\$-1\\/2') - }) +test('invalid characters are escaped', () => { + expect(escapeClassName('w:_$-1/2')).toEqual('w\\:_\\$-1\\/2') }) diff --git a/tests/evaluateTailwindFunctions.test.js b/tests/evaluateTailwindFunctions.test.js index 34eb5adc0..52f1fed07 100644 --- a/tests/evaluateTailwindFunctions.test.js +++ b/tests/evaluateTailwindFunctions.test.js @@ -2,7 +2,7 @@ import fs from 'fs' import path from 'path' import postcss from 'postcss' import plugin from '../src/lib/evaluateTailwindFunctions' -import { crosscheck, run as runFull, html, css } from './util/run' +import { run as runFull, html, css } from './util/run' function run(input, opts = {}) { return postcss([ @@ -15,1404 +15,1399 @@ function run(input, opts = {}) { ]).process(input, { from: undefined }) } -crosscheck(({ stable, oxide }) => { - test('it looks up values in the theme using dot notation', () => { - let input = css` - .banana { - color: theme('colors.yellow'); - } - ` - - let output = css` - .banana { - color: #f7cc50; - } - ` - - return run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('it looks up values in the theme using bracket notation', () => { - let input = css` - .banana { - color: theme('colors[yellow]'); - } - ` - - let output = css` - .banana { - color: #f7cc50; - } - ` - - return run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('it looks up values in the theme using consecutive bracket notation', () => { - let input = css` - .banana { - color: theme('colors[yellow][100]'); - } - ` - - let output = css` - .banana { - color: #f7cc50; - } - ` - - return run(input, { - theme: { - colors: { - yellow: { - 100: '#f7cc50', - }, - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('it looks up values in the theme using bracket notation that have dots in them', () => { - let input = css` - .banana { - padding-top: theme('spacing[1.5]'); - } - ` - - let output = css` - .banana { - padding-top: 0.375rem; - } - ` - - return run(input, { - theme: { - spacing: { - '1.5': '0.375rem', - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('theme with mismatched brackets throws an error ', async () => { - let config = { - theme: { - spacing: { - '1.5': '0.375rem', - }, - }, +test('it looks up values in the theme using dot notation', () => { + let input = css` + .banana { + color: theme('colors.yellow'); } + ` - let input = (path) => css` - .banana { - padding-top: theme('${path}'); - } - ` + let output = css` + .banana { + color: #f7cc50; + } + ` - await expect(run(input('spacing[1.5]]'), config)).rejects.toThrowError( - `Path is invalid. Has unbalanced brackets: spacing[1.5]]` - ) - - await expect(run(input('spacing[[1.5]'), config)).rejects.toThrowError( - `Path is invalid. Has unbalanced brackets: spacing[[1.5]` - ) - - await expect(run(input('spacing[a['), config)).rejects.toThrowError( - `Path is invalid. Has unbalanced brackets: spacing[a[` - ) - }) - - test('color can be a function', () => { - let input = css` - .backgroundColor { - color: theme('backgroundColor.fn'); - } - .borderColor { - color: theme('borderColor.fn'); - } - .caretColor { - color: theme('caretColor.fn'); - } - .colors { - color: theme('colors.fn'); - } - .divideColor { - color: theme('divideColor.fn'); - } - .fill { - color: theme('fill.fn'); - } - .gradientColorStops { - color: theme('gradientColorStops.fn'); - } - .placeholderColor { - color: theme('placeholderColor.fn'); - } - .ringColor { - color: theme('ringColor.fn'); - } - .ringOffsetColor { - color: theme('ringOffsetColor.fn'); - } - .stroke { - color: theme('stroke.fn'); - } - .textColor { - color: theme('textColor.fn'); - } - ` - - let output = css` - .backgroundColor { - color: #f00; - } - .borderColor { - color: #f00; - } - .caretColor { - color: #f00; - } - .colors { - color: #f00; - } - .divideColor { - color: #f00; - } - .fill { - color: #f00; - } - .gradientColorStops { - color: #f00; - } - .placeholderColor { - color: #f00; - } - .ringColor { - color: #f00; - } - .ringOffsetColor { - color: #f00; - } - .stroke { - color: #f00; - } - .textColor { - color: #f00; - } - ` - - let fn = () => `#f00` - - return run(input, { - theme: { - backgroundColor: { fn }, - borderColor: { fn }, - caretColor: { fn }, - colors: { fn }, - divideColor: { fn }, - fill: { fn }, - gradientColorStops: { fn }, - placeholderColor: { fn }, - ringColor: { fn }, - ringOffsetColor: { fn }, - stroke: { fn }, - textColor: { fn }, + return run(input, { + theme: { + colors: { + yellow: '#f7cc50', }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) }) +}) - test('quotes are optional around the lookup path', () => { - let input = css` - .banana { - color: theme(colors.yellow); - } - ` +test('it looks up values in the theme using bracket notation', () => { + let input = css` + .banana { + color: theme('colors[yellow]'); + } + ` - let output = css` - .banana { - color: #f7cc50; - } - ` + let output = css` + .banana { + color: #f7cc50; + } + ` - return run(input, { + return run(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('it looks up values in the theme using consecutive bracket notation', () => { + let input = css` + .banana { + color: theme('colors[yellow][100]'); + } + ` + + let output = css` + .banana { + color: #f7cc50; + } + ` + + return run(input, { + theme: { + colors: { + yellow: { + 100: '#f7cc50', + }, + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('it looks up values in the theme using bracket notation that have dots in them', () => { + let input = css` + .banana { + padding-top: theme('spacing[1.5]'); + } + ` + + let output = css` + .banana { + padding-top: 0.375rem; + } + ` + + return run(input, { + theme: { + spacing: { + '1.5': '0.375rem', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('theme with mismatched brackets throws an error ', async () => { + let config = { + theme: { + spacing: { + '1.5': '0.375rem', + }, + }, + } + + let input = (path) => css` + .banana { + padding-top: theme('${path}'); + } + ` + + await expect(run(input('spacing[1.5]]'), config)).rejects.toThrowError( + `Path is invalid. Has unbalanced brackets: spacing[1.5]]` + ) + + await expect(run(input('spacing[[1.5]'), config)).rejects.toThrowError( + `Path is invalid. Has unbalanced brackets: spacing[[1.5]` + ) + + await expect(run(input('spacing[a['), config)).rejects.toThrowError( + `Path is invalid. Has unbalanced brackets: spacing[a[` + ) +}) + +test('color can be a function', () => { + let input = css` + .backgroundColor { + color: theme('backgroundColor.fn'); + } + .borderColor { + color: theme('borderColor.fn'); + } + .caretColor { + color: theme('caretColor.fn'); + } + .colors { + color: theme('colors.fn'); + } + .divideColor { + color: theme('divideColor.fn'); + } + .fill { + color: theme('fill.fn'); + } + .gradientColorStops { + color: theme('gradientColorStops.fn'); + } + .placeholderColor { + color: theme('placeholderColor.fn'); + } + .ringColor { + color: theme('ringColor.fn'); + } + .ringOffsetColor { + color: theme('ringOffsetColor.fn'); + } + .stroke { + color: theme('stroke.fn'); + } + .textColor { + color: theme('textColor.fn'); + } + ` + + let output = css` + .backgroundColor { + color: #f00; + } + .borderColor { + color: #f00; + } + .caretColor { + color: #f00; + } + .colors { + color: #f00; + } + .divideColor { + color: #f00; + } + .fill { + color: #f00; + } + .gradientColorStops { + color: #f00; + } + .placeholderColor { + color: #f00; + } + .ringColor { + color: #f00; + } + .ringOffsetColor { + color: #f00; + } + .stroke { + color: #f00; + } + .textColor { + color: #f00; + } + ` + + let fn = () => `#f00` + + return run(input, { + theme: { + backgroundColor: { fn }, + borderColor: { fn }, + caretColor: { fn }, + colors: { fn }, + divideColor: { fn }, + fill: { fn }, + gradientColorStops: { fn }, + placeholderColor: { fn }, + ringColor: { fn }, + ringOffsetColor: { fn }, + stroke: { fn }, + textColor: { fn }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('quotes are optional around the lookup path', () => { + let input = css` + .banana { + color: theme(colors.yellow); + } + ` + + let output = css` + .banana { + color: #f7cc50; + } + ` + + return run(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('a default value can be provided', () => { + let input = css` + .cookieMonster { + color: theme('colors.blue', #0000ff); + } + ` + + let output = css` + .cookieMonster { + color: #0000ff; + } + ` + + return run(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('the default value can use the theme function', () => { + let input = css` + .cookieMonster { + color: theme('colors.blue', theme('colors.yellow')); + } + ` + + let output = css` + .cookieMonster { + color: #f7cc50; + } + ` + + return run(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('quotes are preserved around default values', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans', 'Helvetica Neue'); + } + ` + + let output = css` + .heading { + font-family: 'Helvetica Neue'; + } + ` + + return run(input, { + theme: { + fontFamily: { + serif: 'Constantia', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('an unquoted list is valid as a default value', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans', Helvetica, Arial, sans-serif); + } + ` + + let output = css` + .heading { + font-family: Helvetica, Arial, sans-serif; + } + ` + + return run(input, { + theme: { + fontFamily: { + serif: 'Constantia', + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('a missing root theme value throws', () => { + let input = css` + .heading { + color: theme('colours.gray.100'); + } + ` + + return expect( + run(input, { theme: { colors: { yellow: '#f7cc50', }, }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) }) - }) + ).rejects.toThrowError( + `'colours.gray.100' does not exist in your theme config. Your theme has the following top-level keys: 'colors'` + ) +}) - test('a default value can be provided', () => { - let input = css` - .cookieMonster { - color: theme('colors.blue', #0000ff); - } - ` +test('a missing nested theme property throws', () => { + let input = css` + .heading { + color: theme('colors.red'); + } + ` - let output = css` - .cookieMonster { - color: #0000ff; - } - ` + return expect( + run(input, { + theme: { + colors: { + blue: 'blue', + yellow: '#f7cc50', + }, + }, + }) + ).rejects.toThrowError( + `'colors.red' does not exist in your theme config. 'colors' has the following valid keys: 'blue', 'yellow'` + ) +}) - return run(input, { +test('a missing nested theme property with a close alternative throws with a suggestion', () => { + let input = css` + .heading { + color: theme('colors.yellw'); + } + ` + + return expect( + run(input, { theme: { colors: { yellow: '#f7cc50', }, }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) }) - }) + ).rejects.toThrowError( + `'colors.yellw' does not exist in your theme config. Did you mean 'colors.yellow'?` + ) +}) - test('the default value can use the theme function', () => { - let input = css` - .cookieMonster { - color: theme('colors.blue', theme('colors.yellow')); - } - ` +test('a path through a non-object throws', () => { + let input = css` + .heading { + color: theme('colors.yellow.100'); + } + ` - let output = css` - .cookieMonster { - color: #f7cc50; - } - ` - - return run(input, { + return expect( + run(input, { theme: { colors: { yellow: '#f7cc50', }, }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) }) - }) - - test('quotes are preserved around default values', () => { - let input = css` - .heading { - font-family: theme('fontFamily.sans', 'Helvetica Neue'); - } - ` - - let output = css` - .heading { - font-family: 'Helvetica Neue'; - } - ` - - return run(input, { - theme: { - fontFamily: { - serif: 'Constantia', - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('an unquoted list is valid as a default value', () => { - let input = css` - .heading { - font-family: theme('fontFamily.sans', Helvetica, Arial, sans-serif); - } - ` - - let output = css` - .heading { - font-family: Helvetica, Arial, sans-serif; - } - ` - - return run(input, { - theme: { - fontFamily: { - serif: 'Constantia', - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('a missing root theme value throws', () => { - let input = css` - .heading { - color: theme('colours.gray.100'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }) - ).rejects.toThrowError( - `'colours.gray.100' does not exist in your theme config. Your theme has the following top-level keys: 'colors'` - ) - }) - - test('a missing nested theme property throws', () => { - let input = css` - .heading { - color: theme('colors.red'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - blue: 'blue', - yellow: '#f7cc50', - }, - }, - }) - ).rejects.toThrowError( - `'colors.red' does not exist in your theme config. 'colors' has the following valid keys: 'blue', 'yellow'` - ) - }) - - test('a missing nested theme property with a close alternative throws with a suggestion', () => { - let input = css` - .heading { - color: theme('colors.yellw'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }) - ).rejects.toThrowError( - `'colors.yellw' does not exist in your theme config. Did you mean 'colors.yellow'?` - ) - }) - - test('a path through a non-object throws', () => { - let input = css` - .heading { - color: theme('colors.yellow.100'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }) - ).rejects.toThrowError( - `'colors.yellow.100' does not exist in your theme config. 'colors.yellow' is not an object.` - ) - }) - - test('a path which exists but is not a string throws', () => { - let input = css` - .heading { - color: theme('colors.yellow'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - yellow: Symbol(), - }, - }, - }) - ).rejects.toThrowError(`'colors.yellow' was found but does not resolve to a string.`) - }) - - test('a path which exists but is invalid throws', () => { - let input = css` - .heading { - color: theme('colors'); - } - ` - - return expect( - run(input, { - theme: { - colors: {}, - }, - }) - ).rejects.toThrowError(`'colors' was found but does not resolve to a string.`) - }) - - test('a path which is an object throws with a suggested key', () => { - let input = css` - .heading { - color: theme('colors'); - } - ` - - return expect( - run(input, { - theme: { - colors: { - yellow: '#f7cc50', - }, - }, - }) - ).rejects.toThrowError( - `'colors' was found but does not resolve to a string. Did you mean something like 'colors.yellow'?` - ) - }) - - test('array values are joined by default', () => { - let input = css` - .heading { - font-family: theme('fontFamily.sans'); - } - ` - - let output = css` - .heading { - font-family: Inter, Helvetica, sans-serif; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Inter', 'Helvetica', 'sans-serif'], - }, - }, - }).then((result) => { - expect(result.css).toEqual(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font sizes are retrieved without default line-heights or letter-spacing', () => { - let input = css` - .heading-1 { - font-size: theme('fontSize.lg'); - } - .heading-2 { - font-size: theme('fontSize.xl'); - } - ` - - let output = css` - .heading-1 { - font-size: 20px; - } - .heading-2 { - font-size: 24px; - } - ` - - return run(input, { - theme: { - fontSize: { - lg: ['20px', '28px'], - xl: ['24px', { lineHeight: '32px', letterSpacing: '-0.01em' }], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('outlines are retrieved without default outline-offset', () => { - let input = css` - .element { - outline: theme('outline.black'); - } - ` - - let output = css` - .element { - outline: 2px dotted #000; - } - ` - - return run(input, { - theme: { - outline: { - black: ['2px dotted black', '4px'], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font-family values are retrieved without font-variation-settings', () => { - let input = css` - .heading-1 { - font-family: theme('fontFamily.sans'); - } - .heading-2 { - font-family: theme('fontFamily.serif'); - } - .heading-3 { - font-family: theme('fontFamily.mono'); - } - ` - - let output = css` - .heading-1 { - font-family: Inter; - } - .heading-2 { - font-family: Times, serif; - } - .heading-3 { - font-family: Menlo, monospace; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Inter', { fontVariationSettings: '"opsz" 32' }], - serif: [['Times', 'serif'], { fontVariationSettings: '"opsz" 32' }], - mono: ['Menlo, monospace', { fontVariationSettings: '"opsz" 32' }], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font-variation-settings values can be retrieved', () => { - let input = css` - .heading { - font-family: theme('fontFamily.sans'); - font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings'); - } - ` - - let output = css` - .heading { - font-family: Inter; - font-variation-settings: 'opsz' 32; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Inter', { fontVariationSettings: "'opsz' 32" }], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font-family values are joined when an array', () => { - let input = css` - .element { - font-family: theme('fontFamily.sans'); - } - ` - - let output = css` - .element { - font-family: Helvetica, Arial, sans-serif; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Helvetica', 'Arial', 'sans-serif'], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font-family values are retrieved without font-feature-settings', () => { - let input = css` - .heading-1 { - font-family: theme('fontFamily.sans'); - } - .heading-2 { - font-family: theme('fontFamily.serif'); - } - .heading-3 { - font-family: theme('fontFamily.mono'); - } - ` - - let output = css` - .heading-1 { - font-family: Inter; - } - .heading-2 { - font-family: Times, serif; - } - .heading-3 { - font-family: Menlo, monospace; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Inter', { fontFeatureSettings: '"cv11"' }], - serif: [['Times', 'serif'], { fontFeatureSettings: '"cv11"' }], - mono: ['Menlo, monospace', { fontFeatureSettings: '"cv11"' }], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('font-feature-settings values can be retrieved', () => { - let input = css` - .heading { - font-family: theme('fontFamily.sans'); - font-feature-settings: theme('fontFamily.sans[1].fontFeatureSettings'); - } - ` - - let output = css` - .heading { - font-feature-settings: 'cv11'; - font-family: Inter; - } - ` - - return run(input, { - theme: { - fontFamily: { - sans: ['Inter', { fontFeatureSettings: "'cv11'" }], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('box-shadow values are joined when an array', () => { - let input = css` - .element { - box-shadow: theme('boxShadow.wtf'); - } - ` - - let output = css` - .element { - box-shadow: 0 0 2px #000, 1px 2px 3px #fff; - } - ` - - return run(input, { - theme: { - boxShadow: { - wtf: ['0 0 2px black', '1px 2px 3px white'], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('transition-property values are joined when an array', () => { - let input = css` - .element { - transition-property: theme('transitionProperty.colors'); - } - ` - - let output = css` - .element { - transition-property: color, fill; - } - ` - - return run(input, { - theme: { - transitionProperty: { - colors: ['color', 'fill'], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('transition-duration values are joined when an array', () => { - let input = css` - .element { - transition-duration: theme('transitionDuration.lol'); - } - ` - - let output = css` - .element { - transition-duration: 1s, 2s; - } - ` - - return run(input, { - theme: { - transitionDuration: { - lol: ['1s', '2s'], - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('basic screen function calls are expanded', () => { - let input = css` - @media screen(sm) { - .foo { - color: red; - } - } - ` - - let output = css` - @media (min-width: 600px) { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { sm: '600px' } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('screen function supports max-width screens', () => { - let input = css` - @media screen(sm) { - .foo { - color: red; - } - } - ` - - let output = css` - @media (max-width: 600px) { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { sm: { max: '600px' } } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('screen function supports min-width screens', () => { - let input = css` - @media screen(sm) { - .foo { - color: red; - } - } - ` - - let output = css` - @media (min-width: 600px) { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { sm: { min: '600px' } } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('screen function supports min-width and max-width screens', () => { - let input = css` - @media screen(sm) { - .foo { - color: red; - } - } - ` - - let output = css` - @media (min-width: 600px) and (max-width: 700px) { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { sm: { min: '600px', max: '700px' } } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('screen function supports raw screens', () => { - let input = css` - @media screen(mono) { - .foo { - color: red; - } - } - ` - - let output = css` - @media monochrome { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { mono: { raw: 'monochrome' } } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('screen arguments can be quoted', () => { - let input = css` - @media screen('sm') { - .foo { - color: red; - } - } - ` - - let output = css` - @media (min-width: 600px) { - .foo { - color: red; - } - } - ` - - return run(input, { - theme: { screens: { sm: '600px' } }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (1)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / 50%); - } - ` - - let output = css` - .foo { - color: #3b82f680; - } - ` - - return run(input, { - theme: { - colors: { blue: { 500: '#3b82f6' } }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (2)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / 0.5); - } - ` - - let output = css` - .foo { - color: #3b82f680; - } - ` - - return run(input, { - theme: { - colors: { blue: { 500: '#3b82f6' } }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (3)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / var(--my-alpha)); - } - ` - - let output = css` - .foo { - color: rgb(59 130 246 / var(--my-alpha)); - } - ` - - return run(input, { - theme: { - colors: { blue: { 500: '#3b82f6' } }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (4)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / 50%); - } - ` - - let output = css` - .foo { - color: #3c83f680; - } - ` - - return run(input, { + ).rejects.toThrowError( + `'colors.yellow.100' does not exist in your theme config. 'colors.yellow' is not an object.` + ) +}) + +test('a path which exists but is not a string throws', () => { + let input = css` + .heading { + color: theme('colors.yellow'); + } + ` + + return expect( + run(input, { theme: { colors: { - blue: { 500: 'hsl(217, 91%, 60%)' }, + yellow: Symbol(), }, }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) }) - }) + ).rejects.toThrowError(`'colors.yellow' was found but does not resolve to a string.`) +}) - test('Theme function can extract alpha values for colors (5)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / 0.5); - } - ` +test('a path which exists but is invalid throws', () => { + let input = css` + .heading { + color: theme('colors'); + } + ` - let output = css` - .foo { - color: #3c83f680; - } - ` - - return run(input, { + return expect( + run(input, { theme: { - colors: { - blue: { 500: 'hsl(217, 91%, 60%)' }, - }, + colors: {}, }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) }) - }) + ).rejects.toThrowError(`'colors' was found but does not resolve to a string.`) +}) - test('Theme function can extract alpha values for colors (6)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / var(--my-alpha)); - } - ` +test('a path which is an object throws with a suggested key', () => { + let input = css` + .heading { + color: theme('colors'); + } + ` - let output = css` - .foo { - color: hsl(217 91% 60% / var(--my-alpha)); - } - ` - - return run(input, { - theme: { - colors: { - blue: { 500: 'hsl(217, 91%, 60%)' }, - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (7)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / var(--my-alpha)); - } - ` - - let output = css` - .foo { - color: rgb(var(--foo) / var(--my-alpha)); - } - ` - - return runFull(input, { - theme: { - colors: { - blue: { - 500: 'rgb(var(--foo) / )', - }, - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function can extract alpha values for colors (8)', () => { - let input = css` - .foo { - color: theme(colors.blue.500 / theme(opacity.myalpha)); - } - ` - - let output = css` - .foo { - color: rgb(var(--foo) / 50%); - } - ` - - return runFull(input, { - theme: { - colors: { - blue: { - 500: 'rgb(var(--foo) / )', - }, - }, - - opacity: { - myalpha: '50%', - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme functions replace the alpha value placeholder even with no alpha provided', () => { - let input = css` - .foo { - background: theme(colors.blue.400); - color: theme(colors.blue.500); - } - ` - - let output = css` - .foo { - color: rgb(var(--foo) / 1); - background: #00f; - } - ` - - return runFull(input, { - theme: { - colors: { - blue: { - 400: 'rgb(0 0 255 / )', - 500: 'rgb(var(--foo) / )', - }, - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme functions can reference values with slashes in brackets', () => { - let input = css` - .foo1 { - color: theme(colors[a/b]); - } - .foo2 { - color: theme(colors[a/b]/50%); - } - ` - - let output = css` - .foo1 { - color: #000; - } - .foo2 { - color: #00000080; - } - ` - - return runFull(input, { - theme: { - colors: { - 'a/b': '#000000', - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme functions with alpha value inside quotes', () => { - let input = css` - .foo { - color: theme('colors.yellow / 50%'); - } - ` - - let output = css` - .foo { - color: #f7cc5080; - } - ` - - return runFull(input, { + return expect( + run(input, { theme: { colors: { yellow: '#f7cc50', }, }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) }) + ).rejects.toThrowError( + `'colors' was found but does not resolve to a string. Did you mean something like 'colors.yellow'?` + ) +}) + +test('array values are joined by default', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans'); + } + ` + + let output = css` + .heading { + font-family: Inter, Helvetica, sans-serif; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', 'Helvetica', 'sans-serif'], + }, + }, + }).then((result) => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) }) +}) - test('Theme functions with alpha with quotes value around color only', () => { - let input = css` +test('font sizes are retrieved without default line-heights or letter-spacing', () => { + let input = css` + .heading-1 { + font-size: theme('fontSize.lg'); + } + .heading-2 { + font-size: theme('fontSize.xl'); + } + ` + + let output = css` + .heading-1 { + font-size: 20px; + } + .heading-2 { + font-size: 24px; + } + ` + + return run(input, { + theme: { + fontSize: { + lg: ['20px', '28px'], + xl: ['24px', { lineHeight: '32px', letterSpacing: '-0.01em' }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('outlines are retrieved without default outline-offset', () => { + let input = css` + .element { + outline: theme('outline.black'); + } + ` + + let output = css` + .element { + outline: 2px dotted #000; + } + ` + + return run(input, { + theme: { + outline: { + black: ['2px dotted black', '4px'], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('font-family values are retrieved without font-variation-settings', () => { + let input = css` + .heading-1 { + font-family: theme('fontFamily.sans'); + } + .heading-2 { + font-family: theme('fontFamily.serif'); + } + .heading-3 { + font-family: theme('fontFamily.mono'); + } + ` + + let output = css` + .heading-1 { + font-family: Inter; + } + .heading-2 { + font-family: Times, serif; + } + .heading-3 { + font-family: Menlo, monospace; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontVariationSettings: '"opsz" 32' }], + serif: [['Times', 'serif'], { fontVariationSettings: '"opsz" 32' }], + mono: ['Menlo, monospace', { fontVariationSettings: '"opsz" 32' }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('font-variation-settings values can be retrieved', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans'); + font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings'); + } + ` + + let output = css` + .heading { + font-family: Inter; + font-variation-settings: 'opsz' 32; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontVariationSettings: "'opsz' 32" }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('font-family values are joined when an array', () => { + let input = css` + .element { + font-family: theme('fontFamily.sans'); + } + ` + + let output = css` + .element { + font-family: Helvetica, Arial, sans-serif; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Helvetica', 'Arial', 'sans-serif'], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('font-family values are retrieved without font-feature-settings', () => { + let input = css` + .heading-1 { + font-family: theme('fontFamily.sans'); + } + .heading-2 { + font-family: theme('fontFamily.serif'); + } + .heading-3 { + font-family: theme('fontFamily.mono'); + } + ` + + let output = css` + .heading-1 { + font-family: Inter; + } + .heading-2 { + font-family: Times, serif; + } + .heading-3 { + font-family: Menlo, monospace; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontFeatureSettings: '"cv11"' }], + serif: [['Times', 'serif'], { fontFeatureSettings: '"cv11"' }], + mono: ['Menlo, monospace', { fontFeatureSettings: '"cv11"' }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('font-feature-settings values can be retrieved', () => { + let input = css` + .heading { + font-family: theme('fontFamily.sans'); + font-feature-settings: theme('fontFamily.sans[1].fontFeatureSettings'); + } + ` + + let output = css` + .heading { + font-feature-settings: 'cv11'; + font-family: Inter; + } + ` + + return run(input, { + theme: { + fontFamily: { + sans: ['Inter', { fontFeatureSettings: "'cv11'" }], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('box-shadow values are joined when an array', () => { + let input = css` + .element { + box-shadow: theme('boxShadow.wtf'); + } + ` + + let output = css` + .element { + box-shadow: 0 0 2px #000, 1px 2px 3px #fff; + } + ` + + return run(input, { + theme: { + boxShadow: { + wtf: ['0 0 2px black', '1px 2px 3px white'], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('transition-property values are joined when an array', () => { + let input = css` + .element { + transition-property: theme('transitionProperty.colors'); + } + ` + + let output = css` + .element { + transition-property: color, fill; + } + ` + + return run(input, { + theme: { + transitionProperty: { + colors: ['color', 'fill'], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('transition-duration values are joined when an array', () => { + let input = css` + .element { + transition-duration: theme('transitionDuration.lol'); + } + ` + + let output = css` + .element { + transition-duration: 1s, 2s; + } + ` + + return run(input, { + theme: { + transitionDuration: { + lol: ['1s', '2s'], + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('basic screen function calls are expanded', () => { + let input = css` + @media screen(sm) { .foo { - color: theme('colors.yellow' / 50%); + color: red; } - ` + } + ` - let output = css` + let output = css` + @media (min-width: 600px) { .foo { - color: #f7cc5080; + color: red; } - ` + } + ` - return runFull(input, { - theme: { - colors: { - yellow: '#f7cc50', + return run(input, { + theme: { screens: { sm: '600px' } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('screen function supports max-width screens', () => { + let input = css` + @media screen(sm) { + .foo { + color: red; + } + } + ` + + let output = css` + @media (max-width: 600px) { + .foo { + color: red; + } + } + ` + + return run(input, { + theme: { screens: { sm: { max: '600px' } } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('screen function supports min-width screens', () => { + let input = css` + @media screen(sm) { + .foo { + color: red; + } + } + ` + + let output = css` + @media (min-width: 600px) { + .foo { + color: red; + } + } + ` + + return run(input, { + theme: { screens: { sm: { min: '600px' } } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('screen function supports min-width and max-width screens', () => { + let input = css` + @media screen(sm) { + .foo { + color: red; + } + } + ` + + let output = css` + @media (min-width: 600px) and (max-width: 700px) { + .foo { + color: red; + } + } + ` + + return run(input, { + theme: { screens: { sm: { min: '600px', max: '700px' } } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('screen function supports raw screens', () => { + let input = css` + @media screen(mono) { + .foo { + color: red; + } + } + ` + + let output = css` + @media monochrome { + .foo { + color: red; + } + } + ` + + return run(input, { + theme: { screens: { mono: { raw: 'monochrome' } } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('screen arguments can be quoted', () => { + let input = css` + @media screen('sm') { + .foo { + color: red; + } + } + ` + + let output = css` + @media (min-width: 600px) { + .foo { + color: red; + } + } + ` + + return run(input, { + theme: { screens: { sm: '600px' } }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (1)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / 50%); + } + ` + + let output = css` + .foo { + color: #3b82f680; + } + ` + + return run(input, { + theme: { + colors: { blue: { 500: '#3b82f6' } }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (2)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / 0.5); + } + ` + + let output = css` + .foo { + color: #3b82f680; + } + ` + + return run(input, { + theme: { + colors: { blue: { 500: '#3b82f6' } }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (3)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / var(--my-alpha)); + } + ` + + let output = css` + .foo { + color: rgb(59 130 246 / var(--my-alpha)); + } + ` + + return run(input, { + theme: { + colors: { blue: { 500: '#3b82f6' } }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (4)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / 50%); + } + ` + + let output = css` + .foo { + color: #3c83f680; + } + ` + + return run(input, { + theme: { + colors: { + blue: { 500: 'hsl(217, 91%, 60%)' }, + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (5)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / 0.5); + } + ` + + let output = css` + .foo { + color: #3c83f680; + } + ` + + return run(input, { + theme: { + colors: { + blue: { 500: 'hsl(217, 91%, 60%)' }, + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (6)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / var(--my-alpha)); + } + ` + + let output = css` + .foo { + color: hsl(217 91% 60% / var(--my-alpha)); + } + ` + + return run(input, { + theme: { + colors: { + blue: { 500: 'hsl(217, 91%, 60%)' }, + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function can extract alpha values for colors (7)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / var(--my-alpha)); + } + ` + + let output = css` + .foo { + color: rgb(var(--foo) / var(--my-alpha)); + } + ` + + return runFull(input, { + theme: { + colors: { + blue: { + 500: 'rgb(var(--foo) / )', }, }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) }) +}) - it('can find values with slashes in the theme key while still allowing for alpha values ', () => { - let input = css` +test('Theme function can extract alpha values for colors (8)', () => { + let input = css` + .foo { + color: theme(colors.blue.500 / theme(opacity.myalpha)); + } + ` + + let output = css` + .foo { + color: rgb(var(--foo) / 50%); + } + ` + + return runFull(input, { + theme: { + colors: { + blue: { + 500: 'rgb(var(--foo) / )', + }, + }, + + opacity: { + myalpha: '50%', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme functions replace the alpha value placeholder even with no alpha provided', () => { + let input = css` + .foo { + background: theme(colors.blue.400); + color: theme(colors.blue.500); + } + ` + + let output = css` + .foo { + color: rgb(var(--foo) / 1); + background: #00f; + } + ` + + return runFull(input, { + theme: { + colors: { + blue: { + 400: 'rgb(0 0 255 / )', + 500: 'rgb(var(--foo) / )', + }, + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme functions can reference values with slashes in brackets', () => { + let input = css` + .foo1 { + color: theme(colors[a/b]); + } + .foo2 { + color: theme(colors[a/b]/50%); + } + ` + + let output = css` + .foo1 { + color: #000; + } + .foo2 { + color: #00000080; + } + ` + + return runFull(input, { + theme: { + colors: { + 'a/b': '#000000', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme functions with alpha value inside quotes', () => { + let input = css` + .foo { + color: theme('colors.yellow / 50%'); + } + ` + + let output = css` + .foo { + color: #f7cc5080; + } + ` + + return runFull(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme functions with alpha with quotes value around color only', () => { + let input = css` + .foo { + color: theme('colors.yellow' / 50%); + } + ` + + let output = css` + .foo { + color: #f7cc5080; + } + ` + + return runFull(input, { + theme: { + colors: { + yellow: '#f7cc50', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +it('can find values with slashes in the theme key while still allowing for alpha values ', () => { + let input = css` + .foo00 { + color: theme(colors.foo-5); + } + .foo01 { + color: theme(colors.foo-5/10); + } + .foo02 { + color: theme(colors.foo-5/10/25); + } + .foo03 { + color: theme(colors.foo-5 / 10); + } + .foo04 { + color: theme(colors.foo-5/10 / 25); + } + ` + + return runFull(input, { + theme: { + colors: { + 'foo-5': '#050000', + 'foo-5/10': '#051000', + 'foo-5/10/25': '#051025', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(css` .foo00 { - color: theme(colors.foo-5); + color: #050000; } .foo01 { - color: theme(colors.foo-5/10); + color: #051000; } .foo02 { - color: theme(colors.foo-5/10/25); + color: #051025; } .foo03 { - color: theme(colors.foo-5 / 10); + color: #050000; } .foo04 { - color: theme(colors.foo-5/10 / 25); + color: #051000; + } + `) + }) +}) + +describe('context dependent', () => { + let configPath = path.resolve(__dirname, './evaluate-tailwind-functions.tailwind.config.js') + let filePath = path.resolve(__dirname, './evaluate-tailwind-functions.test.html') + let config = { + content: [filePath], + corePlugins: { preflight: false }, + } + + // Rebuild the config file for each test + beforeEach(() => fs.promises.writeFile(configPath, `module.exports = ${JSON.stringify(config)};`)) + afterEach(() => fs.promises.unlink(configPath)) + + test('should not generate when theme fn doesnt resolve', async () => { + await fs.promises.writeFile( + filePath, + html` +
+
+ ` + ) + + // TODO: We need a way to reuse the context in our test suite without requiring writing to files + // It should be an explicit thing tho — like we create a context and pass it in or something + let result = await runFull('@tailwind utilities', configPath) + + // 1. On first run it should work because it's been removed from the class cache + expect(result.css).toMatchCss(css` + .underline { + text-decoration-line: underline; + } + `) + + // 2. But we get a warning in the console + expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) + + // 3. The second run should work fine because it's been removed from the class cache + result = await runFull('@tailwind utilities', configPath) + + expect(result.css).toMatchCss(css` + .underline { + text-decoration-line: underline; + } + `) + + // 4. But we've not received any further logs about it + expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) + }) + + test('it works mayhaps', async () => { + let input = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*theme("spacing.4"))); + /* prettier-ignore */ + padding: calc(-1 * (2* theme("spacing.4"))); } ` - return runFull(input, { + let output = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*1rem)); + /* prettier-ignore */ + padding: calc(-1 * (2* 1rem)); + } + ` + + return run(input, { theme: { - colors: { - 'foo-5': '#050000', - 'foo-5/10': '#051000', - 'foo-5/10/25': '#051025', + spacing: { + 4: '1rem', }, }, }).then((result) => { - expect(result.css).toMatchCss(css` - .foo00 { - color: #050000; - } - .foo01 { - color: #051000; - } - .foo02 { - color: #051025; - } - .foo03 { - color: #050000; - } - .foo04 { - color: #051000; - } - `) - }) - }) - - describe('context dependent', () => { - let configPath = path.resolve(__dirname, './evaluate-tailwind-functions.tailwind.config.js') - let filePath = path.resolve(__dirname, './evaluate-tailwind-functions.test.html') - let config = { - content: [filePath], - corePlugins: { preflight: false }, - } - - // Rebuild the config file for each test - beforeEach(() => - fs.promises.writeFile(configPath, `module.exports = ${JSON.stringify(config)};`) - ) - afterEach(() => fs.promises.unlink(configPath)) - - oxide.test.todo('should not generate when theme fn doesnt resolve') - stable.test('should not generate when theme fn doesnt resolve', async () => { - await fs.promises.writeFile( - filePath, - html` -
-
- ` - ) - - // TODO: We need a way to reuse the context in our test suite without requiring writing to files - // It should be an explicit thing tho — like we create a context and pass it in or something - let result = await runFull('@tailwind utilities', configPath) - - // 1. On first run it should work because it's been removed from the class cache - expect(result.css).toMatchCss(css` - .underline { - text-decoration-line: underline; - } - `) - - // 2. But we get a warning in the console - expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) - - // 3. The second run should work fine because it's been removed from the class cache - result = await runFull('@tailwind utilities', configPath) - - expect(result.css).toMatchCss(css` - .underline { - text-decoration-line: underline; - } - `) - - // 4. But we've not received any further logs about it - expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) - }) - - test('it works mayhaps', async () => { - let input = css` - .test { - /* prettier-ignore */ - inset: calc(-1 * (2*theme("spacing.4"))); - /* prettier-ignore */ - padding: calc(-1 * (2* theme("spacing.4"))); - } - ` - - let output = css` - .test { - /* prettier-ignore */ - inset: calc(-1 * (2*1rem)); - /* prettier-ignore */ - padding: calc(-1 * (2* 1rem)); - } - ` - - return run(input, { - theme: { - spacing: { - 4: '1rem', - }, - }, - }).then((result) => { - expect(result.css).toMatchCss(output) - expect(result.warnings().length).toBe(0) - }) + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) }) }) }) diff --git a/tests/experimental.test.js b/tests/experimental.test.js index 3497783f9..b3fa49185 100644 --- a/tests/experimental.test.js +++ b/tests/experimental.test.js @@ -1,197 +1,195 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('experimental universal selector improvements (box-shadow)', () => { - let config = { - experimental: 'all', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +test('experimental universal selector improvements (box-shadow)', () => { + let config = { + experimental: 'all', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .shadow { - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - } - .resize { - resize: both; - } - .shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) - }) - - test('experimental universal selector improvements (pseudo hover)', () => { - let config = { - experimental: 'all', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .hover\:shadow { - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - } - .resize { - resize: both; - } - .hover\:shadow:hover { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) - }) - - test('experimental universal selector improvements (multiple classes: group)', () => { - let config = { - experimental: 'all', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .group-hover\:shadow { - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - } - .resize { - resize: both; - } - .group:hover .group-hover\:shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) - }) - - test('experimental universal selector improvements (child selectors: divide-y)', () => { - let config = { - experimental: 'all', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .resize { - resize: both; - } - .divide-y > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); - } - `) - }) - }) - - test('experimental universal selector improvements (hover:divide-y)', () => { - let config = { - experimental: 'all', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .resize { - resize: both; - } - .hover\:divide-y:hover > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); - } - `) - }) - }) - - test('experimental universal selector improvements (#app important)', () => { - let config = { - experimental: 'all', - important: '#app', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .shadow { - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - } - #app :is(.resize) { - resize: both; - } - #app :is(.divide-y > :not([hidden]) ~ :not([hidden])) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); - } - #app :is(.shadow) { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .shadow { + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .resize { + resize: both; + } + .shadow { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) + }) +}) + +test('experimental universal selector improvements (pseudo hover)', () => { + let config = { + experimental: 'all', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .hover\:shadow { + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .resize { + resize: both; + } + .hover\:shadow:hover { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) + }) +}) + +test('experimental universal selector improvements (multiple classes: group)', () => { + let config = { + experimental: 'all', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .group-hover\:shadow { + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .resize { + resize: both; + } + .group:hover .group-hover\:shadow { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) + }) +}) + +test('experimental universal selector improvements (child selectors: divide-y)', () => { + let config = { + experimental: 'all', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .resize { + resize: both; + } + .divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); + } + `) + }) +}) + +test('experimental universal selector improvements (hover:divide-y)', () => { + let config = { + experimental: 'all', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .resize { + resize: both; + } + .hover\:divide-y:hover > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); + } + `) + }) +}) + +test('experimental universal selector improvements (#app important)', () => { + let config = { + experimental: 'all', + important: '#app', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .shadow { + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + #app :is(.resize) { + resize: both; + } + #app :is(.divide-y > :not([hidden]) ~ :not([hidden])) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); + } + #app :is(.shadow) { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + `) }) }) diff --git a/tests/extractor-edge-cases.test.js b/tests/extractor-edge-cases.test.js index 412702b35..83ca99e37 100644 --- a/tests/extractor-edge-cases.test.js +++ b/tests/extractor-edge-cases.test.js @@ -1,34 +1,32 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('PHP arrays', async () => { - let config = { - content: [ - { - raw: html`

">Hello world

`, - }, - ], - } +test('PHP arrays', async () => { + let config = { + content: [ + { + raw: html`

">Hello world

`, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .max-w-\[16rem\] { - max-width: 16rem; - } - `) - }) - }) - - test('arbitrary values with quotes', async () => { - let config = { content: [{ raw: html`
` }] } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .content-\[\'hello\]\'\] { - --tw-content: 'hello]'; - content: var(--tw-content); - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .max-w-\[16rem\] { + max-width: 16rem; + } + `) + }) +}) + +test('arbitrary values with quotes', async () => { + let config = { content: [{ raw: html`
` }] } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .content-\[\'hello\]\'\] { + --tw-content: 'hello]'; + content: var(--tw-content); + } + `) }) }) diff --git a/tests/flattenColorPalette.test.js b/tests/flattenColorPalette.test.js index dae66e806..e8edcd9aa 100644 --- a/tests/flattenColorPalette.test.js +++ b/tests/flattenColorPalette.test.js @@ -1,84 +1,81 @@ -import { crosscheck } from './util/run' import flattenColorPalette from '../src/util/flattenColorPalette' -crosscheck(() => { - test('it flattens nested color objects', () => { - expect( - flattenColorPalette({ - purple: 'purple', - white: { - 25: 'rgba(255,255,255,.25)', - 50: 'rgba(255,255,255,.5)', - 75: 'rgba(255,255,255,.75)', - DEFAULT: '#fff', - }, - red: { - 1: 'rgb(33,0,0)', - 2: 'rgb(67,0,0)', - 3: 'rgb(100,0,0)', - }, - green: { - 1: 'rgb(0,33,0)', - 2: 'rgb(0,67,0)', - 3: 'rgb(0,100,0)', - }, - blue: { - 1: 'rgb(0,0,33)', - 2: 'rgb(0,0,67)', - 3: 'rgb(0,0,100)', - }, - }) - ).toEqual({ +test('it flattens nested color objects', () => { + expect( + flattenColorPalette({ purple: 'purple', - 'white-25': 'rgba(255,255,255,.25)', - 'white-50': 'rgba(255,255,255,.5)', - 'white-75': 'rgba(255,255,255,.75)', - white: '#fff', - 'red-1': 'rgb(33,0,0)', - 'red-2': 'rgb(67,0,0)', - 'red-3': 'rgb(100,0,0)', - 'green-1': 'rgb(0,33,0)', - 'green-2': 'rgb(0,67,0)', - 'green-3': 'rgb(0,100,0)', - 'blue-1': 'rgb(0,0,33)', - 'blue-2': 'rgb(0,0,67)', - 'blue-3': 'rgb(0,0,100)', + white: { + 25: 'rgba(255,255,255,.25)', + 50: 'rgba(255,255,255,.5)', + 75: 'rgba(255,255,255,.75)', + DEFAULT: '#fff', + }, + red: { + 1: 'rgb(33,0,0)', + 2: 'rgb(67,0,0)', + 3: 'rgb(100,0,0)', + }, + green: { + 1: 'rgb(0,33,0)', + 2: 'rgb(0,67,0)', + 3: 'rgb(0,100,0)', + }, + blue: { + 1: 'rgb(0,0,33)', + 2: 'rgb(0,0,67)', + 3: 'rgb(0,0,100)', + }, }) - }) - - test('it flattens deeply nested color objects', () => { - expect( - flattenColorPalette({ - primary: 'purple', - secondary: { - DEFAULT: 'blue', - hover: 'cyan', - focus: 'red', - }, - button: { - primary: { - DEFAULT: 'magenta', - hover: 'green', - focus: { - DEFAULT: 'yellow', - variant: 'orange', - }, - }, - }, - }) - ).toEqual({ - primary: 'purple', - secondary: 'blue', - 'secondary-hover': 'cyan', - 'secondary-focus': 'red', - 'button-primary': 'magenta', - 'button-primary-hover': 'green', - 'button-primary-focus': 'yellow', - 'button-primary-focus-variant': 'orange', - }) - }) - - test('it handles empty objects', () => { - expect(flattenColorPalette({})).toEqual({}) + ).toEqual({ + purple: 'purple', + 'white-25': 'rgba(255,255,255,.25)', + 'white-50': 'rgba(255,255,255,.5)', + 'white-75': 'rgba(255,255,255,.75)', + white: '#fff', + 'red-1': 'rgb(33,0,0)', + 'red-2': 'rgb(67,0,0)', + 'red-3': 'rgb(100,0,0)', + 'green-1': 'rgb(0,33,0)', + 'green-2': 'rgb(0,67,0)', + 'green-3': 'rgb(0,100,0)', + 'blue-1': 'rgb(0,0,33)', + 'blue-2': 'rgb(0,0,67)', + 'blue-3': 'rgb(0,0,100)', }) }) + +test('it flattens deeply nested color objects', () => { + expect( + flattenColorPalette({ + primary: 'purple', + secondary: { + DEFAULT: 'blue', + hover: 'cyan', + focus: 'red', + }, + button: { + primary: { + DEFAULT: 'magenta', + hover: 'green', + focus: { + DEFAULT: 'yellow', + variant: 'orange', + }, + }, + }, + }) + ).toEqual({ + primary: 'purple', + secondary: 'blue', + 'secondary-hover': 'cyan', + 'secondary-focus': 'red', + 'button-primary': 'magenta', + 'button-primary-hover': 'green', + 'button-primary-focus': 'yellow', + 'button-primary-focus-variant': 'orange', + }) +}) + +test('it handles empty objects', () => { + expect(flattenColorPalette({})).toEqual({}) +}) diff --git a/tests/format-variant-selector.test.js b/tests/format-variant-selector.test.js index 9a53f3aac..3873f5d2d 100644 --- a/tests/format-variant-selector.test.js +++ b/tests/format-variant-selector.test.js @@ -1,367 +1,364 @@ import { finalizeSelector } from '../src/util/formatVariantSelector' -import { crosscheck } from './util/run' -crosscheck(() => { - it('should be possible to add a simple variant to a simple selector', () => { - let selector = '.text-center' - let candidate = 'hover:text-center' +it('should be possible to add a simple variant to a simple selector', () => { + let selector = '.text-center' + let candidate = 'hover:text-center' + + let formats = [{ format: '&:hover', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual('.hover\\:text-center:hover') +}) + +it('should be possible to add a multiple simple variants to a simple selector', () => { + let selector = '.text-center' + let candidate = 'focus:hover:text-center' + + let formats = [ + { format: '&:hover', isArbitraryVariant: false }, + { format: '&:focus', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.focus\\:hover\\:text-center:hover:focus' + ) +}) + +it('should be possible to add a simple variant to a selector containing escaped parts', () => { + let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]' + let candidate = 'hover:bg-[rgba(0,0,0)]' + + let formats = [{ format: '&:hover', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' + ) +}) + +it('should be possible to add a simple variant to a selector containing escaped parts (escape is slightly different)', () => { + let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]' + let candidate = 'hover:bg-[rgba(0,0,0)]' + + let formats = [{ format: '&:hover', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' + ) +}) + +it('should be possible to add a simple variant to a more complex selector', () => { + let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' + let candidate = 'hover:space-x-4' + + let formats = [{ format: '&:hover', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])' + ) +}) + +it('should be possible to add multiple simple variants to a more complex selector', () => { + let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' + let candidate = 'disabled:focus:hover:space-x-4' + + let formats = [ + { format: '&:hover', isArbitraryVariant: false }, + { format: '&:focus', isArbitraryVariant: false }, + { format: '&:disabled', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.disabled\\:focus\\:hover\\:space-x-4:hover:focus:disabled > :not([hidden]) ~ :not([hidden])' + ) +}) + +it('should be possible to add a single merge variant to a simple selector', () => { + let selector = '.text-center' + let candidate = 'group-hover:text-center' + + let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:hover .group-hover\\:text-center' + ) +}) + +it('should be possible to add multiple merge variants to a simple selector', () => { + let selector = '.text-center' + let candidate = 'group-focus:group-hover:text-center' + + let formats = [ + { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.group):focus &', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:focus:hover .group-focus\\:group-hover\\:text-center' + ) +}) + +it('should be possible to add a single merge variant to a more complex selector', () => { + let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])' + let candidate = 'group-hover:space-x-4' + + let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])' + ) +}) + +it('should be possible to add multiple merge variants to a more complex selector', () => { + let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])' + let candidate = 'group-focus:group-hover:space-x-4' + + let formats = [ + { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.group):focus &', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:focus:hover .group-focus\\:group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])' + ) +}) + +it('should be possible to add multiple unique merge variants to a simple selector', () => { + let selector = '.text-center' + let candidate = 'peer-focus:group-hover:text-center' + + let formats = [ + { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.peer):focus ~ &' }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.peer:focus ~ .group:hover .peer-focus\\:group-hover\\:text-center' + ) +}) + +it('should be possible to add multiple unique merge variants to a simple selector', () => { + let selector = '.text-center' + let candidate = 'group-hover:peer-focus:text-center' + + let formats = [ + { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:hover .peer:focus ~ .group-hover\\:peer-focus\\:text-center' + ) +}) + +it('should be possible to use multiple :merge() calls with different "arguments"', () => { + let selector = '.foo' + let candidate = 'peer-focus:group-focus:peer-hover:group-hover:foo' + + let formats = [ + { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.peer):hover ~ &', isArbitraryVariant: false }, + { format: ':merge(.group):focus &', isArbitraryVariant: false }, + { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.peer:focus:hover ~ .group:focus:hover .peer-focus\\:group-focus\\:peer-hover\\:group-hover\\:foo' + ) +}) + +it('group hover and prose headings combination', () => { + let selector = '.text-center' + let candidate = 'group-hover:prose-headings:text-center' + let formats = [ + { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings + { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:hover :where(.group-hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4)' + ) +}) + +it('group hover and prose headings combination flipped', () => { + let selector = '.text-center' + let candidate = 'prose-headings:group-hover:text-center' + let formats = [ + { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover + { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + ':where(.group:hover .prose-headings\\:group-hover\\:text-center) :is(h1, h2, h3, h4)' + ) +}) + +it('should be possible to handle a complex utility', () => { + let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' + let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4' + let formats = [ + { format: '&:hover', isArbitraryVariant: false }, // Hover + { format: '&:focus', isArbitraryVariant: false }, // Focus + { format: ':merge(.group):focus &', isArbitraryVariant: false }, // Group focus + { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group hover + { format: ':merge(.peer):first-child ~ &', isArbitraryVariant: false }, // Peer first-child + { format: ':merge(.peer):disabled ~ &', isArbitraryVariant: false }, // Peer disabled + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.peer:disabled:first-child ~ .group:hover:focus .peer-disabled\\:peer-first-child\\:group-hover\\:group-focus\\:focus\\:hover\\:space-x-4:hover:focus > :not([hidden]) ~ :not([hidden])' + ) +}) + +it('should match base utilities that are prefixed', () => { + let context = { tailwindConfig: { prefix: 'tw-' } } + let selector = '.tw-text-center' + let candidate = 'tw-text-center' + let formats = [] + + expect(finalizeSelector(selector, formats, { candidate, context })).toEqual('.tw-text-center') +}) + +it('should prefix classes from variants', () => { + let context = { tailwindConfig: { prefix: 'tw-' } } + let selector = '.tw-text-center' + let candidate = 'foo:tw-text-center' + let formats = [{ format: '.foo &', isArbitraryVariant: false }] + + expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( + '.tw-foo .foo\\:tw-text-center' + ) +}) + +it('should not prefix classes from arbitrary variants', () => { + let context = { tailwindConfig: { prefix: 'tw-' } } + let selector = '.tw-text-center' + let candidate = '[.foo_&]:tw-text-center' + let formats = [{ format: '.foo &', isArbitraryVariant: true }] + + expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( + '.foo .\\[\\.foo_\\&\\]\\:tw-text-center' + ) +}) + +it('Merged selectors with mixed combinators uses the first one', () => { + // This isn't explicitly specced behavior but it is how it works today + + let selector = '.text-center' + let candidate = 'text-center' + let formats = [ + { format: ':merge(.group):focus > &', isArbitraryVariant: true }, + { format: ':merge(.group):hover &', isArbitraryVariant: true }, + ] + + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.group:hover:focus > .text-center' + ) +}) + +describe('real examples', () => { + it('example a', () => { + let selector = '.placeholder-red-500::placeholder' + let candidate = 'hover:placeholder-red-500' let formats = [{ format: '&:hover', isArbitraryVariant: false }] - expect(finalizeSelector(selector, formats, { candidate })).toEqual('.hover\\:text-center:hover') + expect(finalizeSelector(selector, formats, { candidate })).toEqual( + '.hover\\:placeholder-red-500:hover::placeholder' + ) }) - it('should be possible to add a multiple simple variants to a simple selector', () => { - let selector = '.text-center' - let candidate = 'focus:hover:text-center' + it('example b', () => { + let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' + let candidate = 'group-hover:hover:space-x-4' let formats = [ { format: '&:hover', isArbitraryVariant: false }, - { format: '&:focus', isArbitraryVariant: false }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.focus\\:hover\\:text-center:hover:focus' - ) - }) - - it('should be possible to add a simple variant to a selector containing escaped parts', () => { - let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]' - let candidate = 'hover:bg-[rgba(0,0,0)]' - - let formats = [{ format: '&:hover', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' - ) - }) - - it('should be possible to add a simple variant to a selector containing escaped parts (escape is slightly different)', () => { - let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]' - let candidate = 'hover:bg-[rgba(0,0,0)]' - - let formats = [{ format: '&:hover', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' - ) - }) - - it('should be possible to add a simple variant to a more complex selector', () => { - let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' - let candidate = 'hover:space-x-4' - - let formats = [{ format: '&:hover', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should be possible to add multiple simple variants to a more complex selector', () => { - let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' - let candidate = 'disabled:focus:hover:space-x-4' - - let formats = [ - { format: '&:hover', isArbitraryVariant: false }, - { format: '&:focus', isArbitraryVariant: false }, - { format: '&:disabled', isArbitraryVariant: false }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.disabled\\:focus\\:hover\\:space-x-4:hover:focus:disabled > :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should be possible to add a single merge variant to a simple selector', () => { - let selector = '.text-center' - let candidate = 'group-hover:text-center' - - let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover .group-hover\\:text-center' - ) - }) - - it('should be possible to add multiple merge variants to a simple selector', () => { - let selector = '.text-center' - let candidate = 'group-focus:group-hover:text-center' - - let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:focus:hover .group-focus\\:group-hover\\:text-center' - ) - }) - - it('should be possible to add a single merge variant to a more complex selector', () => { - let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])' - let candidate = 'group-hover:space-x-4' - - let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should be possible to add multiple merge variants to a more complex selector', () => { - let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])' - let candidate = 'group-focus:group-hover:space-x-4' - - let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:focus:hover .group-focus\\:group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should be possible to add multiple unique merge variants to a simple selector', () => { - let selector = '.text-center' - let candidate = 'peer-focus:group-hover:text-center' - - let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.peer):focus ~ &' }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.peer:focus ~ .group:hover .peer-focus\\:group-hover\\:text-center' - ) - }) - - it('should be possible to add multiple unique merge variants to a simple selector', () => { - let selector = '.text-center' - let candidate = 'group-hover:peer-focus:text-center' - - let formats = [ - { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, { format: ':merge(.group):hover &', isArbitraryVariant: false }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover .peer:focus ~ .group-hover\\:peer-focus\\:text-center' + '.group:hover .group-hover\\:hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])' ) }) - it('should be possible to use multiple :merge() calls with different "arguments"', () => { - let selector = '.foo' - let candidate = 'peer-focus:group-focus:peer-hover:group-hover:foo' + it('should work for group-hover and class dark mode combinations', () => { + let selector = '.text-center' + let candidate = 'dark:group-hover:text-center' let formats = [ { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.peer):hover ~ &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, - { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, + { format: '.dark &', isArbitraryVariant: false }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.peer:focus:hover ~ .group:focus:hover .peer-focus\\:group-focus\\:peer-hover\\:group-hover\\:foo' + '.dark .group:hover .dark\\:group-hover\\:text-center' ) }) - it('group hover and prose headings combination', () => { + it('should work for group-hover and class dark mode combinations (reversed)', () => { let selector = '.text-center' - let candidate = 'group-hover:prose-headings:text-center' + let candidate = 'group-hover:dark:text-center' + let formats = [ - { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover + { format: '.dark &' }, + { format: ':merge(.group):hover &', isArbitraryVariant: false }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover :where(.group-hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4)' + '.group:hover .dark .group-hover\\:dark\\:text-center' ) }) - it('group hover and prose headings combination flipped', () => { - let selector = '.text-center' - let candidate = 'prose-headings:group-hover:text-center' - let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover - { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - ':where(.group:hover .prose-headings\\:group-hover\\:text-center) :is(h1, h2, h3, h4)' - ) - }) - - it('should be possible to handle a complex utility', () => { - let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' - let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4' - let formats = [ - { format: '&:hover', isArbitraryVariant: false }, // Hover - { format: '&:focus', isArbitraryVariant: false }, // Focus - { format: ':merge(.group):focus &', isArbitraryVariant: false }, // Group focus - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group hover - { format: ':merge(.peer):first-child ~ &', isArbitraryVariant: false }, // Peer first-child - { format: ':merge(.peer):disabled ~ &', isArbitraryVariant: false }, // Peer disabled - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.peer:disabled:first-child ~ .group:hover:focus .peer-disabled\\:peer-first-child\\:group-hover\\:group-focus\\:focus\\:hover\\:space-x-4:hover:focus > :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should match base utilities that are prefixed', () => { - let context = { tailwindConfig: { prefix: 'tw-' } } - let selector = '.tw-text-center' - let candidate = 'tw-text-center' - let formats = [] - - expect(finalizeSelector(selector, formats, { candidate, context })).toEqual('.tw-text-center') - }) - - it('should prefix classes from variants', () => { - let context = { tailwindConfig: { prefix: 'tw-' } } - let selector = '.tw-text-center' - let candidate = 'foo:tw-text-center' - let formats = [{ format: '.foo &', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( - '.tw-foo .foo\\:tw-text-center' - ) - }) - - it('should not prefix classes from arbitrary variants', () => { - let context = { tailwindConfig: { prefix: 'tw-' } } - let selector = '.tw-text-center' - let candidate = '[.foo_&]:tw-text-center' - let formats = [{ format: '.foo &', isArbitraryVariant: true }] - - expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( - '.foo .\\[\\.foo_\\&\\]\\:tw-text-center' - ) - }) - - it('Merged selectors with mixed combinators uses the first one', () => { - // This isn't explicitly specced behavior but it is how it works today - - let selector = '.text-center' - let candidate = 'text-center' - let formats = [ - { format: ':merge(.group):focus > &', isArbitraryVariant: true }, - { format: ':merge(.group):hover &', isArbitraryVariant: true }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover:focus > .text-center' - ) - }) - - describe('real examples', () => { - it('example a', () => { - let selector = '.placeholder-red-500::placeholder' - let candidate = 'hover:placeholder-red-500' - - let formats = [{ format: '&:hover', isArbitraryVariant: false }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.hover\\:placeholder-red-500:hover::placeholder' - ) - }) - - it('example b', () => { - let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' - let candidate = 'group-hover:hover:space-x-4' - - let formats = [ - { format: '&:hover', isArbitraryVariant: false }, - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - ] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover .group-hover\\:hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])' - ) - }) - - it('should work for group-hover and class dark mode combinations', () => { + describe('prose-headings', () => { + it('should be possible to use hover:prose-headings:text-center', () => { let selector = '.text-center' - let candidate = 'dark:group-hover:text-center' + let candidate = 'hover:prose-headings:text-center' - let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: '.dark &', isArbitraryVariant: false }, - ] + let formats = [{ format: ':where(&) :is(h1, h2, h3, h4)' }, { format: '&:hover' }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.dark .group:hover .dark\\:group-hover\\:text-center' + ':where(.hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4):hover' ) }) - it('should work for group-hover and class dark mode combinations (reversed)', () => { + it('should be possible to use prose-headings:hover:text-center', () => { let selector = '.text-center' - let candidate = 'group-hover:dark:text-center' + let candidate = 'prose-headings:hover:text-center' - let formats = [ - { format: '.dark &' }, - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - ] + let formats = [{ format: '&:hover' }, { format: ':where(&) :is(h1, h2, h3, h4)' }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( - '.group:hover .dark .group-hover\\:dark\\:text-center' + ':where(.prose-headings\\:hover\\:text-center:hover) :is(h1, h2, h3, h4)' ) }) - - describe('prose-headings', () => { - it('should be possible to use hover:prose-headings:text-center', () => { - let selector = '.text-center' - let candidate = 'hover:prose-headings:text-center' - - let formats = [{ format: ':where(&) :is(h1, h2, h3, h4)' }, { format: '&:hover' }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - ':where(.hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4):hover' - ) - }) - - it('should be possible to use prose-headings:hover:text-center', () => { - let selector = '.text-center' - let candidate = 'prose-headings:hover:text-center' - - let formats = [{ format: '&:hover' }, { format: ':where(&) :is(h1, h2, h3, h4)' }] - - expect(finalizeSelector(selector, formats, { candidate })).toEqual( - ':where(.prose-headings\\:hover\\:text-center:hover) :is(h1, h2, h3, h4)' - ) - }) - }) - }) - - describe('pseudo elements', () => { - it.each` - before | after - ${'&::before'} | ${'&::before'} - ${'&::before:hover'} | ${'&:hover::before'} - ${'&:before:hover'} | ${'&:hover:before'} - ${'&::file-selector-button:hover'} | ${'&::file-selector-button:hover'} - ${'&:hover::file-selector-button'} | ${'&:hover::file-selector-button'} - ${'.parent:hover &'} | ${'.parent:hover &'} - ${'.parent::before &'} | ${'.parent &::before'} - ${'.parent::before &:hover'} | ${'.parent &:hover::before'} - ${':where(&::before) :is(h1, h2, h3, h4)'} | ${':where(&) :is(h1, h2, h3, h4)::before'} - ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} | ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} - ${'#app :is(.dark &::before)'} | ${'#app :is(.dark &)::before'} - ${'#app :is(:is(.dark &)::before)'} | ${'#app :is(:is(.dark &))::before'} - ${'#app :is(.foo::file-selector-button)'} | ${'#app :is(.foo)::file-selector-button'} - ${'#app :is(.foo::-webkit-progress-bar)'} | ${'#app :is(.foo)::-webkit-progress-bar'} - ${'.parent::marker li'} | ${'.parent li::marker'} - ${'.parent::selection li'} | ${'.parent li::selection'} - ${'.parent::placeholder input'} | ${'.parent input::placeholder'} - ${'.parent::backdrop dialog'} | ${'.parent dialog::backdrop'} - `('should translate "$before" into "$after"', ({ before, after }) => { - let result = finalizeSelector('.a', [{ format: before, isArbitraryVariant: false }], { - candidate: 'a', - }) - - expect(result).toEqual(after.replace('&', '.a')) - }) + }) +}) + +describe('pseudo elements', () => { + it.each` + before | after + ${'&::before'} | ${'&::before'} + ${'&::before:hover'} | ${'&:hover::before'} + ${'&:before:hover'} | ${'&:hover:before'} + ${'&::file-selector-button:hover'} | ${'&::file-selector-button:hover'} + ${'&:hover::file-selector-button'} | ${'&:hover::file-selector-button'} + ${'.parent:hover &'} | ${'.parent:hover &'} + ${'.parent::before &'} | ${'.parent &::before'} + ${'.parent::before &:hover'} | ${'.parent &:hover::before'} + ${':where(&::before) :is(h1, h2, h3, h4)'} | ${':where(&) :is(h1, h2, h3, h4)::before'} + ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} | ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} + ${'#app :is(.dark &::before)'} | ${'#app :is(.dark &)::before'} + ${'#app :is(:is(.dark &)::before)'} | ${'#app :is(:is(.dark &))::before'} + ${'#app :is(.foo::file-selector-button)'} | ${'#app :is(.foo)::file-selector-button'} + ${'#app :is(.foo::-webkit-progress-bar)'} | ${'#app :is(.foo)::-webkit-progress-bar'} + ${'.parent::marker li'} | ${'.parent li::marker'} + ${'.parent::selection li'} | ${'.parent li::selection'} + ${'.parent::placeholder input'} | ${'.parent input::placeholder'} + ${'.parent::backdrop dialog'} | ${'.parent dialog::backdrop'} + `('should translate "$before" into "$after"', ({ before, after }) => { + let result = finalizeSelector('.a', [{ format: before, isArbitraryVariant: false }], { + candidate: 'a', + }) + + expect(result).toEqual(after.replace('&', '.a')) }) }) diff --git a/tests/generate-rules.test.js b/tests/generate-rules.test.js index 3aa8592bc..57740c128 100644 --- a/tests/generate-rules.test.js +++ b/tests/generate-rules.test.js @@ -1,48 +1,46 @@ import { generateRules } from '../src/lib/generateRules' import resolveConfig from '../src/public/resolve-config' import { createContext } from '../src/lib/setupContextUtils' -import { crosscheck, css } from './util/run' +import { css } from './util/run' -crosscheck(() => { - it('should not generate rules that are incorrect', () => { - let config = { - plugins: [ - ({ matchVariant }) => { - matchVariant('@', (value) => `@container (min-width: ${value})`) - }, - ], +it('should not generate rules that are incorrect', () => { + let config = { + plugins: [ + ({ matchVariant }) => { + matchVariant('@', (value) => `@container (min-width: ${value})`) + }, + ], + } + let context = createContext(resolveConfig(config)) + let rules = generateRules( + new Set([ + // Invalid, missing `-` + 'group[:hover]:underline', + + // Invalid, `-` should not be there + '@-[200px]:underline', + + // Valid + 'group-[:hover]:underline', + '@[200px]:underline', + ]), + context + ) + + // Ensure we only have 2 valid rules + expect(rules).toHaveLength(2) + + // Ensure we have the correct values + expect(rules[0][1].toString()).toMatchFormattedCss(css` + .group:hover .group-\[\:hover\]\:underline { + text-decoration-line: underline; } - let context = createContext(resolveConfig(config)) - let rules = generateRules( - new Set([ - // Invalid, missing `-` - 'group[:hover]:underline', - - // Invalid, `-` should not be there - '@-[200px]:underline', - - // Valid - 'group-[:hover]:underline', - '@[200px]:underline', - ]), - context - ) - - // Ensure we only have 2 valid rules - expect(rules).toHaveLength(2) - - // Ensure we have the correct values - expect(rules[0][1].toString()).toMatchFormattedCss(css` - .group:hover .group-\[\:hover\]\:underline { + `) + expect(rules[1][1].toString()).toMatchFormattedCss(css` + @container (min-width: 200px) { + .\@\[200px\]\:underline { text-decoration-line: underline; } - `) - expect(rules[1][1].toString()).toMatchFormattedCss(css` - @container (min-width: 200px) { - .\@\[200px\]\:underline { - text-decoration-line: underline; - } - } - `) - }) + } + `) }) diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index 671a767ec..63dfea88c 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -1,228 +1,225 @@ import resolveConfig from '../src/public/resolve-config' import plugin from '../src/public/create-plugin' import { createContext } from '../src/lib/setupContextUtils' -import { crosscheck } from './util/run' -crosscheck(() => { - it('should generate every possible class, without variants', () => { - let config = {} +it('should generate every possible class, without variants', () => { + let config = {} - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() - expect(classes).toBeInstanceOf(Array) + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() + expect(classes).toBeInstanceOf(Array) - // Verify we have a `container` for the 'components' section. - expect(classes).toContain('container') + // Verify we have a `container` for the 'components' section. + expect(classes).toContain('container') - // Verify we handle the DEFAULT case correctly - expect(classes).toContain('border') + // Verify we handle the DEFAULT case correctly + expect(classes).toContain('border') - // Verify we handle negative values correctly - expect(classes).toContain('-inset-1/4') - expect(classes).toContain('-m-0') - expect(classes).not.toContain('-uppercase') - expect(classes).not.toContain('-opacity-50') + // Verify we handle negative values correctly + expect(classes).toContain('-inset-1/4') + expect(classes).toContain('-m-0') + expect(classes).not.toContain('-uppercase') + expect(classes).not.toContain('-opacity-50') - config = { theme: { extend: { margin: { DEFAULT: '5px' } } } } - context = createContext(resolveConfig(config)) - classes = context.getClassList() + config = { theme: { extend: { margin: { DEFAULT: '5px' } } } } + context = createContext(resolveConfig(config)) + classes = context.getClassList() - expect(classes).not.toContain('-m-DEFAULT') - }) + expect(classes).not.toContain('-m-DEFAULT') +}) - it('should generate every possible class while handling negatives and prefixes', () => { - let config = { prefix: 'tw-' } - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() - expect(classes).toBeInstanceOf(Array) +it('should generate every possible class while handling negatives and prefixes', () => { + let config = { prefix: 'tw-' } + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() + expect(classes).toBeInstanceOf(Array) - // Verify we have a `container` for the 'components' section. - expect(classes).toContain('tw-container') + // Verify we have a `container` for the 'components' section. + expect(classes).toContain('tw-container') - // Verify we handle the DEFAULT case correctly - expect(classes).toContain('tw-border') + // Verify we handle the DEFAULT case correctly + expect(classes).toContain('tw-border') - // Verify we handle negative values correctly - expect(classes).toContain('-tw-inset-1/4') - expect(classes).toContain('-tw-m-0') - expect(classes).not.toContain('-tw-uppercase') - expect(classes).not.toContain('-tw-opacity-50') + // Verify we handle negative values correctly + expect(classes).toContain('-tw-inset-1/4') + expect(classes).toContain('-tw-m-0') + expect(classes).not.toContain('-tw-uppercase') + expect(classes).not.toContain('-tw-opacity-50') - // These utilities do work but there's no reason to generate - // them alongside the `-{prefix}-{utility}` versions - expect(classes).not.toContain('tw--inset-1/4') - expect(classes).not.toContain('tw--m-0') + // These utilities do work but there's no reason to generate + // them alongside the `-{prefix}-{utility}` versions + expect(classes).not.toContain('tw--inset-1/4') + expect(classes).not.toContain('tw--m-0') - config = { - prefix: 'tw-', - theme: { extend: { margin: { DEFAULT: '5px' } } }, - } - context = createContext(resolveConfig(config)) - classes = context.getClassList() + config = { + prefix: 'tw-', + theme: { extend: { margin: { DEFAULT: '5px' } } }, + } + context = createContext(resolveConfig(config)) + classes = context.getClassList() - expect(classes).not.toContain('-tw-m-DEFAULT') - }) + expect(classes).not.toContain('-tw-m-DEFAULT') +}) - it('should not generate utilities with opacity by default', () => { - let config = {} - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() +it('should not generate utilities with opacity by default', () => { + let config = {} + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() - expect(classes).not.toContain('bg-red-500/50') - }) + expect(classes).not.toContain('bg-red-500/50') +}) - it('should not include metadata by default', () => { - let config = {} - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() +it('should not include metadata by default', () => { + let config = {} + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() - expect(classes.every((cls) => typeof cls === 'string')).toEqual(true) + expect(classes.every((cls) => typeof cls === 'string')).toEqual(true) - expect(classes).toContain('bg-red-500') - expect(classes).toContain('text-2xl') - }) + expect(classes).toContain('bg-red-500') + expect(classes).toContain('text-2xl') +}) - it('should generate utilities with modifier data when requested', () => { - let config = {} - let context = createContext(resolveConfig(config)) - let classes = context.getClassList({ includeMetadata: true }) +it('should generate utilities with modifier data when requested', () => { + let config = {} + let context = createContext(resolveConfig(config)) + let classes = context.getClassList({ includeMetadata: true }) - expect(classes).not.toContain('bg-red-500') - expect(classes).not.toContain('text-2xl') + expect(classes).not.toContain('bg-red-500') + expect(classes).not.toContain('text-2xl') - expect(classes).toContainEqual([ - 'bg-red-500', - { - modifiers: [ - '0', - '5', - '10', - '20', - '25', - '30', - '40', - '50', - '60', - '70', - '75', - '80', - '90', - '95', - '100', - ], - }, - ]) - expect(classes).toContainEqual([ - 'text-2xl', - { - modifiers: [ - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - 'none', - 'tight', - 'snug', - 'normal', - 'relaxed', - 'loose', - ], - }, - ]) - }) - - it('should generate plugin-defined utilities with modifier data when requested', () => { - let config = { - plugins: [ - plugin(function ({ matchUtilities }) { - matchUtilities( - { - foo: (value) => { - return { margin: value } - }, - }, - { - values: { xl: '32px' }, - modifiers: { bar: 'something' }, - } - ) - matchUtilities( - { - 'foo-negative': (value) => { - return { margin: value } - }, - }, - { - values: { xl: '32px' }, - modifiers: { bar: 'something' }, - supportsNegativeValues: true, - } - ) - }), + expect(classes).toContainEqual([ + 'bg-red-500', + { + modifiers: [ + '0', + '5', + '10', + '20', + '25', + '30', + '40', + '50', + '60', + '70', + '75', + '80', + '90', + '95', + '100', ], - } - let context = createContext(resolveConfig(config)) - let classes = context.getClassList({ includeMetadata: true }) - - expect(classes).toContainEqual(['foo-xl', { modifiers: ['bar'] }]) - expect(classes).toContainEqual(['foo-negative-xl', { modifiers: ['bar'] }]) - expect(classes).toContainEqual(['-foo-negative-xl', { modifiers: ['bar'] }]) - expect(classes).not.toContain('foo-xl') - expect(classes).not.toContain('-foo-xl') - expect(classes).not.toContainEqual(['-foo-xl', { modifiers: ['bar'] }]) - }) - - it('should not generate utilities with opacity even if safe-listed', () => { - let config = { - safelist: [ - { - pattern: /^bg-red-(400|500)(\/(40|50))?$/, - }, + }, + ]) + expect(classes).toContainEqual([ + 'text-2xl', + { + modifiers: [ + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + 'none', + 'tight', + 'snug', + 'normal', + 'relaxed', + 'loose', ], - } + }, + ]) +}) - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() - - expect(classes).not.toContain('bg-red-500/50') - }) - - it('should not generate utilities that are set to undefined or null to so that they are removed', () => { - let config = { - theme: { - extend: { - colors: { - red: null, - green: undefined, - blue: { - 100: null, - 200: undefined, +it('should generate plugin-defined utilities with modifier data when requested', () => { + let config = { + plugins: [ + plugin(function ({ matchUtilities }) { + matchUtilities( + { + foo: (value) => { + return { margin: value } }, }, + { + values: { xl: '32px' }, + modifiers: { bar: 'something' }, + } + ) + matchUtilities( + { + 'foo-negative': (value) => { + return { margin: value } + }, + }, + { + values: { xl: '32px' }, + modifiers: { bar: 'something' }, + supportsNegativeValues: true, + } + ) + }), + ], + } + let context = createContext(resolveConfig(config)) + let classes = context.getClassList({ includeMetadata: true }) + + expect(classes).toContainEqual(['foo-xl', { modifiers: ['bar'] }]) + expect(classes).toContainEqual(['foo-negative-xl', { modifiers: ['bar'] }]) + expect(classes).toContainEqual(['-foo-negative-xl', { modifiers: ['bar'] }]) + expect(classes).not.toContain('foo-xl') + expect(classes).not.toContain('-foo-xl') + expect(classes).not.toContainEqual(['-foo-xl', { modifiers: ['bar'] }]) +}) + +it('should not generate utilities with opacity even if safe-listed', () => { + let config = { + safelist: [ + { + pattern: /^bg-red-(400|500)(\/(40|50))?$/, + }, + ], + } + + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() + + expect(classes).not.toContain('bg-red-500/50') +}) + +it('should not generate utilities that are set to undefined or null to so that they are removed', () => { + let config = { + theme: { + extend: { + colors: { + red: null, + green: undefined, + blue: { + 100: null, + 200: undefined, + }, }, }, - safelist: [ - { - pattern: /^bg-(red|green|blue)-.*$/, - }, - ], - } + }, + safelist: [ + { + pattern: /^bg-(red|green|blue)-.*$/, + }, + ], + } - let context = createContext(resolveConfig(config)) - let classes = context.getClassList() + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() - expect(classes).not.toContain('bg-red-100') // Red is `null` + expect(classes).not.toContain('bg-red-100') // Red is `null` - expect(classes).not.toContain('bg-green-100') // Green is `undefined` + expect(classes).not.toContain('bg-green-100') // Green is `undefined` - expect(classes).not.toContain('bg-blue-100') // Blue.100 is `null` - expect(classes).not.toContain('bg-blue-200') // Blue.200 is `undefined` + expect(classes).not.toContain('bg-blue-100') // Blue.100 is `null` + expect(classes).not.toContain('bg-blue-200') // Blue.200 is `undefined` - expect(classes).toContain('bg-blue-50') - expect(classes).toContain('bg-blue-300') - }) + expect(classes).toContain('bg-blue-50') + expect(classes).toContain('bg-blue-300') }) diff --git a/tests/getSortOrder.test.js b/tests/getSortOrder.test.js index 4526c4874..0863d268d 100644 --- a/tests/getSortOrder.test.js +++ b/tests/getSortOrder.test.js @@ -1,7 +1,6 @@ import resolveConfig from '../src/public/resolve-config' import { createContext } from '../src/lib/setupContextUtils' import bigSign from '../src/util/bigSign' -import { crosscheck } from './util/run' /** * This is a function that the prettier-plugin-tailwindcss would use. It would @@ -25,121 +24,119 @@ function defaultSort(arrayOfTuples) { .join(' ') } -crosscheck(() => { - it('should return a list of tuples with the sort order', () => { - let input = 'font-bold underline hover:font-medium unknown' - let config = {} - let context = createContext(resolveConfig(config)) - expect(context.getClassOrder(input.split(' '))).toEqual([ - ['font-bold', expect.any(BigInt)], - ['underline', expect.any(BigInt)], - ['hover:font-medium', expect.any(BigInt)], +it('should return a list of tuples with the sort order', () => { + let input = 'font-bold underline hover:font-medium unknown' + let config = {} + let context = createContext(resolveConfig(config)) + expect(context.getClassOrder(input.split(' '))).toEqual([ + ['font-bold', expect.any(BigInt)], + ['underline', expect.any(BigInt)], + ['hover:font-medium', expect.any(BigInt)], - // Unknown values receive `null` - ['unknown', null], - ]) - }) + // Unknown values receive `null` + ['unknown', null], + ]) +}) - it.each([ - // Utitlies - ['px-3 p-1 py-3', 'p-1 px-3 py-3'], +it.each([ + // Utitlies + ['px-3 p-1 py-3', 'p-1 px-3 py-3'], - // Utitlies and components - ['px-4 container', 'container px-4'], + // Utitlies and components + ['px-4 container', 'container px-4'], - // Utilities with variants - ['px-3 focus:hover:p-3 hover:p-1 py-3', 'px-3 py-3 hover:p-1 focus:hover:p-3'], + // Utilities with variants + ['px-3 focus:hover:p-3 hover:p-1 py-3', 'px-3 py-3 hover:p-1 focus:hover:p-3'], - // Utitlies with important - ['px-3 !py-4', '!py-4 px-3'], - ['!py-4 px-3', '!py-4 px-3'], + // Utitlies with important + ['px-3 !py-4', '!py-4 px-3'], + ['!py-4 px-3', '!py-4 px-3'], - // Components with variants - ['hover:container container', 'container hover:container'], + // Components with variants + ['hover:container container', 'container hover:container'], - // Components and utilities with variants - [ - 'focus:hover:container hover:underline hover:container p-1', - 'p-1 hover:container focus:hover:container hover:underline', - ], + // Components and utilities with variants + [ + 'focus:hover:container hover:underline hover:container p-1', + 'p-1 hover:container focus:hover:container hover:underline', + ], - // Leave user css order alone, and move to the front - ['b p-1 a', 'b a p-1'], - ['hover:b focus:p-1 a', 'hover:b a focus:p-1'], + // Leave user css order alone, and move to the front + ['b p-1 a', 'b a p-1'], + ['hover:b focus:p-1 a', 'hover:b a focus:p-1'], - // Add special treatment for `group` and `peer` - ['a peer container underline', 'a peer container underline'], - ])('should sort "%s" based on the order we generate them in to "%s"', (input, output) => { - let config = {} + // Add special treatment for `group` and `peer` + ['a peer container underline', 'a peer container underline'], +])('should sort "%s" based on the order we generate them in to "%s"', (input, output) => { + let config = {} + let context = createContext(resolveConfig(config)) + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) +}) + +it.each([ + // Utitlies + ['tw-px-3 tw-p-1 tw-py-3', 'tw-p-1 tw-px-3 tw-py-3'], + + // Utitlies and components + ['tw-px-4 tw-container', 'tw-container tw-px-4'], + + // Utilities with variants + [ + 'tw-px-3 focus:hover:tw-p-3 hover:tw-p-1 tw-py-3', + 'tw-px-3 tw-py-3 hover:tw-p-1 focus:hover:tw-p-3', + ], + + // Utitlies with important + ['tw-px-3 !tw-py-4', '!tw-py-4 tw-px-3'], + ['!tw-py-4 tw-px-3', '!tw-py-4 tw-px-3'], + + // Components with variants + ['hover:tw-container tw-container', 'tw-container hover:tw-container'], + + // Components and utilities with variants + [ + 'focus:hover:tw-container hover:tw-underline hover:tw-container tw-p-1', + 'tw-p-1 hover:tw-container focus:hover:tw-container hover:tw-underline', + ], + + // Leave user css order alone, and move to the front + ['b tw-p-1 a', 'b a tw-p-1'], + ['hover:b focus:tw-p-1 a', 'hover:b a focus:tw-p-1'], + + // Add special treatment for `group` and `peer` + ['a tw-peer tw-container tw-underline', 'a tw-peer tw-container tw-underline'], +])( + 'should sort "%s" with prefixex based on the order we generate them in to "%s"', + (input, output) => { + let config = { prefix: 'tw-' } let context = createContext(resolveConfig(config)) expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) - }) + } +) - it.each([ - // Utitlies - ['tw-px-3 tw-p-1 tw-py-3', 'tw-p-1 tw-px-3 tw-py-3'], - - // Utitlies and components - ['tw-px-4 tw-container', 'tw-container tw-px-4'], - - // Utilities with variants +it('sorts classes deterministically across multiple class lists', () => { + let classes = [ [ - 'tw-px-3 focus:hover:tw-p-3 hover:tw-p-1 tw-py-3', - 'tw-px-3 tw-py-3 hover:tw-p-1 focus:hover:tw-p-3', + 'a-class px-3 p-1 b-class py-3 bg-red-500 bg-blue-500', + 'a-class b-class bg-blue-500 bg-red-500 p-1 px-3 py-3', ], - - // Utitlies with important - ['tw-px-3 !tw-py-4', '!tw-py-4 tw-px-3'], - ['!tw-py-4 tw-px-3', '!tw-py-4 tw-px-3'], - - // Components with variants - ['hover:tw-container tw-container', 'tw-container hover:tw-container'], - - // Components and utilities with variants [ - 'focus:hover:tw-container hover:tw-underline hover:tw-container tw-p-1', - 'tw-p-1 hover:tw-container focus:hover:tw-container hover:tw-underline', + 'px-3 b-class p-1 py-3 bg-blue-500 a-class bg-red-500', + 'b-class a-class bg-blue-500 bg-red-500 p-1 px-3 py-3', ], + ] - // Leave user css order alone, and move to the front - ['b tw-p-1 a', 'b a tw-p-1'], - ['hover:b focus:tw-p-1 a', 'hover:b a focus:tw-p-1'], + let config = {} - // Add special treatment for `group` and `peer` - ['a tw-peer tw-container tw-underline', 'a tw-peer tw-container tw-underline'], - ])( - 'should sort "%s" with prefixex based on the order we generate them in to "%s"', - (input, output) => { - let config = { prefix: 'tw-' } - let context = createContext(resolveConfig(config)) - expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) - } - ) + // Same context, different class lists + let context = createContext(resolveConfig(config)) + for (const [input, output] of classes) { + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) + } - it('sorts classes deterministically across multiple class lists', () => { - let classes = [ - [ - 'a-class px-3 p-1 b-class py-3 bg-red-500 bg-blue-500', - 'a-class b-class bg-blue-500 bg-red-500 p-1 px-3 py-3', - ], - [ - 'px-3 b-class p-1 py-3 bg-blue-500 a-class bg-red-500', - 'b-class a-class bg-blue-500 bg-red-500 p-1 px-3 py-3', - ], - ] - - let config = {} - - // Same context, different class lists - let context = createContext(resolveConfig(config)) - for (const [input, output] of classes) { - expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) - } - - // Different context, different class lists - for (const [input, output] of classes) { - context = createContext(resolveConfig(config)) - expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) - } - }) + // Different context, different class lists + for (const [input, output] of classes) { + context = createContext(resolveConfig(config)) + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) + } }) diff --git a/tests/getVariants.test.js b/tests/getVariants.test.js index 8d79f8243..af34fd0e3 100644 --- a/tests/getVariants.test.js +++ b/tests/getVariants.test.js @@ -2,188 +2,185 @@ import postcss from 'postcss' import selectorParser from 'postcss-selector-parser' import resolveConfig from '../src/public/resolve-config' import { createContext } from '../src/lib/setupContextUtils' -import { crosscheck } from './util/run' -crosscheck(() => { - it('should return a list of variants with meta information about the variant', () => { - let config = {} - let context = createContext(resolveConfig(config)) +it('should return a list of variants with meta information about the variant', () => { + let config = {} + let context = createContext(resolveConfig(config)) - let variants = context.getVariants() + let variants = context.getVariants() - expect(variants).toContainEqual({ - name: 'hover', - isArbitrary: false, - hasDash: true, - values: [], - selectors: expect.any(Function), - }) - - expect(variants).toContainEqual({ - name: 'group', - isArbitrary: true, - hasDash: true, - values: expect.any(Array), - selectors: expect.any(Function), - }) - - // `group-hover` now belongs to the `group` variant. The information exposed for the `group` - // variant is all you need. - expect(variants.find((v) => v.name === 'group-hover')).toBeUndefined() + expect(variants).toContainEqual({ + name: 'hover', + isArbitrary: false, + hasDash: true, + values: [], + selectors: expect.any(Function), }) - it('should provide selectors for simple variants', () => { - let config = {} - let context = createContext(resolveConfig(config)) - - let variants = context.getVariants() - - let variant = variants.find((v) => v.name === 'hover') - expect(variant.selectors()).toEqual(['&:hover']) + expect(variants).toContainEqual({ + name: 'group', + isArbitrary: true, + hasDash: true, + values: expect.any(Array), + selectors: expect.any(Function), }) - it('should provide selectors for parallel variants', () => { - let config = {} - let context = createContext(resolveConfig(config)) + // `group-hover` now belongs to the `group` variant. The information exposed for the `group` + // variant is all you need. + expect(variants.find((v) => v.name === 'group-hover')).toBeUndefined() +}) - let variants = context.getVariants() +it('should provide selectors for simple variants', () => { + let config = {} + let context = createContext(resolveConfig(config)) - let variant = variants.find((v) => v.name === 'marker') - expect(variant.selectors()).toEqual(['& *::marker', '&::marker']) - }) + let variants = context.getVariants() - it('should provide selectors for complex matchVariant variants like `group`', () => { - let config = {} - let context = createContext(resolveConfig(config)) + let variant = variants.find((v) => v.name === 'hover') + expect(variant.selectors()).toEqual(['&:hover']) +}) - let variants = context.getVariants() +it('should provide selectors for parallel variants', () => { + let config = {} + let context = createContext(resolveConfig(config)) - let variant = variants.find((v) => v.name === 'group') - expect(variant.selectors()).toEqual(['.group &']) - expect(variant.selectors({})).toEqual(['.group &']) - expect(variant.selectors({ value: 'hover' })).toEqual(['.group:hover &']) - expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .group &']) - expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual(['.group\\/foo:hover &']) - expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &']) - }) + let variants = context.getVariants() - it('should provide selectors for variants with atrules', () => { - let config = {} - let context = createContext(resolveConfig(config)) + let variant = variants.find((v) => v.name === 'marker') + expect(variant.selectors()).toEqual(['& *::marker', '&::marker']) +}) - let variants = context.getVariants() +it('should provide selectors for complex matchVariant variants like `group`', () => { + let config = {} + let context = createContext(resolveConfig(config)) - let variant = variants.find((v) => v.name === 'supports') - expect(variant.selectors({ value: 'display:grid' })).toEqual(['@supports (display:grid)']) - expect(variant.selectors({ value: 'aspect-ratio' })).toEqual([ - '@supports (aspect-ratio: var(--tw))', - ]) - }) + let variants = context.getVariants() - it('should provide selectors for custom plugins that do a combination of parallel variants with modifiers with arbitrary values and with atrules', () => { - let config = { - plugins: [ - function ({ matchVariant }) { - matchVariant('foo', (value, { modifier }) => { - return [ - ` + let variant = variants.find((v) => v.name === 'group') + expect(variant.selectors()).toEqual(['.group &']) + expect(variant.selectors({})).toEqual(['.group &']) + expect(variant.selectors({ value: 'hover' })).toEqual(['.group:hover &']) + expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .group &']) + expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual(['.group\\/foo:hover &']) + expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &']) +}) + +it('should provide selectors for variants with atrules', () => { + let config = {} + let context = createContext(resolveConfig(config)) + + let variants = context.getVariants() + + let variant = variants.find((v) => v.name === 'supports') + expect(variant.selectors({ value: 'display:grid' })).toEqual(['@supports (display:grid)']) + expect(variant.selectors({ value: 'aspect-ratio' })).toEqual([ + '@supports (aspect-ratio: var(--tw))', + ]) +}) + +it('should provide selectors for custom plugins that do a combination of parallel variants with modifiers with arbitrary values and with atrules', () => { + let config = { + plugins: [ + function ({ matchVariant }) { + matchVariant('foo', (value, { modifier }) => { + return [ + ` @supports (foo: ${modifier}) { @media (width <= 400px) { &:hover } } `, - `.${modifier}\\/${value} &:focus`, - ] - }) - }, - ], - } - let context = createContext(resolveConfig(config)) + `.${modifier}\\/${value} &:focus`, + ] + }) + }, + ], + } + let context = createContext(resolveConfig(config)) - let variants = context.getVariants() + let variants = context.getVariants() - let variant = variants.find((v) => v.name === 'foo') - expect(variant.selectors({ modifier: 'bar', value: 'baz' })).toEqual([ - '@supports (foo: bar) { @media (width <= 400px) { &:hover } }', - '.bar\\/baz &:focus', - ]) - }) - - it('should work for plugins that still use the modifySelectors API', () => { - let config = { - plugins: [ - function ({ addVariant }) { - addVariant('foo', ({ modifySelectors, container }) => { - // Manually mutating the selector - modifySelectors(({ selector }) => { - return selectorParser((selectors) => { - selectors.walkClasses((classNode) => { - classNode.value = `foo:${classNode.value}` - classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `)) - }) - }).processSync(selector) - }) - - // Manually wrap in supports query - let wrapper = postcss.atRule({ name: 'supports', params: 'display: grid' }) - let nodes = container.nodes - container.removeAll() - wrapper.append(nodes) - container.append(wrapper) - }) - }, - ], - } - let context = createContext(resolveConfig(config)) - - let variants = context.getVariants() - - let variant = variants.find((v) => v.name === 'foo') - expect(variant.selectors({})).toEqual(['@supports (display: grid) { .foo .foo\\:& }']) - }) - - it('should special case the `@`', () => { - let config = { - plugins: [ - ({ matchVariant }) => { - matchVariant( - '@', - (value, { modifier }) => `@container ${modifier ?? ''} (min-width: ${value})`, - { - modifiers: 'any', - values: { - xs: '20rem', - sm: '24rem', - md: '28rem', - lg: '32rem', - xl: '36rem', - '2xl': '42rem', - '3xl': '48rem', - '4xl': '56rem', - '5xl': '64rem', - '6xl': '72rem', - '7xl': '80rem', - }, - } - ) - }, - ], - } - let context = createContext(resolveConfig(config)) - - let variants = context.getVariants() - - let variant = variants.find((v) => v.name === '@') - expect(variant).toEqual({ - name: '@', - isArbitrary: true, - hasDash: false, - values: expect.any(Array), - selectors: expect.any(Function), - }) - expect(variant.selectors({ value: 'xs', modifier: 'foo' })).toEqual([ - '@container foo (min-width: 20rem)', - ]) - }) + let variant = variants.find((v) => v.name === 'foo') + expect(variant.selectors({ modifier: 'bar', value: 'baz' })).toEqual([ + '@supports (foo: bar) { @media (width <= 400px) { &:hover } }', + '.bar\\/baz &:focus', + ]) +}) + +it('should work for plugins that still use the modifySelectors API', () => { + let config = { + plugins: [ + function ({ addVariant }) { + addVariant('foo', ({ modifySelectors, container }) => { + // Manually mutating the selector + modifySelectors(({ selector }) => { + return selectorParser((selectors) => { + selectors.walkClasses((classNode) => { + classNode.value = `foo:${classNode.value}` + classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `)) + }) + }).processSync(selector) + }) + + // Manually wrap in supports query + let wrapper = postcss.atRule({ name: 'supports', params: 'display: grid' }) + let nodes = container.nodes + container.removeAll() + wrapper.append(nodes) + container.append(wrapper) + }) + }, + ], + } + let context = createContext(resolveConfig(config)) + + let variants = context.getVariants() + + let variant = variants.find((v) => v.name === 'foo') + expect(variant.selectors({})).toEqual(['@supports (display: grid) { .foo .foo\\:& }']) +}) + +it('should special case the `@`', () => { + let config = { + plugins: [ + ({ matchVariant }) => { + matchVariant( + '@', + (value, { modifier }) => `@container ${modifier ?? ''} (min-width: ${value})`, + { + modifiers: 'any', + values: { + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + '7xl': '80rem', + }, + } + ) + }, + ], + } + let context = createContext(resolveConfig(config)) + + let variants = context.getVariants() + + let variant = variants.find((v) => v.name === '@') + expect(variant).toEqual({ + name: '@', + isArbitrary: true, + hasDash: false, + values: expect.any(Array), + selectors: expect.any(Function), + }) + expect(variant.selectors({ value: 'xs', modifier: 'foo' })).toEqual([ + '@container foo (min-width: 20rem)', + ]) }) diff --git a/tests/import-processing.test.js b/tests/import-processing.test.js index 962b7b28c..ef59be348 100644 --- a/tests/import-processing.test.js +++ b/tests/import-processing.test.js @@ -1,79 +1,76 @@ -import { html, css, run, crosscheck } from './util/run' +import { html, css, run } from './util/run' -crosscheck(({ oxide, stable }) => { - stable.test.todo('Import processing is an oxide engine feature') - oxide.describe('import processing', () => { - it('should be possible to import another css file', async () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, +describe('import processing', () => { + it('should be possible to import another css file', async () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @import './import-processing-a.css'; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .underline { + text-decoration-line: underline; + } + `) + }) + + it('should be possible to import another css file after @tailwind directive', async () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + + @import './import-processing-b.css'; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .underline { + text-decoration-line: underline; } - let input = css` - @import './import-processing-a.css'; - ` + .foo { + color: red; + } + `) + }) - let result = await run(input, config) + it('should be possible to add @config before @import statements', async () => { + let input = css` + @config "./import-processing-c.js"; + @import './import-processing-c.css'; + ` - expect(result.css).toMatchFormattedCss(css` - .underline { - text-decoration-line: underline; - } - `) - }) + let result = await run(input) - it('should be possible to import another css file after @tailwind directive', async () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, + expect(result.css).toMatchFormattedCss(css` + .underline { + text-decoration-line: underline; } - let input = css` - @tailwind utilities; - - @import './import-processing-b.css'; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .underline { - text-decoration-line: underline; - } - - .foo { - color: red; - } - `) - }) - - it('should be possible to add @config before @import statements', async () => { - let input = css` - @config "./import-processing-c.js"; - @import './import-processing-c.css'; - ` - - let result = await run(input) - - expect(result.css).toMatchFormattedCss(css` - .underline { - text-decoration-line: underline; - } - - .foo { - color: red; - } - `) - }) + .foo { + color: red; + } + `) }) }) diff --git a/tests/import-syntax.test.js b/tests/import-syntax.test.js index 992ef81d5..99f5163f7 100644 --- a/tests/import-syntax.test.js +++ b/tests/import-syntax.test.js @@ -1,129 +1,82 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('using @import instead of @tailwind', () => { - let config = { - content: [ - { - raw: html` -

Hello world!

-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ addBase }) { - addBase({ - h1: { - fontSize: '32px', - }, - }) - }, - ], - } +test('using @import instead of @tailwind', () => { + let config = { + content: [ + { + raw: html` +

Hello world!

+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addBase }) { + addBase({ + h1: { + fontSize: '32px', + }, + }) + }, + ], + } - let input = css` - @import 'tailwindcss/base'; - @import 'tailwindcss/components'; - @import 'tailwindcss/utilities'; - ` + let input = css` + @import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - h1 { - font-size: 32px; - } - ${defaults} + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + h1 { + font-size: 32px; + } + ${defaults} + .container { + width: 100%; + } + @media (min-width: 640px) { .container { - width: 100%; + max-width: 640px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - .mt-6 { - margin-top: 1.5rem; - } - .bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - @media (min-width: 768px) { - .md\:hover\:text-center:hover { - text-align: center; - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - h1 { - font-size: 32px; - } - ${defaults} + } + @media (min-width: 768px) { .container { - width: 100%; + max-width: 768px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; } - @media (min-width: 768px) { - .container { - max-width: 768px; - } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } + } + .mt-6 { + margin-top: 1.5rem; + } + .bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + @media (min-width: 768px) { + .md\:hover\:text-center:hover { + text-align: center; } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - .mt-6 { - margin-top: 1.5rem; - } - .bg-black { - background-color: #000; - } - @media (min-width: 768px) { - .md\:hover\:text-center:hover { - text-align: center; - } - } - `) - }) + } + `) }) }) diff --git a/tests/important-boolean.test.js b/tests/important-boolean.test.js index 895ca053c..d66f21edc 100644 --- a/tests/important-boolean.test.js +++ b/tests/important-boolean.test.js @@ -2,230 +2,228 @@ import fs from 'fs' import path from 'path' import * as sharedState from '../src/lib/sharedState' -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - test('important boolean', () => { - let config = { - important: true, - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ addComponents, addUtilities }) { - addComponents( - { - '.btn': { - button: 'yes', - }, +test('important boolean', () => { + let config = { + important: true, + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addComponents, addUtilities }) { + addComponents( + { + '.btn': { + button: 'yes', }, - { respectImportant: true } - ) - addComponents( - { - '@font-face': { - 'font-family': 'Inter', - }, - '@page': { - margin: '1cm', - }, + }, + { respectImportant: true } + ) + addComponents( + { + '@font-face': { + 'font-family': 'Inter', }, - { respectImportant: true } - ) - addUtilities( - { - '.custom-util': { - button: 'no', - }, + '@page': { + margin: '1cm', }, - { respectImportant: false } - ) - }, - ], + }, + { respectImportant: true } + ) + addUtilities( + { + '.custom-util': { + button: 'no', + }, + }, + { respectImportant: false } + ) + }, + ], + } + + let input = css` + @tailwind base; + @tailwind components; + @layer components { + .custom-component { + @apply font-bold; + } + .custom-important-component { + @apply text-center !important; + } } + @tailwind utilities; + ` - let input = css` - @tailwind base; - @tailwind components; - @layer components { - .custom-component { - @apply font-bold; - } - .custom-important-component { - @apply text-center !important; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .container { + width: 100%; + } + @media (min-width: 640px) { + .container { + max-width: 640px; } } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; + } + } + .btn { + button: yes !important; + } + @font-face { + font-family: Inter; + } + @page { + margin: 1cm; + } + .custom-component { + font-weight: 700; + } + .custom-important-component { + text-align: center !important; + } + @keyframes spin { + to { + transform: rotate(360deg); + } + } + .animate-spin { + animation: 1s linear infinite spin !important; + } + .font-bold { + font-weight: 700 !important; + } + .custom-util { + button: no; + } + .group:hover .group-hover\:focus-within\:text-left:focus-within { + text-align: left !important; + } + :is([dir='rtl'] .rtl\:active\:text-center:active) { + text-align: center !important; + } + @media (prefers-reduced-motion: no-preference) { + .motion-safe\:hover\:text-center:hover { + text-align: center !important; + } + } + :is(.dark .dark\:focus\:text-left:focus) { + text-align: left !important; + } + @media (min-width: 768px) { + .md\:hover\:text-right:hover { + text-align: right !important; + } + } + `) + }) +}) + +// This is in a describe block so we can use `afterEach` :) +describe('duplicate elision', () => { + let filePath = path.resolve(__dirname, './important-boolean-duplicates.test.html') + + afterEach(async () => await fs.promises.unlink(filePath)) + + test('important rules are not duplicated when rebuilding', async () => { + let config = { + important: true, + content: [filePath], + } + + await fs.promises.writeFile( + config.content[0], + html` +
+
+ ` + ) + + let input = css` @tailwind utilities; ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .container { - width: 100%; - } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - .btn { - button: yes !important; - } - @font-face { - font-family: Inter; - } - @page { - margin: 1cm; - } - .custom-component { - font-weight: 700; - } - .custom-important-component { - text-align: center !important; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .animate-spin { - animation: 1s linear infinite spin !important; - } - .font-bold { - font-weight: 700 !important; - } - .custom-util { - button: no; - } - .group:hover .group-hover\:focus-within\:text-left:focus-within { - text-align: left !important; - } - :is([dir='rtl'] .rtl\:active\:text-center:active) { - text-align: center !important; - } - @media (prefers-reduced-motion: no-preference) { - .motion-safe\:hover\:text-center:hover { - text-align: center !important; - } - } - :is(.dark .dark\:focus\:text-left:focus) { - text-align: left !important; - } - @media (min-width: 768px) { - .md\:hover\:text-right:hover { - text-align: right !important; - } - } - `) - }) - }) + let result = await run(input, config) + let allContexts = Array.from(sharedState.contextMap.values()) - // This is in a describe block so we can use `afterEach` :) - describe('duplicate elision', () => { - let filePath = path.resolve(__dirname, './important-boolean-duplicates.test.html') + let context = allContexts[allContexts.length - 1] - afterEach(async () => await fs.promises.unlink(filePath)) + let ruleCacheSize1 = context.ruleCache.size - test('important rules are not duplicated when rebuilding', async () => { - let config = { - important: true, - content: [filePath], + expect(result.css).toMatchFormattedCss(css` + .ml-2 { + margin-left: 0.5rem !important; } + .ml-4 { + margin-left: 1rem !important; + } + `) - await fs.promises.writeFile( - config.content[0], - html` -
-
- ` - ) - - let input = css` - @tailwind utilities; + await fs.promises.writeFile( + config.content[0], + html` +
+
` + ) - let result = await run(input, config) - let allContexts = Array.from(sharedState.contextMap.values()) + result = await run(input, config) - let context = allContexts[allContexts.length - 1] + let ruleCacheSize2 = context.ruleCache.size - let ruleCacheSize1 = context.ruleCache.size + expect(result.css).toMatchFormattedCss(css` + .ml-2 { + margin-left: 0.5rem !important; + } + .ml-4 { + margin-left: 1rem !important; + } + .ml-6 { + margin-left: 1.5rem !important; + } + `) - expect(result.css).toMatchFormattedCss(css` - .ml-2 { - margin-left: 0.5rem !important; - } - .ml-4 { - margin-left: 1rem !important; - } - `) - - await fs.promises.writeFile( - config.content[0], - html` -
-
- ` - ) - - result = await run(input, config) - - let ruleCacheSize2 = context.ruleCache.size - - expect(result.css).toMatchFormattedCss(css` - .ml-2 { - margin-left: 0.5rem !important; - } - .ml-4 { - margin-left: 1rem !important; - } - .ml-6 { - margin-left: 1.5rem !important; - } - `) - - // The rule cache was effectively doubling in size previously - // because the rule cache was never de-duped - // This ensures this behavior doesn't return - expect(ruleCacheSize2 - ruleCacheSize1).toBeLessThan(10) - }) + // The rule cache was effectively doubling in size previously + // because the rule cache was never de-duped + // This ensures this behavior doesn't return + expect(ruleCacheSize2 - ruleCacheSize1).toBeLessThan(10) }) }) diff --git a/tests/important-modifier-prefix.test.js b/tests/important-modifier-prefix.test.js index 782ec809e..2c6a8bb62 100644 --- a/tests/important-modifier-prefix.test.js +++ b/tests/important-modifier-prefix.test.js @@ -1,79 +1,77 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - test('important modifier with prefix', () => { - let config = { - important: false, - prefix: 'tw-', - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
`, - }, - ], - corePlugins: { preflight: false }, - } +test('important modifier with prefix', () => { + let config = { + important: false, + prefix: 'tw-', + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .\!tw-container { + width: 100% !important; + } + @media (min-width: 640px) { .\!tw-container { - width: 100% !important; + max-width: 640px !important; } - @media (min-width: 640px) { - .\!tw-container { - max-width: 640px !important; - } + } + @media (min-width: 768px) { + .\!tw-container { + max-width: 768px !important; } - @media (min-width: 768px) { - .\!tw-container { - max-width: 768px !important; - } + } + @media (min-width: 1024px) { + .\!tw-container { + max-width: 1024px !important; } - @media (min-width: 1024px) { - .\!tw-container { - max-width: 1024px !important; - } + } + @media (min-width: 1280px) { + .\!tw-container { + max-width: 1280px !important; } - @media (min-width: 1280px) { - .\!tw-container { - max-width: 1280px !important; - } + } + @media (min-width: 1536px) { + .\!tw-container { + max-width: 1536px !important; } - @media (min-width: 1536px) { - .\!tw-container { - max-width: 1536px !important; - } + } + .\!tw-font-bold { + font-weight: 700 !important; + } + .hover\:\!tw-text-center:hover { + text-align: center !important; + } + @media (min-width: 1024px) { + .lg\:\!tw-opacity-50 { + opacity: 0.5 !important; } - .\!tw-font-bold { - font-weight: 700 !important; + } + @media (min-width: 1280px) { + .xl\:focus\:disabled\:\!tw-float-right:disabled:focus { + float: right !important; } - .hover\:\!tw-text-center:hover { - text-align: center !important; - } - @media (min-width: 1024px) { - .lg\:\!tw-opacity-50 { - opacity: 0.5 !important; - } - } - @media (min-width: 1280px) { - .xl\:focus\:disabled\:\!tw-float-right:disabled:focus { - float: right !important; - } - } - `) - }) + } + `) }) }) diff --git a/tests/important-modifier.test.js b/tests/important-modifier.test.js index 6d30fd0bc..c736951d0 100644 --- a/tests/important-modifier.test.js +++ b/tests/important-modifier.test.js @@ -1,153 +1,151 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('important modifier', () => { - let config = { - important: false, - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ theme, matchUtilities, addComponents }) { - matchUtilities( - { - 'custom-parent': (value) => { - return { - '.custom-child': { - margin: value, - }, - } - }, +test('important modifier', () => { + let config = { + important: false, + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ theme, matchUtilities, addComponents }) { + matchUtilities( + { + 'custom-parent': (value) => { + return { + '.custom-child': { + margin: value, + }, + } }, - { values: theme('spacing') } - ) - addComponents({ - '.btn': { - '&.disabled, &:disabled': { - color: 'gray', - }, + }, + { values: theme('spacing') } + ) + addComponents({ + '.btn': { + '&.disabled, &:disabled': { + color: 'gray', }, - }) - }, - ], - } + }, + }) + }, + ], + } - let input = css` - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\!container { + width: 100% !important; + } + @media (min-width: 640px) { .\!container { - width: 100% !important; + max-width: 640px !important; } - @media (min-width: 640px) { - .\!container { - max-width: 640px !important; - } + } + @media (min-width: 768px) { + .\!container { + max-width: 768px !important; } - @media (min-width: 768px) { - .\!container { - max-width: 768px !important; - } + } + @media (min-width: 1024px) { + .\!container { + max-width: 1024px !important; } - @media (min-width: 1024px) { - .\!container { - max-width: 1024px !important; - } + } + @media (min-width: 1280px) { + .\!container { + max-width: 1280px !important; } - @media (min-width: 1280px) { - .\!container { - max-width: 1280px !important; - } + } + @media (min-width: 1536px) { + .\!container { + max-width: 1536px !important; } - @media (min-width: 1536px) { - .\!container { - max-width: 1536px !important; - } + } + .btn.disabled, + .btn:disabled { + color: gray; + } + .btn.\!disabled { + color: gray !important; + } + .\!font-bold { + font-weight: 700 !important; + } + .\!custom-parent-5 .custom-child { + margin: 1.25rem !important; + } + .hover\:\!text-center:hover { + text-align: center !important; + } + @media (min-width: 1024px) { + .lg\:\!opacity-50 { + opacity: 0.5 !important; } - .btn.disabled, - .btn:disabled { - color: gray; + } + @media (min-width: 1280px) { + .xl\:focus\:disabled\:\!float-right:disabled:focus { + float: right !important; } - .btn.\!disabled { - color: gray !important; - } - .\!font-bold { - font-weight: 700 !important; - } - .\!custom-parent-5 .custom-child { - margin: 1.25rem !important; - } - .hover\:\!text-center:hover { - text-align: center !important; - } - @media (min-width: 1024px) { - .lg\:\!opacity-50 { - opacity: 0.5 !important; - } - } - @media (min-width: 1280px) { - .xl\:focus\:disabled\:\!float-right:disabled:focus { - float: right !important; - } - } - `) - }) - }) - - test('the important modifier works on utilities using :where()', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ addComponents }) { - addComponents({ - ':where(.btn)': { - backgroundColor: '#00f', - }, - }) - }, - ], - } - - let input = css` - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - :where(.\!btn) { - background-color: #00f !important; - } - :where(.btn) { - background-color: #00f; - } - :where(.hover\:btn:hover) { - background-color: #00f; - } - :where(.hover\:focus\:disabled\:\!btn:disabled:focus:hover) { - background-color: #00f !important; - } - `) - }) + } + `) + }) +}) + +test('the important modifier works on utilities using :where()', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addComponents }) { + addComponents({ + ':where(.btn)': { + backgroundColor: '#00f', + }, + }) + }, + ], + } + + let input = css` + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + :where(.\!btn) { + background-color: #00f !important; + } + :where(.btn) { + background-color: #00f; + } + :where(.hover\:btn:hover) { + background-color: #00f; + } + :where(.hover\:focus\:disabled\:\!btn:disabled:focus:hover) { + background-color: #00f !important; + } + `) }) }) diff --git a/tests/important-selector.test.js b/tests/important-selector.test.js index 22edafb94..a5399bdf7 100644 --- a/tests/important-selector.test.js +++ b/tests/important-selector.test.js @@ -1,205 +1,195 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('important selector', () => { - let config = { - important: '#app', - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - function ({ addComponents, addUtilities }) { - addComponents( - { - '.btn': { - button: 'yes', - }, +test('important selector', () => { + let config = { + important: '#app', + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addComponents, addUtilities }) { + addComponents( + { + '.btn': { + button: 'yes', }, - { respectImportant: true } - ) - addComponents( - { - '@font-face': { - 'font-family': 'Inter', - }, - '@page': { - margin: '1cm', - }, + }, + { respectImportant: true } + ) + addComponents( + { + '@font-face': { + 'font-family': 'Inter', }, - { respectImportant: true } - ) - addUtilities( - { - '.custom-util': { - button: 'no', - }, + '@page': { + margin: '1cm', }, - { respectImportant: false } - ) - }, - ], + }, + { respectImportant: true } + ) + addUtilities( + { + '.custom-util': { + button: 'no', + }, + }, + { respectImportant: false } + ) + }, + ], + } + + let input = css` + @tailwind base; + @tailwind components; + @layer components { + .custom-component { + @apply font-bold; + } + .custom-important-component { + @apply text-center !important; + } } + @tailwind utilities; + ` - let input = css` - @tailwind base; - @tailwind components; - @layer components { - .custom-component { - @apply font-bold; - } - .custom-important-component { - @apply text-center !important; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .container { + width: 100%; + } + @media (min-width: 640px) { + .container { + max-width: 640px; } } - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} + @media (min-width: 768px) { .container { - width: 100%; + max-width: 768px; } - @media (min-width: 640px) { - .container { - max-width: 640px; - } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; } - @media (min-width: 768px) { - .container { - max-width: 768px; - } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } + } + #app .btn { + button: yes; + } + @font-face { + font-family: Inter; + } + @page { + margin: 1cm; + } + .custom-component { + font-weight: 700; + } + .custom-important-component { + text-align: center !important; + } + @keyframes spin { + to { + transform: rotate(360deg); } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - #app .btn { - button: yes; - } - @font-face { - font-family: Inter; - } - @page { - margin: 1cm; - } - .custom-component { - font-weight: 700; - } - .custom-important-component { - text-align: center !important; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - #app :is(.animate-spin) { - animation: 1s linear infinite spin; - } - #app :is(.font-bold) { - font-weight: 700; - } - .custom-util { - button: no; - } - #app :is(.group:hover .group-hover\:focus-within\:text-left:focus-within) { - text-align: left; - } - #app :is(:is([dir='rtl'] .rtl\:active\:text-center:active)) { + } + #app :is(.animate-spin) { + animation: 1s linear infinite spin; + } + #app :is(.font-bold) { + font-weight: 700; + } + .custom-util { + button: no; + } + #app :is(.group:hover .group-hover\:focus-within\:text-left:focus-within) { + text-align: left; + } + #app :is(:is([dir='rtl'] .rtl\:active\:text-center:active)) { + text-align: center; + } + @media (prefers-reduced-motion: no-preference) { + #app :is(.motion-safe\:hover\:text-center:hover) { text-align: center; } - @media (prefers-reduced-motion: no-preference) { - #app :is(.motion-safe\:hover\:text-center:hover) { - text-align: center; - } + } + #app :is(.dark .dark\:before\:underline):before { + content: var(--tw-content); + text-decoration-line: underline; + } + #app :is(:is(.dark .dark\:focus\:text-left:focus)) { + text-align: left; + } + @media (min-width: 768px) { + #app :is(.md\:hover\:text-right:hover) { + text-align: right; } - #app :is(.dark .dark\:before\:underline):before { - content: var(--tw-content); - text-decoration-line: underline; - } - #app :is(:is(.dark .dark\:focus\:text-left:focus)) { - text-align: left; - } - @media (min-width: 768px) { - #app :is(.md\:hover\:text-right:hover) { - text-align: right; - } - } - #app - :is( - [dir='rtl'] :is(.dark .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100) - )::file-selector-button:hover { - background-color: #000; - } - `) - }) - }) - - test('pseudo-elements are appended after the `:is()`', () => { - let config = { - important: '#app', - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - ${defaults} - #app :is(.dark .dark\:before\:bg-black)::before { - content: var(--tw-content); - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - ${defaults} - #app :is(.dark .dark\:before\:bg-black)::before { - content: var(--tw-content); - background-color: #000; - } - `) - }) + } + #app + :is( + [dir='rtl'] :is(.dark .hover\:\[\&\:\:file-selector-button\]\:rtl\:dark\:bg-black\/100) + )::file-selector-button:hover { + background-color: #000; + } + `) + }) +}) + +test('pseudo-elements are appended after the `:is()`', () => { + let config = { + important: '#app', + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + #app :is(.dark .dark\:before\:flex)::before { + content: var(--tw-content); + display: flex; + } + `) }) }) diff --git a/tests/kitchen-sink.test.js b/tests/kitchen-sink.test.js index 3b02c8966..8f4b7cae2 100644 --- a/tests/kitchen-sink.test.js +++ b/tests/kitchen-sink.test.js @@ -1,1374 +1,812 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('it works', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+test('it works', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- - `, + things: Array /* PropType */, + }, + setup: () => { + const count = ref(0) + // Weird regex-looking stuff that once caused a stack overflow in candidatePermutations + const pattern = ' ]-[] ' + return { + count, + stuff: [] /* string[] | undefined */, + } + }, + }) + + `, + }, + ], + corePlugins: { preflight: false }, + theme: { + extend: { + screens: { + range: { min: '1280px', max: '1535px' }, + multi: [{ min: '640px', max: '767px' }, { max: '868px' }], }, - ], - corePlugins: { preflight: false }, - theme: { - extend: { - screens: { - range: { min: '1280px', max: '1535px' }, - multi: [{ min: '640px', max: '767px' }, { max: '868px' }], - }, - gradientColorStops: { - foo: '#bada55', - }, - backgroundImage: { - 'hero--home-1': "url('/images/homepage-1.jpg')", - }, + gradientColorStops: { + foo: '#bada55', + }, + backgroundImage: { + 'hero--home-1': "url('/images/homepage-1.jpg')", }, }, - plugins: [ - function ({ addVariant }) { - addVariant( - 'foo', - ({ container }) => { - container.walkRules((rule) => { - rule.selector = `.foo\\:${rule.selector.slice(1)}` - rule.walkDecls((decl) => { - decl.important = true - }) + }, + plugins: [ + function ({ addVariant }) { + addVariant( + 'foo', + ({ container }) => { + container.walkRules((rule) => { + rule.selector = `.foo\\:${rule.selector.slice(1)}` + rule.walkDecls((decl) => { + decl.important = true }) + }) + }, + { before: 'sm' } + ) + }, + function ({ addUtilities, addBase, theme }) { + addBase({ + h1: { + fontSize: theme('fontSize.2xl'), + fontWeight: theme('fontWeight.bold'), + '&:first-child': { + marginTop: '0px', }, - { before: 'sm' } - ) - }, - function ({ addUtilities, addBase, theme }) { - addBase({ - h1: { - fontSize: theme('fontSize.2xl'), - fontWeight: theme('fontWeight.bold'), - '&:first-child': { - marginTop: '0px', - }, + }, + }) + addUtilities( + { + '.magic-none': { + magic: 'none', }, - }) - addUtilities( - { - '.magic-none': { - magic: 'none', - }, - '.magic-tons': { - magic: 'tons', - }, + '.magic-tons': { + magic: 'tons', }, - ['responsive', 'hover'] - ) - }, - ], - } + }, + ['responsive', 'hover'] + ) + }, + ], + } - let input = css` - @layer utilities { - .custom-util { - background: #abcdef; - } - *, - ::before, - ::after, - ::backdrop { - margin: 10px; - } + let input = css` + @layer utilities { + .custom-util { + background: #abcdef; } - @layer components { - .test-apply-font-variant { - @apply ordinal tabular-nums; - } - .custom-component { - background: #123456; - } - *, - ::before, - ::after, - ::backdrop { - padding: 5px; - } - .foo .bg-black { - appearance: none; - } + *, + ::before, + ::after, + ::backdrop { + margin: 10px; } - @layer base { - div { - background: #654321; - } + } + @layer components { + .test-apply-font-variant { + @apply ordinal tabular-nums; } + .custom-component { + background: #123456; + } + *, + ::before, + ::after, + ::backdrop { + padding: 5px; + } + .foo .bg-black { + appearance: none; + } + } + @layer base { + div { + background: #654321; + } + } + .theme-test { + font-family: theme('fontFamily.sans'); + color: theme('colors.blue.500'); + } + @screen lg { + .screen-test { + color: purple; + } + } + .apply-1 { + @apply mt-6; + } + .apply-2 { + @apply mt-6; + } + .apply-test { + @apply mt-6 bg-pink-500 hover:font-bold focus:hover:font-bold sm:bg-green-500 sm:focus:even:bg-pink-200; + } + .apply-components { + @apply container mx-auto; + } + .drop-empty-rules { + @apply hover:font-bold; + } + .apply-group { + @apply group-hover:font-bold; + } + .apply-dark-mode { + @apply dark:font-bold; + } + .apply-with-existing:hover { + @apply font-normal sm:bg-green-500; + } + .multiple, + .selectors { + @apply font-bold group-hover:font-normal; + } + .list { + @apply space-y-4; + } + .nested { + .example { + @apply font-bold hover:font-normal; + } + } + .apply-order-a { + @apply m-5 mt-6; + } + .apply-order-b { + @apply m-5 mt-6; + } + .apply-dark-group-example-a { + @apply dark:group-hover:bg-green-500; + } + .crazy-example { + @apply sm:motion-safe:group-active:focus:opacity-10; + } + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` .theme-test { - font-family: theme('fontFamily.sans'); - color: theme('colors.blue.500'); + color: #3b82f6; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, + Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, + Segoe UI Symbol, Noto Color Emoji; } - @screen lg { + @media (min-width: 1024px) { .screen-test { color: purple; } } - .apply-1 { - @apply mt-6; - } + .apply-1, .apply-2 { - @apply mt-6; + margin-top: 1.5rem; } .apply-test { - @apply mt-6 bg-pink-500 hover:font-bold focus:hover:font-bold sm:bg-green-500 sm:focus:even:bg-pink-200; + --tw-bg-opacity: 1; + background-color: rgb(236 72 153 / var(--tw-bg-opacity)); + margin-top: 1.5rem; + } + .apply-test:hover, + .apply-test:hover:focus { + font-weight: 700; + } + @media (min-width: 640px) { + .apply-test { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); + } + .apply-test:nth-child(2n):focus { + --tw-bg-opacity: 1; + background-color: rgb(251 207 232 / var(--tw-bg-opacity)); + } } .apply-components { - @apply container mx-auto; + width: 100%; } - .drop-empty-rules { - @apply hover:font-bold; + @media (min-width: 640px) { + .apply-components { + max-width: 640px; + } } - .apply-group { - @apply group-hover:font-bold; + @media (min-width: 768px) { + .apply-components { + max-width: 768px; + } } - .apply-dark-mode { - @apply dark:font-bold; + @media (min-width: 1024px) { + .apply-components { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .apply-components { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .apply-components { + max-width: 1536px; + } + } + .apply-components { + margin-left: auto; + margin-right: auto; + } + .drop-empty-rules:hover, + .group:hover .apply-group, + :is(.dark .apply-dark-mode) { + font-weight: 700; } .apply-with-existing:hover { - @apply font-normal sm:bg-green-500; + font-weight: 400; + } + @media (min-width: 640px) { + .apply-with-existing:hover { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); + } } .multiple, .selectors { - @apply font-bold group-hover:font-normal; + font-weight: 700; } - .list { - @apply space-y-4; + .group:hover .multiple, + .group:hover .selectors { + font-weight: 400; } - .nested { - .example { - @apply font-bold hover:font-normal; - } + .list > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } - .apply-order-a { - @apply m-5 mt-6; + .nested .example { + font-weight: 700; } + .nested .example:hover { + font-weight: 400; + } + .apply-order-a, .apply-order-b { - @apply m-5 mt-6; + margin: 1.5rem 1.25rem 1.25rem; } - .apply-dark-group-example-a { - @apply dark:group-hover:bg-green-500; + :is(.dark .group:hover .apply-dark-group-example-a) { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); } - .crazy-example { - @apply sm:motion-safe:group-active:focus:opacity-10; + @media (min-width: 640px) { + @media (prefers-reduced-motion: no-preference) { + .group:active .crazy-example:focus { + opacity: 0.1; + } + } } - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .theme-test { - color: #3b82f6; - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, - Segoe UI Symbol, Noto Color Emoji; - } - @media (min-width: 1024px) { - .screen-test { - color: purple; - } - } - .apply-1, - .apply-2 { - margin-top: 1.5rem; - } - .apply-test { - --tw-bg-opacity: 1; - background-color: rgb(236 72 153 / var(--tw-bg-opacity)); - margin-top: 1.5rem; - } - .apply-test:hover, - .apply-test:hover:focus { - font-weight: 700; - } - @media (min-width: 640px) { - .apply-test { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity)); - } - .apply-test:nth-child(2n):focus { - --tw-bg-opacity: 1; - background-color: rgb(251 207 232 / var(--tw-bg-opacity)); - } - } - .apply-components { - width: 100%; - } - @media (min-width: 640px) { - .apply-components { - max-width: 640px; - } - } - @media (min-width: 768px) { - .apply-components { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .apply-components { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .apply-components { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .apply-components { - max-width: 1536px; - } - } - .apply-components { - margin-left: auto; - margin-right: auto; - } - .drop-empty-rules:hover, - .group:hover .apply-group, - :is(.dark .apply-dark-mode) { - font-weight: 700; - } - .apply-with-existing:hover { - font-weight: 400; - } - @media (min-width: 640px) { - .apply-with-existing:hover { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity)); - } - } - .multiple, - .selectors { - font-weight: 700; - } - .group:hover .multiple, - .group:hover .selectors { - font-weight: 400; - } - .list > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); - } - .nested .example { - font-weight: 700; - } - .nested .example:hover { - font-weight: 400; - } - .apply-order-a, - .apply-order-b { - margin: 1.5rem 1.25rem 1.25rem; - } - :is(.dark .group:hover .apply-dark-group-example-a) { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity)); - } - @media (min-width: 640px) { - @media (prefers-reduced-motion: no-preference) { - .group:active .crazy-example:focus { - opacity: 0.1; - } - } - } - h1 { - font-size: 1.5rem; - font-weight: 700; - } - h1:first-child { - margin-top: 0; - } - div { - background: #654321; - } - ${defaults} + h1 { + font-size: 1.5rem; + font-weight: 700; + } + h1:first-child { + margin-top: 0; + } + div { + background: #654321; + } + ${defaults} + .container { + width: 100%; + } + @media (min-width: 640px) { .container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .container { + max-width: 1536px; + } + } + .test-apply-font-variant { + --tw-ordinal: ordinal; + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) + var(--tw-numeric-spacing) var(--tw-numeric-fraction); + } + .custom-component { + background: #123456; + } + *, + :before, + :after, + ::backdrop { + padding: 5px; + } + .foo .bg-black { + appearance: none; + } + .inset-6 { + inset: 1.5rem; + } + .inset-x-1 { + left: 0.25rem; + right: 0.25rem; + } + .end-8 { + inset-inline-end: 2rem; + } + .start-4 { + inset-inline-start: 1rem; + } + .mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; + } + .me-8 { + margin-inline-end: 2rem; + } + .ms-4 { + margin-inline-start: 1rem; + } + .mt-6 { + margin-top: 1.5rem; + } + .line-clamp-2 { + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-\[33\] { + -webkit-line-clamp: 33; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-\[var\(--line-clamp-variable\)\] { + -webkit-line-clamp: var(--line-clamp-variable); + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-none { + -webkit-line-clamp: none; + -webkit-box-orient: horizontal; + display: block; + overflow: visible; + } + .scale-50 { + --tw-scale-x: 0.5; + --tw-scale-y: 0.5; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { + grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; + } + .rounded-e { + border-start-end-radius: 0.25rem; + border-end-end-radius: 0.25rem; + } + .rounded-s { + border-start-start-radius: 0.25rem; + border-end-start-radius: 0.25rem; + } + .rounded-es { + border-end-start-radius: 0.25rem; + } + .rounded-ss { + border-start-start-radius: 0.25rem; + } + .border-2 { + border-width: 2px; + } + .border-e-4 { + border-inline-end-width: 4px; + } + .border-s-0 { + border-inline-start-width: 0; + } + .border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0 / var(--tw-border-opacity)); + } + .border-e-red-400 { + --tw-border-opacity: 1; + border-inline-end-color: rgb(248 113 113 / var(--tw-border-opacity)); + } + .border-s-green-500 { + --tw-border-opacity: 1; + border-inline-start-color: rgb(34 197 94 / var(--tw-border-opacity)); + } + .bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); + } + .bg-opacity-50 { + --tw-bg-opacity: 0.5; + } + .bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); + } + .bg-hero--home-1 { + background-image: url('/images/homepage-1.jpg'); + } + .from-foo { + --tw-gradient-from: #bada55 var(--tw-gradient-from-position); + --tw-gradient-to: #bada5500 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; + } + .pe-8 { + padding-inline-end: 2rem; + } + .ps-4 { + padding-inline-start: 1rem; + } + .pt-6 { + padding-top: 1.5rem; + } + .text-center { + text-align: center; + } + .font-medium { + font-weight: 500; + } + .shadow-md { + --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), + 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .shadow-sm { + --tw-shadow: 0 1px 2px 0 #0000000d; + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .magic-none { + magic: none; + } + .magic-tons { + magic: tons; + } + .custom-util { + background: #abcdef; + } + *, + :before, + :after, + ::backdrop { + margin: 10px; + } + .hover\:container:hover { + width: 100%; + } + @media (min-width: 640px) { + .hover\:container:hover { + max-width: 640px; + } + } + @media (min-width: 768px) { + .hover\:container:hover { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .hover\:container:hover { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .hover\:container:hover { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .hover\:container:hover { + max-width: 1536px; + } + } + @media (min-width: 640px) { + .sm\:container { width: 100%; } @media (min-width: 640px) { - .container { + .sm\:container { max-width: 640px; } } @media (min-width: 768px) { - .container { + .sm\:container { max-width: 768px; } } @media (min-width: 1024px) { - .container { + .sm\:container { max-width: 1024px; } } @media (min-width: 1280px) { - .container { + .sm\:container { max-width: 1280px; } } @media (min-width: 1536px) { - .container { + .sm\:container { max-width: 1536px; } } - .test-apply-font-variant { - --tw-ordinal: ordinal; + } + @media (min-width: 768px) { + .md\:container { + width: 100%; + } + @media (min-width: 640px) { + .md\:container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .md\:container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .md\:container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .md\:container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .md\:container { + max-width: 1536px; + } + } + } + .first\:pt-0:first-child { + padding-top: 0; + } + .hover\:scale-75:hover { + --tw-scale-x: 0.75; + --tw-scale-y: 0.75; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .hover\:font-bold:hover { + font-weight: 700; + } + .hover\:shadow-lg:hover { + --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), + 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .hover\:custom-util:hover { + background: #abcdef; + } + .focus\:font-normal:focus { + font-weight: 400; + } + .focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + .focus\:ring-blue-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); + } + .focus\:hover\:font-light:hover:focus { + font-weight: 300; + } + .disabled\:font-bold:disabled { + font-weight: 700; + } + .group:hover .group-hover\:opacity-100 { + opacity: 1; + } + .group:hover .group-hover\:custom-util { + background: #abcdef; + } + .group:active .group-active\:opacity-10 { + opacity: 0.1; + } + .foo\:custom-util, + .foo\:hover\:custom-util:hover { + background: #abcdef !important; + } + @media (prefers-reduced-motion: no-preference) { + .motion-safe\:transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, + stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 0.15s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + .motion-safe\:custom-util { + background: #abcdef; + } + } + @media (prefers-reduced-motion: reduce) { + .motion-reduce\:transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, + stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-duration: 0.15s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + } + :is(.dark .dark\:custom-util) { + background: #abcdef; + } + @media (min-width: 640px) { + .sm\:text-center { + text-align: center; + } + .sm\:tabular-nums { --tw-numeric-spacing: tabular-nums; font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); } - .custom-component { - background: #123456; + .sm\:custom-util { + background: #abcdef; } - *, - :before, - :after, - ::backdrop { - padding: 5px; + @media (prefers-reduced-motion: no-preference) { + .group:active .sm\:motion-safe\:group-active\:focus\:opacity-10:focus { + opacity: 0.1; + } } - .foo .bg-black { - appearance: none; - } - .inset-6 { - inset: 1.5rem; - } - .inset-x-1 { - left: 0.25rem; - right: 0.25rem; - } - .end-8 { - inset-inline-end: 2rem; - } - .start-4 { - inset-inline-start: 1rem; - } - .mx-1 { - margin-left: 0.25rem; - margin-right: 0.25rem; - } - .me-8 { - margin-inline-end: 2rem; - } - .ms-4 { - margin-inline-start: 1rem; - } - .mt-6 { - margin-top: 1.5rem; - } - .line-clamp-2 { - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-\[33\] { - -webkit-line-clamp: 33; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-\[var\(--line-clamp-variable\)\] { - -webkit-line-clamp: var(--line-clamp-variable); - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-none { - -webkit-line-clamp: none; - -webkit-box-orient: horizontal; - display: block; - overflow: visible; - } - .scale-50 { - --tw-scale-x: 0.5; - --tw-scale-y: 0.5; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { - grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; - } - .rounded-e { - border-start-end-radius: 0.25rem; - border-end-end-radius: 0.25rem; - } - .rounded-s { - border-start-start-radius: 0.25rem; - border-end-start-radius: 0.25rem; - } - .rounded-es { - border-end-start-radius: 0.25rem; - } - .rounded-ss { - border-start-start-radius: 0.25rem; - } - .border-2 { - border-width: 2px; - } - .border-e-4 { - border-inline-end-width: 4px; - } - .border-s-0 { - border-inline-start-width: 0; - } - .border-black { - --tw-border-opacity: 1; - border-color: rgb(0 0 0 / var(--tw-border-opacity)); - } - .border-e-red-400 { - --tw-border-opacity: 1; - border-inline-end-color: rgb(248 113 113 / var(--tw-border-opacity)); - } - .border-s-green-500 { - --tw-border-opacity: 1; - border-inline-start-color: rgb(34 197 94 / var(--tw-border-opacity)); - } - .bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - .bg-green-500 { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity)); - } - .bg-opacity-50 { - --tw-bg-opacity: 0.5; - } - .bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); - } - .bg-hero--home-1 { - background-image: url('/images/homepage-1.jpg'); - } - .from-foo { - --tw-gradient-from: #bada55 var(--tw-gradient-from-position); - --tw-gradient-to: #bada5500 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; - } - .pe-8 { - padding-inline-end: 2rem; - } - .ps-4 { - padding-inline-start: 1rem; - } - .pt-6 { - padding-top: 1.5rem; - } - .text-center { + } + @media (min-width: 768px) { + .md\:text-center { text-align: center; } - .font-medium { - font-weight: 500; + .md\:opacity-50 { + opacity: 0.5; } - .shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .shadow-sm { + .md\:shadow-sm { --tw-shadow: 0 1px 2px 0 #0000000d; --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } - .magic-none { - magic: none; + .md\:hover\:border-r-blue-500\/30:hover { + border-right-color: #3b82f64d; } - .magic-tons { - magic: tons; - } - .custom-util { - background: #abcdef; - } - *, - :before, - :after, - ::backdrop { - margin: 10px; - } - .hover\:container:hover { - width: 100%; - } - @media (min-width: 640px) { - .hover\:container:hover { - max-width: 640px; - } - } - @media (min-width: 768px) { - .hover\:container:hover { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .hover\:container:hover { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .hover\:container:hover { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .hover\:container:hover { - max-width: 1536px; - } - } - @media (min-width: 640px) { - .sm\:container { - width: 100%; - } - @media (min-width: 640px) { - .sm\:container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .sm\:container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .sm\:container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .sm\:container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .sm\:container { - max-width: 1536px; - } - } - } - @media (min-width: 768px) { - .md\:container { - width: 100%; - } - @media (min-width: 640px) { - .md\:container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .md\:container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .md\:container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .md\:container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .md\:container { - max-width: 1536px; - } - } - } - .first\:pt-0:first-child { - padding-top: 0; - } - .hover\:scale-75:hover { - --tw-scale-x: 0.75; - --tw-scale-y: 0.75; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .hover\:font-bold:hover { - font-weight: 700; - } - .hover\:shadow-lg:hover { - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .hover\:custom-util:hover { - background: #abcdef; - } - .focus\:font-normal:focus { - font-weight: 400; - } - .focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - .focus\:ring-blue-500:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); - } - .focus\:hover\:font-light:hover:focus { - font-weight: 300; - } - .disabled\:font-bold:disabled { - font-weight: 700; - } - .group:hover .group-hover\:opacity-100 { - opacity: 1; - } - .group:hover .group-hover\:custom-util { - background: #abcdef; - } - .group:active .group-active\:opacity-10 { - opacity: 0.1; - } - .foo\:custom-util, - .foo\:hover\:custom-util:hover { - background: #abcdef !important; + .md\:hover\:opacity-20:hover { + opacity: 0.2; } @media (prefers-reduced-motion: no-preference) { - .motion-safe\:transition { + .md\:motion-safe\:hover\:transition:hover { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } - .motion-safe\:custom-util { - background: #abcdef; + :is(.dark .md\:dark\:motion-safe\:foo\:active\:custom-util:active) { + background: #abcdef !important; } } - @media (prefers-reduced-motion: reduce) { - .motion-reduce\:transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, - stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - :is(.dark .dark\:custom-util) { - background: #abcdef; - } @media (min-width: 640px) { - .sm\:text-center { + .md\:sm\:text-center { text-align: center; } - .sm\:tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - } - .sm\:custom-util { - background: #abcdef; - } - @media (prefers-reduced-motion: no-preference) { - .group:active .sm\:motion-safe\:group-active\:focus\:opacity-10:focus { - opacity: 0.1; - } - } } - @media (min-width: 768px) { - .md\:text-center { - text-align: center; - } - .md\:opacity-50 { - opacity: 0.5; - } - .md\:shadow-sm { - --tw-shadow: 0 1px 2px 0 #0000000d; - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .md\:hover\:border-r-blue-500\/30:hover { - border-right-color: #3b82f64d; - } - .md\:hover\:opacity-20:hover { - opacity: 0.2; - } - @media (prefers-reduced-motion: no-preference) { - .md\:motion-safe\:hover\:transition:hover { - transition-property: color, background-color, border-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - :is(.dark .md\:dark\:motion-safe\:foo\:active\:custom-util:active) { - background: #abcdef !important; - } - } - @media (min-width: 640px) { - .md\:sm\:text-center { - text-align: center; - } - } + } + @media (min-width: 1280px) and (max-width: 1535px) { + .range\:text-right { + text-align: right; } - @media (min-width: 1280px) and (max-width: 1535px) { - .range\:text-right { - text-align: right; - } + } + @media (min-width: 640px) and (max-width: 767px), (max-width: 868px) { + .multi\:text-left { + text-align: left; } - @media (min-width: 640px) and (max-width: 767px), (max-width: 868px) { - .multi\:text-left { - text-align: left; - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .theme-test { - color: #3b82f6; - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, - Segoe UI Symbol, Noto Color Emoji; - } - @media (min-width: 1024px) { - .screen-test { - color: purple; - } - } - .apply-1, - .apply-2 { - margin-top: 1.5rem; - } - .apply-test { - background-color: #ec4899; - margin-top: 1.5rem; - } - .apply-test:hover, - .apply-test:hover:focus { - font-weight: 700; - } - @media (min-width: 640px) { - .apply-test { - background-color: #22c55e; - } - .apply-test:nth-child(2n):focus { - background-color: #fbcfe8; - } - } - .apply-components { - width: 100%; - } - @media (min-width: 640px) { - .apply-components { - max-width: 640px; - } - } - @media (min-width: 768px) { - .apply-components { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .apply-components { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .apply-components { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .apply-components { - max-width: 1536px; - } - } - .apply-components { - margin-left: auto; - margin-right: auto; - } - .drop-empty-rules:hover, - .group:hover .apply-group, - :is(.dark .apply-dark-mode) { - font-weight: 700; - } - .apply-with-existing:hover { - font-weight: 400; - } - @media (min-width: 640px) { - .apply-with-existing:hover { - background-color: #22c55e; - } - } - .multiple, - .selectors { - font-weight: 700; - } - .group:hover .multiple, - .group:hover .selectors { - font-weight: 400; - } - .list > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); - } - .nested .example { - font-weight: 700; - } - .nested .example:hover { - font-weight: 400; - } - .apply-order-a, - .apply-order-b { - margin: 1.5rem 1.25rem 1.25rem; - } - :is(.dark .group:hover .apply-dark-group-example-a) { - background-color: #22c55e; - } - @media (min-width: 640px) { - @media (prefers-reduced-motion: no-preference) { - .group:active .crazy-example:focus { - opacity: 0.1; - } - } - } - h1 { - font-size: 1.5rem; - font-weight: 700; - } - h1:first-child { - margin-top: 0; - } - div { - background: #654321; - } - ${defaults} - .container { - width: 100%; - } - @media (min-width: 640px) { - .container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .container { - max-width: 1536px; - } - } - .test-apply-font-variant { - --tw-ordinal: ordinal; - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - } - .custom-component { - background: #123456; - } - *, - :before, - :after, - ::backdrop { - padding: 5px; - } - .foo .bg-black { - appearance: none; - } - .inset-6 { - inset: 1.5rem; - } - .inset-x-1 { - left: 0.25rem; - right: 0.25rem; - } - .end-8 { - inset-inline-end: 2rem; - } - .start-4 { - inset-inline-start: 1rem; - } - .mx-1 { - margin-left: 0.25rem; - margin-right: 0.25rem; - } - .me-8 { - margin-inline-end: 2rem; - } - .ms-4 { - margin-inline-start: 1rem; - } - .mt-6 { - margin-top: 1.5rem; - } - .line-clamp-2 { - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-\[33\] { - -webkit-line-clamp: 33; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-\[var\(--line-clamp-variable\)\] { - -webkit-line-clamp: var(--line-clamp-variable); - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - .line-clamp-none { - -webkit-line-clamp: none; - -webkit-box-orient: horizontal; - display: block; - overflow: visible; - } - .scale-50 { - --tw-scale-x: 0.5; - --tw-scale-y: 0.5; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] { - grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; - } - .rounded-e { - border-start-end-radius: 0.25rem; - border-end-end-radius: 0.25rem; - } - .rounded-s { - border-start-start-radius: 0.25rem; - border-end-start-radius: 0.25rem; - } - .rounded-es { - border-end-start-radius: 0.25rem; - } - .rounded-ss { - border-start-start-radius: 0.25rem; - } - .border-2 { - border-width: 2px; - } - .border-e-4 { - border-inline-end-width: 4px; - } - .border-s-0 { - border-inline-start-width: 0; - } - .border-black { - border-color: #000; - } - .border-e-red-400 { - border-inline-end-color: #f87171; - } - .border-s-green-500 { - border-inline-start-color: #22c55e; - } - .bg-black { - background-color: #000; - } - .bg-green-500 { - background-color: #22c55e; - } - .bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); - } - .bg-hero--home-1 { - background-image: url('/images/homepage-1.jpg'); - } - .from-foo { - --tw-gradient-from: #bada55 var(--tw-gradient-from-position); - --tw-gradient-to: #bada5500 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; - } - .pe-8 { - padding-inline-end: 2rem; - } - .ps-4 { - padding-inline-start: 1rem; - } - .pt-6 { - padding-top: 1.5rem; - } - .text-center { - text-align: center; - } - .font-medium { - font-weight: 500; - } - .shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .shadow-sm { - --tw-shadow: 0 1px 2px 0 #0000000d; - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .magic-none { - magic: none; - } - .magic-tons { - magic: tons; - } - .custom-util { - background: #abcdef; - } - *, - :before, - :after, - ::backdrop { - margin: 10px; - } - .hover\:container:hover { - width: 100%; - } - @media (min-width: 640px) { - .hover\:container:hover { - max-width: 640px; - } - } - @media (min-width: 768px) { - .hover\:container:hover { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .hover\:container:hover { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .hover\:container:hover { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .hover\:container:hover { - max-width: 1536px; - } - } - @media (min-width: 640px) { - .sm\:container { - width: 100%; - } - @media (min-width: 640px) { - .sm\:container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .sm\:container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .sm\:container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .sm\:container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .sm\:container { - max-width: 1536px; - } - } - } - @media (min-width: 768px) { - .md\:container { - width: 100%; - } - @media (min-width: 640px) { - .md\:container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .md\:container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .md\:container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .md\:container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .md\:container { - max-width: 1536px; - } - } - } - .first\:pt-0:first-child { - padding-top: 0; - } - .hover\:scale-75:hover { - --tw-scale-x: 0.75; - --tw-scale-y: 0.75; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .hover\:font-bold:hover { - font-weight: 700; - } - .hover\:shadow-lg:hover { - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .hover\:custom-util:hover { - background: #abcdef; - } - .focus\:font-normal:focus { - font-weight: 400; - } - .focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - .focus\:ring-blue-500:focus { - --tw-ring-color: #3b82f6; - } - .focus\:hover\:font-light:hover:focus { - font-weight: 300; - } - .disabled\:font-bold:disabled { - font-weight: 700; - } - .group:hover .group-hover\:opacity-100 { - opacity: 1; - } - .group:hover .group-hover\:custom-util { - background: #abcdef; - } - .group:active .group-active\:opacity-10 { - opacity: 0.1; - } - .foo\:custom-util, - .foo\:hover\:custom-util:hover { - background: #abcdef !important; - } - @media (prefers-reduced-motion: no-preference) { - .motion-safe\:transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, - stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - .motion-safe\:custom-util { - background: #abcdef; - } - } - @media (prefers-reduced-motion: reduce) { - .motion-reduce\:transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, - stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - :is(.dark .dark\:custom-util) { - background: #abcdef; - } - @media (min-width: 640px) { - .sm\:text-center { - text-align: center; - } - .sm\:tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); - } - .sm\:custom-util { - background: #abcdef; - } - @media (prefers-reduced-motion: no-preference) { - .group:active .sm\:motion-safe\:group-active\:focus\:opacity-10:focus { - opacity: 0.1; - } - } - } - @media (min-width: 768px) { - .md\:text-center { - text-align: center; - } - .md\:opacity-50 { - opacity: 0.5; - } - .md\:shadow-sm { - --tw-shadow: 0 1px 2px 0 #0000000d; - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .md\:hover\:border-r-blue-500\/30:hover { - border-right-color: #3b82f64d; - } - .md\:hover\:opacity-20:hover { - opacity: 0.2; - } - @media (prefers-reduced-motion: no-preference) { - .md\:motion-safe\:hover\:transition:hover { - transition-property: color, background-color, border-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - :is(.dark .md\:dark\:motion-safe\:foo\:active\:custom-util:active) { - background: #abcdef !important; - } - } - @media (min-width: 640px) { - .md\:sm\:text-center { - text-align: center; - } - } - } - @media (min-width: 1280px) and (max-width: 1535px) { - .range\:text-right { - text-align: right; - } - } - @media (min-width: 640px) and (max-width: 767px), (max-width: 868px) { - .multi\:text-left { - text-align: left; - } - } - `) - }) + } + `) }) }) diff --git a/tests/layer-at-rules.test.js b/tests/layer-at-rules.test.js index db98bbeb5..1c4604835 100644 --- a/tests/layer-at-rules.test.js +++ b/tests/layer-at-rules.test.js @@ -1,321 +1,319 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - test('custom user-land utilities', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], +test('custom user-land utilities', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } + + let input = css` + @layer utilities { + .align-banana { + text-align: banana; + } } - let input = css` - @layer utilities { - .align-banana { - text-align: banana; - } + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer utilities { + .align-chocolate { + text-align: chocolate; } + } + ` - @tailwind base; - @tailwind components; - @tailwind utilities; - - @layer utilities { - .align-chocolate { - text-align: chocolate; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .uppercase { + text-transform: uppercase; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .uppercase { - text-transform: uppercase; - } - .align-banana, - .hover\:align-banana:hover { - text-align: banana; - } - .focus\:hover\:align-chocolate:hover:focus { - text-align: chocolate; - } - `) - }) + .align-banana, + .hover\:align-banana:hover { + text-align: banana; + } + .focus\:hover\:align-chocolate:hover:focus { + text-align: chocolate; + } + `) }) +}) - test('comments can be used inside layers without crashing', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], +test('comments can be used inside layers without crashing', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer base { + /* Important base */ + div { + background-color: #bada55; + } } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - - @layer base { - /* Important base */ - div { - background-color: #bada55; - } + @layer utilities { + /* Important utility */ + .important-utility { + text-align: banana; } + } - @layer utilities { - /* Important utility */ + @layer components { + /* Important component */ + .important-component { + text-align: banana; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + div { + background-color: #bada55; + } + ${defaults} + .important-component, .important-utility { - text-align: banana; - } + text-align: banana; } - - @layer components { - /* Important component */ - .important-component { - text-align: banana; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - div { - background-color: #bada55; - } - ${defaults} - .important-component, - .important-utility { - text-align: banana; - } - `) - }) + `) }) +}) - test('comments can be used inside layers (with important) without crashing', () => { - let config = { - important: true, - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], +test('comments can be used inside layers (with important) without crashing', () => { + let config = { + important: true, + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer base { + /* Important base */ + div { + background-color: #bada55; + } } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - - @layer base { - /* Important base */ - div { - background-color: #bada55; - } + @layer utilities { + /* Important utility */ + .important-utility { + text-align: banana; } + } - @layer utilities { - /* Important utility */ - .important-utility { - text-align: banana; - } + @layer components { + /* Important component */ + .important-component { + text-align: banana; } + } + ` - @layer components { - /* Important component */ - .important-component { - text-align: banana; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + div { + background-color: #bada55; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - div { - background-color: #bada55; - } - ${defaults} - .important-component { - text-align: banana; - } - .important-utility { - text-align: banana !important; - } - `) - }) + ${defaults} + .important-component { + text-align: banana; + } + .important-utility { + text-align: banana !important; + } + `) }) +}) - test('layers are grouped and inserted at the matching @tailwind rule', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addBase, addComponents, addUtilities }) { - addBase({ body: { margin: 0 } }) +test('layers are grouped and inserted at the matching @tailwind rule', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addBase, addComponents, addUtilities }) { + addBase({ body: { margin: 0 } }) - addComponents({ - '.input': { background: 'white' }, - }) + addComponents({ + '.input': { background: 'white' }, + }) - addUtilities({ - '.float-squirrel': { float: 'squirrel' }, - }) - }, - ], - corePlugins: { preflight: false }, + addUtilities({ + '.float-squirrel': { float: 'squirrel' }, + }) + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @layer vanilla { + strong { + font-weight: medium; + } } - let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer components { + .btn { + background: blue; + } + } + + @layer utilities { + .align-banana { + text-align: banana; + } + } + + @layer base { + h1 { + font-weight: bold; + } + } + + @layer components { + .card { + border-radius: 12px; + } + } + + @layer base { + p { + font-weight: normal; + } + } + + @layer utilities { + .align-sandwich { + text-align: sandwich; + } + } + + @layer chocolate { + a { + text-decoration: underline; + } + } + ` + + expect.assertions(2) + + return run(input, config).then((result) => { + expect(result.warnings().length).toBe(0) + expect(result.css).toMatchFormattedCss(css` @layer vanilla { strong { font-weight: medium; } } - - @tailwind base; - @tailwind components; - @tailwind utilities; - - @layer components { - .btn { - background: blue; - } + body { + margin: 0; } - - @layer utilities { - .align-banana { - text-align: banana; - } + h1 { + font-weight: bold; } - - @layer base { - h1 { - font-weight: bold; - } + p { + font-weight: normal; } - - @layer components { - .card { - border-radius: 12px; - } + ${defaults} + .input { + background: #fff; } - - @layer base { - p { - font-weight: normal; - } + .btn { + background: #00f; } - - @layer utilities { - .align-sandwich { - text-align: sandwich; - } + .card { + border-radius: 12px; + } + .float-squirrel { + float: squirrel; + } + .align-banana { + text-align: banana; + } + .align-sandwich { + text-align: sandwich; } - @layer chocolate { a { text-decoration: underline; } } - ` - - expect.assertions(2) - - return run(input, config).then((result) => { - expect(result.warnings().length).toBe(0) - expect(result.css).toMatchFormattedCss(css` - @layer vanilla { - strong { - font-weight: medium; - } - } - body { - margin: 0; - } - h1 { - font-weight: bold; - } - p { - font-weight: normal; - } - ${defaults} - .input { - background: #fff; - } - .btn { - background: #00f; - } - .card { - border-radius: 12px; - } - .float-squirrel { - float: squirrel; - } - .align-banana { - text-align: banana; - } - .align-sandwich { - text-align: sandwich; - } - @layer chocolate { - a { - text-decoration: underline; - } - } - `) - }) - }) - - it('should keep `@supports` rules inside `@layer`s', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - } - - let input = css` - @tailwind utilities; - - @layer utilities { - .test { - --tw-test: 1; - } - - @supports (backdrop-filter: blur(1px)) { - .test { - --tw-test: 0.9; - } - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .test { - --tw-test: 1; - } - @supports (backdrop-filter: blur(1px)) { - .test { - --tw-test: 0.9; - } - } - `) - }) + `) + }) +}) + +it('should keep `@supports` rules inside `@layer`s', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind utilities; + + @layer utilities { + .test { + --tw-test: 1; + } + + @supports (backdrop-filter: blur(1px)) { + .test { + --tw-test: 0.9; + } + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .test { + --tw-test: 1; + } + @supports (backdrop-filter: blur(1px)) { + .test { + --tw-test: 0.9; + } + } + `) }) }) diff --git a/tests/layer-without-tailwind.test.js b/tests/layer-without-tailwind.test.js index f15d43351..4122873c9 100644 --- a/tests/layer-without-tailwind.test.js +++ b/tests/layer-without-tailwind.test.js @@ -1,81 +1,79 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('using @layer without @tailwind', async () => { - let config = { - content: [{ raw: html`
` }], - } +test('using @layer without @tailwind', async () => { + let config = { + content: [{ raw: html`
` }], + } - let input = css` - @layer components { - .foo { - color: black; - } + let input = css` + @layer components { + .foo { + color: black; } - ` - - await expect(run(input, config)).rejects.toThrowError( - '`@layer components` is used but no matching `@tailwind components` directive is present.' - ) - }) - - test('using @responsive without @tailwind', async () => { - let config = { - content: [{ raw: html`
` }], } + ` - let input = css` - @responsive { - .foo { - color: black; - } + await expect(run(input, config)).rejects.toThrowError( + '`@layer components` is used but no matching `@tailwind components` directive is present.' + ) +}) + +test('using @responsive without @tailwind', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @responsive { + .foo { + color: black; } - ` - - await expect(run(input, config)).rejects.toThrowError( - '`@responsive` is used but `@tailwind utilities` is missing.' - ) - }) - - test('using @variants without @tailwind', async () => { - let config = { - content: [{ raw: html`
` }], } + ` - let input = css` - @variants hover { - .foo { - color: black; - } + await expect(run(input, config)).rejects.toThrowError( + '`@responsive` is used but `@tailwind utilities` is missing.' + ) +}) + +test('using @variants without @tailwind', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @variants hover { + .foo { + color: black; } - ` - - await expect(run(input, config)).rejects.toThrowError( - '`@variants` is used but `@tailwind utilities` is missing.' - ) - }) - - test('non-Tailwind @layer rules are okay', async () => { - let config = { - content: [{ raw: html`
` }], } + ` - let input = css` + await expect(run(input, config)).rejects.toThrowError( + '`@variants` is used but `@tailwind utilities` is missing.' + ) +}) + +test('non-Tailwind @layer rules are okay', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @layer custom { + .foo { + color: black; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` @layer custom { .foo { - color: black; + color: #000; } } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @layer custom { - .foo { - color: #000; - } - } - `) - }) + `) }) }) diff --git a/tests/match-components.test.js b/tests/match-components.test.js index a8b546127..8f6241724 100644 --- a/tests/match-components.test.js +++ b/tests/match-components.test.js @@ -1,86 +1,82 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(() => { - it('should be possible to matchComponents', () => { - let config = { - content: [ - { - raw: html`
-
-
- -
`, - }, - ], - corePlugins: { - preflight: false, +it('should be possible to matchComponents', () => { + let config = { + content: [ + { + raw: html`
+
+
+ +
`, }, - plugins: [ - function ({ matchComponents }) { - matchComponents({ - card: (value) => { - return [ - { color: value }, - { - '.card-header': { - borderTopWidth: 3, - borderTopColor: value, - }, + ], + corePlugins: { + preflight: false, + }, + plugins: [ + function ({ matchComponents }) { + matchComponents({ + card: (value) => { + return [ + { color: value }, + { + '.card-header': { + borderTopWidth: 3, + borderTopColor: value, }, - { - '.card-footer': { - borderBottomWidth: 3, - borderBottomColor: value, - }, + }, + { + '.card-footer': { + borderBottomWidth: 3, + borderBottomColor: value, }, - ] - }, - }) - }, - ], - } + }, + ] + }, + }) + }, + ], + } - return run('@tailwind base; @tailwind components; @tailwind utilities', config).then( - (result) => { - return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .card-\[\#0088cc\] { - color: #08c; - } - .card-\[\#0088cc\] .card-header { - border-top-width: 3px; - border-top-color: #08c; - } - .card-\[\#0088cc\] .card-footer { - border-bottom-width: 3px; - border-bottom-color: #08c; - } - .text-center { - text-align: center; - } - .font-bold { - font-weight: 700; - } - .shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - .hover\:card-\[\#f0f\]:hover { - color: #f0f; - } - .hover\:card-\[\#f0f\]:hover .card-header { - border-top-width: 3px; - border-top-color: #f0f; - } - .hover\:card-\[\#f0f\]:hover .card-footer { - border-bottom-width: 3px; - border-bottom-color: #f0f; - } - `) + return run('@tailwind base; @tailwind components; @tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .card-\[\#0088cc\] { + color: #08c; } - ) + .card-\[\#0088cc\] .card-header { + border-top-width: 3px; + border-top-color: #08c; + } + .card-\[\#0088cc\] .card-footer { + border-bottom-width: 3px; + border-bottom-color: #08c; + } + .text-center { + text-align: center; + } + .font-bold { + font-weight: 700; + } + .shadow { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .hover\:card-\[\#f0f\]:hover { + color: #f0f; + } + .hover\:card-\[\#f0f\]:hover .card-header { + border-top-width: 3px; + border-top-color: #f0f; + } + .hover\:card-\[\#f0f\]:hover .card-footer { + border-bottom-width: 3px; + border-bottom-color: #f0f; + } + `) }) }) diff --git a/tests/match-utilities.test.js b/tests/match-utilities.test.js index 9b3cd4c56..41ef29a29 100644 --- a/tests/match-utilities.test.js +++ b/tests/match-utilities.test.js @@ -1,575 +1,573 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('match utilities with modifiers', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, +test('match utilities with modifiers', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, - plugins: [ - ({ matchUtilities }) => { - matchUtilities( - { - test: (value, { modifier }) => ({ - color: `${value}_${modifier}`, - }), + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => ({ + color: `${value}_${modifier}`, + }), + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + '2': 'two', + '1/foo': 'onefoo', + '[8]/[9]': 'eightnine', }, - { - values: { - DEFAULT: 'default', - bar: 'bar', - '1': 'one', - '2': 'two', - '1/foo': 'onefoo', - '[8]/[9]': 'eightnine', - }, - modifiers: 'any', - } - ) - }, - ], + modifiers: 'any', + } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; } + .test-1\/\[foo\] { + color: one_[foo]; + } + .test-1\/foo { + color: onefoo_null; + } + .test-2\/foo { + color: two_foo; + } + .test-\[8\]\/\[9\] { + color: eightnine_null; + } + .test\/\[foo\] { + color: default_[foo]; + } + .test\/foo { + color: default_foo; + } + `) +}) - let input = css` - @tailwind utilities; - ` +test('match utilities with modifiers in the config', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, - let result = await run(input, config) + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => ({ + color: `${value}_${modifier}`, + }), + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + }, + modifiers: { + foo: 'mewtwo', + }, + } + ) + }, + ], + } - expect(result.css).toMatchFormattedCss(css` - .test { - color: default_null; - } - .test-1\/\[foo\] { - color: one_[foo]; - } - .test-1\/foo { - color: onefoo_null; - } - .test-2\/foo { - color: two_foo; - } - .test-\[8\]\/\[9\] { - color: eightnine_null; - } - .test\/\[foo\] { - color: default_[foo]; - } - .test\/foo { - color: default_foo; + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; + } + .test-1\/\[bar\] { + color: one_bar; + } + .test-1\/foo { + color: one_mewtwo; + } + .test\/\[bar\] { + color: default_bar; + } + .test\/foo { + color: default_mewtwo; + } + `) +}) + +test('match utilities can omit utilities by returning null', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => + modifier === 'bad' + ? null + : { + color: `${value}_${modifier}`, + }, + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + }, + modifiers: 'any', + } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; + } + .test\/good { + color: default_good; + } + `) +}) + +test('matching utilities with a basic configured value', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value) => ({ value }), + }, + { + values: { + foo: 'value_foo', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo { + value: value_foo; + } + `) + }) +}) + +test('matching utilities with an arbitrary value and configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: { + bar: 'configured_bar', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/bar { + value: foo; + modifier: configured_bar; + } + `) + }) +}) + +test('matching utilities with an configured value and an arbitrary modifier (raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: 'any', // Raw `[value]` + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: configured_foo; + modifier: [bar]; + } + `) + }) +}) + +test('matching utilities with an configured value and an arbitrary modifier (non-raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: configured_foo; + modifier: bar; + } + `) + }) +}) + +test('matching utilities with an configured value and a configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: { + bar: 'configured_bar', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/bar { + value: configured_foo; + modifier: configured_bar; + } + `) + }) +}) + +test('matching utilities with an arbitrary value and an arbitrary modifier (raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: 'any', + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: foo; + modifier: [bar]; + } + `) + }) +}) + +test('matching utilities with an arbitrary value and an arbitrary modifier (non-raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: foo; + modifier: bar; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = any)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + modifiers: 'any', + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = {})', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and a configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/bar': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/bar { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like a configured value and an arbitrary modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + 'foo/[bar]': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that does not match the configured type', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'not-a-percentage', + }, + type: ['percentage'], + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo { + value: not-a-percentage; } `) }) - - test('match utilities with modifiers in the config', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - - plugins: [ - ({ matchUtilities }) => { - matchUtilities( - { - test: (value, { modifier }) => ({ - color: `${value}_${modifier}`, - }), - }, - { - values: { - DEFAULT: 'default', - bar: 'bar', - '1': 'one', - }, - modifiers: { - foo: 'mewtwo', - }, - } - ) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .test { - color: default_null; - } - .test-1\/\[bar\] { - color: one_bar; - } - .test-1\/foo { - color: one_mewtwo; - } - .test\/\[bar\] { - color: default_bar; - } - .test\/foo { - color: default_mewtwo; - } - `) - }) - - test('match utilities can omit utilities by returning null', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - - plugins: [ - ({ matchUtilities }) => { - matchUtilities( - { - test: (value, { modifier }) => - modifier === 'bad' - ? null - : { - color: `${value}_${modifier}`, - }, - }, - { - values: { - DEFAULT: 'default', - bar: 'bar', - '1': 'one', - }, - modifiers: 'any', - } - ) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .test { - color: default_null; - } - .test\/good { - color: default_good; - } - `) - }) - - test('matching utilities with a basic configured value', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value) => ({ value }), - }, - { - values: { - foo: 'value_foo', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo { - value: value_foo; - } - `) - }) - }) - - test('matching utilities with an arbitrary value and configured modifier', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - modifiers: { - bar: 'configured_bar', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/bar { - value: foo; - modifier: configured_bar; - } - `) - }) - }) - - test('matching utilities with an configured value and an arbitrary modifier (raw)', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - foo: 'configured_foo', - }, - modifiers: 'any', // Raw `[value]` - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo\/\[bar\] { - value: configured_foo; - modifier: [bar]; - } - `) - }) - }) - - test('matching utilities with an configured value and an arbitrary modifier (non-raw)', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - foo: 'configured_foo', - }, - modifiers: {}, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo\/\[bar\] { - value: configured_foo; - modifier: bar; - } - `) - }) - }) - - test('matching utilities with an configured value and a configured modifier', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - foo: 'configured_foo', - }, - modifiers: { - bar: 'configured_bar', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo\/bar { - value: configured_foo; - modifier: configured_bar; - } - `) - }) - }) - - test('matching utilities with an arbitrary value and an arbitrary modifier (raw)', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - modifiers: 'any', - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/\[bar\] { - value: foo; - modifier: [bar]; - } - `) - }) - }) - - test('matching utilities with an arbitrary value and an arbitrary modifier (non-raw)', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - modifiers: {}, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/\[bar\] { - value: foo; - modifier: bar; - } - `) - }) - }) - - test('matching utilities with a lookup value that looks like an arbitrary value and modifier', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - '[foo]/[bar]': 'hello', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/\[bar\] { - value: hello; - } - `) - }) - }) - - test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = any)', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - '[foo]/[bar]': 'hello', - }, - modifiers: 'any', - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/\[bar\] { - value: hello; - } - `) - }) - }) - - test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = {})', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - '[foo]/[bar]': 'hello', - }, - modifiers: {}, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/\[bar\] { - value: hello; - } - `) - }) - }) - - test('matching utilities with a lookup value that looks like an arbitrary value and a configured modifier', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - '[foo]/bar': 'hello', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-\[foo\]\/bar { - value: hello; - } - `) - }) - }) - - test('matching utilities with a lookup value that looks like a configured value and an arbitrary modifier', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - 'foo/[bar]': 'hello', - }, - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo\/\[bar\] { - value: hello; - } - `) - }) - }) - - test('matching utilities with a lookup value that does not match the configured type', () => { - let config = { - content: [{ raw: html`
` }], - theme: {}, - plugins: [ - function ({ matchUtilities }) { - matchUtilities( - { - test: (value, { modifier }) => ({ value, modifier }), - }, - { - values: { - foo: 'not-a-percentage', - }, - type: ['percentage'], - } - ) - }, - ], - corePlugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .test-foo { - value: not-a-percentage; - } - `) - }) - }) }) diff --git a/tests/match-variants.test.js b/tests/match-variants.test.js index e4330f7a9..2923b10c0 100644 --- a/tests/match-variants.test.js +++ b/tests/match-variants.test.js @@ -1,856 +1,820 @@ import resolveConfig from '../src/public/resolve-config' import { createContext } from '../src/lib/setupContextUtils' -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - test('partial arbitrary variants', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('potato', (flavor) => `.potato-${flavor} &`) - }, - ], - } +test('partial arbitrary variants', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('potato', (flavor) => `.potato-${flavor} &`) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .potato-baked .potato-\[baked\]\:w-3 { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .potato-baked .potato-\[baked\]\:w-3 { + width: 0.75rem; + } + .potato-yellow .potato-\[yellow\]\:bg-yellow-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 240 138 / var(--tw-bg-opacity)); + } + `) + }) +}) + +test('partial arbitrary variants with at-rules', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('potato', (flavor) => `@media (potato: ${flavor})`) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (potato: baked) { + .potato-\[baked\]\:w-3 { width: 0.75rem; } - .potato-yellow .potato-\[yellow\]\:bg-yellow-200 { + } + @media (potato: yellow) { + .potato-\[yellow\]\:bg-yellow-200 { --tw-bg-opacity: 1; background-color: rgb(254 240 138 / var(--tw-bg-opacity)); } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .potato-baked .potato-\[baked\]\:w-3 { + } + `) + }) +}) + +test('partial arbitrary variants with at-rules and placeholder', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('potato', (flavor) => `@media (potato: ${flavor}) { &:potato }`) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (potato: baked) { + .potato-\[baked\]\:w-3:potato { width: 0.75rem; } - .potato-yellow .potato-\[yellow\]\:bg-yellow-200 { - background-color: #fef08a; + } + @media (potato: yellow) { + .potato-\[yellow\]\:bg-yellow-200:potato { + --tw-bg-opacity: 1; + background-color: rgb(254 240 138 / var(--tw-bg-opacity)); } - `) - }) + } + `) }) +}) - test('partial arbitrary variants with at-rules', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('potato', (flavor) => `@media (potato: ${flavor})`) - }, - ], - } +test('partial arbitrary variants with default values', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('tooltip', (side) => `&${side}`, { + values: { + bottom: '[data-location="bottom"]', + top: '[data-location="top"]', + }, + }) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - @media (potato: baked) { - .potato-\[baked\]\:w-3 { - width: 0.75rem; - } - } - @media (potato: yellow) { - .potato-\[yellow\]\:bg-yellow-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 240 138 / var(--tw-bg-opacity)); - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - @media (potato: baked) { - .potato-\[baked\]\:w-3 { - width: 0.75rem; - } - } - @media (potato: yellow) { - .potato-\[yellow\]\:bg-yellow-200 { - background-color: #fef08a; - } - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tooltip-bottom\:mt-2[data-location='bottom'] { + margin-top: 0.5rem; + } + .tooltip-top\:mb-2[data-location='top'] { + margin-bottom: 0.5rem; + } + `) }) +}) - test('partial arbitrary variants with at-rules and placeholder', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('potato', (flavor) => `@media (potato: ${flavor}) { &:potato }`) - }, - ], - } +test('matched variant values maintain the sort order they are registered in', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('alphabet', (side) => `&${side}`, { + values: { + a: '[data-value="a"]', + b: '[data-value="b"]', + c: '[data-value="c"]', + d: '[data-value="d"]', + }, + }) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - @media (potato: baked) { - .potato-\[baked\]\:w-3:potato { - width: 0.75rem; - } - } - @media (potato: yellow) { - .potato-\[yellow\]\:bg-yellow-200:potato { - --tw-bg-opacity: 1; - background-color: rgb(254 240 138 / var(--tw-bg-opacity)); - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - @media (potato: baked) { - .potato-\[baked\]\:w-3:potato { - width: 0.75rem; - } - } - @media (potato: yellow) { - .potato-\[yellow\]\:bg-yellow-200:potato { - background-color: #fef08a; - } - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .alphabet-a\:underline[data-value='a'], + .alphabet-b\:underline[data-value='b'], + .alphabet-c\:underline[data-value='c'], + .alphabet-d\:underline[data-value='d'] { + text-decoration-line: underline; + } + `) }) +}) - test('partial arbitrary variants with default values', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('tooltip', (side) => `&${side}`, { - values: { - bottom: '[data-location="bottom"]', - top: '[data-location="top"]', - }, - }) - }, - ], - } +test('matchVariant can return an array of format strings from the function', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('test', (selector) => + selector.split(',').map((selector) => `&.${selector} > *`) + ) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .tooltip-bottom\:mt-2[data-location='bottom'] { - margin-top: 0.5rem; - } - .tooltip-top\:mb-2[data-location='top'] { - margin-bottom: 0.5rem; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .test-\[a\,b\,c\]\:underline.a > *, + .test-\[a\,b\,c\]\:underline.b > *, + .test-\[a\,b\,c\]\:underline.c > * { + text-decoration-line: underline; + } + `) }) +}) - test('matched variant values maintain the sort order they are registered in', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('alphabet', (side) => `&${side}`, { - values: { - a: '[data-value="a"]', - b: '[data-value="b"]', - c: '[data-value="c"]', - d: '[data-value="d"]', - }, - }) - }, - ], - } +it('should be possible to sort variants', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .alphabet-a\:underline[data-value='a'], - .alphabet-b\:underline[data-value='b'], - .alphabet-c\:underline[data-value='c'], - .alphabet-d\:underline[data-value='d'] { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 500px) { + .testmin-\[500px\]\:underline { text-decoration-line: underline; } - `) - }) - }) - - test('matchVariant can return an array of format strings from the function', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('test', (selector) => - selector.split(',').map((selector) => `&.${selector} > *`) - ) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .test-\[a\,b\,c\]\:underline.a > *, - .test-\[a\,b\,c\]\:underline.b > *, - .test-\[a\,b\,c\]\:underline.c > * { - text-decoration-line: underline; + } + @media (min-width: 700px) { + .testmin-\[700px\]\:italic { + font-style: italic; } - `) - }) + } + `) }) +}) - it('should be possible to sort variants', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) - }, - ], - } +it('should be possible to compare arbitrary variants and hardcoded variants', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + values: { + example: '600px', + }, + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) + }, + ], + } - let input = css` - @tailwind utilities; - ` + let input = css` + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 500px) { - .testmin-\[500px\]\:underline { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 500px) { + .testmin-\[500px\]\:italic { + font-style: italic; + } + } + @media (min-width: 600px) { + .testmin-example\:italic { + font-style: italic; + } + } + @media (min-width: 700px) { + .testmin-\[700px\]\:italic { + font-style: italic; + } + } + `) + }) +}) + +it('should be possible to sort stacked arbitrary variants correctly', () => { + let config = { + content: [ + { + raw: html` +
+ +
+ +
+ +
+ +
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) + + matchVariant('testmax', (value) => `@media (max-width: ${value})`, { + sort(a, z) { + return parseInt(z.value) - parseInt(a.value) + }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 100px) { + @media (max-width: 400px) { + .testmin-\[100px\]\:testmax-\[400px\]\:underline { text-decoration-line: underline; } } - @media (min-width: 700px) { - .testmin-\[700px\]\:italic { - font-style: italic; + @media (max-width: 350px) { + .testmin-\[100px\]\:testmax-\[350px\]\:underline { + text-decoration-line: underline; } } - `) - }) + @media (max-width: 300px) { + .testmin-\[100px\]\:testmax-\[300px\]\:underline { + text-decoration-line: underline; + } + } + } + @media (min-width: 150px) { + @media (max-width: 400px) { + .testmin-\[150px\]\:testmax-\[400px\]\:underline { + text-decoration-line: underline; + } + } + } + `) }) +}) - it('should be possible to compare arbitrary variants and hardcoded variants', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - values: { - example: '600px', - }, - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) - }, - ], - } +it('should maintain sort from other variants, if sort functions of arbitrary variants return 0', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) - let input = css` - @tailwind utilities; - ` + matchVariant('testmax', (value) => `@media (max-width: ${value})`, { + sort(a, z) { + return parseInt(z.value) - parseInt(a.value) + }, + }) + }, + ], + } - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 500px) { - .testmin-\[500px\]\:italic { - font-style: italic; + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 100px) { + @media (max-width: 200px) { + .testmin-\[100px\]\:testmax-\[200px\]\:hover\:underline:hover, + .testmin-\[100px\]\:testmax-\[200px\]\:focus\:underline:focus { + text-decoration-line: underline; } } - @media (min-width: 600px) { - .testmin-example\:italic { - font-style: italic; - } - } - @media (min-width: 700px) { - .testmin-\[700px\]\:italic { - font-style: italic; - } - } - `) - }) + } + `) }) +}) - it('should be possible to sort stacked arbitrary variants correctly', () => { - let config = { - content: [ - { - raw: html` -
- -
- -
- -
- -
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) +it('should sort arbitrary variants left to right (1)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) + matchVariant('testmax', (value) => `@media (max-width: ${value})`, { + sort(a, z) { + return parseInt(z.value) - parseInt(a.value) + }, + }) + }, + ], + } - matchVariant('testmax', (value) => `@media (max-width: ${value})`, { - sort(a, z) { - return parseInt(z.value) - parseInt(a.value) - }, - }) - }, - ], - } + let input = css` + @tailwind utilities; + ` - let input = css` - @tailwind utilities; - ` + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 100px) { + @media (max-width: 400px) { + .testmin-\[100px\]\:testmax-\[400px\]\:underline { + text-decoration-line: underline; + } + } + @media (max-width: 300px) { + .testmin-\[100px\]\:testmax-\[300px\]\:underline { + text-decoration-line: underline; + } + } + } + @media (min-width: 200px) { + @media (max-width: 400px) { + .testmin-\[200px\]\:testmax-\[400px\]\:underline { + text-decoration-line: underline; + } + } + @media (max-width: 300px) { + .testmin-\[200px\]\:testmax-\[300px\]\:underline { + text-decoration-line: underline; + } + } + } + `) + }) +}) - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` +it('should sort arbitrary variants left to right (2)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + return parseInt(a.value) - parseInt(z.value) + }, + }) + matchVariant('testmax', (value) => `@media (max-width: ${value})`, { + sort(a, z) { + return parseInt(z.value) - parseInt(a.value) + }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (max-width: 400px) { @media (min-width: 100px) { - @media (max-width: 400px) { - .testmin-\[100px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - @media (max-width: 350px) { - .testmin-\[100px\]\:testmax-\[350px\]\:underline { - text-decoration-line: underline; - } - } - @media (max-width: 300px) { - .testmin-\[100px\]\:testmax-\[300px\]\:underline { - text-decoration-line: underline; - } - } - } - @media (min-width: 150px) { - @media (max-width: 400px) { - .testmin-\[150px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - } - `) - }) - }) - - it('should maintain sort from other variants, if sort functions of arbitrary variants return 0', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) - - matchVariant('testmax', (value) => `@media (max-width: ${value})`, { - sort(a, z) { - return parseInt(z.value) - parseInt(a.value) - }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 100px) { - @media (max-width: 200px) { - .testmin-\[100px\]\:testmax-\[200px\]\:hover\:underline:hover, - .testmin-\[100px\]\:testmax-\[200px\]\:focus\:underline:focus { - text-decoration-line: underline; - } - } - } - `) - }) - }) - - it('should sort arbitrary variants left to right (1)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) - matchVariant('testmax', (value) => `@media (max-width: ${value})`, { - sort(a, z) { - return parseInt(z.value) - parseInt(a.value) - }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 100px) { - @media (max-width: 400px) { - .testmin-\[100px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - @media (max-width: 300px) { - .testmin-\[100px\]\:testmax-\[300px\]\:underline { - text-decoration-line: underline; - } + .testmax-\[400px\]\:testmin-\[100px\]\:underline { + text-decoration-line: underline; } } @media (min-width: 200px) { - @media (max-width: 400px) { - .testmin-\[200px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - @media (max-width: 300px) { - .testmin-\[200px\]\:testmax-\[300px\]\:underline { - text-decoration-line: underline; - } + .testmax-\[400px\]\:testmin-\[200px\]\:underline { + text-decoration-line: underline; } } - `) - }) - }) + } - it('should sort arbitrary variants left to right (2)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - return parseInt(a.value) - parseInt(z.value) - }, - }) - matchVariant('testmax', (value) => `@media (max-width: ${value})`, { - sort(a, z) { - return parseInt(z.value) - parseInt(a.value) - }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (max-width: 400px) { - @media (min-width: 100px) { - .testmax-\[400px\]\:testmin-\[100px\]\:underline { - text-decoration-line: underline; - } + @media (max-width: 300px) { + @media (min-width: 100px) { + .testmax-\[300px\]\:testmin-\[100px\]\:underline { + text-decoration-line: underline; } - @media (min-width: 200px) { - .testmax-\[400px\]\:testmin-\[200px\]\:underline { - text-decoration-line: underline; + } + @media (min-width: 200px) { + .testmax-\[300px\]\:testmin-\[200px\]\:underline { + text-decoration-line: underline; + } + } + } + `) + }) +}) + +it('should guarantee that we are not passing values from other variants to the wrong function', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('testmin', (value) => `@media (min-width: ${value})`, { + sort(a, z) { + let lookup = ['100px', '200px'] + if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { + throw new Error('We are seeing values that should not be there!') } + return lookup.indexOf(a.value) - lookup.indexOf(z.value) + }, + }) + matchVariant('testmax', (value) => `@media (max-width: ${value})`, { + sort(a, z) { + let lookup = ['300px', '400px'] + if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { + throw new Error('We are seeing values that should not be there!') + } + return lookup.indexOf(z.value) - lookup.indexOf(a.value) + }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 100px) { + @media (max-width: 400px) { + .testmin-\[100px\]\:testmax-\[400px\]\:underline { + text-decoration-line: underline; } } @media (max-width: 300px) { - @media (min-width: 100px) { - .testmax-\[300px\]\:testmin-\[100px\]\:underline { - text-decoration-line: underline; - } - } - @media (min-width: 200px) { - .testmax-\[300px\]\:testmin-\[200px\]\:underline { - text-decoration-line: underline; - } + .testmin-\[100px\]\:testmax-\[300px\]\:underline { + text-decoration-line: underline; } } - `) - }) - }) + } - it('should guarantee that we are not passing values from other variants to the wrong function', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('testmin', (value) => `@media (min-width: ${value})`, { - sort(a, z) { - let lookup = ['100px', '200px'] - if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { - throw new Error('We are seeing values that should not be there!') - } - return lookup.indexOf(a.value) - lookup.indexOf(z.value) - }, - }) - matchVariant('testmax', (value) => `@media (max-width: ${value})`, { - sort(a, z) { - let lookup = ['300px', '400px'] - if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { - throw new Error('We are seeing values that should not be there!') - } - return lookup.indexOf(z.value) - lookup.indexOf(a.value) - }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 100px) { - @media (max-width: 400px) { - .testmin-\[100px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - - @media (max-width: 300px) { - .testmin-\[100px\]\:testmax-\[300px\]\:underline { - text-decoration-line: underline; - } + @media (min-width: 200px) { + @media (max-width: 400px) { + .testmin-\[200px\]\:testmax-\[400px\]\:underline { + text-decoration-line: underline; } } - @media (min-width: 200px) { - @media (max-width: 400px) { - .testmin-\[200px\]\:testmax-\[400px\]\:underline { - text-decoration-line: underline; - } - } - - @media (max-width: 300px) { - .testmin-\[200px\]\:testmax-\[300px\]\:underline { - text-decoration-line: underline; - } + @media (max-width: 300px) { + .testmin-\[200px\]\:testmax-\[300px\]\:underline { + text-decoration-line: underline; } } - `) - }) - }) - - it('should default to the DEFAULT value for variants', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('foo', (value) => `.foo${value} &`, { - values: { - DEFAULT: '.bar', - }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo.bar .foo\:underline { - text-decoration-line: underline; - } - `) - }) - }) - - it('should not generate anything if the matchVariant does not have a DEFAULT value configured', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('foo', (value) => `.foo${value} &`) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css``) - }) - }) - - it('should be possible to use `null` as a DEFAULT value', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('foo', (value) => `.foo${value === null ? '-good' : '-bad'} &`, { - values: { DEFAULT: null }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo-good .foo\:underline { - text-decoration-line: underline; - } - `) - }) - }) - - it('should be possible to use `undefined` as a DEFAULT value', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, { - values: { DEFAULT: undefined }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo-good .foo\:underline { - text-decoration-line: underline; - } - `) - }) - }) - - it('should be possible to use `undefined` as a DEFAULT value', () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - plugins: [ - ({ matchVariant }) => { - matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, { - values: { DEFAULT: undefined }, - }) - }, - ], - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo-good .foo\:underline { - text-decoration-line: underline; - } - `) - }) - }) - - it('should not break things', () => { - let config = {} - - let context = createContext(resolveConfig(config)) - let [[, fn]] = context.variantMap.get('group') - - let format - - expect( - fn({ - format(input) { - format = input - }, - }) - ).toBe(undefined) - - expect(format).toBe(':merge(.group) &') + } + `) }) }) + +it('should default to the DEFAULT value for variants', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('foo', (value) => `.foo${value} &`, { + values: { + DEFAULT: '.bar', + }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo.bar .foo\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('should not generate anything if the matchVariant does not have a DEFAULT value configured', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('foo', (value) => `.foo${value} &`) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css``) + }) +}) + +it('should be possible to use `null` as a DEFAULT value', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('foo', (value) => `.foo${value === null ? '-good' : '-bad'} &`, { + values: { DEFAULT: null }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo-good .foo\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('should be possible to use `undefined` as a DEFAULT value', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, { + values: { DEFAULT: undefined }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo-good .foo\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('should be possible to use `undefined` as a DEFAULT value', () => { + let config = { + content: [ + { + raw: html` +
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + ({ matchVariant }) => { + matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, { + values: { DEFAULT: undefined }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo-good .foo\:underline { + text-decoration-line: underline; + } + `) + }) +}) + +it('should not break things', () => { + let config = {} + + let context = createContext(resolveConfig(config)) + let [[, fn]] = context.variantMap.get('group') + + let format + + expect( + fn({ + format(input) { + format = input + }, + }) + ).toBe(undefined) + + expect(format).toBe(':merge(.group) &') +}) diff --git a/tests/min-max-screen-variants.test.js b/tests/min-max-screen-variants.test.js index a86c6f6cd..beab47ac0 100644 --- a/tests/min-max-screen-variants.test.js +++ b/tests/min-max-screen-variants.test.js @@ -1,656 +1,642 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - let defaultScreens = { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', +let defaultScreens = { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', +} + +it('sorts min and max correctly relative to screens and each other', async () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: defaultScreens, + }, } - it('sorts min and max correctly relative to screens and each other', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: defaultScreens, - }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (max-width: 800px) { - .max-\[800px\]\:font-bold { - font-weight: 700; - } - } - @media (max-width: 700px) { - .max-\[700px\]\:font-bold { - font-weight: 700; - } - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 700px) { - .min-\[700px\]\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - @media (min-width: 800px) { - .min-\[800px\]\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('works when using min variants screens config is empty and variants all use the same unit', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: {}, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) + let input = css` + @tailwind utilities; + ` + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` .font-bold { font-weight: 700; } + @media (max-width: 800px) { + .max-\[800px\]\:font-bold { + font-weight: 700; + } + } + @media (max-width: 700px) { + .max-\[700px\]\:font-bold { + font-weight: 700; + } + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } @media (min-width: 700px) { .min-\[700px\]\:font-bold { font-weight: 700; } } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } @media (min-width: 800px) { .min-\[800px\]\:font-bold { font-weight: 700; } } `) - - expect().not.toHaveBeenWarned() }) +}) - it('works when using max variants screens config is empty and variants all use the same unit', () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: {}, +it('works when using min variants screens config is empty and variants all use the same unit', async () => { + let config = { + content: [ + { + raw: html` +
+ `, }, + ], + corePlugins: { preflight: false }, + theme: { + screens: {}, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; } + @media (min-width: 700px) { + .min-\[700px\]\:font-bold { + font-weight: 700; + } + } + @media (min-width: 800px) { + .min-\[800px\]\:font-bold { + font-weight: 700; + } + } + `) - let input = css` - @tailwind utilities; - ` + expect().not.toHaveBeenWarned() +}) - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (max-width: 800px) { - .max-\[800px\]\:font-bold { - font-weight: 700; - } - } - @media (max-width: 700px) { - .max-\[700px\]\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('converts simple min-width screens for max variant', () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: defaultScreens, +it('works when using max variants screens config is empty and variants all use the same unit', () => { + let config = { + content: [ + { + raw: html` +
+ `, }, - } + ], + corePlugins: { preflight: false }, + theme: { + screens: {}, + }, + } - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media not all and (min-width: 1024px) { - .max-lg\:font-bold { - font-weight: 700; - } - } - @media (max-width: 700px) { - .max-\[700px\]\:font-bold { - font-weight: 700; - } - } - @media not all and (min-width: 640px) { - .max-sm\:font-bold { - font-weight: 700; - } - } - @media (max-width: 300px) { - .max-\[300px\]\:font-bold { - font-weight: 700; - } - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('does not have keyed screens for min variant', () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: defaultScreens, - }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 300px) { - .min-\[300px\]\:font-bold { - font-weight: 700; - } - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 700px) { - .min-\[700px\]\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - }) - }) - - it('warns when using min variants with complex screen configs', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - - // Any presence of an object makes it complex - yodawg: { min: '700px' }, - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) + let input = css` + @tailwind utilities; + ` + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` .font-bold { font-weight: 700; } - @media (min-width: 640px) { - .sm\:font-bold { + @media (max-width: 800px) { + .max-\[800px\]\:font-bold { font-weight: 700; } } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['complex-screen-config']) - }) - - it('warns when using min variants with simple configs containing mixed units', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '48rem', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 48rem) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['mixed-screen-units']) - }) - - it('warns when using min variants with mixed units (with screens config)', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) - }) - - it('warns when using min variants with mixed units (with no screens config)', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: {}, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 700px) { - .min-\[700px\]\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) - }) - - it('warns when using max variants with complex screen configs', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - - // Any presence of an object makes it complex - yodawg: { min: '700px' }, - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['complex-screen-config']) - }) - - it('warns when using max variants with simple configs containing mixed units', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '48rem', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 48rem) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['mixed-screen-units']) - }) - - it('warns when using max variants with mixed units (with screens config)', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1536px', - }, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - @media (min-width: 640px) { - .sm\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - `) - - expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) - }) - - it('warns when using max variants with mixed units (with no screens config)', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: {}, - }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } @media (max-width: 700px) { .max-\[700px\]\:font-bold { font-weight: 700; } } `) - - expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) }) +}) - it('warns when using min and max variants with mixed units (with no screens config)', async () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - screens: {}, +it('converts simple min-width screens for max variant', () => { + let config = { + content: [ + { + raw: html` +
+ `, }, - } + ], + corePlugins: { preflight: false }, + theme: { + screens: defaultScreens, + }, + } - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) + let input = css` + @tailwind utilities; + ` + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` .font-bold { font-weight: 700; } - @media (max-width: 700rem) { - .max-\[700rem\]\:font-bold { + @media not all and (min-width: 1024px) { + .max-lg\:font-bold { + font-weight: 700; + } + } + @media (max-width: 700px) { + .max-\[700px\]\:font-bold { + font-weight: 700; + } + } + @media not all and (min-width: 640px) { + .max-sm\:font-bold { + font-weight: 700; + } + } + @media (max-width: 300px) { + .max-\[300px\]\:font-bold { + font-weight: 700; + } + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { font-weight: 700; } } `) - - expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) }) }) + +it('does not have keyed screens for min variant', () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: defaultScreens, + }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 300px) { + .min-\[300px\]\:font-bold { + font-weight: 700; + } + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 700px) { + .min-\[700px\]\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + `) + }) +}) + +it('warns when using min variants with complex screen configs', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + + // Any presence of an object makes it complex + yodawg: { min: '700px' }, + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['complex-screen-config']) +}) + +it('warns when using min variants with simple configs containing mixed units', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '48rem', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 48rem) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['mixed-screen-units']) +}) + +it('warns when using min variants with mixed units (with screens config)', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) +}) + +it('warns when using min variants with mixed units (with no screens config)', async () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: {}, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 700px) { + .min-\[700px\]\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) +}) + +it('warns when using max variants with complex screen configs', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + + // Any presence of an object makes it complex + yodawg: { min: '700px' }, + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['complex-screen-config']) +}) + +it('warns when using max variants with simple configs containing mixed units', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '48rem', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 48rem) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['mixed-screen-units']) +}) + +it('warns when using max variants with mixed units (with screens config)', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (min-width: 640px) { + .sm\:font-bold { + font-weight: 700; + } + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) +}) + +it('warns when using max variants with mixed units (with no screens config)', async () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: {}, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (max-width: 700px) { + .max-\[700px\]\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) +}) + +it('warns when using min and max variants with mixed units (with no screens config)', async () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: { + screens: {}, + }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + @media (max-width: 700rem) { + .max-\[700rem\]\:font-bold { + font-weight: 700; + } + } + `) + + expect().toHaveBeenWarnedWith(['minmax-have-mixed-units']) +}) diff --git a/tests/minimum-impact-selector.test.js b/tests/minimum-impact-selector.test.js index ed2fad914..d8292e467 100644 --- a/tests/minimum-impact-selector.test.js +++ b/tests/minimum-impact-selector.test.js @@ -1,32 +1,29 @@ import { elementSelectorParser } from '../src/lib/resolveDefaultsAtRules' -import { crosscheck } from './util/run' -crosscheck(() => { - it.each` - before | after - ${'*'} | ${'*'} - ${'*:hover'} | ${'*'} - ${'* > *'} | ${'* > *'} - ${'.foo'} | ${'.foo'} - ${'.foo:hover'} | ${'.foo'} - ${'.foo:focus:hover'} | ${'.foo'} - ${'li:first-child'} | ${'li'} - ${'li:before'} | ${'li:before'} - ${'li::before'} | ${'li::before'} - ${'#app .foo'} | ${'.foo'} - ${'#app'} | ${'[id=app]'} - ${'#app.other'} | ${'.other'} - ${'input[type="text"]'} | ${'[type="text"]'} - ${'input[type="text"].foo'} | ${'.foo'} - ${'.group .group\\:foo'} | ${'.group\\:foo'} - ${'.group:hover .group-hover\\:foo'} | ${'.group-hover\\:foo'} - ${'.owl > * + *'} | ${'.owl > *'} - ${'.owl > :not([hidden]) + :not([hidden])'} | ${'.owl > *'} - ${'.group:hover .group-hover\\:owl > :not([hidden]) + :not([hidden])'} | ${'.group-hover\\:owl > *'} - ${'.peer:first-child ~ .peer-first\\:shadow-md'} | ${'.peer-first\\:shadow-md'} - ${'.whats ~ .next > span:hover'} | ${'span'} - ${'.foo .bar ~ .baz > .next > span > article:hover'} | ${'article'} - `('should generate "$after" from "$before"', ({ before, after }) => { - expect(elementSelectorParser.transformSync(before).join(', ')).toEqual(after) - }) +it.each` + before | after + ${'*'} | ${'*'} + ${'*:hover'} | ${'*'} + ${'* > *'} | ${'* > *'} + ${'.foo'} | ${'.foo'} + ${'.foo:hover'} | ${'.foo'} + ${'.foo:focus:hover'} | ${'.foo'} + ${'li:first-child'} | ${'li'} + ${'li:before'} | ${'li:before'} + ${'li::before'} | ${'li::before'} + ${'#app .foo'} | ${'.foo'} + ${'#app'} | ${'[id=app]'} + ${'#app.other'} | ${'.other'} + ${'input[type="text"]'} | ${'[type="text"]'} + ${'input[type="text"].foo'} | ${'.foo'} + ${'.group .group\\:foo'} | ${'.group\\:foo'} + ${'.group:hover .group-hover\\:foo'} | ${'.group-hover\\:foo'} + ${'.owl > * + *'} | ${'.owl > *'} + ${'.owl > :not([hidden]) + :not([hidden])'} | ${'.owl > *'} + ${'.group:hover .group-hover\\:owl > :not([hidden]) + :not([hidden])'} | ${'.group-hover\\:owl > *'} + ${'.peer:first-child ~ .peer-first\\:shadow-md'} | ${'.peer-first\\:shadow-md'} + ${'.whats ~ .next > span:hover'} | ${'span'} + ${'.foo .bar ~ .baz > .next > span > article:hover'} | ${'article'} +`('should generate "$after" from "$before"', ({ before, after }) => { + expect(elementSelectorParser.transformSync(before).join(', ')).toEqual(after) }) diff --git a/tests/modify-selectors.test.js b/tests/modify-selectors.test.js index 3176b17f3..e6ce3cf14 100644 --- a/tests/modify-selectors.test.js +++ b/tests/modify-selectors.test.js @@ -1,95 +1,93 @@ import selectorParser from 'postcss-selector-parser' -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('modify selectors', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-

Lorem ipsum dolor sit amet...

-
-
-

Lorem ipsum dolor sit amet...

-
-
-

Lorem ipsum dolor sit amet...

-
-
-

Lorem ipsum dolor sit amet...

-
- `, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [ - function ({ addVariant }) { - addVariant('foo', ({ modifySelectors, separator }) => { - modifySelectors(({ selector }) => { - return selectorParser((selectors) => { - selectors.walkClasses((classNode) => { - classNode.value = `foo${separator}${classNode.value}` - classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `)) - }) - }).processSync(selector) - }) +test('modify selectors', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+

Lorem ipsum dolor sit amet...

+
+
+

Lorem ipsum dolor sit amet...

+
+
+

Lorem ipsum dolor sit amet...

+
+
+

Lorem ipsum dolor sit amet...

+
+ `, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [ + function ({ addVariant }) { + addVariant('foo', ({ modifySelectors, separator }) => { + modifySelectors(({ selector }) => { + return selectorParser((selectors) => { + selectors.walkClasses((classNode) => { + classNode.value = `foo${separator}${classNode.value}` + classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `)) + }) + }).processSync(selector) }) - }, - ], + }) + }, + ], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + @layer components { + .markdown > p { + margin-top: 12px; + } } + ` - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .markdown > p { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .markdown > p { + margin-top: 12px; + } + .font-bold { + font-weight: 700; + } + .foo .foo\:markdown > p, + .foo .foo\:visited\:markdown:visited > p { + margin-top: 12px; + } + @media (min-width: 1024px) { + .foo .lg\:foo\:disabled\:markdown:disabled > p { margin-top: 12px; } } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .markdown > p { - margin-top: 12px; - } - .font-bold { + .foo .foo\:font-bold, + .foo .foo\:hover\:font-bold:hover { + font-weight: 700; + } + @media (min-width: 640px) { + .foo .sm\:foo\:font-bold { font-weight: 700; } - .foo .foo\:markdown > p, - .foo .foo\:visited\:markdown:visited > p { - margin-top: 12px; - } - @media (min-width: 1024px) { - .foo .lg\:foo\:disabled\:markdown:disabled > p { - margin-top: 12px; - } - } - .foo .foo\:font-bold, - .foo .foo\:hover\:font-bold:hover { + } + @media (min-width: 768px) { + .foo .md\:foo\:focus\:font-bold:focus { font-weight: 700; } - @media (min-width: 640px) { - .foo .sm\:foo\:font-bold { - font-weight: 700; - } - } - @media (min-width: 768px) { - .foo .md\:foo\:focus\:font-bold:focus { - font-weight: 700; - } - } - `) - }) + } + `) }) }) diff --git a/tests/mutable.test.js b/tests/mutable.test.js index 45419380f..5c3f9a487 100644 --- a/tests/mutable.test.js +++ b/tests/mutable.test.js @@ -1,54 +1,52 @@ import path from 'path' import postcss from 'postcss' -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - function pluginThatMutatesRules() { - return (root) => { - root.walkRules((rule) => { - rule.nodes - .filter((node) => node.prop === 'background-image') - .forEach((node) => { - node.value = 'url("./bar.png")' - }) +function pluginThatMutatesRules() { + return (root) => { + root.walkRules((rule) => { + rule.nodes + .filter((node) => node.prop === 'background-image') + .forEach((node) => { + node.value = 'url("./bar.png")' + }) - return rule - }) - } + return rule + }) + } +} + +test('plugins mutating rules after tailwind doesnt break it', async () => { + let config = { + content: [{ raw: html`
` }], + theme: { + backgroundImage: { + foo: 'url("./foo.png")', + }, + }, + plugins: [], } - test('plugins mutating rules after tailwind doesnt break it', async () => { - let config = { - content: [{ raw: html`
` }], - theme: { - backgroundImage: { - foo: 'url("./foo.png")', - }, - }, - plugins: [], - } + function checkResult(result) { + expect(result.css).toMatchFormattedCss(css` + .bg-foo { + background-image: url('./foo.png'); + } + `) + } - function checkResult(result) { - expect(result.css).toMatchFormattedCss(css` - .bg-foo { - background-image: url('./foo.png'); - } - `) - } + // Verify the first run produces the expected result + let firstRun = await run('@tailwind utilities', config) + checkResult(firstRun) - // Verify the first run produces the expected result - let firstRun = await run('@tailwind utilities', config) - checkResult(firstRun) - - // Outside of the context of tailwind jit more postcss plugins may operate on the AST: - // In this case we have a plugin that mutates rules directly - await postcss([pluginThatMutatesRules()]).process(firstRun, { - from: path.resolve(__filename), - }) - - // Verify subsequent runs don't produce mutated rules - let secondRun = await run('@tailwind utilities', config) - checkResult(secondRun) + // Outside of the context of tailwind jit more postcss plugins may operate on the AST: + // In this case we have a plugin that mutates rules directly + await postcss([pluginThatMutatesRules()]).process(firstRun, { + from: path.resolve(__filename), }) + + // Verify subsequent runs don't produce mutated rules + let secondRun = await run('@tailwind utilities', config) + checkResult(secondRun) }) diff --git a/tests/negateValue.test.js b/tests/negateValue.test.js index 052132f91..c31741d48 100644 --- a/tests/negateValue.test.js +++ b/tests/negateValue.test.js @@ -1,17 +1,14 @@ import negateValue from '../src/util/negateValue' -import { crosscheck } from './util/run' -crosscheck(() => { - test('it negates numeric CSS values', () => { - expect(negateValue('5')).toEqual('-5') - expect(negateValue('10px')).toEqual('-10px') - expect(negateValue('18rem')).toEqual('-18rem') - expect(negateValue('-10')).toEqual('10') - expect(negateValue('-7ch')).toEqual('7ch') - }) - - test('values that cannot be negated become undefined', () => { - expect(negateValue('auto')).toBeUndefined() - expect(negateValue('cover')).toBeUndefined() - }) +test('it negates numeric CSS values', () => { + expect(negateValue('5')).toEqual('-5') + expect(negateValue('10px')).toEqual('-10px') + expect(negateValue('18rem')).toEqual('-18rem') + expect(negateValue('-10')).toEqual('10') + expect(negateValue('-7ch')).toEqual('7ch') +}) + +test('values that cannot be negated become undefined', () => { + expect(negateValue('auto')).toBeUndefined() + expect(negateValue('cover')).toBeUndefined() }) diff --git a/tests/negated-content.test.js b/tests/negated-content.test.js index 66e231946..6d46e3a8d 100644 --- a/tests/negated-content.test.js +++ b/tests/negated-content.test.js @@ -1,29 +1,27 @@ import * as path from 'path' -import { crosscheck, run, css, defaults } from './util/run' +import { run, css, defaults } from './util/run' -crosscheck(() => { - it('should be possible to use negated content patterns', () => { - let config = { - content: [ - path.resolve(__dirname, './negated-content-*.test.html'), - '!' + path.resolve(__dirname, './negated-content-ignore.test.html'), - ], - corePlugins: { preflight: false }, - } +it('should be possible to use negated content patterns', () => { + let config = { + content: [ + path.resolve(__dirname, './negated-content-*.test.html'), + '!' + path.resolve(__dirname, './negated-content-ignore.test.html'), + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .uppercase { - text-transform: uppercase; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .uppercase { + text-transform: uppercase; + } + `) }) }) diff --git a/tests/negative-prefix.test.js b/tests/negative-prefix.test.js index c799bb54e..8b4949b1a 100644 --- a/tests/negative-prefix.test.js +++ b/tests/negative-prefix.test.js @@ -1,346 +1,342 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('using a negative prefix with a negative scale value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - 2: '8px', - '-2': '-4px', - }, +test('using a negative prefix with a negative scale value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + 2: '8px', + '-2': '-4px', }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-2 { - margin-top: -4px; - } - .mt-2 { - margin-top: 8px; - } - `) - }) - }) - - test('using a negative scale value with a plugin that does not support dynamic negative values', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - opacity: { - '-50': '0.5', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-opacity-50 { - opacity: 0.5; - } - `) - }) - }) - - test('using a negative prefix without a negative scale value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - 5: '20px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-5 { - margin-top: -20px; - } - .mt-5 { - margin-top: 20px; - } - `) - }) - }) - - test('being an asshole', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - '-[10px]': '55px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-\[10px\] { - margin-top: 55px; - } - `) - }) - }) - - test('being a real asshole', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - '[10px]': '55px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-\[10px\] { - margin-top: -55px; - } - `) - }) - }) - - test('a value that includes a variable', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - 5: 'var(--sizing-5)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-5 { - margin-top: calc(var(--sizing-5) * -1); - } - .mt-5 { - margin-top: var(--sizing-5); - } - `) - }) - }) - - test('a value that includes a calc', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - 5: 'calc(52px * -3)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-5 { - margin-top: 156px; - } - .mt-5 { - margin-top: -156px; - } - `) - }) - }) - - test('a value that includes min/max/clamp functions', () => { - let config = { - content: [ - { raw: html`
` }, - ], - theme: { - margin: { - min: 'min(100vmin, 3rem)', - max: 'max(100vmax, 3rem)', - clamp: 'clamp(1rem, 100vh, 3rem)', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-clamp { - margin-top: calc(-1 * clamp(1rem, 100vh, 3rem)); - } - .-mt-max { - margin-top: calc(-1 * max(100vmax, 3rem)); - } - .-mt-min { - margin-top: calc(-1 * min(100vmin, 3rem)); - } - .mt-clamp { - margin-top: clamp(1rem, 100vh, 3rem); - } - .mt-max { - margin-top: max(100vmax, 3rem); - } - .mt-min { - margin-top: min(100vmin, 3rem); - } - `) - }) - }) - - test('a keyword value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - auto: 'auto', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .mt-auto { - margin-top: auto; - } - `) - }) - }) - - test('a zero value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - 0: '0', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-0, - .mt-0 { - margin-top: 0; - } - `) - }) - }) - - test('a color', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - colors: { - red: 'red', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css``) - }) - }) - - test('arbitrary values', () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-\[10px\] { - margin-top: -10px; - } - `) - }) - }) - - test('negating a negative scale value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - weird: '-15px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt-weird { - margin-top: 15px; - } - `) - }) - }) - - test('negating a default value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - DEFAULT: '15px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt { - margin-top: -15px; - } - `) - }) - }) - - test('using a negative prefix with a negative default scale value', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - margin: { - DEFAULT: '8px', - '-DEFAULT': '-4px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .-mt { - margin-top: -4px; - } - .mt { - margin-top: 8px; - } - `) - }) - }) - - test('negating a default value with a configured prefix', () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - theme: { - margin: { - DEFAULT: '15px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .tw--mt { - margin-top: -15px; - } - `) - }) - }) - - test('arbitrary value keywords should be ignored', () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css``) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-2 { + margin-top: -4px; + } + .mt-2 { + margin-top: 8px; + } + `) + }) +}) + +test('using a negative scale value with a plugin that does not support dynamic negative values', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + opacity: { + '-50': '0.5', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-opacity-50 { + opacity: 0.5; + } + `) + }) +}) + +test('using a negative prefix without a negative scale value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + 5: '20px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-5 { + margin-top: -20px; + } + .mt-5 { + margin-top: 20px; + } + `) + }) +}) + +test('being an asshole', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + '-[10px]': '55px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-\[10px\] { + margin-top: 55px; + } + `) + }) +}) + +test('being a real asshole', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + '[10px]': '55px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-\[10px\] { + margin-top: -55px; + } + `) + }) +}) + +test('a value that includes a variable', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + 5: 'var(--sizing-5)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-5 { + margin-top: calc(var(--sizing-5) * -1); + } + .mt-5 { + margin-top: var(--sizing-5); + } + `) + }) +}) + +test('a value that includes a calc', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + 5: 'calc(52px * -3)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-5 { + margin-top: 156px; + } + .mt-5 { + margin-top: -156px; + } + `) + }) +}) + +test('a value that includes min/max/clamp functions', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + min: 'min(100vmin, 3rem)', + max: 'max(100vmax, 3rem)', + clamp: 'clamp(1rem, 100vh, 3rem)', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-clamp { + margin-top: calc(-1 * clamp(1rem, 100vh, 3rem)); + } + .-mt-max { + margin-top: calc(-1 * max(100vmax, 3rem)); + } + .-mt-min { + margin-top: calc(-1 * min(100vmin, 3rem)); + } + .mt-clamp { + margin-top: clamp(1rem, 100vh, 3rem); + } + .mt-max { + margin-top: max(100vmax, 3rem); + } + .mt-min { + margin-top: min(100vmin, 3rem); + } + `) + }) +}) + +test('a keyword value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + auto: 'auto', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .mt-auto { + margin-top: auto; + } + `) + }) +}) + +test('a zero value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + 0: '0', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-0, + .mt-0 { + margin-top: 0; + } + `) + }) +}) + +test('a color', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + colors: { + red: 'red', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css``) + }) +}) + +test('arbitrary values', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-\[10px\] { + margin-top: -10px; + } + `) + }) +}) + +test('negating a negative scale value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + weird: '-15px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt-weird { + margin-top: 15px; + } + `) + }) +}) + +test('negating a default value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + DEFAULT: '15px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt { + margin-top: -15px; + } + `) + }) +}) + +test('using a negative prefix with a negative default scale value', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + margin: { + DEFAULT: '8px', + '-DEFAULT': '-4px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .-mt { + margin-top: -4px; + } + .mt { + margin-top: 8px; + } + `) + }) +}) + +test('negating a default value with a configured prefix', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + theme: { + margin: { + DEFAULT: '15px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .tw--mt { + margin-top: -15px; + } + `) + }) +}) + +test('arbitrary value keywords should be ignored', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css``) }) }) diff --git a/tests/normalize-config.test.js b/tests/normalize-config.test.js index 47980cbdd..0468c8c78 100644 --- a/tests/normalize-config.test.js +++ b/tests/normalize-config.test.js @@ -1,164 +1,155 @@ import { normalizeConfig } from '../src/util/normalizeConfig' import resolveConfig from '../src/public/resolve-config' -import { crosscheck, run, css } from './util/run' +import { run, css } from './util/run' -crosscheck(({ stable, oxide }) => { - it.each` - config - ${{ purge: [{ raw: 'text-center' }] }} - ${{ purge: { content: [{ raw: 'text-center' }] } }} - ${{ content: { files: [], content: [{ raw: 'text-center' }] } }} - `('should normalize content $config', ({ config }) => { - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .text-center { - text-align: center; - } - `) - }) - }) - - it.each` - config - ${{ purge: { safelist: ['text-center'] } }} - ${{ purge: { options: { safelist: ['text-center'] } } }} - ${{ content: { files: [], safelist: ['text-center'] } }} - `('should normalize safelist $config', ({ config }) => { - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .text-center { - text-align: center; - } - `) - }) - }) - - oxide.test.todo('should normalize extractors') - stable.test.each` - config - ${{ content: [{ raw: 'text-center' }], purge: { extract: () => ['font-bold'] } }} - ${{ content: [{ raw: 'text-center' }], purge: { extract: { DEFAULT: () => ['font-bold'] } } }} - ${{ - content: [{ raw: 'text-center' }], - purge: { options: { defaultExtractor: () => ['font-bold'] } }, - }} - ${{ - content: [{ raw: 'text-center' }], - purge: { - options: { extractors: [{ extractor: () => ['font-bold'], extensions: ['html'] }] }, - }, - }} - ${{ content: [{ raw: 'text-center' }], purge: { extract: { html: () => ['font-bold'] } } }} - `('should normalize extractors $config', ({ config }) => { - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .font-bold { - font-weight: 700; - } - `) - }) - }) - - oxide.test.todo('should still be possible to use the "old" v2 config') - stable.test('should still be possible to use the "old" v2 config', () => { - let config = { - purge: { - content: [ - { raw: 'text-svelte', extension: 'svelte' }, - { raw: '# My Big Heading', extension: 'md' }, - ], - options: { - defaultExtractor(content) { - return content.split(' ').concat(['font-bold']) - }, - }, - extract: { - svelte(content) { - return content.replace('svelte', 'center').split(' ') - }, - }, - transform: { - md() { - return 'text-4xl' - }, - }, - }, - theme: { - extends: {}, - }, - variants: { - extends: {}, - }, - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .text-center { - text-align: center; - } - .text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; - } - .font-bold { - font-weight: 700; - } - `) - }) - }) - - it('should keep content files with globs', () => { - let config = { - content: ['./example-folder/**/*.{html,js}'], - } - - stable.expect(normalizeConfig(resolveConfig(config)).content).toEqual({ - files: ['./example-folder/**/*.{html,js}'], - relative: false, - extract: {}, - transform: {}, - }) - oxide.expect(normalizeConfig(resolveConfig(config)).content).toEqual({ - files: ['./example-folder/**/*.{html,js}'], - relative: true, - extract: {}, - transform: {}, - }) - }) - - it('should warn when we detect invalid globs with incorrect brace expansion', () => { - let config = { - content: [ - './{example-folder}/**/*.{html,js}', - './{example-folder}/**/*.{html}', - './example-folder/**/*.{html}', - ], - } - - let normalizedConfig = normalizeConfig(resolveConfig(config)).content - - // No rewrite happens - stable.expect(normalizedConfig).toEqual({ - files: [ - './{example-folder}/**/*.{html,js}', - './{example-folder}/**/*.{html}', - './example-folder/**/*.{html}', - ], - relative: false, - extract: {}, - transform: {}, - }) - oxide.expect(normalizedConfig).toEqual({ - files: [ - './{example-folder}/**/*.{html,js}', - './{example-folder}/**/*.{html}', - './example-folder/**/*.{html}', - ], - relative: true, - extract: {}, - transform: {}, - }) - - expect().toHaveBeenWarnedWith(['invalid-glob-braces']) +it.each` + config + ${{ purge: [{ raw: 'text-center' }] }} + ${{ purge: { content: [{ raw: 'text-center' }] } }} + ${{ content: { files: [], content: [{ raw: 'text-center' }] } }} +`('should normalize content $config', ({ config }) => { + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .text-center { + text-align: center; + } + `) }) }) + +it.each` + config + ${{ purge: { safelist: ['text-center'] } }} + ${{ purge: { options: { safelist: ['text-center'] } } }} + ${{ content: { files: [], safelist: ['text-center'] } }} +`('should normalize safelist $config', ({ config }) => { + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .text-center { + text-align: center; + } + `) + }) +}) + +test.each` + config + ${{ + purge: { content: [{ raw: 'text-center' }], extract: () => ['font-bold'] }, +}} + ${{ + purge: { content: [{ raw: 'text-center' }], extract: { DEFAULT: () => ['font-bold'] } }, +}} + ${{ + purge: { + content: [{ raw: 'text-center' }], + options: { defaultExtractor: () => ['font-bold'] }, + }, +}} + ${{ + purge: { + content: [{ raw: 'text-center' }], + options: { extractors: [{ extractor: () => ['font-bold'], extensions: ['html'] }] }, + }, +}} + ${{ + purge: { + content: [{ raw: 'text-center' }], + extract: { html: () => ['font-bold'] }, + }, +}} +`('should normalize extractors $config', ({ config }) => { + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .font-bold { + font-weight: 700; + } + `) + }) +}) + +test('should still be possible to use the "old" v2 config', () => { + let config = { + purge: { + content: [ + { raw: 'text-svelte', extension: 'svelte' }, + { raw: '# My Big Heading', extension: 'md' }, + ], + options: { + defaultExtractor(content) { + return content.split(' ').concat(['font-bold']) + }, + }, + extract: { + svelte(content) { + return content.replace('svelte', 'center').split(' ') + }, + }, + transform: { + md() { + return 'text-4xl' + }, + }, + }, + theme: { + extends: {}, + }, + variants: { + extends: {}, + }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .text-center { + text-align: center; + } + .text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + .font-bold { + font-weight: 700; + } + `) + }) +}) + +it('should keep content files with globs', () => { + let config = { + content: ['./example-folder/**/*.{html,js}'], + } + + expect(normalizeConfig(resolveConfig(config)).content).toEqual({ + files: ['./example-folder/**/*.{html,js}'], + relative: false, + extract: {}, + transform: {}, + }) +}) + +it('should warn when we detect invalid globs with incorrect brace expansion', () => { + let config = { + content: [ + './{example-folder}/**/*.{html,js}', + './{example-folder}/**/*.{html}', + './example-folder/**/*.{html}', + ], + } + + let normalizedConfig = normalizeConfig(resolveConfig(config)).content + + // No rewrite happens + expect(normalizedConfig).toEqual({ + files: [ + './{example-folder}/**/*.{html,js}', + './{example-folder}/**/*.{html}', + './example-folder/**/*.{html}', + ], + relative: false, + extract: {}, + transform: {}, + }) + + expect().toHaveBeenWarnedWith(['invalid-glob-braces']) +}) diff --git a/tests/normalize-data-types.test.js b/tests/normalize-data-types.test.js index eff2fa677..d99ce1c18 100644 --- a/tests/normalize-data-types.test.js +++ b/tests/normalize-data-types.test.js @@ -1,5 +1,4 @@ import { normalize } from '../src/util/dataTypes' -import { crosscheck } from './util/run' let table = [ ['foo', 'foo'], @@ -47,8 +46,6 @@ let table = [ ['color(0_0_0_/_1.0)', 'color(0 0 0 / 1.0)'], ] -crosscheck(() => { - it.each(table)('normalize data: %s', (input, output) => { - expect(normalize(input)).toBe(output) - }) +it.each(table)('normalize data: %s', (input, output) => { + expect(normalize(input)).toBe(output) }) diff --git a/tests/normalize-screens.test.js b/tests/normalize-screens.test.js index 464623f9f..da31c50d3 100644 --- a/tests/normalize-screens.test.js +++ b/tests/normalize-screens.test.js @@ -1,66 +1,63 @@ import { normalizeScreens } from '../src/util/normalizeScreens' -import { crosscheck } from './util/run' -crosscheck(() => { - it('should normalize an array of string values', () => { - let screens = ['768px', '1200px'] +it('should normalize an array of string values', () => { + let screens = ['768px', '1200px'] - expect(normalizeScreens(screens)).toEqual([ - { name: '768px', not: false, values: [{ min: '768px', max: undefined }] }, - { name: '1200px', not: false, values: [{ min: '1200px', max: undefined }] }, - ]) - }) - - it('should normalize an object with string values', () => { - let screens = { - a: '768px', - b: '1200px', - } - - expect(normalizeScreens(screens)).toEqual([ - { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, - { name: 'b', not: false, values: [{ min: '1200px', max: undefined }] }, - ]) - }) - - it('should normalize an object with object values', () => { - let screens = { - a: { min: '768px' }, - b: { max: '1200px' }, - } - - expect(normalizeScreens(screens)).toEqual([ - { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, - { name: 'b', not: false, values: [{ min: undefined, max: '1200px' }] }, - ]) - }) - - it('should normalize an object with multiple object values', () => { - let screens = { - a: [{ min: '768px' }, { max: '1200px' }], - } - - expect(normalizeScreens(screens)).toEqual([ - { - name: 'a', - not: false, - values: [ - { max: undefined, min: '768px', raw: undefined }, - { max: '1200px', min: undefined, raw: undefined }, - ], - }, - ]) - }) - - it('should normalize an object with object values (min-width normalized to width)', () => { - let screens = { - a: { 'min-width': '768px' }, - b: { max: '1200px' }, - } - - expect(normalizeScreens(screens)).toEqual([ - { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, - { name: 'b', not: false, values: [{ min: undefined, max: '1200px' }] }, - ]) - }) + expect(normalizeScreens(screens)).toEqual([ + { name: '768px', not: false, values: [{ min: '768px', max: undefined }] }, + { name: '1200px', not: false, values: [{ min: '1200px', max: undefined }] }, + ]) +}) + +it('should normalize an object with string values', () => { + let screens = { + a: '768px', + b: '1200px', + } + + expect(normalizeScreens(screens)).toEqual([ + { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, + { name: 'b', not: false, values: [{ min: '1200px', max: undefined }] }, + ]) +}) + +it('should normalize an object with object values', () => { + let screens = { + a: { min: '768px' }, + b: { max: '1200px' }, + } + + expect(normalizeScreens(screens)).toEqual([ + { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, + { name: 'b', not: false, values: [{ min: undefined, max: '1200px' }] }, + ]) +}) + +it('should normalize an object with multiple object values', () => { + let screens = { + a: [{ min: '768px' }, { max: '1200px' }], + } + + expect(normalizeScreens(screens)).toEqual([ + { + name: 'a', + not: false, + values: [ + { max: undefined, min: '768px', raw: undefined }, + { max: '1200px', min: undefined, raw: undefined }, + ], + }, + ]) +}) + +it('should normalize an object with object values (min-width normalized to width)', () => { + let screens = { + a: { 'min-width': '768px' }, + b: { max: '1200px' }, + } + + expect(normalizeScreens(screens)).toEqual([ + { name: 'a', not: false, values: [{ min: '768px', max: undefined }] }, + { name: 'b', not: false, values: [{ min: undefined, max: '1200px' }] }, + ]) }) diff --git a/tests/opacity.test.js b/tests/opacity.test.js index 82b437ce8..4b2b0eadf 100644 --- a/tests/opacity.test.js +++ b/tests/opacity.test.js @@ -1,1115 +1,1047 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - test('opacity', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
- `, - }, - ], - corePlugins: { - backgroundOpacity: false, - borderOpacity: false, - divideOpacity: false, - placeholderOpacity: false, - textOpacity: false, +test('opacity', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+ `, }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-black > :not([hidden]) ~ :not([hidden]), - .border-black { - border-color: #000; - } - .bg-black { - background-color: #000; - } - .text-black, - .placeholder-black::placeholder { - color: #000; - } - `) - }) - }) - - test('colors defined as functions work when opacity plugins are disabled', () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
- `, - }, - ], - theme: { - colors: { - primary: ({ opacityValue }) => - opacityValue === undefined - ? 'rgb(var(--color-primary))' - : `rgb(var(--color-primary) / ${opacityValue})`, - }, - }, - corePlugins: { - backgroundOpacity: false, - borderOpacity: false, - divideOpacity: false, - placeholderOpacity: false, - textOpacity: false, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]), - .border-primary { - border-color: rgb(var(--color-primary)); - } - .bg-primary { - background-color: rgb(var(--color-primary)); - } - .text-primary, - .placeholder-primary::placeholder { - color: rgb(var(--color-primary)); - } - `) - }) - }) - - it('can use defining custom properties for colors (opacity plugins enabled)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(var(--color-primary) / var(--tw-divide-opacity)); - } - .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 0.5; - } - .border-primary { - --tw-border-opacity: 1; - border-color: rgb(var(--color-primary) / var(--tw-border-opacity)); - } - .border-opacity-50 { - --tw-border-opacity: 0.5; - } - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(var(--color-primary) / var(--tw-bg-opacity)); - } - .bg-opacity-50 { - --tw-bg-opacity: 0.5; - } - .text-primary { - --tw-text-opacity: 1; - color: rgb(var(--color-primary) / var(--tw-text-opacity)); - } - .text-opacity-50 { - --tw-text-opacity: 0.5; - } - .placeholder-primary::placeholder { - --tw-placeholder-opacity: 1; - color: rgb(var(--color-primary) / var(--tw-placeholder-opacity)); - } - .placeholder-opacity-50::placeholder { - --tw-placeholder-opacity: 0.5; - } - .ring-primary { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(var(--color-primary) / var(--tw-ring-opacity)); - } - .ring-opacity-50 { - --tw-ring-opacity: 0.5; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]), - .border-primary { - border-color: rgb(var(--color-primary) / 1); - } - .bg-primary { - background-color: rgb(var(--color-primary) / 1); - } - .text-primary, - .placeholder-primary::placeholder { - color: rgb(var(--color-primary) / 1); - } - .ring-primary { - --tw-ring-color: rgb(var(--color-primary) / 1); - } - `) - }) - }) - - it('can use rgb helper when defining custom properties for colors (opacity plugins disabled)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - corePlugins: { - backgroundOpacity: false, - borderOpacity: false, - divideOpacity: false, - placeholderOpacity: false, - textOpacity: false, - ringOpacity: false, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]) { - border-color: rgb(var(--color-primary) / 1); - } - .divide-primary\/50 > :not([hidden]) ~ :not([hidden]) { - border-color: rgb(var(--color-primary) / 0.5); - } - .border-primary { - border-color: rgb(var(--color-primary) / 1); - } - .border-primary\/50 { - border-color: rgb(var(--color-primary) / 0.5); - } - .bg-primary { - background-color: rgb(var(--color-primary) / 1); - } - .bg-primary\/50 { - background-color: rgb(var(--color-primary) / 0.5); - } - .text-primary { - color: rgb(var(--color-primary) / 1); - } - .text-primary\/50 { - color: rgb(var(--color-primary) / 0.5); - } - .placeholder-primary::placeholder { - color: rgb(var(--color-primary) / 1); - } - .placeholder-primary\/50::placeholder { - color: rgb(var(--color-primary) / 0.5); - } - .ring-primary { - --tw-ring-color: rgb(var(--color-primary) / 1); - } - .ring-primary\/50 { - --tw-ring-color: rgb(var(--color-primary) / 0.5); - } - `) - }) - }) - - it('can use hsl helper when defining custom properties for colors (opacity plugins enabled)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - theme: { - colors: { - primary: 'hsl(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: hsl(var(--color-primary) / var(--tw-divide-opacity)); - } - .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 0.5; - } - .border-primary { - --tw-border-opacity: 1; - border-color: hsl(var(--color-primary) / var(--tw-border-opacity)); - } - .border-opacity-50 { - --tw-border-opacity: 0.5; - } - .bg-primary { - --tw-bg-opacity: 1; - background-color: hsl(var(--color-primary) / var(--tw-bg-opacity)); - } - .bg-opacity-50 { - --tw-bg-opacity: 0.5; - } - .text-primary { - --tw-text-opacity: 1; - color: hsl(var(--color-primary) / var(--tw-text-opacity)); - } - .text-opacity-50 { - --tw-text-opacity: 0.5; - } - .placeholder-primary::placeholder { - --tw-placeholder-opacity: 1; - color: hsl(var(--color-primary) / var(--tw-placeholder-opacity)); - } - .placeholder-opacity-50::placeholder { - --tw-placeholder-opacity: 0.5; - } - .ring-primary { - --tw-ring-opacity: 1; - --tw-ring-color: hsl(var(--color-primary) / var(--tw-ring-opacity)); - } - .ring-opacity-50 { - --tw-ring-opacity: 0.5; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]), - .border-primary { - border-color: hsl(var(--color-primary) / 1); - } - .bg-primary { - background-color: hsl(var(--color-primary) / 1); - } - .text-primary, - .placeholder-primary::placeholder { - color: hsl(var(--color-primary) / 1); - } - .ring-primary { - --tw-ring-color: hsl(var(--color-primary) / 1); - } - `) - }) - }) - - it('can use hsl helper when defining custom properties for colors (opacity plugins disabled)', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - theme: { - colors: { - primary: 'hsl(var(--color-primary) / )', - }, - }, - corePlugins: { - backgroundOpacity: false, - borderOpacity: false, - divideOpacity: false, - placeholderOpacity: false, - textOpacity: false, - ringOpacity: false, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-primary > :not([hidden]) ~ :not([hidden]) { - border-color: hsl(var(--color-primary) / 1); - } - .divide-primary\/50 > :not([hidden]) ~ :not([hidden]) { - border-color: hsl(var(--color-primary) / 0.5); - } - .border-primary { - border-color: hsl(var(--color-primary) / 1); - } - .border-primary\/50 { - border-color: hsl(var(--color-primary) / 0.5); - } - .bg-primary { - background-color: hsl(var(--color-primary) / 1); - } - .bg-primary\/50 { - background-color: hsl(var(--color-primary) / 0.5); - } - .text-primary { - color: hsl(var(--color-primary) / 1); - } - .text-primary\/50 { - color: hsl(var(--color-primary) / 0.5); - } - .placeholder-primary::placeholder { - color: hsl(var(--color-primary) / 1); - } - .placeholder-primary\/50::placeholder { - color: hsl(var(--color-primary) / 0.5); - } - .ring-primary { - --tw-ring-color: hsl(var(--color-primary) / 1); - } - .ring-primary\/50 { - --tw-ring-color: hsl(var(--color-primary) / 0.5); - } - `) - }) - }) - - test('Theme function in JS can apply alpha values to colors (1)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: #3b82f680; - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: '#3b82f6' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / 50%'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (2)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: #3b82f680; - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: '#3b82f6' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / 0.5'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (3)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: rgb(59 130 246 / var(--my-alpha)); - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: '#3b82f6' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / var(--my-alpha)'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (4)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: #3c83f680; - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / 50%'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (5)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: #3c83f680; - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / 0.5'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (6)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: hsl(217 91% 60% / var(--my-alpha)); - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / var(--my-alpha)'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function in JS can apply alpha values to colors (7)', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: rgb(var(--foo) / var(--my-alpha)); - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { - blue: { - 500: 'rgb(var(--foo) / )', - }, - }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / var(--my-alpha)'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - test('Theme function prefers existing values in config', () => { - let input = css` - @tailwind utilities; - ` - - let output = css` - .text-foo { - color: purple; - } - ` - - return run(input, { - content: [{ raw: html`text-foo` }], - corePlugins: { textOpacity: false }, - theme: { - colors: { - blue: { - '500 / 50%': 'purple', - }, - }, - extend: { - textColor: ({ theme }) => ({ - foo: theme('colors.blue.500 / 50%'), - }), - }, - }, - }).then((result) => { - expect(result.css).toMatchFormattedCss(output) - expect(result.warnings().length).toBe(0) - }) - }) - - it('should be possible to use an as part of the color definition', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: ['backgroundColor', 'backgroundOpacity'], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-primary { - --tw-bg-opacity: 1; - background-color: rgb(var(--color-primary) / var(--tw-bg-opacity)); - } - `) - }) - }) - - it('should be possible to use an as part of the color definition with an opacity modifiers', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: ['backgroundColor', 'backgroundOpacity'], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-primary\/50 { - background-color: rgb(var(--color-primary) / 0.5); - } - `) - }) - }) - - it('should be possible to use an as part of the color definition with an opacity modifiers', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: ['backgroundColor'], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-primary { - background-color: rgb(var(--color-primary) / 1); - } - `) - }) - }) - - it('should be possible to use inside arbitrary values', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: ['backgroundColor', 'backgroundOpacity'], - theme: { - colors: { - primary: 'rgb(var(--color-primary) / )', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-\[rgb\(var\(--color-primary\)\/\\)\]\/50 { - background-color: rgb(var(--color-primary) / 0.5); - } - `) - }) - }) - - it('Theme functions can reference values with slashes in brackets', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - theme: { - colors: { - 'a/b': '#000000', - }, - extend: { - backgroundColor: ({ theme }) => ({ - foo1: theme('colors[a/b]'), - foo2: theme('colors[a/b]/50%'), - }), - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-foo1 { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - .bg-foo2 { - background-color: #00000080; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-foo1 { - background-color: #000; - } - .bg-foo2 { - background-color: #00000080; - } - `) - }) - }) - - it('works with opacity values defined as a placeholder or a function in when colors is a function', () => { - let config = { - content: [ - { - raw: html` -
- `, - }, - ], - theme: { - colors: () => ({ - foobar1: ({ opacityValue }) => `rgb(255 100 0 / ${opacityValue ?? '100%'})`, - foobar2: `rgb(255 100 0 / )`, - foobar3: { - 100: ({ opacityValue }) => `rgb(255 100 0 / ${opacityValue ?? '100%'})`, - 200: `rgb(255 100 0 / )`, - }, - }), - extend: { - backgroundColor: ({ theme }) => ({ - foo10: theme('colors.foobar1'), - foo20: theme('colors.foobar2'), - foo30: theme('colors.foobar3.100'), - foo40: theme('colors.foobar3.200'), - foo11: theme('colors.foobar1 / 50%'), - foo21: theme('colors.foobar2 / 50%'), - foo31: theme('colors.foobar3.100 / 50%'), - foo41: theme('colors.foobar3.200 / 50%'), - }), - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-foo10 { - background-color: #ff6400; - } - .bg-foo11 { - background-color: #ff640080; - } - .bg-foo20 { - --tw-bg-opacity: 1; - background-color: rgb(255 100 0 / var(--tw-bg-opacity)); - } - .bg-foo21 { - background-color: #ff640080; - } - .bg-foo30 { - background-color: #ff6400; - } - .bg-foo31 { - background-color: #ff640080; - } - .bg-foo40 { - --tw-bg-opacity: 1; - background-color: rgb(255 100 0 / var(--tw-bg-opacity)); - } - .bg-foo41 { - background-color: #ff640080; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-foo10 { - background-color: #ff6400; - } - .bg-foo11 { - background-color: #ff640080; - } - .bg-foo20 { - background-color: #ff6400; - } - .bg-foo21 { - background-color: #ff640080; - } - .bg-foo30 { - background-color: #ff6400; - } - .bg-foo31 { - background-color: #ff640080; - } - .bg-foo40 { - background-color: #ff6400; - } - .bg-foo41 { - background-color: #ff640080; - } - `) - }) - }) - - it('The disableColorOpacityUtilitiesByDefault flag disables the color opacity plugins and removes their variables', () => { - let config = { - future: { - disableColorOpacityUtilitiesByDefault: true, - }, - content: [ - { - raw: html` -
-
-
-
- `, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-blue-300 > :not([hidden]) ~ :not([hidden]) { - border-color: #93c5fd; - } - .divide-blue-300\/50 > :not([hidden]) ~ :not([hidden]) { - border-color: #93c5fd80; - } - .divide-blue-300\/\[var\(--my-opacity\)\] > :not([hidden]) ~ :not([hidden]) { - border-color: rgb(147 197 253 / var(--my-opacity)); - } - .border-blue-300 { - border-color: #93c5fd; - } - .border-blue-300\/50 { - border-color: #93c5fd80; - } - .border-blue-300\/\[var\(--my-opacity\)\] { - border-color: rgb(147 197 253 / var(--my-opacity)); - } - .bg-blue-300 { - background-color: #93c5fd; - } - .bg-blue-300\/50 { - background-color: #93c5fd80; - } - .bg-blue-300\/\[var\(--my-opacity\)\] { - background-color: rgb(147 197 253 / var(--my-opacity)); - } - .text-blue-300 { - color: #93c5fd; - } - .text-blue-300\/50 { - color: #93c5fd80; - } - .text-blue-300\/\[var\(--my-opacity\)\] { - color: rgb(147 197 253 / var(--my-opacity)); - } - .placeholder-blue-300::placeholder { - color: #93c5fd; - } - .placeholder-blue-300\/50::placeholder { - color: #93c5fd80; - } - .placeholder-blue-300\/\[var\(--my-opacity\)\]::placeholder { - color: rgb(147 197 253 / var(--my-opacity)); - } - .ring-blue-300 { - --tw-ring-color: #93c5fd; - } - .ring-blue-300\/50 { - --tw-ring-color: #93c5fd80; - } - .ring-blue-300\/\[var\(--my-opacity\)\] { - --tw-ring-color: rgb(147 197 253 / var(--my-opacity)); - } - `) - }) - }) - - it('You can re-enable any opacity plugin even when disableColorOpacityUtilitiesByDefault is enabled', () => { - let config = { - future: { - disableColorOpacityUtilitiesByDefault: true, - }, - corePlugins: { - backgroundOpacity: true, - borderOpacity: true, - divideOpacity: true, - placeholderOpacity: true, - ringOpacity: true, - textOpacity: true, - }, - content: [ - { - raw: html` -
-
-
-
- `, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .divide-blue-300 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: rgb(147 197 253 / var(--tw-divide-opacity)); - } - .divide-blue-300\/50 > :not([hidden]) ~ :not([hidden]) { - border-color: #93c5fd80; - } - .divide-blue-300\/\[var\(--my-opacity\)\] > :not([hidden]) ~ :not([hidden]) { - border-color: rgb(147 197 253 / var(--my-opacity)); - } - .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 0.5; - } - .border-blue-300 { - --tw-border-opacity: 1; - border-color: rgb(147 197 253 / var(--tw-border-opacity)); - } - .border-blue-300\/50 { - border-color: #93c5fd80; - } - .border-blue-300\/\[var\(--my-opacity\)\] { - border-color: rgb(147 197 253 / var(--my-opacity)); - } - .border-opacity-50 { - --tw-border-opacity: 0.5; - } - .bg-blue-300 { - --tw-bg-opacity: 1; - background-color: rgb(147 197 253 / var(--tw-bg-opacity)); - } - .bg-blue-300\/50 { - background-color: #93c5fd80; - } - .bg-blue-300\/\[var\(--my-opacity\)\] { - background-color: rgb(147 197 253 / var(--my-opacity)); - } - .bg-opacity-50 { - --tw-bg-opacity: 0.5; - } - .text-blue-300 { - --tw-text-opacity: 1; - color: rgb(147 197 253 / var(--tw-text-opacity)); - } - .text-blue-300\/50 { - color: #93c5fd80; - } - .text-blue-300\/\[var\(--my-opacity\)\] { - color: rgb(147 197 253 / var(--my-opacity)); - } - .text-opacity-50 { - --tw-text-opacity: 0.5; - } - .placeholder-blue-300::placeholder { - --tw-placeholder-opacity: 1; - color: rgb(147 197 253 / var(--tw-placeholder-opacity)); - } - .placeholder-blue-300\/50::placeholder { - color: #93c5fd80; - } - .placeholder-blue-300\/\[var\(--my-opacity\)\]::placeholder { - color: rgb(147 197 253 / var(--my-opacity)); - } - .placeholder-opacity-50::placeholder { - --tw-placeholder-opacity: 0.5; - } - .ring-blue-300 { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity)); - } - .ring-blue-300\/50 { - --tw-ring-color: #93c5fd80; - } - .ring-blue-300\/\[var\(--my-opacity\)\] { - --tw-ring-color: rgb(147 197 253 / var(--my-opacity)); - } - .ring-opacity-50 { - --tw-ring-opacity: 0.5; - } - `) - }) - }) - - it('can replace the potential alpha value in rgba/hsla syntax', async () => { - let config = { - content: [{ raw: html`
` }], - theme: { - colors: { - 'primary-rgba': 'rgba(var(--color), 0.1)', - 'primary-hsla': 'hsla(var(--color), 0.1)', - }, - }, - } - - let result = await run('@tailwind utilities', config) + ], + corePlugins: { + backgroundOpacity: false, + borderOpacity: false, + divideOpacity: false, + placeholderOpacity: false, + textOpacity: false, + }, + } + return run('@tailwind utilities', config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .text-primary-hsla\/50 { - color: hsla(var(--color), 0.5); + .divide-black > :not([hidden]) ~ :not([hidden]), + .border-black { + border-color: #000; } - .text-primary-rgba\/50 { - color: rgba(var(--color), 0.5); + .bg-black { + background-color: #000; + } + .text-black, + .placeholder-black::placeholder { + color: #000; } `) }) }) + +test('colors defined as functions work when opacity plugins are disabled', () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+ `, + }, + ], + theme: { + colors: { + primary: ({ opacityValue }) => + opacityValue === undefined + ? 'rgb(var(--color-primary))' + : `rgb(var(--color-primary) / ${opacityValue})`, + }, + }, + corePlugins: { + backgroundOpacity: false, + borderOpacity: false, + divideOpacity: false, + placeholderOpacity: false, + textOpacity: false, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-primary > :not([hidden]) ~ :not([hidden]), + .border-primary { + border-color: rgb(var(--color-primary)); + } + .bg-primary { + background-color: rgb(var(--color-primary)); + } + .text-primary, + .placeholder-primary::placeholder { + color: rgb(var(--color-primary)); + } + `) + }) +}) + +it('can use defining custom properties for colors (opacity plugins enabled)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-primary > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(var(--color-primary) / var(--tw-divide-opacity)); + } + .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 0.5; + } + .border-primary { + --tw-border-opacity: 1; + border-color: rgb(var(--color-primary) / var(--tw-border-opacity)); + } + .border-opacity-50 { + --tw-border-opacity: 0.5; + } + .bg-primary { + --tw-bg-opacity: 1; + background-color: rgb(var(--color-primary) / var(--tw-bg-opacity)); + } + .bg-opacity-50 { + --tw-bg-opacity: 0.5; + } + .text-primary { + --tw-text-opacity: 1; + color: rgb(var(--color-primary) / var(--tw-text-opacity)); + } + .text-opacity-50 { + --tw-text-opacity: 0.5; + } + .placeholder-primary::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(var(--color-primary) / var(--tw-placeholder-opacity)); + } + .placeholder-opacity-50::placeholder { + --tw-placeholder-opacity: 0.5; + } + .ring-primary { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(var(--color-primary) / var(--tw-ring-opacity)); + } + .ring-opacity-50 { + --tw-ring-opacity: 0.5; + } + `) + }) +}) + +it('can use rgb helper when defining custom properties for colors (opacity plugins disabled)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + corePlugins: { + backgroundOpacity: false, + borderOpacity: false, + divideOpacity: false, + placeholderOpacity: false, + textOpacity: false, + ringOpacity: false, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-primary > :not([hidden]) ~ :not([hidden]) { + border-color: rgb(var(--color-primary) / 1); + } + .divide-primary\/50 > :not([hidden]) ~ :not([hidden]) { + border-color: rgb(var(--color-primary) / 0.5); + } + .border-primary { + border-color: rgb(var(--color-primary) / 1); + } + .border-primary\/50 { + border-color: rgb(var(--color-primary) / 0.5); + } + .bg-primary { + background-color: rgb(var(--color-primary) / 1); + } + .bg-primary\/50 { + background-color: rgb(var(--color-primary) / 0.5); + } + .text-primary { + color: rgb(var(--color-primary) / 1); + } + .text-primary\/50 { + color: rgb(var(--color-primary) / 0.5); + } + .placeholder-primary::placeholder { + color: rgb(var(--color-primary) / 1); + } + .placeholder-primary\/50::placeholder { + color: rgb(var(--color-primary) / 0.5); + } + .ring-primary { + --tw-ring-color: rgb(var(--color-primary) / 1); + } + .ring-primary\/50 { + --tw-ring-color: rgb(var(--color-primary) / 0.5); + } + `) + }) +}) + +it('can use hsl helper when defining custom properties for colors (opacity plugins enabled)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + theme: { + colors: { + primary: 'hsl(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-primary > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: hsl(var(--color-primary) / var(--tw-divide-opacity)); + } + .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 0.5; + } + .border-primary { + --tw-border-opacity: 1; + border-color: hsl(var(--color-primary) / var(--tw-border-opacity)); + } + .border-opacity-50 { + --tw-border-opacity: 0.5; + } + .bg-primary { + --tw-bg-opacity: 1; + background-color: hsl(var(--color-primary) / var(--tw-bg-opacity)); + } + .bg-opacity-50 { + --tw-bg-opacity: 0.5; + } + .text-primary { + --tw-text-opacity: 1; + color: hsl(var(--color-primary) / var(--tw-text-opacity)); + } + .text-opacity-50 { + --tw-text-opacity: 0.5; + } + .placeholder-primary::placeholder { + --tw-placeholder-opacity: 1; + color: hsl(var(--color-primary) / var(--tw-placeholder-opacity)); + } + .placeholder-opacity-50::placeholder { + --tw-placeholder-opacity: 0.5; + } + .ring-primary { + --tw-ring-opacity: 1; + --tw-ring-color: hsl(var(--color-primary) / var(--tw-ring-opacity)); + } + .ring-opacity-50 { + --tw-ring-opacity: 0.5; + } + `) + }) +}) + +it('can use hsl helper when defining custom properties for colors (opacity plugins disabled)', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + theme: { + colors: { + primary: 'hsl(var(--color-primary) / )', + }, + }, + corePlugins: { + backgroundOpacity: false, + borderOpacity: false, + divideOpacity: false, + placeholderOpacity: false, + textOpacity: false, + ringOpacity: false, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-primary > :not([hidden]) ~ :not([hidden]) { + border-color: hsl(var(--color-primary) / 1); + } + .divide-primary\/50 > :not([hidden]) ~ :not([hidden]) { + border-color: hsl(var(--color-primary) / 0.5); + } + .border-primary { + border-color: hsl(var(--color-primary) / 1); + } + .border-primary\/50 { + border-color: hsl(var(--color-primary) / 0.5); + } + .bg-primary { + background-color: hsl(var(--color-primary) / 1); + } + .bg-primary\/50 { + background-color: hsl(var(--color-primary) / 0.5); + } + .text-primary { + color: hsl(var(--color-primary) / 1); + } + .text-primary\/50 { + color: hsl(var(--color-primary) / 0.5); + } + .placeholder-primary::placeholder { + color: hsl(var(--color-primary) / 1); + } + .placeholder-primary\/50::placeholder { + color: hsl(var(--color-primary) / 0.5); + } + .ring-primary { + --tw-ring-color: hsl(var(--color-primary) / 1); + } + .ring-primary\/50 { + --tw-ring-color: hsl(var(--color-primary) / 0.5); + } + `) + }) +}) + +test('Theme function in JS can apply alpha values to colors (1)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: #3b82f680; + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: '#3b82f6' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / 50%'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (2)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: #3b82f680; + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: '#3b82f6' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / 0.5'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (3)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: rgb(59 130 246 / var(--my-alpha)); + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: '#3b82f6' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / var(--my-alpha)'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (4)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: #3c83f680; + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / 50%'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (5)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: #3c83f680; + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / 0.5'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (6)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: hsl(217 91% 60% / var(--my-alpha)); + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { blue: { 500: 'hsl(217, 91%, 60%)' } }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / var(--my-alpha)'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function in JS can apply alpha values to colors (7)', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: rgb(var(--foo) / var(--my-alpha)); + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { + blue: { + 500: 'rgb(var(--foo) / )', + }, + }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / var(--my-alpha)'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +test('Theme function prefers existing values in config', () => { + let input = css` + @tailwind utilities; + ` + + let output = css` + .text-foo { + color: purple; + } + ` + + return run(input, { + content: [{ raw: html`text-foo` }], + corePlugins: { textOpacity: false }, + theme: { + colors: { + blue: { + '500 / 50%': 'purple', + }, + }, + extend: { + textColor: ({ theme }) => ({ + foo: theme('colors.blue.500 / 50%'), + }), + }, + }, + }).then((result) => { + expect(result.css).toMatchFormattedCss(output) + expect(result.warnings().length).toBe(0) + }) +}) + +it('should be possible to use an as part of the color definition', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-primary { + --tw-bg-opacity: 1; + background-color: rgb(var(--color-primary) / var(--tw-bg-opacity)); + } + `) + }) +}) + +it('should be possible to use an as part of the color definition with an opacity modifiers', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-primary\/50 { + background-color: rgb(var(--color-primary) / 0.5); + } + `) + }) +}) + +it('should be possible to use an as part of the color definition with an opacity modifiers', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-primary { + background-color: rgb(var(--color-primary) / 1); + } + `) + }) +}) + +it('should be possible to use inside arbitrary values', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['backgroundColor', 'backgroundOpacity'], + theme: { + colors: { + primary: 'rgb(var(--color-primary) / )', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-\[rgb\(var\(--color-primary\)\/\\)\]\/50 { + background-color: rgb(var(--color-primary) / 0.5); + } + `) + }) +}) + +it('Theme functions can reference values with slashes in brackets', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + theme: { + colors: { + 'a/b': '#000000', + }, + extend: { + backgroundColor: ({ theme }) => ({ + foo1: theme('colors[a/b]'), + foo2: theme('colors[a/b]/50%'), + }), + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-foo1 { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .bg-foo2 { + background-color: #00000080; + } + `) + }) +}) + +it('works with opacity values defined as a placeholder or a function in when colors is a function', () => { + let config = { + content: [ + { + raw: html` +
+ `, + }, + ], + theme: { + colors: () => ({ + foobar1: ({ opacityValue }) => `rgb(255 100 0 / ${opacityValue ?? '100%'})`, + foobar2: `rgb(255 100 0 / )`, + foobar3: { + 100: ({ opacityValue }) => `rgb(255 100 0 / ${opacityValue ?? '100%'})`, + 200: `rgb(255 100 0 / )`, + }, + }), + extend: { + backgroundColor: ({ theme }) => ({ + foo10: theme('colors.foobar1'), + foo20: theme('colors.foobar2'), + foo30: theme('colors.foobar3.100'), + foo40: theme('colors.foobar3.200'), + foo11: theme('colors.foobar1 / 50%'), + foo21: theme('colors.foobar2 / 50%'), + foo31: theme('colors.foobar3.100 / 50%'), + foo41: theme('colors.foobar3.200 / 50%'), + }), + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-foo10 { + background-color: #ff6400; + } + .bg-foo11 { + background-color: #ff640080; + } + .bg-foo20 { + --tw-bg-opacity: 1; + background-color: rgb(255 100 0 / var(--tw-bg-opacity)); + } + .bg-foo21 { + background-color: #ff640080; + } + .bg-foo30 { + background-color: #ff6400; + } + .bg-foo31 { + background-color: #ff640080; + } + .bg-foo40 { + --tw-bg-opacity: 1; + background-color: rgb(255 100 0 / var(--tw-bg-opacity)); + } + .bg-foo41 { + background-color: #ff640080; + } + `) + }) +}) + +it('The disableColorOpacityUtilitiesByDefault flag disables the color opacity plugins and removes their variables', () => { + let config = { + future: { + disableColorOpacityUtilitiesByDefault: true, + }, + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-blue-300 > :not([hidden]) ~ :not([hidden]) { + border-color: #93c5fd; + } + .divide-blue-300\/50 > :not([hidden]) ~ :not([hidden]) { + border-color: #93c5fd80; + } + .divide-blue-300\/\[var\(--my-opacity\)\] > :not([hidden]) ~ :not([hidden]) { + border-color: rgb(147 197 253 / var(--my-opacity)); + } + .border-blue-300 { + border-color: #93c5fd; + } + .border-blue-300\/50 { + border-color: #93c5fd80; + } + .border-blue-300\/\[var\(--my-opacity\)\] { + border-color: rgb(147 197 253 / var(--my-opacity)); + } + .bg-blue-300 { + background-color: #93c5fd; + } + .bg-blue-300\/50 { + background-color: #93c5fd80; + } + .bg-blue-300\/\[var\(--my-opacity\)\] { + background-color: rgb(147 197 253 / var(--my-opacity)); + } + .text-blue-300 { + color: #93c5fd; + } + .text-blue-300\/50 { + color: #93c5fd80; + } + .text-blue-300\/\[var\(--my-opacity\)\] { + color: rgb(147 197 253 / var(--my-opacity)); + } + .placeholder-blue-300::placeholder { + color: #93c5fd; + } + .placeholder-blue-300\/50::placeholder { + color: #93c5fd80; + } + .placeholder-blue-300\/\[var\(--my-opacity\)\]::placeholder { + color: rgb(147 197 253 / var(--my-opacity)); + } + .ring-blue-300 { + --tw-ring-color: #93c5fd; + } + .ring-blue-300\/50 { + --tw-ring-color: #93c5fd80; + } + .ring-blue-300\/\[var\(--my-opacity\)\] { + --tw-ring-color: rgb(147 197 253 / var(--my-opacity)); + } + `) + }) +}) + +it('You can re-enable any opacity plugin even when disableColorOpacityUtilitiesByDefault is enabled', () => { + let config = { + future: { + disableColorOpacityUtilitiesByDefault: true, + }, + corePlugins: { + backgroundOpacity: true, + borderOpacity: true, + divideOpacity: true, + placeholderOpacity: true, + ringOpacity: true, + textOpacity: true, + }, + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .divide-blue-300 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(147 197 253 / var(--tw-divide-opacity)); + } + .divide-blue-300\/50 > :not([hidden]) ~ :not([hidden]) { + border-color: #93c5fd80; + } + .divide-blue-300\/\[var\(--my-opacity\)\] > :not([hidden]) ~ :not([hidden]) { + border-color: rgb(147 197 253 / var(--my-opacity)); + } + .divide-opacity-50 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 0.5; + } + .border-blue-300 { + --tw-border-opacity: 1; + border-color: rgb(147 197 253 / var(--tw-border-opacity)); + } + .border-blue-300\/50 { + border-color: #93c5fd80; + } + .border-blue-300\/\[var\(--my-opacity\)\] { + border-color: rgb(147 197 253 / var(--my-opacity)); + } + .border-opacity-50 { + --tw-border-opacity: 0.5; + } + .bg-blue-300 { + --tw-bg-opacity: 1; + background-color: rgb(147 197 253 / var(--tw-bg-opacity)); + } + .bg-blue-300\/50 { + background-color: #93c5fd80; + } + .bg-blue-300\/\[var\(--my-opacity\)\] { + background-color: rgb(147 197 253 / var(--my-opacity)); + } + .bg-opacity-50 { + --tw-bg-opacity: 0.5; + } + .text-blue-300 { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); + } + .text-blue-300\/50 { + color: #93c5fd80; + } + .text-blue-300\/\[var\(--my-opacity\)\] { + color: rgb(147 197 253 / var(--my-opacity)); + } + .text-opacity-50 { + --tw-text-opacity: 0.5; + } + .placeholder-blue-300::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(147 197 253 / var(--tw-placeholder-opacity)); + } + .placeholder-blue-300\/50::placeholder { + color: #93c5fd80; + } + .placeholder-blue-300\/\[var\(--my-opacity\)\]::placeholder { + color: rgb(147 197 253 / var(--my-opacity)); + } + .placeholder-opacity-50::placeholder { + --tw-placeholder-opacity: 0.5; + } + .ring-blue-300 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity)); + } + .ring-blue-300\/50 { + --tw-ring-color: #93c5fd80; + } + .ring-blue-300\/\[var\(--my-opacity\)\] { + --tw-ring-color: rgb(147 197 253 / var(--my-opacity)); + } + .ring-opacity-50 { + --tw-ring-opacity: 0.5; + } + `) + }) +}) + +it('can replace the potential alpha value in rgba/hsla syntax', async () => { + let config = { + content: [{ raw: html`
` }], + theme: { + colors: { + 'primary-rgba': 'rgba(var(--color), 0.1)', + 'primary-hsla': 'hsla(var(--color), 0.1)', + }, + }, + } + + let result = await run('@tailwind utilities', config) + + expect(result.css).toMatchFormattedCss(css` + .text-primary-hsla\/50 { + color: hsla(var(--color), 0.5); + } + .text-primary-rgba\/50 { + color: rgba(var(--color), 0.5); + } + `) +}) diff --git a/tests/oxide.test.js b/tests/oxide.test.js deleted file mode 100644 index d2a697626..000000000 --- a/tests/oxide.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { crosscheck, run, html, css, defaults } from './util/run' - -crosscheck(({ stable, oxide }) => { - stable.test.todo('space-x uses physical properties') - oxide.test('space-x uses logical properties', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchCss(css` - ${defaults} - .space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); - } - `) - }) - }) -}) diff --git a/tests/parallel-variants.test.js b/tests/parallel-variants.test.js index a4d4dfc8f..61fb2cb33 100644 --- a/tests/parallel-variants.test.js +++ b/tests/parallel-variants.test.js @@ -1,138 +1,136 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('basic parallel variants', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function test({ addVariant }) { - addVariant('test', ['& *::test', '&::test']) - }, - ], - } +test('basic parallel variants', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function test({ addVariant }) { + addVariant('test', ['& *::test', '&::test']) + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-normal { - font-weight: 400; - } - .test\:font-bold ::test { - font-weight: 700; - } - .test\:font-medium ::test { - font-weight: 500; - } - .hover\:test\:font-black ::test:hover { - font-weight: 900; - } - .test\:font-bold::test { - font-weight: 700; - } - .test\:font-medium::test { - font-weight: 500; - } - .hover\:test\:font-black::test:hover { - font-weight: 900; - } - `) - }) - }) - - test('parallel variants can be generated using a function that returns parallel variants', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function test({ addVariant }) { - addVariant('test', () => ['& *::test', '&::test']) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-normal { - font-weight: 400; - } - .test\:font-bold ::test { - font-weight: 700; - } - .test\:font-medium ::test { - font-weight: 500; - } - .test\:font-bold::test { - font-weight: 700; - } - .test\:font-medium::test { - font-weight: 500; - } - .hover\:test\:font-black ::test:hover { - font-weight: 900; - } - .hover\:test\:font-black::test:hover { - font-weight: 900; - } - `) - }) - }) - - test('a function that returns parallel variants can modify the container', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function test({ addVariant }) { - addVariant('test', ({ container }) => { - container.walkDecls((decl) => { - decl.value = `calc(0 + ${decl.value})` - }) - - return ['& *::test', '&::test'] - }) - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-normal { - font-weight: 400; - } - .test\:font-bold ::test { - font-weight: 700; - } - .test\:font-medium ::test { - font-weight: 500; - } - .test\:font-bold::test { - font-weight: 700; - } - .test\:font-medium::test { - font-weight: 500; - } - .hover\:test\:font-black ::test:hover { - font-weight: 900; - } - .hover\:test\:font-black::test:hover { - font-weight: 900; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-normal { + font-weight: 400; + } + .test\:font-bold ::test { + font-weight: 700; + } + .test\:font-medium ::test { + font-weight: 500; + } + .hover\:test\:font-black ::test:hover { + font-weight: 900; + } + .test\:font-bold::test { + font-weight: 700; + } + .test\:font-medium::test { + font-weight: 500; + } + .hover\:test\:font-black::test:hover { + font-weight: 900; + } + `) + }) +}) + +test('parallel variants can be generated using a function that returns parallel variants', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function test({ addVariant }) { + addVariant('test', () => ['& *::test', '&::test']) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-normal { + font-weight: 400; + } + .test\:font-bold ::test { + font-weight: 700; + } + .test\:font-medium ::test { + font-weight: 500; + } + .test\:font-bold::test { + font-weight: 700; + } + .test\:font-medium::test { + font-weight: 500; + } + .hover\:test\:font-black ::test:hover { + font-weight: 900; + } + .hover\:test\:font-black::test:hover { + font-weight: 900; + } + `) + }) +}) + +test('a function that returns parallel variants can modify the container', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function test({ addVariant }) { + addVariant('test', ({ container }) => { + container.walkDecls((decl) => { + decl.value = `calc(0 + ${decl.value})` + }) + + return ['& *::test', '&::test'] + }) + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-normal { + font-weight: 400; + } + .test\:font-bold ::test { + font-weight: 700; + } + .test\:font-medium ::test { + font-weight: 500; + } + .test\:font-bold::test { + font-weight: 700; + } + .test\:font-medium::test { + font-weight: 500; + } + .hover\:test\:font-black ::test:hover { + font-weight: 900; + } + .hover\:test\:font-black::test:hover { + font-weight: 900; + } + `) }) }) diff --git a/tests/parseAnimationValue.test.js b/tests/parseAnimationValue.test.js index 098178079..293d6fbec 100644 --- a/tests/parseAnimationValue.test.js +++ b/tests/parseAnimationValue.test.js @@ -1,203 +1,200 @@ import parseAnimationValue from '../src/util/parseAnimationValue' -import { crosscheck } from './util/run' -crosscheck(() => { - describe('Tailwind Defaults', () => { - it.each([ - [ - 'spin 1s linear infinite', - { - value: 'spin 1s linear infinite', - name: 'spin', - duration: '1s', - timingFunction: 'linear', - iterationCount: 'infinite', - }, - ], - [ - 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', - { - value: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', - name: 'ping', - duration: '1s', - timingFunction: 'cubic-bezier(0, 0, 0.2, 1)', - iterationCount: 'infinite', - }, - ], - [ - 'bounce 1s infinite', - { - value: 'bounce 1s infinite', - name: 'bounce', - duration: '1s', - iterationCount: 'infinite', - }, - ], - ])('should be possible to parse: "%s"', (input, expected) => { - const parsed = parseAnimationValue(input) - expect(parsed).toHaveLength(1) - expect(parsed[0]).toEqual(expected) - }) - }) - - describe('css variables', () => { - it('should be possible to use css variables', () => { - let parsed = parseAnimationValue('jump var(--animation-duration, 10s) linear infinite') - expect(parsed[0]).toEqual({ - value: 'jump var(--animation-duration, 10s) linear infinite', - name: 'jump', +describe('Tailwind Defaults', () => { + it.each([ + [ + 'spin 1s linear infinite', + { + value: 'spin 1s linear infinite', + name: 'spin', + duration: '1s', timingFunction: 'linear', iterationCount: 'infinite', - unknown: ['var(--animation-duration, 10s)'], - }) - }) - }) - - describe('MDN Examples', () => { - it.each([ - [ - '3s ease-in 1s 2 reverse both paused slidein', - { - value: '3s ease-in 1s 2 reverse both paused slidein', - delay: '1s', - direction: 'reverse', - duration: '3s', - fillMode: 'both', - iterationCount: '2', - name: 'slidein', - playState: 'paused', - timingFunction: 'ease-in', - }, - ], - [ - 'slidein 3s linear 1s', - { - value: 'slidein 3s linear 1s', - delay: '1s', - duration: '3s', - name: 'slidein', - timingFunction: 'linear', - }, - ], - ['slidein 3s', { value: 'slidein 3s', duration: '3s', name: 'slidein' }], - ])('should be possible to parse: "%s"', (input, expected) => { - const parsed = parseAnimationValue(input) - expect(parsed).toHaveLength(1) - expect(parsed[0]).toEqual(expected) - }) - }) - - describe('duration & delay', () => { - it.each([ - // Positive seconds (integer) - ['spin 2s 1s linear', { duration: '2s', delay: '1s' }], - - // Negative seconds (integer) - ['spin -2s -1s linear', { duration: '-2s', delay: '-1s' }], - - // Positive seconds (float) - ['spin 2.321s 1.321s linear', { duration: '2.321s', delay: '1.321s' }], - - // Negative seconds (float) - ['spin -2.321s -1.321s linear', { duration: '-2.321s', delay: '-1.321s' }], - - // Positive milliseconds (integer) - ['spin 200ms 100ms linear', { duration: '200ms', delay: '100ms' }], - - // Negative milliseconds (integer) - ['spin -200ms -100ms linear', { duration: '-200ms', delay: '-100ms' }], - - // Positive milliseconds (float) - ['spin 200.321ms 100.321ms linear', { duration: '200.321ms', delay: '100.321ms' }], - - // Negative milliseconds (float) - ['spin -200.321ms -100.321ms linear', { duration: '-200.321ms', delay: '-100.321ms' }], - ])('should be possible to parse "%s" into %o', (input, { duration, delay }) => { - const parsed = parseAnimationValue(input) - expect(parsed).toHaveLength(1) - expect(parsed[0].duration).toEqual(duration) - expect(parsed[0].delay).toEqual(delay) - }) - }) - - describe('iteration count', () => { - it.each([ - // Number - ['1 spin 200s 100s linear', '1'], - ['spin 2 200s 100s linear', '2'], - ['spin 200s 3 100s linear', '3'], - ['spin 200s 100s 4 linear', '4'], - ['spin 200s 100s linear 5', '5'], - - // Infinite - ['infinite spin 200s 100s linear', 'infinite'], - ['spin infinite 200s 100s linear', 'infinite'], - ['spin 200s infinite 100s linear', 'infinite'], - ['spin 200s 100s infinite linear', 'infinite'], - ['spin 200s 100s linear infinite', 'infinite'], - ])( - 'should be possible to parse "%s" with an iteraction count of "%s"', - (input, iterationCount) => { - const parsed = parseAnimationValue(input) - expect(parsed).toHaveLength(1) - expect(parsed[0].iterationCount).toEqual(iterationCount) - } - ) - }) - - describe('multiple animations', () => { - it('should be possible to parse multiple applications at once', () => { - const input = [ - 'spin 1s linear infinite', - 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', - 'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite', - ].join(',') - - const parsed = parseAnimationValue(input) - expect(parsed).toHaveLength(3) - expect(parsed).toEqual([ - { - value: 'spin 1s linear infinite', - name: 'spin', - duration: '1s', - timingFunction: 'linear', - iterationCount: 'infinite', - }, - { - value: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', - name: 'ping', - duration: '1s', - timingFunction: 'cubic-bezier(0, 0, 0.2, 1)', - iterationCount: 'infinite', - }, - { - value: 'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite', - name: 'pulse', - duration: '2s', - timingFunction: 'cubic-bezier(0.4, 0, 0.6)', - iterationCount: 'infinite', - }, - ]) - }) - }) - - it.each` - input | value | direction | playState | fillMode | iterationCount | timingFunction | duration | delay | name - ${'1s spin 1s infinite'} | ${'1s spin 1s infinite'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'spin'} - ${'infinite infinite 1s 1s'} | ${'infinite infinite 1s 1s'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'infinite'} - ${'ease 1s ease 1s'} | ${'ease 1s ease 1s'} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'ease'} | ${'1s'} | ${'1s'} | ${'ease'} - ${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'paused backwards infinite ease-in 1s 2s name normal'} | ${'paused backwards infinite ease-in 1s 2s name normal'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'backwards infinite ease-in 1s 2s name normal paused'} | ${'backwards infinite ease-in 1s 2s name normal paused'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'infinite ease-in 1s 2s name normal paused backwards'} | ${'infinite ease-in 1s 2s name normal paused backwards'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'ease-in 1s 2s name normal paused backwards infinite'} | ${'ease-in 1s 2s name normal paused backwards infinite'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'1s 2s name normal paused backwards infinite ease-in'} | ${'1s 2s name normal paused backwards infinite ease-in'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${'2s name normal paused backwards infinite ease-in 1s'} | ${'2s name normal paused backwards infinite ease-in 1s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'2s'} | ${'1s'} | ${'name'} - ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - ${' name normal paused backwards infinite ease-in 1s 2s '} | ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} - `('should parse "$input" correctly', ({ input, ...expected }) => { - let parsed = parseAnimationValue(input) + }, + ], + [ + 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + { + value: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + name: 'ping', + duration: '1s', + timingFunction: 'cubic-bezier(0, 0, 0.2, 1)', + iterationCount: 'infinite', + }, + ], + [ + 'bounce 1s infinite', + { + value: 'bounce 1s infinite', + name: 'bounce', + duration: '1s', + iterationCount: 'infinite', + }, + ], + ])('should be possible to parse: "%s"', (input, expected) => { + const parsed = parseAnimationValue(input) expect(parsed).toHaveLength(1) expect(parsed[0]).toEqual(expected) }) }) + +describe('css variables', () => { + it('should be possible to use css variables', () => { + let parsed = parseAnimationValue('jump var(--animation-duration, 10s) linear infinite') + expect(parsed[0]).toEqual({ + value: 'jump var(--animation-duration, 10s) linear infinite', + name: 'jump', + timingFunction: 'linear', + iterationCount: 'infinite', + unknown: ['var(--animation-duration, 10s)'], + }) + }) +}) + +describe('MDN Examples', () => { + it.each([ + [ + '3s ease-in 1s 2 reverse both paused slidein', + { + value: '3s ease-in 1s 2 reverse both paused slidein', + delay: '1s', + direction: 'reverse', + duration: '3s', + fillMode: 'both', + iterationCount: '2', + name: 'slidein', + playState: 'paused', + timingFunction: 'ease-in', + }, + ], + [ + 'slidein 3s linear 1s', + { + value: 'slidein 3s linear 1s', + delay: '1s', + duration: '3s', + name: 'slidein', + timingFunction: 'linear', + }, + ], + ['slidein 3s', { value: 'slidein 3s', duration: '3s', name: 'slidein' }], + ])('should be possible to parse: "%s"', (input, expected) => { + const parsed = parseAnimationValue(input) + expect(parsed).toHaveLength(1) + expect(parsed[0]).toEqual(expected) + }) +}) + +describe('duration & delay', () => { + it.each([ + // Positive seconds (integer) + ['spin 2s 1s linear', { duration: '2s', delay: '1s' }], + + // Negative seconds (integer) + ['spin -2s -1s linear', { duration: '-2s', delay: '-1s' }], + + // Positive seconds (float) + ['spin 2.321s 1.321s linear', { duration: '2.321s', delay: '1.321s' }], + + // Negative seconds (float) + ['spin -2.321s -1.321s linear', { duration: '-2.321s', delay: '-1.321s' }], + + // Positive milliseconds (integer) + ['spin 200ms 100ms linear', { duration: '200ms', delay: '100ms' }], + + // Negative milliseconds (integer) + ['spin -200ms -100ms linear', { duration: '-200ms', delay: '-100ms' }], + + // Positive milliseconds (float) + ['spin 200.321ms 100.321ms linear', { duration: '200.321ms', delay: '100.321ms' }], + + // Negative milliseconds (float) + ['spin -200.321ms -100.321ms linear', { duration: '-200.321ms', delay: '-100.321ms' }], + ])('should be possible to parse "%s" into %o', (input, { duration, delay }) => { + const parsed = parseAnimationValue(input) + expect(parsed).toHaveLength(1) + expect(parsed[0].duration).toEqual(duration) + expect(parsed[0].delay).toEqual(delay) + }) +}) + +describe('iteration count', () => { + it.each([ + // Number + ['1 spin 200s 100s linear', '1'], + ['spin 2 200s 100s linear', '2'], + ['spin 200s 3 100s linear', '3'], + ['spin 200s 100s 4 linear', '4'], + ['spin 200s 100s linear 5', '5'], + + // Infinite + ['infinite spin 200s 100s linear', 'infinite'], + ['spin infinite 200s 100s linear', 'infinite'], + ['spin 200s infinite 100s linear', 'infinite'], + ['spin 200s 100s infinite linear', 'infinite'], + ['spin 200s 100s linear infinite', 'infinite'], + ])( + 'should be possible to parse "%s" with an iteraction count of "%s"', + (input, iterationCount) => { + const parsed = parseAnimationValue(input) + expect(parsed).toHaveLength(1) + expect(parsed[0].iterationCount).toEqual(iterationCount) + } + ) +}) + +describe('multiple animations', () => { + it('should be possible to parse multiple applications at once', () => { + const input = [ + 'spin 1s linear infinite', + 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + 'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite', + ].join(',') + + const parsed = parseAnimationValue(input) + expect(parsed).toHaveLength(3) + expect(parsed).toEqual([ + { + value: 'spin 1s linear infinite', + name: 'spin', + duration: '1s', + timingFunction: 'linear', + iterationCount: 'infinite', + }, + { + value: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + name: 'ping', + duration: '1s', + timingFunction: 'cubic-bezier(0, 0, 0.2, 1)', + iterationCount: 'infinite', + }, + { + value: 'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite', + name: 'pulse', + duration: '2s', + timingFunction: 'cubic-bezier(0.4, 0, 0.6)', + iterationCount: 'infinite', + }, + ]) + }) +}) + +it.each` + input | value | direction | playState | fillMode | iterationCount | timingFunction | duration | delay | name + ${'1s spin 1s infinite'} | ${'1s spin 1s infinite'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'spin'} + ${'infinite infinite 1s 1s'} | ${'infinite infinite 1s 1s'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'infinite'} + ${'ease 1s ease 1s'} | ${'ease 1s ease 1s'} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'ease'} | ${'1s'} | ${'1s'} | ${'ease'} + ${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'paused backwards infinite ease-in 1s 2s name normal'} | ${'paused backwards infinite ease-in 1s 2s name normal'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'backwards infinite ease-in 1s 2s name normal paused'} | ${'backwards infinite ease-in 1s 2s name normal paused'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'infinite ease-in 1s 2s name normal paused backwards'} | ${'infinite ease-in 1s 2s name normal paused backwards'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'ease-in 1s 2s name normal paused backwards infinite'} | ${'ease-in 1s 2s name normal paused backwards infinite'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'1s 2s name normal paused backwards infinite ease-in'} | ${'1s 2s name normal paused backwards infinite ease-in'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${'2s name normal paused backwards infinite ease-in 1s'} | ${'2s name normal paused backwards infinite ease-in 1s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'2s'} | ${'1s'} | ${'name'} + ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} + ${' name normal paused backwards infinite ease-in 1s 2s '} | ${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'} +`('should parse "$input" correctly', ({ input, ...expected }) => { + let parsed = parseAnimationValue(input) + expect(parsed).toHaveLength(1) + expect(parsed[0]).toEqual(expected) +}) diff --git a/tests/parseObjectStyles.test.js b/tests/parseObjectStyles.test.js index c6121977f..b823801ee 100644 --- a/tests/parseObjectStyles.test.js +++ b/tests/parseObjectStyles.test.js @@ -1,319 +1,317 @@ import parseObjectStyles from '../src/util/parseObjectStyles' import postcss from 'postcss' -import { crosscheck, css } from './util/run' +import { css } from './util/run' function toCss(nodes) { return postcss.root({ nodes }).toString() } -crosscheck(() => { - test('it parses simple single class definitions', () => { - const result = parseObjectStyles({ - '.foobar': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foobar { - color: #fff; - background-color: red; - padding: 1rem; - } - `) +test('it parses simple single class definitions', () => { + const result = parseObjectStyles({ + '.foobar': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + }, }) - test('it parses multiple class definitions', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - }, - '.bar': { - width: '200px', - height: '100px', - }, - }) + expect(toCss(result)).toMatchFormattedCss(css` + .foobar { + color: #fff; + background-color: red; + padding: 1rem; + } + `) +}) - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - .bar { - width: 200px; - height: 100px; - } - `) +test('it parses multiple class definitions', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + }, + '.bar': { + width: '200px', + height: '100px', + }, }) - test('it parses nested pseudo-selectors', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - '&:hover': { - backgroundColor: 'orange', - }, - '&:focus': { - backgroundColor: 'blue', - }, - }, - }) + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + .bar { + width: 200px; + height: 100px; + } + `) +}) - expect(toCss(result)).toMatchFormattedCss(css` +test('it parses nested pseudo-selectors', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '&:hover': { + backgroundColor: 'orange', + }, + '&:focus': { + backgroundColor: 'blue', + }, + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + .foo:hover { + background-color: orange; + } + .foo:focus { + background-color: #00f; + } + `) +}) + +test('it parses top-level media queries', () => { + const result = parseObjectStyles({ + '@media (min-width: 200px)': { + '.foo': { + backgroundColor: 'orange', + }, + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + @media (min-width: 200px) { .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - .foo:hover { background-color: orange; } - .foo:focus { - background-color: #00f; - } - `) - }) + } + `) +}) - test('it parses top-level media queries', () => { - const result = parseObjectStyles({ +test('it parses nested media queries', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', '@media (min-width: 200px)': { - '.foo': { - backgroundColor: 'orange', - }, + backgroundColor: 'orange', }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - @media (min-width: 200px) { - .foo { - background-color: orange; - } - } - `) + }, }) - test('it parses nested media queries', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + @media (min-width: 200px) { + .foo { + background-color: orange; + } + } + `) +}) + +test('it bubbles nested screen rules', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '@screen sm': { + backgroundColor: 'orange', + }, + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + @screen sm { + .foo { + background-color: orange; + } + } + `) +}) + +test('it parses pseudo-selectors in nested media queries', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '&:hover': { '@media (min-width: 200px)': { backgroundColor: 'orange', }, }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - @media (min-width: 200px) { - .foo { - background-color: orange; - } - } - `) + }, }) - test('it bubbles nested screen rules', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - '@screen sm': { - backgroundColor: 'orange', - }, - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - @screen sm { - .foo { - background-color: orange; - } - } - `) - }) - - test('it parses pseudo-selectors in nested media queries', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - '&:hover': { - '@media (min-width: 200px)': { - backgroundColor: 'orange', - }, - }, - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - @media (min-width: 200px) { - .foo:hover { - background-color: orange; - } - } - `) - }) - - test('it parses descendant selectors', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - '.bar': { - backgroundColor: 'orange', - }, - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - .foo .bar { + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + @media (min-width: 200px) { + .foo:hover { background-color: orange; } - `) + } + `) +}) + +test('it parses descendant selectors', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '.bar': { + backgroundColor: 'orange', + }, + }, }) - test('it parses nested multi-class selectors', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + .foo .bar { + background-color: orange; + } + `) +}) + +test('it parses nested multi-class selectors', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '&.bar': { + backgroundColor: 'orange', + }, + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + .foo.bar { + background-color: orange; + } + `) +}) + +test('it parses nested multi-class selectors in media queries', () => { + const result = parseObjectStyles({ + '.foo': { + backgroundColor: 'red', + color: 'white', + padding: '1rem', + '@media (min-width: 200px)': { '&.bar': { backgroundColor: 'orange', }, }, - }) + }, + }) - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + color: #fff; + background-color: red; + padding: 1rem; + } + @media (min-width: 200px) { .foo.bar { background-color: orange; } - `) - }) - - test('it parses nested multi-class selectors in media queries', () => { - const result = parseObjectStyles({ - '.foo': { - backgroundColor: 'red', - color: 'white', - padding: '1rem', - '@media (min-width: 200px)': { - '&.bar': { - backgroundColor: 'orange', - }, - }, - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - color: #fff; - background-color: red; - padding: 1rem; - } - @media (min-width: 200px) { - .foo.bar { - background-color: orange; - } - } - `) - }) - - test('it strips empty selectors when nesting', () => { - const result = parseObjectStyles({ - '.foo': { - '.bar': { - backgroundColor: 'orange', - }, - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo .bar { - background-color: orange; - } - `) - }) - - test('it can parse an array of styles', () => { - const result = parseObjectStyles([ - { - '.foo': { - backgroundColor: 'orange', - }, - }, - { - '.bar': { - backgroundColor: 'red', - }, - }, - { - '.foo': { - backgroundColor: 'blue', - }, - }, - ]) - - expect(toCss(result)).toMatchFormattedCss(css` - .foo { - background-color: orange; - } - .bar { - background-color: red; - } - .foo { - background-color: #00f; - } - `) - }) - - test('custom properties preserve their case', () => { - const result = parseObjectStyles({ - ':root': { - '--colors-aColor-500': '0', - }, - }) - - expect(toCss(result)).toMatchFormattedCss(css` - :root { - --colors-aColor-500: 0; - } - `) - }) + } + `) +}) + +test('it strips empty selectors when nesting', () => { + const result = parseObjectStyles({ + '.foo': { + '.bar': { + backgroundColor: 'orange', + }, + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + .foo .bar { + background-color: orange; + } + `) +}) + +test('it can parse an array of styles', () => { + const result = parseObjectStyles([ + { + '.foo': { + backgroundColor: 'orange', + }, + }, + { + '.bar': { + backgroundColor: 'red', + }, + }, + { + '.foo': { + backgroundColor: 'blue', + }, + }, + ]) + + expect(toCss(result)).toMatchFormattedCss(css` + .foo { + background-color: orange; + } + .bar { + background-color: red; + } + .foo { + background-color: #00f; + } + `) +}) + +test('custom properties preserve their case', () => { + const result = parseObjectStyles({ + ':root': { + '--colors-aColor-500': '0', + }, + }) + + expect(toCss(result)).toMatchFormattedCss(css` + :root { + --colors-aColor-500: 0; + } + `) }) diff --git a/tests/plugins/divide.test.js b/tests/plugins/divide.test.js index 314233bea..37bd012ac 100644 --- a/tests/plugins/divide.test.js +++ b/tests/plugins/divide.test.js @@ -1,102 +1,91 @@ -import { crosscheck, run, html, css, defaults } from '../util/run' +import { run, html, css, defaults } from '../util/run' -crosscheck(({ stable, oxide }) => { - it('should add the divide styles for divide-y and a default border color', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } +it('should add the divide styles for divide-y and a default border color', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchCss(css` - ${defaults} + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} - .divide-y > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); - } - `) - }) - }) - - it('should add the divide styles for divide-x and a default border color', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - stable.expect(result.css).toMatchCss(css` - ${defaults} - .divide-x > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-right-width: calc(1px * var(--tw-divide-x-reverse)); - border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - } - `) - - oxide.expect(result.css).toMatchCss(css` - ${defaults} - .divide-x > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(1px * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - } - `) - }) - }) - - it('should add the divide styles for divide-y-reverse and a default border color', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchCss(css` - ${defaults} - .divide-y-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 1; - } - `) - }) - }) - - it('should add the divide styles for divide-x-reverse and a default border color', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchCss(css` - ${defaults} - .divide-x-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 1; - } - `) - }) - }) - - it('should only inject the base styles once if we use divide and border at the same time', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base; @tailwind utilities;', config).then((result) => { - expect(result.css).toMatchCss(css` - ${defaults} - .divide-y > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); - } - .border-r { - border-right-width: 1px; - } - `) - }) + .divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); + } + `) + }) +}) + +it('should add the divide styles for divide-x and a default border color', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .divide-x > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(1px * var(--tw-divide-x-reverse)); + border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); + } + `) + }) +}) + +it('should add the divide styles for divide-y-reverse and a default border color', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .divide-y-reverse > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 1; + } + `) + }) +}) + +it('should add the divide styles for divide-x-reverse and a default border color', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .divide-x-reverse > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 1; + } + `) + }) +}) + +it('should only inject the base styles once if we use divide and border at the same time', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); + } + .border-r { + border-right-width: 1px; + } + `) }) }) diff --git a/tests/plugins/fontFamily.test.js b/tests/plugins/fontFamily.test.js index 1170dac8e..f5718909d 100644 --- a/tests/plugins/fontFamily.test.js +++ b/tests/plugins/fontFamily.test.js @@ -1,101 +1,99 @@ -import { crosscheck, run, html, css } from '../util/run' +import { run, html, css } from '../util/run' -crosscheck(() => { - test('font-family utilities can be defined as a string', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontFamily: { - sans: 'Helvetica, Arial, sans-serif', - }, +test('font-family utilities can be defined as a string', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontFamily: { + sans: 'Helvetica, Arial, sans-serif', }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-sans { - font-family: Helvetica, Arial, sans-serif; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-sans { + font-family: Helvetica, Arial, sans-serif; + } + `) }) +}) - test('font-family utilities can be defined as an array', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontFamily: { - sans: ['Helvetica', 'Arial', 'sans-serif'], - }, +test('font-family utilities can be defined as an array', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontFamily: { + sans: ['Helvetica', 'Arial', 'sans-serif'], }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-sans { - font-family: Helvetica, Arial, sans-serif; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-sans { + font-family: Helvetica, Arial, sans-serif; + } + `) }) +}) - test('font-family values are not automatically escaped', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontFamily: { - sans: ["'Exo 2'", 'sans-serif'], - }, +test('font-family values are not automatically escaped', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontFamily: { + sans: ["'Exo 2'", 'sans-serif'], }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-sans { - font-family: 'Exo 2', sans-serif; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-sans { + font-family: 'Exo 2', sans-serif; + } + `) }) +}) - test('font-feature-settings can be provided when families are defined as a string', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontFamily: { - sans: ['Helvetica, Arial, sans-serif', { fontFeatureSettings: '"cv11", "ss01"' }], - }, +test('font-feature-settings can be provided when families are defined as a string', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontFamily: { + sans: ['Helvetica, Arial, sans-serif', { fontFeatureSettings: '"cv11", "ss01"' }], }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-sans { - font-feature-settings: 'cv11', 'ss01'; - font-family: Helvetica, Arial, sans-serif; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-sans { + font-feature-settings: 'cv11', 'ss01'; + font-family: Helvetica, Arial, sans-serif; + } + `) }) +}) - test('font-feature-settings can be provided when families are defined as an array', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontFamily: { - sans: [['Helvetica', 'Arial', 'sans-serif'], { fontFeatureSettings: '"cv11", "ss01"' }], - }, +test('font-feature-settings can be provided when families are defined as an array', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontFamily: { + sans: [['Helvetica', 'Arial', 'sans-serif'], { fontFeatureSettings: '"cv11", "ss01"' }], }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-sans { - font-feature-settings: 'cv11', 'ss01'; - font-family: Helvetica, Arial, sans-serif; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-sans { + font-feature-settings: 'cv11', 'ss01'; + font-family: Helvetica, Arial, sans-serif; + } + `) }) }) diff --git a/tests/plugins/fontSize.test.js b/tests/plugins/fontSize.test.js index 08e88bb76..32214650f 100644 --- a/tests/plugins/fontSize.test.js +++ b/tests/plugins/fontSize.test.js @@ -1,197 +1,195 @@ -import { crosscheck, run, html, css } from '../util/run' +import { run, html, css } from '../util/run' -crosscheck(() => { - test('font-size utilities can include a default line-height', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontSize: { - sm: '12px', - md: ['16px', '24px'], - lg: ['20px', '28px'], - }, +test('font-size utilities can include a default line-height', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontSize: { + sm: '12px', + md: ['16px', '24px'], + lg: ['20px', '28px'], }, - } + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .text-lg { - font-size: 20px; - line-height: 28px; - } - .text-md { - font-size: 16px; - line-height: 24px; - } - .text-sm { - font-size: 12px; - } - `) - }) - }) - - test('font-size utilities can include a default letter-spacing', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontSize: { - sm: '12px', - md: ['16px', { letterSpacing: '-0.01em' }], - lg: ['20px', { letterSpacing: '-0.02em' }], - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .text-lg { - letter-spacing: -0.02em; - font-size: 20px; - } - .text-md { - letter-spacing: -0.01em; - font-size: 16px; - } - .text-sm { - font-size: 12px; - } - `) - }) - }) - - test('font-size utilities can include a default line-height and letter-spacing', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontSize: { - sm: '12px', - md: ['16px', { lineHeight: '24px', letterSpacing: '-0.01em' }], - lg: ['20px', { lineHeight: '28px', letterSpacing: '-0.02em' }], - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .text-lg { - letter-spacing: -0.02em; - font-size: 20px; - line-height: 28px; - } - .text-md { - letter-spacing: -0.01em; - font-size: 16px; - line-height: 24px; - } - .text-sm { - font-size: 12px; - } - `) - }) - }) - - test('font-size utilities can include a font-weight', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - fontSize: { - sm: '12px', - md: ['16px', { lineHeight: '24px', fontWeight: 500 }], - lg: ['20px', { lineHeight: '28px', fontWeight: 'bold' }], - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .text-lg { - font-size: 20px; - font-weight: bold; - line-height: 28px; - } - .text-md { - font-size: 16px; - font-weight: 500; - line-height: 24px; - } - .text-sm { - font-size: 12px; - } - `) - }) - }) - - test('font-size utilities can include a line-height modifier', () => { - let config = { - content: [ - { - raw: html`
-
-
-
-
-
-
`, - }, - ], - theme: { - fontSize: { - sm: ['12px', '20px'], - base: ['16px', '24px'], - }, - lineHeight: { - 6: '24px', - 7: '28px', - 8: '32px', - }, - }, - } - - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchCss(css` - .text-\[13px\]\/6 { - font-size: 13px; - line-height: 24px; - } - .text-\[17px\]\/\[23px\] { - font-size: 17px; - line-height: 23px; - } - .text-sm { - font-size: 12px; - line-height: 20px; - } - .text-sm\/6 { - font-size: 12px; - line-height: 24px; - } - .text-sm\/\[21px\] { - font-size: 12px; - line-height: 21px; - } - @media (min-width: 768px) { - .md\:text-\[19px\]\/8 { - font-size: 19px; - line-height: 32px; - } - .md\:text-\[21px\]\/\[29px\] { - font-size: 21px; - line-height: 29px; - } - .md\:text-base { - font-size: 16px; - line-height: 24px; - } - .md\:text-base\/7 { - font-size: 16px; - line-height: 28px; - } - .md\:text-base\/\[33px\] { - font-size: 16px; - line-height: 33px; - } - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .text-lg { + font-size: 20px; + line-height: 28px; + } + .text-md { + font-size: 16px; + line-height: 24px; + } + .text-sm { + font-size: 12px; + } + `) + }) +}) + +test('font-size utilities can include a default letter-spacing', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontSize: { + sm: '12px', + md: ['16px', { letterSpacing: '-0.01em' }], + lg: ['20px', { letterSpacing: '-0.02em' }], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .text-lg { + letter-spacing: -0.02em; + font-size: 20px; + } + .text-md { + letter-spacing: -0.01em; + font-size: 16px; + } + .text-sm { + font-size: 12px; + } + `) + }) +}) + +test('font-size utilities can include a default line-height and letter-spacing', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontSize: { + sm: '12px', + md: ['16px', { lineHeight: '24px', letterSpacing: '-0.01em' }], + lg: ['20px', { lineHeight: '28px', letterSpacing: '-0.02em' }], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .text-lg { + letter-spacing: -0.02em; + font-size: 20px; + line-height: 28px; + } + .text-md { + letter-spacing: -0.01em; + font-size: 16px; + line-height: 24px; + } + .text-sm { + font-size: 12px; + } + `) + }) +}) + +test('font-size utilities can include a font-weight', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + fontSize: { + sm: '12px', + md: ['16px', { lineHeight: '24px', fontWeight: 500 }], + lg: ['20px', { lineHeight: '28px', fontWeight: 'bold' }], + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .text-lg { + font-size: 20px; + font-weight: bold; + line-height: 28px; + } + .text-md { + font-size: 16px; + font-weight: 500; + line-height: 24px; + } + .text-sm { + font-size: 12px; + } + `) + }) +}) + +test('font-size utilities can include a line-height modifier', () => { + let config = { + content: [ + { + raw: html`
+
+
+
+
+
+
`, + }, + ], + theme: { + fontSize: { + sm: ['12px', '20px'], + base: ['16px', '24px'], + }, + lineHeight: { + 6: '24px', + 7: '28px', + 8: '32px', + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .text-\[13px\]\/6 { + font-size: 13px; + line-height: 24px; + } + .text-\[17px\]\/\[23px\] { + font-size: 17px; + line-height: 23px; + } + .text-sm { + font-size: 12px; + line-height: 20px; + } + .text-sm\/6 { + font-size: 12px; + line-height: 24px; + } + .text-sm\/\[21px\] { + font-size: 12px; + line-height: 21px; + } + @media (min-width: 768px) { + .md\:text-\[19px\]\/8 { + font-size: 19px; + line-height: 32px; + } + .md\:text-\[21px\]\/\[29px\] { + font-size: 21px; + line-height: 29px; + } + .md\:text-base { + font-size: 16px; + line-height: 24px; + } + .md\:text-base\/7 { + font-size: 16px; + line-height: 28px; + } + .md\:text-base\/\[33px\] { + font-size: 16px; + line-height: 33px; + } + } + `) }) }) diff --git a/tests/plugins/gradientColorStops.test.js b/tests/plugins/gradientColorStops.test.js index de79cc8fc..c9647d50f 100644 --- a/tests/plugins/gradientColorStops.test.js +++ b/tests/plugins/gradientColorStops.test.js @@ -1,191 +1,155 @@ -import { crosscheck, run, html, css } from '../util/run' +import { run, html, css } from '../util/run' -crosscheck(({ stable, oxide }) => { - test('opacity variables are given to colors defined as closures', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - theme: { - colors: { - primary: ({ opacityVariable, opacityValue }) => { - if (opacityValue !== undefined) { - return `rgba(31,31,31,${opacityValue})` - } - - if (opacityVariable !== undefined) { - return `rgba(31,31,31,var(${opacityVariable},1))` - } - - return `rgb(31,31,31)` - }, - secondary: 'hsl(10, 50%, 50%)', - }, - opacity: { - 50: '0.5', - }, +test('opacity variables are given to colors defined as closures', () => { + let config = { + content: [ + { + raw: html`
`, }, - } + ], + theme: { + colors: { + primary: ({ opacityVariable, opacityValue }) => { + if (opacityValue !== undefined) { + return `rgba(31,31,31,${opacityValue})` + } - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .from-primary { - --tw-gradient-from: #1f1f1f var(--tw-gradient-from-position); - --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .from-secondary { - --tw-gradient-from: #bf5540 var(--tw-gradient-from-position); - --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .via-primary { - --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #1f1f1f var(--tw-gradient-via-position), - var(--tw-gradient-to); - } - .via-secondary { - --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #bf5540 var(--tw-gradient-via-position), - var(--tw-gradient-to); - } - .to-primary { - --tw-gradient-to: #1f1f1f var(--tw-gradient-to-position); - } - .to-secondary { - --tw-gradient-to: #bf5540 var(--tw-gradient-to-position); - } - .text-primary { - --tw-text-opacity: 1; - color: rgba(31, 31, 31, var(--tw-text-opacity)); - } - .text-secondary { - --tw-text-opacity: 1; - color: hsl(10 50% 50% / var(--tw-text-opacity)); - } - .text-opacity-50 { - --tw-text-opacity: 0.5; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .from-primary { - --tw-gradient-from: #1f1f1f var(--tw-gradient-from-position); - --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .from-secondary { - --tw-gradient-from: #bf5540 var(--tw-gradient-from-position); - --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .via-primary { - --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #1f1f1f var(--tw-gradient-via-position), - var(--tw-gradient-to); - } - .via-secondary { - --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #bf5540 var(--tw-gradient-via-position), - var(--tw-gradient-to); - } - .to-primary { - --tw-gradient-to: #1f1f1f var(--tw-gradient-to-position); - } - .to-secondary { - --tw-gradient-to: #bf5540 var(--tw-gradient-to-position); - } - .text-primary { - color: #1f1f1f; - } - .text-secondary { - color: #bf5540; - } - `) - }) - }) + if (opacityVariable !== undefined) { + return `rgba(31,31,31,var(${opacityVariable},1))` + } - test('gradient color stop position', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
- `, + return `rgb(31,31,31)` }, - ], - theme: {}, - } + secondary: 'hsl(10, 50%, 50%)', + }, + opacity: { + 50: '0.5', + }, + }, + } - return run('@tailwind utilities', config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); - } - .from-\[--from-value\] { - --tw-gradient-from: var(--from-value) var(--tw-gradient-from-position); - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .from-red-500 { - --tw-gradient-from: #ef4444 var(--tw-gradient-from-position); - --tw-gradient-to: #ef444400 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); - } - .from-10\% { - --tw-gradient-from-position: 10%; - } - .from-\[11\%\] { - --tw-gradient-from-position: 11%; - } - .from-\[12px\] { - --tw-gradient-from-position: 12px; - } - .via-\[--via-value\] { - --tw-gradient-to: #fff0 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), - var(--via-value) var(--tw-gradient-via-position), var(--tw-gradient-to); - } - .via-pink-500 { - --tw-gradient-to: #ec489900 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #ec4899 var(--tw-gradient-via-position), - var(--tw-gradient-to); - } - .via-20\% { - --tw-gradient-via-position: 20%; - } - .via-\[12\%\] { - --tw-gradient-via-position: 12%; - } - .via-\[123px\] { - --tw-gradient-via-position: 123px; - } - .to-\[--to-value\] { - --tw-gradient-to: var(--to-value) var(--tw-gradient-to-position); - } - .to-violet-400 { - --tw-gradient-to: #a78bfa var(--tw-gradient-to-position); - } - .to-30\% { - --tw-gradient-to-position: 30%; - } - .to-\[13\%\] { - --tw-gradient-to-position: 13%; - } - .to-\[14px\] { - --tw-gradient-to-position: 14px; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .from-primary { + --tw-gradient-from: #1f1f1f var(--tw-gradient-from-position); + --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .from-secondary { + --tw-gradient-from: #bf5540 var(--tw-gradient-from-position); + --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .via-primary { + --tw-gradient-to: #1f1f1f00 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #1f1f1f var(--tw-gradient-via-position), + var(--tw-gradient-to); + } + .via-secondary { + --tw-gradient-to: #bf554000 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #bf5540 var(--tw-gradient-via-position), + var(--tw-gradient-to); + } + .to-primary { + --tw-gradient-to: #1f1f1f var(--tw-gradient-to-position); + } + .to-secondary { + --tw-gradient-to: #bf5540 var(--tw-gradient-to-position); + } + .text-primary { + --tw-text-opacity: 1; + color: rgba(31, 31, 31, var(--tw-text-opacity)); + } + .text-secondary { + --tw-text-opacity: 1; + color: hsl(10 50% 50% / var(--tw-text-opacity)); + } + .text-opacity-50 { + --tw-text-opacity: 0.5; + } + `) + }) +}) + +test('gradient color stop position', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+ `, + }, + ], + theme: {}, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); + } + .from-\[--from-value\] { + --tw-gradient-from: var(--from-value) var(--tw-gradient-from-position); + --tw-gradient-to: #fff0 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .from-red-500 { + --tw-gradient-from: #ef4444 var(--tw-gradient-from-position); + --tw-gradient-to: #ef444400 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + } + .from-10\% { + --tw-gradient-from-position: 10%; + } + .from-\[11\%\] { + --tw-gradient-from-position: 11%; + } + .from-\[12px\] { + --tw-gradient-from-position: 12px; + } + .via-\[--via-value\] { + --tw-gradient-to: #fff0 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), + var(--via-value) var(--tw-gradient-via-position), var(--tw-gradient-to); + } + .via-pink-500 { + --tw-gradient-to: #ec489900 var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #ec4899 var(--tw-gradient-via-position), + var(--tw-gradient-to); + } + .via-20\% { + --tw-gradient-via-position: 20%; + } + .via-\[12\%\] { + --tw-gradient-via-position: 12%; + } + .via-\[123px\] { + --tw-gradient-via-position: 123px; + } + .to-\[--to-value\] { + --tw-gradient-to: var(--to-value) var(--tw-gradient-to-position); + } + .to-violet-400 { + --tw-gradient-to: #a78bfa var(--tw-gradient-to-position); + } + .to-30\% { + --tw-gradient-to-position: 30%; + } + .to-\[13\%\] { + --tw-gradient-to-position: 13%; + } + .to-\[14px\] { + --tw-gradient-to-position: 14px; + } + `) }) }) diff --git a/tests/prefers-contrast.test.js b/tests/prefers-contrast.test.js index e3b940da8..52420ee1e 100644 --- a/tests/prefers-contrast.test.js +++ b/tests/prefers-contrast.test.js @@ -1,102 +1,71 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - it('should be possible to use contrast-more and contrast-less variants', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } +it('should be possible to use contrast-more and contrast-less variants', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - ${defaults} - .bg-white { + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + .bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + } + @media (prefers-contrast: more) { + .contrast-more\:bg-pink-500 { + --tw-bg-opacity: 1; + background-color: rgb(236 72 153 / var(--tw-bg-opacity)); + } + } + @media (prefers-contrast: less) { + .contrast-less\:bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + } + `) + }) +}) + +it('dark mode should appear after the contrast variants', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (prefers-contrast: more) { + .contrast-more\:bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + } + @media (prefers-color-scheme: dark) { + .dark\:bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } - @media (prefers-contrast: more) { - .contrast-more\:bg-pink-500 { - --tw-bg-opacity: 1; - background-color: rgb(236 72 153 / var(--tw-bg-opacity)); - } - } - @media (prefers-contrast: less) { - .contrast-less\:bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - ${defaults} - .bg-white { - background-color: #fff; - } - @media (prefers-contrast: more) { - .contrast-more\:bg-pink-500 { - background-color: #ec4899; - } - } - @media (prefers-contrast: less) { - .contrast-less\:bg-black { - background-color: #000; - } - } - `) - }) - }) - - it('dark mode should appear after the contrast variants', () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-contrast: more) { - .contrast-more\:bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - } - @media (prefers-color-scheme: dark) { - .dark\:bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (prefers-contrast: more) { - .contrast-more\:bg-black { - background-color: #000; - } - } - @media (prefers-color-scheme: dark) { - .dark\:bg-white { - background-color: #fff; - } - } - `) - }) + } + `) }) }) diff --git a/tests/prefix.test.js b/tests/prefix.test.js index ea3d041ff..c9c0e5f25 100644 --- a/tests/prefix.test.js +++ b/tests/prefix.test.js @@ -1,625 +1,609 @@ -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - oxide.test.todo('prefix') - stable.test('prefix', () => { - let config = { - prefix: 'tw-', - darkMode: 'class', - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - theme: { - animation: { - spin: 'spin 1s linear infinite', - ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', - }, - keyframes: { - spin: { to: { transform: 'rotate(360deg)' } }, - }, +test('prefix', () => { + let config = { + prefix: 'tw-', + darkMode: 'class', + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, }, - plugins: [ - function ({ addComponents, addUtilities }) { - addComponents({ - '.btn-prefix': { + ], + corePlugins: { preflight: false }, + theme: { + animation: { + spin: 'spin 1s linear infinite', + ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + }, + keyframes: { + spin: { to: { transform: 'rotate(360deg)' } }, + }, + }, + plugins: [ + function ({ addComponents, addUtilities }) { + addComponents({ + '.btn-prefix': { + button: 'yes', + }, + }) + addComponents( + { + '.btn-no-prefix': { button: 'yes', }, - }) - addComponents( - { - '.btn-no-prefix': { - button: 'yes', - }, - }, - { respectPrefix: false } - ) - addUtilities({ - '.custom-util-prefix': { + }, + { respectPrefix: false } + ) + addUtilities({ + '.custom-util-prefix': { + button: 'no', + }, + }) + addUtilities( + { + '.custom-util-no-prefix': { button: 'no', }, - }) - addUtilities( - { - '.custom-util-no-prefix': { - button: 'no', - }, - }, - { respectPrefix: false } - ) - }, - ], - } - - let input = css` - @tailwind base; - @tailwind components; - @layer components { - .custom-component { - @apply tw-font-bold dark:group-hover:tw-font-normal; - } - } - @tailwind utilities; - @layer utilities { - .custom-utility { - foo: bar; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - .tw-container { - width: 100%; - } - @media (min-width: 640px) { - .tw-container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .tw-container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .tw-container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .tw-container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .tw-container { - max-width: 1536px; - } - } - .tw-btn-prefix, - .btn-no-prefix { - button: yes; - } - .custom-component { - font-weight: 700; - } - :is(.tw-dark .tw-group:hover .custom-component) { - font-weight: 400; - } - .tw--ml-4 { - margin-left: -1rem; - } - .tw-animate-ping { - animation: 1s cubic-bezier(0, 0, 0.2, 1) infinite ping; - } - @keyframes tw-spin { - to { - transform: rotate(360deg); - } - } - .tw-animate-spin { - animation: 1s linear infinite tw-spin; - } - .tw-font-bold { - font-weight: 700; - } - .tw-custom-util-prefix, - .custom-util-no-prefix { - button: no; - } - .tw-group:hover .group-hover\:focus-within\:tw-text-left:focus-within { - text-align: left; - } - :is([dir='rtl'] .rtl\:active\:tw-text-center:active) { - text-align: center; - } - @media (prefers-reduced-motion: no-preference) { - .motion-safe\:hover\:tw-text-center:hover { - text-align: center; - } - } - :is(.tw-dark .dark\:tw-bg-\[rgb\(255\,0\,0\)\]) { - --tw-bg-opacity: 1; - background-color: rgb(255 0 0 / var(--tw-bg-opacity)); - } - :is(.tw-dark .dark\:focus\:tw-text-left:focus) { - text-align: left; - } - @media (min-width: 768px) { - .md\:tw--ml-5 { - margin-left: -1.25rem; - } - .md\:hover\:tw--ml-6:hover { - margin-left: -1.5rem; - } - .md\:hover\:tw-text-right:hover { - text-align: right; - } - } - `) - }) - }) - - oxide.test.todo('negative values: marker before prefix') - stable.test('negative values: marker before prefix', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .-tw-top-1 { - top: -0.25rem; - } - `) - }) - - oxide.test.todo('negative values: marker after prefix') - stable.test('negative values: marker after prefix', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .tw--top-1 { - top: -0.25rem; - } - `) - }) - - oxide.test.todo('negative values: marker before prefix and arbitrary value') - stable.test('negative values: marker before prefix and arbitrary value', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .-tw-top-\[1px\] { - top: -1px; - } - `) - }) - - oxide.test.todo('negative values: marker after prefix and arbitrary value') - stable.test('negative values: marker after prefix and arbitrary value', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .tw--top-\[1px\] { - top: -1px; - } - `) - }) - - oxide.test.todo('negative values: no marker and arbitrary value') - stable.test('negative values: no marker and arbitrary value', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .tw-top-\[-1px\] { - top: -1px; - } - `) - }) - - oxide.test.todo('negative values: variant versions') - stable.test('negative values: variant versions', async () => { - let config = { - prefix: 'tw-', - content: [ - { - raw: html` -
-
-
- - -
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .hover\:-tw-top-1:hover { - top: -0.25rem; - } - .hover\:-tw-top-\[1px\]:hover { - top: -1px; - } - .hover\:tw--top-1:hover { - top: -0.25rem; - } - .hover\:tw--top-\[1px\]:hover, - .hover\:tw-top-\[-1px\]:hover { - top: -1px; - } - `) - }) - - oxide.test.todo('negative values: prefix and apply') - stable.test('negative values: prefix and apply', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - - .a { - @apply hover:tw--top-1; - } - .b { - @apply hover:-tw-top-1; - } - .c { - @apply hover:-tw-top-[1px]; - } - .d { - @apply hover:tw--top-[1px]; - } - .e { - @apply hover:tw-top-[-1px]; - } - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .a:hover, - .b:hover { - top: -0.25rem; - } - .c:hover, - .d:hover, - .e:hover { - top: -1px; - } - `) - }) - - oxide.test.todo('negative values: prefix in the safelist') - stable.test('negative values: prefix in the safelist', async () => { - let config = { - prefix: 'tw-', - safelist: [{ pattern: /-tw-top-1/g }, { pattern: /tw--top-1/g }], - theme: { - inset: { - 1: '0.25rem', - }, + }, + { respectPrefix: false } + ) }, - content: [{ raw: html`` }], - corePlugins: { preflight: false }, - } + ], + } - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .-tw-top-1, - .tw--top-1 { - top: -0.25rem; + let input = css` + @tailwind base; + @tailwind components; + @layer components { + .custom-component { + @apply tw-font-bold dark:group-hover:tw-font-normal; } - `) - }) - - oxide.test.todo('prefix with negative values and variants in the safelist') - stable.test('prefix with negative values and variants in the safelist', async () => { - let config = { - prefix: 'tw-', - safelist: [ - { pattern: /-tw-top-1/, variants: ['hover', 'sm:hover'] }, - { pattern: /tw--top-1/, variants: ['hover', 'sm:hover'] }, - ], - theme: { - inset: { - 1: '0.25rem', - }, - }, - content: [{ raw: html`` }], - corePlugins: { preflight: false }, } + @tailwind utilities; + @layer utilities { + .custom-utility { + foo: bar; + } + } + ` - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - const result = await run(input, config) - + return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .-tw-top-1, - .tw--top-1, - .hover\:-tw-top-1:hover, - .hover\:tw--top-1:hover { - top: -0.25rem; + ${defaults} + .tw-container { + width: 100%; } @media (min-width: 640px) { - .sm\:hover\:-tw-top-1:hover, - .sm\:hover\:tw--top-1:hover { - top: -0.25rem; + .tw-container { + max-width: 640px; } } - `) - }) - - oxide.test.todo('prefix does not detect and generate unnecessary classes') - stable.test('prefix does not detect and generate unnecessary classes', async () => { - let config = { - prefix: 'tw-_', - content: [{ raw: html`-aaa-filter aaaa-table aaaa-hidden` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css``) - }) - - oxide.test.todo('supports prefixed utilities using arbitrary values') - stable.test('supports prefixed utilities using arbitrary values', async () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`foo` }], - corePlugins: { preflight: false }, - } - - let input = css` - .foo { - @apply tw-text-[color:rgb(var(--button-background,var(--primary-button-background)))]; - @apply tw-ease-[cubic-bezier(0.77,0,0.175,1)]; - @apply tw-rounded-[min(4px,var(--input-border-radius))]; - } - ` - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .foo { - color: rgb(var(--button-background, var(--primary-button-background))); - border-radius: min(4px, var(--input-border-radius)); - transition-timing-function: cubic-bezier(0.77, 0, 0.175, 1); - } - `) - }) - - oxide.test.todo('supports non-word prefixes (1)') - stable.test('supports non-word prefixes (1)', async () => { - let config = { - prefix: '@', - content: [ - { - raw: html` -
-
-
-
-
-
- - -
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .my-utility { - color: orange; + @media (min-width: 768px) { + .tw-container { + max-width: 768px; } } - .foo { - @apply @text-white; - @apply [background-color:red]; + @media (min-width: 1024px) { + .tw-container { + max-width: 1024px; + } } - ` - - const result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .\@bg-black { + @media (min-width: 1280px) { + .tw-container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .tw-container { + max-width: 1536px; + } + } + .tw-btn-prefix, + .btn-no-prefix { + button: yes; + } + .custom-component { + font-weight: 700; + } + :is(.tw-dark .tw-group:hover .custom-component) { + font-weight: 400; + } + .tw--ml-4 { + margin-left: -1rem; + } + .tw-animate-ping { + animation: 1s cubic-bezier(0, 0, 0.2, 1) infinite ping; + } + @keyframes tw-spin { + to { + transform: rotate(360deg); + } + } + .tw-animate-spin { + animation: 1s linear infinite tw-spin; + } + .tw-font-bold { + font-weight: 700; + } + .tw-custom-util-prefix, + .custom-util-no-prefix { + button: no; + } + .tw-group:hover .group-hover\:focus-within\:tw-text-left:focus-within { + text-align: left; + } + :is([dir='rtl'] .rtl\:active\:tw-text-center:active) { + text-align: center; + } + @media (prefers-reduced-motion: no-preference) { + .motion-safe\:hover\:tw-text-center:hover { + text-align: center; + } + } + :is(.tw-dark .dark\:tw-bg-\[rgb\(255\,0\,0\)\]) { --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + background-color: rgb(255 0 0 / var(--tw-bg-opacity)); } - .\@underline { - text-decoration-line: underline; + :is(.tw-dark .dark\:focus\:tw-text-left:focus) { + text-align: left; } - .my-utility { - color: orange; - } - .foo { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - background-color: red; - } - .hover\:before\:\@content-\[\'Hovering\'\]:hover:before { - --tw-content: 'Hovering'; - content: var(--tw-content); - } - `) - }) - - oxide.test.todo('supports non-word prefixes (2)') - stable.test('supports non-word prefixes (2)', async () => { - let config = { - prefix: '@]$', - content: [ - { - raw: html` -
-
-
-
-
-
- - -
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .my-utility { - color: orange; + @media (min-width: 768px) { + .md\:tw--ml-5 { + margin-left: -1.25rem; + } + .md\:hover\:tw--ml-6:hover { + margin-left: -1.5rem; + } + .md\:hover\:tw-text-right:hover { + text-align: right; } - } - .foo { - @apply @]$text-white; - @apply [background-color:red]; - } - ` - - const result = await run(input, config) - - // TODO: The class `.hover\:before\:\@\]\$content-\[\'Hovering\'\]:hover::before` is not generated - // This happens because of the parenthesis/brace/bracket clipping performed on candidates - - expect(result.css).toMatchFormattedCss(css` - .\@\]\$bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } - .\@\]\$underline { - text-decoration-line: underline; - } - .my-utility { - color: orange; - } - .foo { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - background-color: red; } `) }) }) + +test('negative values: marker before prefix', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .-tw-top-1 { + top: -0.25rem; + } + `) +}) + +test('negative values: marker after prefix', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .tw--top-1 { + top: -0.25rem; + } + `) +}) + +test('negative values: marker before prefix and arbitrary value', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .-tw-top-\[1px\] { + top: -1px; + } + `) +}) + +test('negative values: marker after prefix and arbitrary value', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .tw--top-\[1px\] { + top: -1px; + } + `) +}) + +test('negative values: no marker and arbitrary value', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .tw-top-\[-1px\] { + top: -1px; + } + `) +}) + +test('negative values: variant versions', async () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: html` +
+
+
+ + +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .hover\:-tw-top-1:hover { + top: -0.25rem; + } + .hover\:-tw-top-\[1px\]:hover { + top: -1px; + } + .hover\:tw--top-1:hover { + top: -0.25rem; + } + .hover\:tw--top-\[1px\]:hover, + .hover\:tw-top-\[-1px\]:hover { + top: -1px; + } + `) +}) + +test('negative values: prefix and apply', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + + .a { + @apply hover:tw--top-1; + } + .b { + @apply hover:-tw-top-1; + } + .c { + @apply hover:-tw-top-[1px]; + } + .d { + @apply hover:tw--top-[1px]; + } + .e { + @apply hover:tw-top-[-1px]; + } + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .a:hover, + .b:hover { + top: -0.25rem; + } + .c:hover, + .d:hover, + .e:hover { + top: -1px; + } + `) +}) + +test('negative values: prefix in the safelist', async () => { + let config = { + prefix: 'tw-', + safelist: [{ pattern: /-tw-top-1/g }, { pattern: /tw--top-1/g }], + theme: { + inset: { + 1: '0.25rem', + }, + }, + content: [{ raw: html`` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .-tw-top-1, + .tw--top-1 { + top: -0.25rem; + } + `) +}) + +test('prefix with negative values and variants in the safelist', async () => { + let config = { + prefix: 'tw-', + safelist: [ + { pattern: /-tw-top-1/, variants: ['hover', 'sm:hover'] }, + { pattern: /tw--top-1/, variants: ['hover', 'sm:hover'] }, + ], + theme: { + inset: { + 1: '0.25rem', + }, + }, + content: [{ raw: html`` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .-tw-top-1, + .tw--top-1, + .hover\:-tw-top-1:hover, + .hover\:tw--top-1:hover { + top: -0.25rem; + } + @media (min-width: 640px) { + .sm\:hover\:-tw-top-1:hover, + .sm\:hover\:tw--top-1:hover { + top: -0.25rem; + } + } + `) +}) + +test('prefix does not detect and generate unnecessary classes', async () => { + let config = { + prefix: 'tw-_', + content: [{ raw: html`-aaa-filter aaaa-table aaaa-hidden` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css``) +}) + +test('supports prefixed utilities using arbitrary values', async () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`foo` }], + corePlugins: { preflight: false }, + } + + let input = css` + .foo { + @apply tw-text-[color:rgb(var(--button-background,var(--primary-button-background)))]; + @apply tw-ease-[cubic-bezier(0.77,0,0.175,1)]; + @apply tw-rounded-[min(4px,var(--input-border-radius))]; + } + ` + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .foo { + color: rgb(var(--button-background, var(--primary-button-background))); + border-radius: min(4px, var(--input-border-radius)); + transition-timing-function: cubic-bezier(0.77, 0, 0.175, 1); + } + `) +}) + +test('supports non-word prefixes (1)', async () => { + let config = { + prefix: '@', + content: [ + { + raw: html` +
+
+
+
+
+
+ + +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .my-utility { + color: orange; + } + } + .foo { + @apply @text-white; + @apply [background-color:red]; + } + ` + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .\@bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .\@underline { + text-decoration-line: underline; + } + .my-utility { + color: orange; + } + .foo { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + background-color: red; + } + .hover\:before\:\@content-\[\'Hovering\'\]:hover:before { + --tw-content: 'Hovering'; + content: var(--tw-content); + } + `) +}) + +test('supports non-word prefixes (2)', async () => { + let config = { + prefix: '@]$', + content: [ + { + raw: html` +
+
+
+
+
+
+ + +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .my-utility { + color: orange; + } + } + .foo { + @apply @]$text-white; + @apply [background-color:red]; + } + ` + + const result = await run(input, config) + + // TODO: The class `.hover\:before\:\@\]\$content-\[\'Hovering\'\]:hover::before` is not generated + // This happens because of the parenthesis/brace/bracket clipping performed on candidates + + expect(result.css).toMatchFormattedCss(css` + .\@\]\$bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .\@\]\$underline { + text-decoration-line: underline; + } + .my-utility { + color: orange; + } + .foo { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + background-color: red; + } + `) +}) diff --git a/tests/prefixSelector.test.js b/tests/prefixSelector.test.js index c7db08d03..92fd6194f 100644 --- a/tests/prefixSelector.test.js +++ b/tests/prefixSelector.test.js @@ -1,20 +1,17 @@ import prefix from '../src/util/prefixSelector' -import { crosscheck } from './util/run' -crosscheck(() => { - test('it prefixes classes with the provided prefix', () => { - expect(prefix('tw-', '.foo')).toEqual('.tw-foo') - }) - - test('it properly prefixes selectors with non-standard characters', () => { - expect(prefix('tw-', '.hello\\:world')).toEqual('.tw-hello\\:world') - expect(prefix('tw-', '.foo\\/bar')).toEqual('.tw-foo\\/bar') - expect(prefix('tw-', '.wew\\.lad')).toEqual('.tw-wew\\.lad') - }) - - test('it prefixes all classes in a selector', () => { - expect(prefix('tw-', '.btn-blue .w-1\\/4 > h1.text-xl + a .bar')).toEqual( - '.tw-btn-blue .tw-w-1\\/4 > h1.tw-text-xl + a .tw-bar' - ) - }) +test('it prefixes classes with the provided prefix', () => { + expect(prefix('tw-', '.foo')).toEqual('.tw-foo') +}) + +test('it properly prefixes selectors with non-standard characters', () => { + expect(prefix('tw-', '.hello\\:world')).toEqual('.tw-hello\\:world') + expect(prefix('tw-', '.foo\\/bar')).toEqual('.tw-foo\\/bar') + expect(prefix('tw-', '.wew\\.lad')).toEqual('.tw-wew\\.lad') +}) + +test('it prefixes all classes in a selector', () => { + expect(prefix('tw-', '.btn-blue .w-1\\/4 > h1.text-xl + a .bar')).toEqual( + '.tw-btn-blue .tw-w-1\\/4 > h1.tw-text-xl + a .tw-bar' + ) }) diff --git a/tests/preflight.test.js b/tests/preflight.test.js index 4f6f722c7..ad654999e 100644 --- a/tests/preflight.test.js +++ b/tests/preflight.test.js @@ -1,25 +1,23 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - it('preflight has a correct border color fallback', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - borderColor: ({ theme }) => theme('colors'), - }, - plugins: [], - corePlugins: { preflight: true }, - } +it('preflight has a correct border color fallback', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + borderColor: ({ theme }) => theme('colors'), + }, + plugins: [], + corePlugins: { preflight: true }, + } - let input = css` - @tailwind base; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable.expect(result.css).toContain(`border-color: currentColor;`) - // Lightning CSS optimizes this to just `border-color: 0 solid;` based on the br value. - oxide.expect(result.css).toContain(`border: 0 solid;`) - }) + return run(input, config).then((result) => { + // Lightning CSS optimizes this to just `border-color: 0 solid;` because `currentcolor` is the default user-agent stylesheet value. + // https://drafts.csswg.org/css-backgrounds/#border-color + expect(result.css).toContain(`border: 0 solid;`) }) }) diff --git a/tests/raw-content.oxide.test.css b/tests/raw-content.oxide.test.css deleted file mode 100644 index aa5abd127..000000000 --- a/tests/raw-content.oxide.test.css +++ /dev/null @@ -1,759 +0,0 @@ -.container { - width: 100%; -} -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} -.sr-only { - width: 1px; - height: 1px; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - margin: -1px; - padding: 0; - position: absolute; - overflow: hidden; -} -.pointer-events-none { - pointer-events: none; -} -.invisible { - visibility: hidden; -} -.collapse { - visibility: collapse; -} -.absolute { - position: absolute; -} -.inset-0 { - inset: 0; -} -.inset-x-2 { - left: 0.5rem; - right: 0.5rem; -} -.inset-y-4 { - top: 1rem; - bottom: 1rem; -} -.bottom-12 { - bottom: 3rem; -} -.left-16 { - left: 4rem; -} -.right-8 { - right: 2rem; -} -.top-6 { - top: 1.5rem; -} -.isolate { - isolation: isolate; -} -.isolation-auto { - isolation: auto; -} -.z-30 { - z-index: 30; -} -.order-2 { - order: 2; -} -.order-last { - order: 9999; -} -.col-span-3 { - grid-column: span 3 / span 3; -} -.col-start-1 { - grid-column-start: 1; -} -.col-end-4 { - grid-column-end: 4; -} -.row-span-2 { - grid-row: span 2 / span 2; -} -.row-start-3 { - grid-row-start: 3; -} -.row-end-5 { - grid-row-end: 5; -} -.float-right { - float: right; -} -.clear-left { - clear: left; -} -.m-4 { - margin: 1rem; -} -.mx-auto { - margin-left: auto; - margin-right: auto; -} -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} -.mb-3 { - margin-bottom: 0.75rem; -} -.ml-4 { - margin-left: 1rem; -} -.mr-1 { - margin-right: 0.25rem; -} -.mt-0 { - margin-top: 0; -} -.box-border { - box-sizing: border-box; -} -.inline-grid { - display: inline-grid; -} -.hidden { - display: none; -} -.h-16 { - height: 4rem; -} -.max-h-screen { - max-height: 100vh; -} -.min-h-0 { - min-height: 0; -} -.w-12 { - width: 3rem; -} -.min-w-min { - min-width: min-content; -} -.max-w-full { - max-width: 100%; -} -.flex-1 { - flex: 1; -} -.flex-shrink { - flex-shrink: 1; -} -.flex-shrink-0 { - flex-shrink: 0; -} -.flex-grow { - flex-grow: 1; -} -.flex-grow-0 { - flex-grow: 0; -} -.table-fixed { - table-layout: fixed; -} -.caption-top { - caption-side: top; -} -.caption-bottom { - caption-side: bottom; -} -.border-collapse { - border-collapse: collapse; -} -.origin-top-right { - transform-origin: 100% 0; -} -.-translate-x-3 { - --tw-translate-x: -0.75rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.-translate-x-4 { - --tw-translate-x: -1rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-x-5 { - --tw-translate-x: 1.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.translate-y-6 { - --tw-translate-y: 1.5rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.rotate-3 { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-x-12 { - --tw-skew-x: 12deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.skew-y-12 { - --tw-skew-y: 12deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.scale-95 { - --tw-scale-x: 0.95; - --tw-scale-y: 0.95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.transform-gpu { - transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) - scaleY(var(--tw-scale-y)); -} -.animate-none { - animation: none; -} -@keyframes spin { - to { - transform: rotate(360deg); - } -} -.animate-spin { - animation: 1s linear infinite spin; -} -.cursor-pointer { - cursor: pointer; -} -.select-none { - user-select: none; -} -.resize-none { - resize: none; -} -.list-inside { - list-style-position: inside; -} -.list-disc { - list-style-type: disc; -} -.appearance-none { - appearance: none; -} -.auto-cols-min { - grid-auto-columns: min-content; -} -.grid-flow-row { - grid-auto-flow: row; -} -.auto-rows-max { - grid-auto-rows: max-content; -} -.grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} -.grid-rows-3 { - grid-template-rows: repeat(3, minmax(0, 1fr)); -} -.flex-row-reverse { - flex-direction: row-reverse; -} -.flex-wrap { - flex-wrap: wrap; -} -.place-content-start { - place-content: start; -} -.place-items-end { - place-items: end; -} -.content-center { - align-content: center; -} -.items-start { - align-items: flex-start; -} -.justify-center { - justify-content: center; -} -.justify-items-end { - justify-items: end; -} -.gap-4 { - gap: 1rem; -} -.gap-x-2 { - column-gap: 0.5rem; -} -.gap-y-3 { - row-gap: 0.75rem; -} -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); -} -.space-y-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); -} -.space-y-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 1; -} -.space-x-reverse > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 1; -} -.divide-x-0 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(0px * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-inline-end-width: calc(2px * var(--tw-divide-x-reverse)); - border-inline-start-width: calc(2px * calc(1 - var(--tw-divide-x-reverse))); -} -.divide-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); -} -.divide-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(4px * var(--tw-divide-y-reverse)); -} -.divide-dotted > :not([hidden]) ~ :not([hidden]) { - border-style: dotted; -} -.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { - border-color: #e5e7eb; -} -.place-self-center { - place-self: center; -} -.self-end { - align-self: flex-end; -} -.justify-self-start { - justify-self: start; -} -.overflow-hidden { - overflow: hidden; -} -.overscroll-contain { - overscroll-behavior: contain; -} -.scroll-smooth { - scroll-behavior: smooth; -} -.truncate { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} -.overflow-ellipsis { - text-overflow: ellipsis; -} -.whitespace-nowrap { - white-space: nowrap; -} -.break-words { - overflow-wrap: break-word; -} -.rounded-md { - border-radius: 0.375rem; -} -.border { - border-width: 1px; -} -.border-2 { - border-width: 2px; -} -.border-solid { - border-style: solid; -} -.border-black { - border-color: #000; -} -.bg-green-500 { - background-color: #22c55e; -} -.bg-gradient-to-r { - background-image: linear-gradient(to right, var(--tw-gradient-stops)); -} -.from-red-300 { - --tw-gradient-from: #fca5a5 var(--tw-gradient-from-position); - --tw-gradient-to: #fca5a500 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} -.via-purple-200 { - --tw-gradient-to: #e9d5ff00 var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), #e9d5ff var(--tw-gradient-via-position), - var(--tw-gradient-to); -} -.to-blue-400 { - --tw-gradient-to: #60a5fa var(--tw-gradient-to-position); -} -.decoration-slice { - -webkit-box-decoration-break: slice; - box-decoration-break: slice; -} -.decoration-clone { - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -.bg-cover { - background-size: cover; -} -.bg-local { - background-attachment: local; -} -.bg-clip-border { - background-clip: border-box; -} -.bg-top { - background-position: top; -} -.bg-no-repeat { - background-repeat: no-repeat; -} -.bg-origin-border { - background-origin: border-box; -} -.bg-origin-padding { - background-origin: padding-box; -} -.bg-origin-content { - background-origin: content-box; -} -.fill-current { - fill: currentColor; -} -.stroke-current { - stroke: currentColor; -} -.stroke-2 { - stroke-width: 2px; -} -.object-cover { - object-fit: cover; -} -.object-bottom { - object-position: bottom; -} -.p-4 { - padding: 1rem; -} -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} -.pb-3 { - padding-bottom: 0.75rem; -} -.pl-4 { - padding-left: 1rem; -} -.pr-2 { - padding-right: 0.5rem; -} -.pt-1 { - padding-top: 0.25rem; -} -.text-center { - text-align: center; -} -.align-middle { - vertical-align: middle; -} -.font-sans { - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, - Noto Color Emoji; -} -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} -.font-medium { - font-weight: 500; -} -.uppercase { - text-transform: uppercase; -} -.not-italic { - font-style: normal; -} -.ordinal { - --tw-ordinal: ordinal; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.diagonal-fractions { - --tw-numeric-fraction: diagonal-fractions; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) - var(--tw-numeric-spacing) var(--tw-numeric-fraction); -} -.leading-5 { - line-height: 1.25rem; -} -.leading-relaxed { - line-height: 1.625; -} -.tracking-tight { - letter-spacing: -0.025em; -} -.text-indigo-500 { - color: #6366f1; -} -.underline { - text-decoration-line: underline; -} -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.placeholder-green-300::placeholder { - color: #86efac; -} -.opacity-90 { - opacity: 0.9; -} -.bg-blend-darken { - background-blend-mode: darken; -} -.bg-blend-difference { - background-blend-mode: difference; -} -.mix-blend-multiply { - mix-blend-mode: multiply; -} -.mix-blend-saturation { - mix-blend-mode: saturation; -} -.shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-lg { - --tw-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), - 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.ring { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-4 { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} -.ring-white { - --tw-ring-color: #fff; -} -.ring-offset-2 { - --tw-ring-offset-width: 2px; -} -.ring-offset-blue-300 { - --tw-ring-offset-color: #93c5fd; -} -.blur-md { - --tw-blur: blur(12px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.brightness-150 { - --tw-brightness: brightness(1.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.contrast-50 { - --tw-contrast: contrast(0.5); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.drop-shadow-md { - --tw-drop-shadow: drop-shadow(0 4px 3px #00000012) drop-shadow(0 2px 2px #0000000f); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.grayscale { - --tw-grayscale: grayscale(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.hue-rotate-60 { - --tw-hue-rotate: hue-rotate(60deg); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.invert { - --tw-invert: invert(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.saturate-200 { - --tw-saturate: saturate(2); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.sepia { - --tw-sepia: sepia(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) - var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} -.filter-none { - filter: none; -} -.backdrop-blur-lg { - --tw-backdrop-blur: blur(16px); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-brightness-50 { - --tw-backdrop-brightness: brightness(0.5); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-contrast-0 { - --tw-backdrop-contrast: contrast(0); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-grayscale { - --tw-backdrop-grayscale: grayscale(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-hue-rotate-90 { - --tw-backdrop-hue-rotate: hue-rotate(90deg); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-invert { - --tw-backdrop-invert: invert(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-opacity-75 { - --tw-backdrop-opacity: opacity(0.75); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-saturate-150 { - --tw-backdrop-saturate: saturate(1.5); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-sepia { - --tw-backdrop-sepia: sepia(100%); - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-filter { - backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) - var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) - var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); -} -.backdrop-filter-none { - backdrop-filter: none; -} -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.transition-all { - transition-property: all; - transition-duration: 0.15s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} -.delay-300 { - transition-delay: 0.3s; -} -.duration-200 { - transition-duration: 0.2s; -} -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} diff --git a/tests/raw-content.test.js b/tests/raw-content.test.js index 417c37485..a55e373fb 100644 --- a/tests/raw-content.test.js +++ b/tests/raw-content.test.js @@ -1,173 +1,164 @@ import fs from 'fs' import path from 'path' -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - it('raw content', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } +it('raw content', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './raw-content.test.css'), 'utf8') - ) - oxide - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './raw-content.oxide.test.css'), 'utf8') - ) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss( + fs.readFileSync(path.resolve(__dirname, './raw-content.test.css'), 'utf8') + ) }) }) diff --git a/tests/relative-purge-paths.test.js b/tests/relative-purge-paths.test.js index 95fbe7bad..0e7e5feed 100644 --- a/tests/relative-purge-paths.test.js +++ b/tests/relative-purge-paths.test.js @@ -1,24 +1,22 @@ -import { crosscheck, run, css } from './util/run' +import { run, css } from './util/run' -crosscheck(() => { - test('relative purge paths', () => { - let config = { - content: ['./tests/relative-purge-paths.test.html'], - corePlugins: { preflight: false }, - } +test('relative purge paths', () => { + let config = { + content: ['./tests/relative-purge-paths.test.html'], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toIncludeCss(css` - .font-bold { - font-weight: 700; - } - `) - }) + return run(input, config).then((result) => { + expect(result.css).toIncludeCss(css` + .font-bold { + font-weight: 700; + } + `) }) }) diff --git a/tests/resolve-defaults-at-rules.test.js b/tests/resolve-defaults-at-rules.test.js index 5c7e0738e..1d80c780b 100644 --- a/tests/resolve-defaults-at-rules.test.js +++ b/tests/resolve-defaults-at-rules.test.js @@ -1,774 +1,771 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('basic utilities', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } +test('basic utilities', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .rotate-3 { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .skew-y-6 { - --tw-skew-y: 6deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .scale-x-110 { - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with pseudo-class variants', async () => { - let config = { - content: [ - { raw: html`
` }, - ], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .hover\:scale-x-110:hover { - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .focus\:rotate-3:focus { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .hover\:focus\:skew-y-6:focus:hover { - --tw-skew-y: 6deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with pseudo-element variants', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .before\:scale-x-110:before { - content: var(--tw-content); - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .after\:rotate-3:after { - content: var(--tw-content); - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with multi-class variants', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .group:hover .group-hover\:scale-x-110 { - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .peer:focus ~ .peer-focus\:rotate-3 { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with multi-class pseudo-element variants', async () => { - let config = { - content: [ - { raw: html`
` }, - ], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .group:hover .group-hover\:before\:scale-x-110:before { - content: var(--tw-content); - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .peer:focus ~ .peer-focus\:after\:rotate-3:after { - content: var(--tw-content); - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with multi-class pseudo-element and pseudo-class variants', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .group:hover .group-hover\:hover\:before\:scale-x-110:hover:before { - content: var(--tw-content); - --tw-scale-x: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .peer:focus ~ .peer-focus\:focus\:after\:rotate-3:focus:after { - content: var(--tw-content); - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('with apply', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - - @layer utilities { - .foo { - @apply rotate-3; - } + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; } - - .bar { - @apply before:scale-110; + .rotate-3 { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } - - .baz::before { - @apply rotate-45; + .skew-y-6 { + --tw-skew-y: 6deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } + .scale-x-110 { + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) +test('with pseudo-class variants', async () => { + let config = { + content: [ + { raw: html`
` }, + ], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .hover\:scale-x-110:hover { + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .focus\:rotate-3:focus { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .hover\:focus\:skew-y-6:focus:hover { + --tw-skew-y: 6deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('with pseudo-element variants', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .before\:scale-x-110:before { + content: var(--tw-content); + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .after\:rotate-3:after { + content: var(--tw-content); + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('with multi-class variants', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .group:hover .group-hover\:scale-x-110 { + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .peer:focus ~ .peer-focus\:rotate-3 { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('with multi-class pseudo-element variants', async () => { + let config = { + content: [ + { raw: html`
` }, + ], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .group:hover .group-hover\:before\:scale-x-110:before { + content: var(--tw-content); + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .peer:focus ~ .peer-focus\:after\:rotate-3:after { + content: var(--tw-content); + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('with multi-class pseudo-element and pseudo-class variants', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .group:hover .group-hover\:hover\:before\:scale-x-110:hover:before { + content: var(--tw-content); + --tw-scale-x: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .peer:focus ~ .peer-focus\:focus\:after\:rotate-3:focus:after { + content: var(--tw-content); + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('with apply', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + + @layer utilities { + .foo { + @apply rotate-3; + } + } + + .bar { + @apply before:scale-110; + } + + .baz::before { + @apply rotate-45; + } + + .whats ~ .next > span:hover { + @apply skew-x-6; + } + + .media-queries { + @apply md:rotate-45; + } + + .a, + .b, + .c { + @apply skew-y-3; + } + + .a, + .b { + @apply rotate-45; + } + + .a::before, + .b::after { + @apply rotate-90; + } + + .recursive { + @apply foo; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + .foo { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .bar:before { + content: var(--tw-content); + --tw-scale-x: 1.1; + --tw-scale-y: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .baz:before { + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } .whats ~ .next > span:hover { - @apply skew-x-6; + --tw-skew-x: 6deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } - - .media-queries { - @apply md:rotate-45; + @media (min-width: 768px) { + .media-queries { + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) + rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) + scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } } - .a, .b, .c { - @apply skew-y-3; + --tw-skew-y: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } - .a, .b { - @apply rotate-45; + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } - - .a::before, - .b::after { - @apply rotate-90; - } - - .recursive { - @apply foo; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .foo { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .bar:before { - content: var(--tw-content); - --tw-scale-x: 1.1; - --tw-scale-y: 1.1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .baz:before { - --tw-rotate: 45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .whats ~ .next > span:hover { - --tw-skew-x: 6deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - @media (min-width: 768px) { - .media-queries { - --tw-rotate: 45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - } - .a, - .b, - .c { - --tw-skew-y: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .a, - .b { - --tw-rotate: 45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .a:before, - .b:after { - --tw-rotate: 90deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .recursive { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('legacy pseudo-element syntax is supported', async () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - - .a:before { - @apply rotate-45; - } - + .a:before, .b:after { - @apply rotate-3; + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } - - .c:first-line { - @apply rotate-1; + .recursive { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); } + `) + }) +}) +test('legacy pseudo-element syntax is supported', async () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + + .a:before { + @apply rotate-45; + } + + .b:after { + @apply rotate-3; + } + + .c:first-line { + @apply rotate-1; + } + + .d:first-letter { + @apply rotate-6; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .a:before, + .b:after, + .c:first-line, .d:first-letter { - @apply rotate-6; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .a:before, - .b:after, - .c:first-line, - .d:first-letter { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - .a:before { - --tw-rotate: 45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .b:after { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .c:first-line { - --tw-rotate: 1deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - .d:first-letter { - --tw-rotate: 6deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) + .a:before { + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .b:after { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .c:first-line { + --tw-rotate: 1deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + .d:first-letter { + --tw-rotate: 6deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) }) +}) - test('with borders', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['borderWidth', 'borderColor', 'borderOpacity'], - } +test('with borders', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['borderWidth', 'borderColor', 'borderOpacity'], + } - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .border { - border-width: 1px; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .border { + border-width: 1px; + } + .border-red-500 { + --tw-border-opacity: 1; + border-color: rgb(239 68 68 / var(--tw-border-opacity)); + } + @media (min-width: 768px) { + .md\:border-2 { + border-width: 2px; } - .border-red-500 { - --tw-border-opacity: 1; - border-color: rgb(239 68 68 / var(--tw-border-opacity)); - } - @media (min-width: 768px) { - .md\:border-2 { - border-width: 2px; - } - } - `) - }) + } + `) }) +}) - test('with shadows', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['boxShadow', 'ringColor', 'ringWidth'], - } +test('with shadows', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['boxShadow', 'ringColor', 'ringWidth'], + } - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #3b82f680; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - } - .shadow { - --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #3b82f680; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + } + .shadow { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), + 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), + var(--tw-shadow); + } + .ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) + var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + } + .ring-black\/25 { + --tw-ring-color: #00000040; + } + @media (min-width: 768px) { + .md\:shadow-xl { + --tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), + 0 8px 10px -6px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } - .ring-1 { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) - var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) - var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); - } - .ring-black\/25 { - --tw-ring-color: #00000040; - } - @media (min-width: 768px) { - .md\:shadow-xl { - --tw-shadow: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), - 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - } - `) - }) - }) - - test('when no utilities that need the defaults are used', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['transform', 'scale', 'rotate', 'skew'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - *, - :before, - :after, - ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - } - `) - }) - }) - - test('when a utility uses defaults but they do not exist', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: ['rotate'], - } - - let input = css` - @tailwind base; - /* --- */ - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .rotate-3 { - --tw-rotate: 3deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('selectors are reduced to the lowest possible specificity', async () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - content: [{ raw: html`
` }], - corePlugins: [], - } - - let input = css` - @defaults test { - --color: black; } - - /* --- */ - - .foo { - @defaults test; - background-color: var(--color); - } - - #app { - @defaults test; - border-color: var(--color); - } - - span#page { - @defaults test; - color: var(--color); - } - - div[data-foo='bar']#other { - @defaults test; - fill: var(--color); - } - - div[data-bar='baz'] { - @defaults test; - stroke: var(--color); - } - - article { - @defaults test; - --article: var(--color); - } - - div[data-foo='bar']#another::before { - @defaults test; - fill: var(--color); - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .foo, - [id='app'], - [id='page'], - [id='other'], - [data-bar='baz'], - article, - [id='another']:before { - --color: black; - } - .foo { - background-color: var(--color); - } - #app { - border-color: var(--color); - } - span#page { - color: var(--color); - } - div[data-foo='bar']#other { - fill: var(--color); - } - div[data-bar='baz'] { - stroke: var(--color); - } - article { - --article: var(--color); - } - div[data-foo='bar']#another:before { - fill: var(--color); - } - `) - }) - }) - - test('No defaults without @tailwind base', () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - // Optimize universal defaults doesn't work well with isolated modules - // We require you to use @tailwind base to inject the defaults - let input = css` - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .scale-150 { - --tw-scale-x: 1.5; - --tw-scale-y: 1.5; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) - }) - - test('no defaults and apply without @tailwind base', () => { - let config = { - experimental: { optimizeUniversalDefaults: true }, - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - // Optimize universal defaults doesn't work well with isolated modules - // We require you to use @tailwind base to inject the defaults - let input = css` - @tailwind components; - @tailwind utilities; - - .my-card { - @apply scale-150; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .my-card { - --tw-scale-x: 1.5; - --tw-scale-y: 1.5; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) - rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) - scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - `) - }) + `) + }) +}) + +test('when no utilities that need the defaults are used', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['transform', 'scale', 'rotate', 'skew'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + } + `) + }) +}) + +test('when a utility uses defaults but they do not exist', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: ['rotate'], + } + + let input = css` + @tailwind base; + /* --- */ + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .rotate-3 { + --tw-rotate: 3deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('selectors are reduced to the lowest possible specificity', async () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: [], + } + + let input = css` + @defaults test { + --color: black; + } + + /* --- */ + + .foo { + @defaults test; + background-color: var(--color); + } + + #app { + @defaults test; + border-color: var(--color); + } + + span#page { + @defaults test; + color: var(--color); + } + + div[data-foo='bar']#other { + @defaults test; + fill: var(--color); + } + + div[data-bar='baz'] { + @defaults test; + stroke: var(--color); + } + + article { + @defaults test; + --article: var(--color); + } + + div[data-foo='bar']#another::before { + @defaults test; + fill: var(--color); + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .foo, + [id='app'], + [id='page'], + [id='other'], + [data-bar='baz'], + article, + [id='another']:before { + --color: black; + } + .foo { + background-color: var(--color); + } + #app { + border-color: var(--color); + } + span#page { + color: var(--color); + } + div[data-foo='bar']#other { + fill: var(--color); + } + div[data-bar='baz'] { + stroke: var(--color); + } + article { + --article: var(--color); + } + div[data-foo='bar']#another:before { + fill: var(--color); + } + `) + }) +}) + +test('No defaults without @tailwind base', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + // Optimize universal defaults doesn't work well with isolated modules + // We require you to use @tailwind base to inject the defaults + let input = css` + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .scale-150 { + --tw-scale-x: 1.5; + --tw-scale-y: 1.5; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) + }) +}) + +test('no defaults and apply without @tailwind base', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + // Optimize universal defaults doesn't work well with isolated modules + // We require you to use @tailwind base to inject the defaults + let input = css` + @tailwind components; + @tailwind utilities; + + .my-card { + @apply scale-150; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .my-card { + --tw-scale-x: 1.5; + --tw-scale-y: 1.5; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) + skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) + scaleY(var(--tw-scale-y)); + } + `) }) }) diff --git a/tests/resolveConfig.test.js b/tests/resolveConfig.test.js index c99a986de..954c40659 100644 --- a/tests/resolveConfig.test.js +++ b/tests/resolveConfig.test.js @@ -1,1797 +1,1794 @@ import resolveConfig from '../src/util/resolveConfig' import corePluginList from '../src/corePluginList' -import { crosscheck } from './util/run' -crosscheck(() => { - test('prefix key overrides default prefix', () => { - const userConfig = { - prefix: 'tw-', - } +test('prefix key overrides default prefix', () => { + const userConfig = { + prefix: 'tw-', + } - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: 'tw-', - important: false, - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + expect(result).toMatchObject({ + prefix: 'tw-', + important: false, + separator: ':', + theme: { + screens: { + mobile: '400px', }, - }) + }, }) +}) - test('important key overrides default important', () => { - const userConfig = { - important: true, - } +test('important key overrides default important', () => { + const userConfig = { + important: true, + } - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '', - important: true, - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + expect(result).toMatchObject({ + prefix: '', + important: true, + separator: ':', + theme: { + screens: { + mobile: '400px', }, - }) + }, }) +}) - test('important (selector) key overrides default important', () => { - const userConfig = { - important: '#app', - } +test('important (selector) key overrides default important', () => { + const userConfig = { + important: '#app', + } - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '', - important: '#app', - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + expect(result).toMatchObject({ + prefix: '', + important: '#app', + separator: ':', + theme: { + screens: { + mobile: '400px', }, - }) + }, }) +}) - test('separator key overrides default separator', () => { - const userConfig = { - separator: '__', - } +test('separator key overrides default separator', () => { + const userConfig = { + separator: '__', + } - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: '__', - theme: { - screens: { - mobile: '400px', - }, + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: '__', + theme: { + screens: { + mobile: '400px', }, - }) + }, }) +}) - test('theme key is merged instead of replaced', () => { - const userConfig = { - theme: { - screens: { - mobile: '400px', - }, +test('theme key is merged instead of replaced', () => { + const userConfig = { + theme: { + screens: { + mobile: '400px', }, - } + }, + } - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - 'grey-darker': '#606f7b', - 'grey-dark': '#8795a1', - grey: '#b8c2cc', - 'grey-light': '#dae1e7', - 'grey-lighter': '#f1f5f8', - }, - fonts: { - sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'], - serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'], - }, - screens: { - sm: '500px', - md: '750px', - lg: '1000px', - }, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + 'grey-darker': '#606f7b', + 'grey-dark': '#8795a1', + grey: '#b8c2cc', + 'grey-light': '#dae1e7', + 'grey-lighter': '#f1f5f8', }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - 'grey-darker': '#606f7b', - 'grey-dark': '#8795a1', - grey: '#b8c2cc', - 'grey-light': '#dae1e7', - 'grey-lighter': '#f1f5f8', - }, - fonts: { - sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'], - serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'], - }, - screens: { - mobile: '400px', - }, + fonts: { + sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'], + serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'], }, - }) + screens: { + sm: '500px', + md: '750px', + lg: '1000px', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + 'grey-darker': '#606f7b', + 'grey-dark': '#8795a1', + grey: '#b8c2cc', + 'grey-light': '#dae1e7', + 'grey-lighter': '#f1f5f8', + }, + fonts: { + sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'], + serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'], + }, + screens: { + mobile: '400px', + }, + }, }) +}) - test('theme key is deeply merged instead of replaced', () => { - const userConfig = { - theme: { - extend: { - colors: { - grey: { - darker: '#606f7b', - dark: '#8795a1', - }, - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - grey: { - grey: '#b8c2cc', - light: '#dae1e7', - lighter: '#f1f5f8', - }, - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { +test('theme key is deeply merged instead of replaced', () => { + const userConfig = { + theme: { + extend: { colors: { grey: { darker: '#606f7b', dark: '#8795a1', - grey: '#b8c2cc', - light: '#dae1e7', - lighter: '#f1f5f8', }, }, }, - }) + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + grey: { + grey: '#b8c2cc', + light: '#dae1e7', + lighter: '#f1f5f8', + }, + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + grey: { + darker: '#606f7b', + dark: '#8795a1', + grey: '#b8c2cc', + light: '#dae1e7', + lighter: '#f1f5f8', + }, + }, + }, }) +}) - test('missing top level keys are pulled from the default config', () => { - const userConfig = {} +test('missing top level keys are pulled from the default config', () => { + const userConfig = {} - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { green: '#00ff00' }, - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { green: '#00ff00' }, + screens: { + mobile: '400px', }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { green: '#00ff00' }, - screens: { - mobile: '400px', - }, + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { green: '#00ff00' }, + screens: { + mobile: '400px', }, - }) + }, }) +}) - test('functions in the default theme section are lazily evaluated', () => { - const userConfig = { - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, +test('functions in the default theme section are lazily evaluated', () => { + const userConfig = { + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', }, - } + }, + } - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - backgroundColors: (theme) => theme('colors'), - textColors: (theme) => theme('colors'), + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', }, - } + backgroundColors: (theme) => theme('colors'), + textColors: (theme) => theme('colors'), + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - backgroundColors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - textColors: { - red: 'red', - green: 'green', - blue: 'blue', - }, + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', }, - }) + backgroundColors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + textColors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + }, }) +}) - test('functions in the user theme section are lazily evaluated', () => { - const userConfig = { - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - backgroundColors: (theme) => ({ - ...theme('colors'), - customBackground: '#bada55', - }), - textColors: (theme) => ({ - ...theme('colors'), - customText: '#facade', - }), +test('functions in the user theme section are lazily evaluated', () => { + const userConfig = { + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', }, - } + backgroundColors: (theme) => ({ + ...theme('colors'), + customBackground: '#bada55', + }), + textColors: (theme) => ({ + ...theme('colors'), + customText: '#facade', + }), + }, + } - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - backgroundColors: ({ colors }) => colors, - textColors: ({ colors }) => colors, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', }, - } + backgroundColors: ({ colors }) => colors, + textColors: ({ colors }) => colors, + }, + } - const result = resolveConfig([userConfig, defaultConfig]) + const result = resolveConfig([userConfig, defaultConfig]) - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - backgroundColors: { - red: 'red', - green: 'green', - blue: 'blue', - customBackground: '#bada55', - }, - textColors: { - red: 'red', - green: 'green', - blue: 'blue', - customText: '#facade', - }, + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', }, - }) + backgroundColors: { + red: 'red', + green: 'green', + blue: 'blue', + customBackground: '#bada55', + }, + textColors: { + red: 'red', + green: 'green', + blue: 'blue', + customText: '#facade', + }, + }, }) +}) - test('theme values in the extend section extend the existing theme', () => { - const userConfig = { - theme: { - extend: { - opacity: { - 25: '25', - 75: '.75', - }, - backgroundColors: { - customBackground: '#bada55', - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, +test('theme values in the extend section extend the existing theme', () => { + const userConfig = { + theme: { + extend: { opacity: { - 0: '0', - 50: '.5', - 100: '1', - }, - backgroundColors: (theme) => theme('colors'), - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - opacity: { - 0: '0', - 50: '.5', - 100: '1', 25: '25', 75: '.75', }, backgroundColors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', customBackground: '#bada55', }, }, - }) + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + opacity: { + 0: '0', + 50: '.5', + 100: '1', + }, + backgroundColors: (theme) => theme('colors'), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + opacity: { + 0: '0', + 50: '.5', + 100: '1', + 25: '25', + 75: '.75', + }, + backgroundColors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + customBackground: '#bada55', + }, + }, }) +}) - test('theme values in the extend section extend the user theme', () => { - const userConfig = { - theme: { - opacity: { - 0: '0', - 20: '.2', - 40: '.4', - }, - height: (theme) => theme('width'), - extend: { - opacity: { - 60: '.6', - 80: '.8', - 100: '1', - }, - height: { - customHeight: '500vh', - }, - }, +test('theme values in the extend section extend the user theme', () => { + const userConfig = { + theme: { + opacity: { + 0: '0', + 20: '.2', + 40: '.4', }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { + height: (theme) => theme('width'), + extend: { opacity: { - 0: '0', - 50: '.5', - 100: '1', - }, - height: { - 0: 0, - full: '100%', - }, - width: { - 0: 0, - 1: '.25rem', - 2: '.5rem', - 3: '.75rem', - 4: '1rem', - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - opacity: { - 0: '0', - 20: '.2', - 40: '.4', 60: '.6', 80: '.8', 100: '1', }, height: { - 0: 0, - 1: '.25rem', - 2: '.5rem', - 3: '.75rem', - 4: '1rem', customHeight: '500vh', }, - width: { - 0: 0, - 1: '.25rem', - 2: '.5rem', - 3: '.75rem', - 4: '1rem', - }, }, - }) + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + opacity: { + 0: '0', + 50: '.5', + 100: '1', + }, + height: { + 0: 0, + full: '100%', + }, + width: { + 0: 0, + 1: '.25rem', + 2: '.5rem', + 3: '.75rem', + 4: '1rem', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + opacity: { + 0: '0', + 20: '.2', + 40: '.4', + 60: '.6', + 80: '.8', + 100: '1', + }, + height: { + 0: 0, + 1: '.25rem', + 2: '.5rem', + 3: '.75rem', + 4: '1rem', + customHeight: '500vh', + }, + width: { + 0: 0, + 1: '.25rem', + 2: '.5rem', + 3: '.75rem', + 4: '1rem', + }, + }, }) +}) - test('theme values in the extend section can extend values that are depended on lazily', () => { - const userConfig = { - theme: { - extend: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - backgroundColors: { - customBackground: '#bada55', - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { +test('theme values in the extend section can extend values that are depended on lazily', () => { + const userConfig = { + theme: { + extend: { colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - backgroundColors: (theme) => theme('colors'), - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', red: 'red', green: 'green', blue: 'blue', }, backgroundColors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - red: 'red', - green: 'green', - blue: 'blue', customBackground: '#bada55', }, }, - }) + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + backgroundColors: (theme) => theme('colors'), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + red: 'red', + green: 'green', + blue: 'blue', + }, + backgroundColors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + red: 'red', + green: 'green', + blue: 'blue', + customBackground: '#bada55', + }, + }, }) +}) - test('theme values in the extend section are not deeply merged when they are simple arrays', () => { - const userConfig = { - theme: { - extend: { - fonts: { - sans: ['Comic Sans'], - serif: ['Papyrus', { fontFeatureSettings: '"cv11"' }], - mono: [['Lobster', 'Papyrus'], { fontFeatureSettings: '"cv11"' }], - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - fonts: { - sans: ['system-ui', 'Helvetica Neue', 'sans-serif'], - serif: ['Constantia', 'Georgia', 'serif'], - mono: ['Menlo', 'Courier New', 'monospace'], - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { +test('theme values in the extend section are not deeply merged when they are simple arrays', () => { + const userConfig = { + theme: { + extend: { fonts: { sans: ['Comic Sans'], serif: ['Papyrus', { fontFeatureSettings: '"cv11"' }], mono: [['Lobster', 'Papyrus'], { fontFeatureSettings: '"cv11"' }], }, }, - }) - }) + }, + } - test('theme values in the extend section are deeply merged, when they are arrays of objects', () => { - const userConfig = { - theme: { - extend: { - typography: { - ArrayArray: { - css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }], - }, - ObjectArray: { - css: { a: { backgroundColor: 'red' } }, - }, - ArrayObject: { - css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }], - }, - }, - }, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + fonts: { + sans: ['system-ui', 'Helvetica Neue', 'sans-serif'], + serif: ['Constantia', 'Georgia', 'serif'], + mono: ['Menlo', 'Courier New', 'monospace'], }, - } + }, + } - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + fonts: { + sans: ['Comic Sans'], + serif: ['Papyrus', { fontFeatureSettings: '"cv11"' }], + mono: [['Lobster', 'Papyrus'], { fontFeatureSettings: '"cv11"' }], + }, + }, + }) +}) + +test('theme values in the extend section are deeply merged, when they are arrays of objects', () => { + const userConfig = { + theme: { + extend: { typography: { ArrayArray: { - css: [{ a: { underline: 'none' } }], + css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }], }, ObjectArray: { - css: [{ a: { underline: 'none' } }], + css: { a: { backgroundColor: 'red' } }, }, ArrayObject: { - css: { a: { underline: 'none' } }, + css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }], }, }, }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - typography: { - ArrayArray: { - css: [ - { a: { underline: 'none' } }, - { a: { backgroundColor: 'red' } }, - { a: { color: 'green' } }, - ], - }, - ObjectArray: { - css: [{ a: { underline: 'none' } }, { a: { backgroundColor: 'red' } }], - }, - ArrayObject: { - css: [ - { a: { underline: 'none' } }, - { a: { backgroundColor: 'red' } }, - { a: { color: 'green' } }, - ], - }, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + typography: { + ArrayArray: { + css: [{ a: { underline: 'none' } }], + }, + ObjectArray: { + css: [{ a: { underline: 'none' } }], + }, + ArrayObject: { + css: { a: { underline: 'none' } }, }, }, - }) + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + typography: { + ArrayArray: { + css: [ + { a: { underline: 'none' } }, + { a: { backgroundColor: 'red' } }, + { a: { color: 'green' } }, + ], + }, + ObjectArray: { + css: [{ a: { underline: 'none' } }, { a: { backgroundColor: 'red' } }], + }, + ArrayObject: { + css: [ + { a: { underline: 'none' } }, + { a: { backgroundColor: 'red' } }, + { a: { color: 'green' } }, + ], + }, + }, + }, }) +}) - test('the theme function can use a default value if the key is missing', () => { - const userConfig = { - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, +test('the theme function can use a default value if the key is missing', () => { + const userConfig = { + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', }, - } + }, + } - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + borderColor: (theme) => ({ + default: theme('colors.gray', 'currentColor'), + ...theme('colors'), + }), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + borderColor: { + default: 'currentColor', + red: 'red', + green: 'green', + blue: 'blue', + }, + }, + }) +}) + +test('the theme function can resolve function values', () => { + const userConfig = { + theme: { + textColor: (theme) => ({ + lime: 'lime', + ...theme('colors'), + }), + backgroundColor: (theme) => ({ + orange: 'orange', + ...theme('textColor'), + }), + borderColor: (theme) => theme('backgroundColor'), + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + textColor: { + lime: 'lime', + red: 'red', + green: 'green', + blue: 'blue', + }, + backgroundColor: { + lime: 'lime', + orange: 'orange', + red: 'red', + green: 'green', + blue: 'blue', + }, + borderColor: { + lime: 'lime', + orange: 'orange', + red: 'red', + green: 'green', + blue: 'blue', + }, + }, + }) +}) + +test('the theme function can resolve deep function values', () => { + const userConfig = { + theme: { + minWidth: (theme) => ({ + '1/3': theme('width.1/3'), + }), + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + spacing: { + 0: '0', + }, + width: (theme) => ({ + ...theme('spacing'), + '1/3': '33.33333%', + }), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + spacing: { + 0: '0', + }, + width: { + 0: '0', + '1/3': '33.33333%', + }, + minWidth: { + '1/3': '33.33333%', + }, + }, + }) +}) + +test('theme values in the extend section are lazily evaluated', () => { + const userConfig = { + theme: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + extend: { colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', + orange: 'orange', }, borderColor: (theme) => ({ - default: theme('colors.gray', 'currentColor'), - ...theme('colors'), + foo: theme('colors.orange'), + bar: theme('colors.red'), }), }, - } + }, + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - borderColor: { - default: 'currentColor', - red: 'red', - green: 'green', - blue: 'blue', - }, + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', }, - }) + borderColor: (theme) => ({ + default: theme('colors.yellow', 'currentColor'), + ...theme('colors'), + }), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + orange: 'orange', + red: 'red', + green: 'green', + blue: 'blue', + }, + borderColor: { + default: 'currentColor', + foo: 'orange', + bar: 'red', + orange: 'orange', + red: 'red', + green: 'green', + blue: 'blue', + }, + }, }) +}) - test('the theme function can resolve function values', () => { - const userConfig = { - theme: { - textColor: (theme) => ({ - lime: 'lime', - ...theme('colors'), +test('lazily evaluated values have access to the config utils', () => { + const userConfig = { + theme: { + inset: (theme) => theme('margin'), + shift: (theme, { negative }) => ({ + ...theme('spacing'), + ...negative(theme('spacing')), + }), + extend: { + nudge: (theme, { negative }) => ({ + ...theme('spacing'), + ...negative(theme('spacing')), }), - backgroundColor: (theme) => ({ + }, + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + spacing: { + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + margin: (theme, { negative }) => ({ + ...theme('spacing'), + ...negative(theme('spacing')), + }), + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + spacing: { + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + inset: { + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + margin: { + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + shift: { + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + nudge: { + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + }, + }) +}) + +test('the original theme is not mutated', () => { + const userConfig = { + theme: { + extend: { + colors: { orange: 'orange', - ...theme('textColor'), + }, + }, + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + }, + } + + resolveConfig([userConfig, defaultConfig]) + + expect(userConfig).toEqual({ + theme: { + extend: { + colors: { + orange: 'orange', + }, + }, + }, + }) +}) + +test('custom properties are multiplied by -1 for negative values', () => { + const userConfig = { + theme: { + spacing: { + 0: 0, + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + auto: 'auto', + foo: 'var(--foo)', + bar: 'var(--bar, 500px)', + baz: 'calc(50% - 10px)', + qux: '10poops', + }, + margin: (theme, { negative }) => ({ + ...theme('spacing'), + ...negative(theme('spacing')), + }), + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: {}, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result.theme.spacing).toEqual({ + 0: 0, + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + auto: 'auto', + foo: 'var(--foo)', + bar: 'var(--bar, 500px)', + baz: 'calc(50% - 10px)', + qux: '10poops', + }) + expect(result.theme.margin).toEqual({ + 0: 0, + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + auto: 'auto', + foo: 'var(--foo)', + bar: 'var(--bar, 500px)', + baz: 'calc(50% - 10px)', + qux: '10poops', + '-0': '0', + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + '-foo': 'calc(var(--foo) * -1)', + '-bar': 'calc(var(--bar, 500px) * -1)', + '-baz': 'calc(calc(50% - 10px) * -1)', + '-qux': '-10poops', + }) +}) + +test('more than two config objects can be resolved', () => { + const firstConfig = { + theme: { + extend: { + fontFamily: () => ({ + code: ['Menlo', 'monospace'], }), - borderColor: (theme) => theme('backgroundColor'), - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { colors: { red: 'red', - green: 'green', - blue: 'blue', - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - textColor: { - lime: 'lime', - red: 'red', - green: 'green', - blue: 'blue', }, backgroundColor: { - lime: 'lime', + customBackgroundOne: '#bada55', + }, + textDecorationColor: { orange: 'orange', - red: 'red', - green: 'green', - blue: 'blue', - }, - borderColor: { - lime: 'lime', - orange: 'orange', - red: 'red', - green: 'green', - blue: 'blue', }, }, - }) - }) + }, + } - test('the theme function can resolve deep function values', () => { - const userConfig = { - theme: { - minWidth: (theme) => ({ - '1/3': theme('width.1/3'), - }), - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - spacing: { - 0: '0', - }, - width: (theme) => ({ - ...theme('spacing'), - '1/3': '33.33333%', - }), - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - spacing: { - 0: '0', - }, - width: { - 0: '0', - '1/3': '33.33333%', - }, - minWidth: { - '1/3': '33.33333%', - }, - }, - }) - }) - - test('theme values in the extend section are lazily evaluated', () => { - const userConfig = { - theme: { - colors: { - red: 'red', - green: 'green', - blue: 'blue', - }, - extend: { - colors: { - orange: 'orange', - }, - borderColor: (theme) => ({ - foo: theme('colors.orange'), - bar: theme('colors.red'), - }), - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - borderColor: (theme) => ({ - default: theme('colors.yellow', 'currentColor'), - ...theme('colors'), - }), - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - colors: { - orange: 'orange', - red: 'red', - green: 'green', - blue: 'blue', - }, - borderColor: { - default: 'currentColor', - foo: 'orange', - bar: 'red', - orange: 'orange', - red: 'red', - green: 'green', - blue: 'blue', - }, - }, - }) - }) - - test('lazily evaluated values have access to the config utils', () => { - const userConfig = { - theme: { - inset: (theme) => theme('margin'), - shift: (theme, { negative }) => ({ - ...theme('spacing'), - ...negative(theme('spacing')), - }), - extend: { - nudge: (theme, { negative }) => ({ - ...theme('spacing'), - ...negative(theme('spacing')), - }), - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - spacing: { - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - margin: (theme, { negative }) => ({ - ...theme('spacing'), - ...negative(theme('spacing')), - }), - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - spacing: { - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - inset: { - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - margin: { - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - shift: { - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - nudge: { - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - }, - }) - }) - - test('the original theme is not mutated', () => { - const userConfig = { - theme: { - extend: { - colors: { - orange: 'orange', - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - colors: { - cyan: 'cyan', - magenta: 'magenta', - yellow: 'yellow', - }, - }, - } - - resolveConfig([userConfig, defaultConfig]) - - expect(userConfig).toEqual({ - theme: { - extend: { - colors: { - orange: 'orange', - }, - }, - }, - }) - }) - - test('custom properties are multiplied by -1 for negative values', () => { - const userConfig = { - theme: { - spacing: { - 0: 0, - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - auto: 'auto', - foo: 'var(--foo)', - bar: 'var(--bar, 500px)', - baz: 'calc(50% - 10px)', - qux: '10poops', - }, - margin: (theme, { negative }) => ({ - ...theme('spacing'), - ...negative(theme('spacing')), - }), - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: {}, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result.theme.spacing).toEqual({ - 0: 0, - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - auto: 'auto', - foo: 'var(--foo)', - bar: 'var(--bar, 500px)', - baz: 'calc(50% - 10px)', - qux: '10poops', - }) - expect(result.theme.margin).toEqual({ - 0: 0, - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - auto: 'auto', - foo: 'var(--foo)', - bar: 'var(--bar, 500px)', - baz: 'calc(50% - 10px)', - qux: '10poops', - '-0': '0', - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - '-foo': 'calc(var(--foo) * -1)', - '-bar': 'calc(var(--bar, 500px) * -1)', - '-baz': 'calc(calc(50% - 10px) * -1)', - '-qux': '-10poops', - }) - }) - - test('more than two config objects can be resolved', () => { - const firstConfig = { - theme: { - extend: { - fontFamily: () => ({ - code: ['Menlo', 'monospace'], - }), - colors: { - red: 'red', - }, - backgroundColor: { - customBackgroundOne: '#bada55', - }, - textDecorationColor: { - orange: 'orange', - }, - }, - }, - } - - const secondConfig = { - prefix: '-', - important: false, - separator: ':', - theme: { - extend: { - fontFamily: { - quote: ['Helvetica', 'serif'], - }, - colors: { - green: 'green', - }, - backgroundColor: { - customBackgroundTwo: '#facade', - }, - textDecorationColor: (theme) => theme('colors'), - }, - }, - } - - const thirdConfig = { - prefix: '-', - important: false, - separator: ':', - theme: { - extend: { - fontFamily: { - hero: ['Futura', 'sans-serif'], - }, - colors: { - pink: 'pink', - }, - backgroundColor: () => ({ - customBackgroundThree: '#c0ffee', - }), - textDecorationColor: { - lime: 'lime', - }, - }, - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { + const secondConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + extend: { fontFamily: { - body: ['Arial', 'sans-serif'], - display: ['Georgia', 'serif'], - }, - colors: { - blue: 'blue', - }, - backgroundColor: (theme) => theme('colors'), - }, - } - - const result = resolveConfig([firstConfig, secondConfig, thirdConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - fontFamily: { - body: ['Arial', 'sans-serif'], - display: ['Georgia', 'serif'], - code: ['Menlo', 'monospace'], quote: ['Helvetica', 'serif'], + }, + colors: { + green: 'green', + }, + backgroundColor: { + customBackgroundTwo: '#facade', + }, + textDecorationColor: (theme) => theme('colors'), + }, + }, + } + + const thirdConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + extend: { + fontFamily: { hero: ['Futura', 'sans-serif'], }, colors: { - red: 'red', - green: 'green', - blue: 'blue', pink: 'pink', }, - backgroundColor: { - red: 'red', - green: 'green', - blue: 'blue', - pink: 'pink', - customBackgroundOne: '#bada55', - customBackgroundTwo: '#facade', + backgroundColor: () => ({ customBackgroundThree: '#c0ffee', - }, + }), textDecorationColor: { - red: 'red', - green: 'green', - blue: 'blue', - pink: 'pink', - orange: 'orange', lime: 'lime', }, }, - }) + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + fontFamily: { + body: ['Arial', 'sans-serif'], + display: ['Georgia', 'serif'], + }, + colors: { + blue: 'blue', + }, + backgroundColor: (theme) => theme('colors'), + }, + } + + const result = resolveConfig([firstConfig, secondConfig, thirdConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + fontFamily: { + body: ['Arial', 'sans-serif'], + display: ['Georgia', 'serif'], + code: ['Menlo', 'monospace'], + quote: ['Helvetica', 'serif'], + hero: ['Futura', 'sans-serif'], + }, + colors: { + red: 'red', + green: 'green', + blue: 'blue', + pink: 'pink', + }, + backgroundColor: { + red: 'red', + green: 'green', + blue: 'blue', + pink: 'pink', + customBackgroundOne: '#bada55', + customBackgroundTwo: '#facade', + customBackgroundThree: '#c0ffee', + }, + textDecorationColor: { + red: 'red', + green: 'green', + blue: 'blue', + pink: 'pink', + orange: 'orange', + lime: 'lime', + }, + }, }) +}) - test('plugin config modifications are applied', () => { - const userConfig = { - plugins: [ - { - config: { - prefix: 'tw-', - }, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', +test('plugin config modifications are applied', () => { + const userConfig = { + plugins: [ + { + config: { + prefix: 'tw-', }, }, - } + ], + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: 'tw-', - important: false, - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - plugins: userConfig.plugins, - }) + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: 'tw-', + important: false, + separator: ':', + theme: { + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('user config takes precedence over plugin config modifications', () => { - const userConfig = { - prefix: 'user-', - plugins: [ - { - config: { - prefix: 'tw-', - }, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', +test('user config takes precedence over plugin config modifications', () => { + const userConfig = { + prefix: 'user-', + plugins: [ + { + config: { + prefix: 'tw-', }, }, - } + ], + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: 'user-', - important: false, - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - plugins: userConfig.plugins, - }) + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: 'user-', + important: false, + separator: ':', + theme: { + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('plugin config can register plugins that also have config', () => { - const userConfig = { - plugins: [ - { - config: { - prefix: 'tw-', - plugins: [ - { - config: { - important: true, - }, +test('plugin config can register plugins that also have config', () => { + const userConfig = { + plugins: [ + { + config: { + prefix: 'tw-', + plugins: [ + { + config: { + important: true, }, - { - config: { - separator: '__', - }, + }, + { + config: { + separator: '__', }, - ], - }, - handler() {}, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', + }, + ], }, + handler() {}, }, - } + ], + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: 'tw-', - important: true, - separator: '__', - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - plugins: userConfig.plugins, - }) + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: 'tw-', + important: true, + separator: '__', + theme: { + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('plugin configs take precedence over plugin configs registered by that plugin', () => { - const userConfig = { - plugins: [ - { - config: { - prefix: 'outer-', - plugins: [ - { - config: { - prefix: 'inner-', - }, +test('plugin configs take precedence over plugin configs registered by that plugin', () => { + const userConfig = { + plugins: [ + { + config: { + prefix: 'outer-', + plugins: [ + { + config: { + prefix: 'inner-', }, - ], - }, - handler() {}, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - screens: { - mobile: '400px', + }, + ], }, + handler() {}, }, - } + ], + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: 'outer-', - important: false, - separator: ':', - theme: { - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + screens: { + mobile: '400px', }, - plugins: userConfig.plugins, - }) + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: 'outer-', + important: false, + separator: ':', + theme: { + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('plugin theme extensions are added even if user overrides top-level theme config', () => { - const userConfig = { - theme: { - width: { - '1px': '1px', - }, +test('plugin theme extensions are added even if user overrides top-level theme config', () => { + const userConfig = { + theme: { + width: { + '1px': '1px', }, - plugins: [ - { - config: { - theme: { - extend: { - width: { - '2px': '2px', - '3px': '3px', - }, + }, + plugins: [ + { + config: { + theme: { + extend: { + width: { + '2px': '2px', + '3px': '3px', }, }, }, - handler() {}, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { - width: { - sm: '1rem', - md: '2rem', - lg: '3rem', - }, - screens: { - mobile: '400px', }, + handler() {}, }, - } + ], + } - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: ':', - theme: { - width: { - '1px': '1px', - '2px': '2px', - '3px': '3px', - }, - screens: { - mobile: '400px', - }, + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + width: { + sm: '1rem', + md: '2rem', + lg: '3rem', }, - plugins: userConfig.plugins, - }) + screens: { + mobile: '400px', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: ':', + theme: { + width: { + '1px': '1px', + '2px': '2px', + '3px': '3px', + }, + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('user theme extensions take precedence over plugin theme extensions with the same key', () => { - const userConfig = { - theme: { - extend: { - width: { - xl: '6rem', - }, - }, - }, - plugins: [ - { - config: { - theme: { - extend: { - width: { - xl: '4rem', - }, - }, - }, - }, - handler() {}, - }, - ], - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: { +test('user theme extensions take precedence over plugin theme extensions with the same key', () => { + const userConfig = { + theme: { + extend: { width: { - sm: '1rem', - md: '2rem', - lg: '3rem', - }, - screens: { - mobile: '400px', - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: ':', - theme: { - width: { - sm: '1rem', - md: '2rem', - lg: '3rem', xl: '6rem', }, - screens: { - mobile: '400px', - }, }, - plugins: userConfig.plugins, - }) + }, + plugins: [ + { + config: { + theme: { + extend: { + width: { + xl: '4rem', + }, + }, + }, + }, + handler() {}, + }, + ], + } + + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: { + width: { + sm: '1rem', + md: '2rem', + lg: '3rem', + }, + screens: { + mobile: '400px', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: ':', + theme: { + width: { + sm: '1rem', + md: '2rem', + lg: '3rem', + xl: '6rem', + }, + screens: { + mobile: '400px', + }, + }, + plugins: userConfig.plugins, }) +}) - test('extensions are applied in the right order', () => { - const userConfig = { - theme: { - extend: { - colors: { - grey: { - light: '#eee', - }, - }, - }, - }, - } - - const otherConfig = { - theme: { - extend: { - colors: { - grey: { - light: '#ddd', - darker: '#111', - }, - }, - }, - }, - } - - const anotherConfig = { - theme: { - extend: { - colors: { - grey: { - darker: '#222', - }, - }, - }, - }, - } - - const defaultConfig = { - content: [], - theme: { - colors: { - grey: { - light: '#ccc', - dark: '#333', - }, - }, - }, - } - - const result = resolveConfig([userConfig, otherConfig, anotherConfig, defaultConfig]) - - expect(result).toMatchObject({ - theme: { +test('extensions are applied in the right order', () => { + const userConfig = { + theme: { + extend: { colors: { grey: { light: '#eee', - dark: '#333', + }, + }, + }, + }, + } + + const otherConfig = { + theme: { + extend: { + colors: { + grey: { + light: '#ddd', darker: '#111', }, }, }, - }) - }) + }, + } - test('core plugin configuration builds on the default list when starting with an empty object', () => { - const userConfig = { - corePlugins: { display: false }, - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: {}, - corePlugins: {}, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: ':', - theme: {}, - corePlugins: corePluginList.filter((c) => c !== 'display'), - }) - }) - - test('core plugins that are disabled by default can be enabled', () => { - const userConfig = { - corePlugins: { display: true }, - } - - const defaultConfig = { - presets: [], - prefix: '', - important: false, - separator: ':', - content: [], - theme: {}, - corePlugins: { display: false }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - expect(result.corePlugins).toContain('display') - }) - - test('core plugin configurations stack', () => { - const userConfig = { - corePlugins: { display: false }, - } - - const otherConfig = { - corePlugins: ({ corePlugins }) => { - return [...corePlugins, 'margin'] - }, - } - - const defaultConfig = { - prefix: '', - important: false, - separator: ':', - content: [], - theme: {}, - corePlugins: ['float', 'display', 'padding'], - } - - const result = resolveConfig([userConfig, otherConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: ':', - theme: {}, - corePlugins: ['float', 'padding', 'margin'], - }) - }) - - test('plugins are merged', () => { - const userConfig = { - plugins: ['3'], - } - - const otherConfig = { - plugins: ['2'], - } - - const defaultConfig = { - plugins: ['1'], - prefix: '', - important: false, - separator: ':', - content: [], - theme: {}, - } - - const result = resolveConfig([userConfig, otherConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '', - important: false, - separator: ':', - theme: {}, - plugins: ['1', '2', '3'], - }) - }) - - test('all helpers can be destructured from the first function argument', () => { - const userConfig = { - theme: { - example: ({ theme, colors, negative, breakpoints }) => ({ - weight: theme('fontWeight.bold'), - black: colors.black, - white: colors.white, - ...negative(theme('spacing')), - ...breakpoints(theme('screens')), - }), - }, - } - - const defaultConfig = { - prefix: '-', - important: false, - separator: ':', - content: [], - theme: { - screens: { - sm: '640px', - md: '768px', - }, - fontWeight: { - bold: 700, - }, - spacing: { - 0: '0px', - 1: '1px', - 2: '2px', - 3: '3px', - 4: '4px', - }, - }, - } - - const result = resolveConfig([userConfig, defaultConfig]) - - expect(result).toMatchObject({ - prefix: '-', - important: false, - separator: ':', - theme: { - example: { - weight: 700, - black: '#000', - white: '#fff', - '-1': '-1px', - '-2': '-2px', - '-3': '-3px', - '-4': '-4px', - 'screen-sm': '640px', - 'screen-md': '768px', - }, - }, - }) - }) - - test('does not duplicate extended configs every time resolveConfig is called', () => { - let shared = { - foo: { bar: { baz: [{ color: 'red' }] } }, - } - - const createConfig = (color) => - resolveConfig([ - { - theme: { - foo: shared.foo, - extend: { - foo: { bar: { baz: { color } } }, - }, + const anotherConfig = { + theme: { + extend: { + colors: { + grey: { + darker: '#222', }, }, - ]) + }, + }, + } - createConfig('orange') - createConfig('yellow') - createConfig('green') + const defaultConfig = { + content: [], + theme: { + colors: { + grey: { + light: '#ccc', + dark: '#333', + }, + }, + }, + } - const result = createConfig('blue') + const result = resolveConfig([userConfig, otherConfig, anotherConfig, defaultConfig]) - expect(shared.foo.bar.baz).toMatchObject([{ color: 'red' }]) - expect(result.theme.foo.bar.baz).toMatchObject([{ color: 'red' }, { color: 'blue' }]) + expect(result).toMatchObject({ + theme: { + colors: { + grey: { + light: '#eee', + dark: '#333', + darker: '#111', + }, + }, + }, }) }) + +test('core plugin configuration builds on the default list when starting with an empty object', () => { + const userConfig = { + corePlugins: { display: false }, + } + + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: {}, + corePlugins: {}, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: ':', + theme: {}, + corePlugins: corePluginList.filter((c) => c !== 'display'), + }) +}) + +test('core plugins that are disabled by default can be enabled', () => { + const userConfig = { + corePlugins: { display: true }, + } + + const defaultConfig = { + presets: [], + prefix: '', + important: false, + separator: ':', + content: [], + theme: {}, + corePlugins: { display: false }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + expect(result.corePlugins).toContain('display') +}) + +test('core plugin configurations stack', () => { + const userConfig = { + corePlugins: { display: false }, + } + + const otherConfig = { + corePlugins: ({ corePlugins }) => { + return [...corePlugins, 'margin'] + }, + } + + const defaultConfig = { + prefix: '', + important: false, + separator: ':', + content: [], + theme: {}, + corePlugins: ['float', 'display', 'padding'], + } + + const result = resolveConfig([userConfig, otherConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: ':', + theme: {}, + corePlugins: ['float', 'padding', 'margin'], + }) +}) + +test('plugins are merged', () => { + const userConfig = { + plugins: ['3'], + } + + const otherConfig = { + plugins: ['2'], + } + + const defaultConfig = { + plugins: ['1'], + prefix: '', + important: false, + separator: ':', + content: [], + theme: {}, + } + + const result = resolveConfig([userConfig, otherConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '', + important: false, + separator: ':', + theme: {}, + plugins: ['1', '2', '3'], + }) +}) + +test('all helpers can be destructured from the first function argument', () => { + const userConfig = { + theme: { + example: ({ theme, colors, negative, breakpoints }) => ({ + weight: theme('fontWeight.bold'), + black: colors.black, + white: colors.white, + ...negative(theme('spacing')), + ...breakpoints(theme('screens')), + }), + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + content: [], + theme: { + screens: { + sm: '640px', + md: '768px', + }, + fontWeight: { + bold: 700, + }, + spacing: { + 0: '0px', + 1: '1px', + 2: '2px', + 3: '3px', + 4: '4px', + }, + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toMatchObject({ + prefix: '-', + important: false, + separator: ':', + theme: { + example: { + weight: 700, + black: '#000', + white: '#fff', + '-1': '-1px', + '-2': '-2px', + '-3': '-3px', + '-4': '-4px', + 'screen-sm': '640px', + 'screen-md': '768px', + }, + }, + }) +}) + +test('does not duplicate extended configs every time resolveConfig is called', () => { + let shared = { + foo: { bar: { baz: [{ color: 'red' }] } }, + } + + const createConfig = (color) => + resolveConfig([ + { + theme: { + foo: shared.foo, + extend: { + foo: { bar: { baz: { color } } }, + }, + }, + }, + ]) + + createConfig('orange') + createConfig('yellow') + createConfig('green') + + const result = createConfig('blue') + + expect(shared.foo.bar.baz).toMatchObject([{ color: 'red' }]) + expect(result.theme.foo.bar.baz).toMatchObject([{ color: 'red' }, { color: 'blue' }]) +}) diff --git a/tests/responsive-and-variants-atrules.test.js b/tests/responsive-and-variants-atrules.test.js index 9d9ed5d85..4d4a0f507 100644 --- a/tests/responsive-and-variants-atrules.test.js +++ b/tests/responsive-and-variants-atrules.test.js @@ -1,157 +1,155 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('responsive and variants atrules', () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } +test('responsive and variants atrules', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind components; - @tailwind utilities; - - @layer utilities { - @responsive { - .responsive-in-utilities { - color: blue; - } - } - @variants { - .variants-in-utilities { - color: red; - } - } - @responsive { - @variants { - .both-in-utilities { - color: green; - } - } - } - } + let input = css` + @tailwind components; + @tailwind utilities; + @layer utilities { @responsive { - .responsive-at-root { - color: white; + .responsive-in-utilities { + color: blue; } } @variants { - .variants-at-root { - color: orange; + .variants-in-utilities { + color: red; } } @responsive { @variants { - .both-at-root { - color: pink; + .both-in-utilities { + color: green; } } } + } - @layer components { - @responsive { - .responsive-in-components { - color: blue; - } - } - @variants { - .variants-in-components { - color: red; - } - } - @responsive { - @variants { - .both-in-components { - color: green; - } - } - } + @responsive { + .responsive-at-root { + color: white; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .responsive-in-components { - color: #00f; - } - .variants-in-components { - color: red; - } - .both-in-components { - color: green; - } - .responsive-in-utilities { - color: #00f; - } - .variants-in-utilities { - color: red; - } - .both-in-utilities { - color: green; - } - .responsive-at-root { - color: #fff; - } - .variants-at-root { - color: orange; - } + } + @variants { + .variants-at-root { + color: orange; + } + } + @responsive { + @variants { .both-at-root { color: pink; } - @media (min-width: 768px) { - .md\:focus\:responsive-in-components:focus { - color: #00f; - } - .md\:focus\:variants-in-components:focus { - color: red; - } - .md\:focus\:both-in-components:focus { + } + } + + @layer components { + @responsive { + .responsive-in-components { + color: blue; + } + } + @variants { + .variants-in-components { + color: red; + } + } + @responsive { + @variants { + .both-in-components { color: green; } - .md\:focus\:responsive-in-utilities:focus { - color: #00f; - } - .md\:focus\:variants-in-utilities:focus { - color: red; - } - .md\:focus\:both-in-utilities:focus { - color: green; - } - .md\:focus\:responsive-at-root:focus { - color: #fff; - } - .md\:focus\:variants-at-root:focus { - color: orange; - } - .md\:focus\:both-at-root:focus { - color: pink; - } } - `) - }) + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .responsive-in-components { + color: #00f; + } + .variants-in-components { + color: red; + } + .both-in-components { + color: green; + } + .responsive-in-utilities { + color: #00f; + } + .variants-in-utilities { + color: red; + } + .both-in-utilities { + color: green; + } + .responsive-at-root { + color: #fff; + } + .variants-at-root { + color: orange; + } + .both-at-root { + color: pink; + } + @media (min-width: 768px) { + .md\:focus\:responsive-in-components:focus { + color: #00f; + } + .md\:focus\:variants-in-components:focus { + color: red; + } + .md\:focus\:both-in-components:focus { + color: green; + } + .md\:focus\:responsive-in-utilities:focus { + color: #00f; + } + .md\:focus\:variants-in-utilities:focus { + color: red; + } + .md\:focus\:both-in-utilities:focus { + color: green; + } + .md\:focus\:responsive-at-root:focus { + color: #fff; + } + .md\:focus\:variants-at-root:focus { + color: orange; + } + .md\:focus\:both-at-root:focus { + color: pink; + } + } + `) }) }) diff --git a/tests/safelist.test.js b/tests/safelist.test.js index cbbd1e245..e2621f846 100644 --- a/tests/safelist.test.js +++ b/tests/safelist.test.js @@ -1,636 +1,491 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ stable, oxide }) => { - it('should not safelist anything', () => { - let config = { - content: [{ raw: html`
` }], - } +it('should not safelist anything', () => { + let config = { + content: [{ raw: html`
` }], + } - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should safelist strings', () => { - let config = { - content: [{ raw: html`
` }], - safelist: ['mt-[20px]', 'font-bold', 'text-gray-200', 'hover:underline'], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .mt-\[20px\] { - margin-top: 20px; - } - .font-bold { - font-weight: 700; - } - .uppercase { - text-transform: uppercase; - } - .text-gray-200 { - --tw-text-opacity: 1; - color: rgb(229 231 235 / var(--tw-text-opacity)); - } - .hover\:underline:hover { - text-decoration-line: underline; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .mt-\[20px\] { - margin-top: 20px; - } - .font-bold { - font-weight: 700; - } - .uppercase { - text-transform: uppercase; - } - .text-gray-200 { - color: #e5e7eb; - } - .hover\:underline:hover { - text-decoration-line: underline; - } - `) - }) - }) - - it('should safelist based on a pattern regex', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^bg-(red)-(100|200)$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - .uppercase { - text-transform: uppercase; - } - .hover\:bg-red-100:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .hover\:bg-red-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - background-color: #fee2e2; - } - .bg-red-200 { - background-color: #fecaca; - } - .uppercase { - text-transform: uppercase; - } - .hover\:bg-red-100:hover { - background-color: #fee2e2; - } - .hover\:bg-red-200:hover { - background-color: #fecaca; - } - `) - }) - }) - - it('should not generate duplicates', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - 'uppercase', - { - pattern: /^bg-(red)-(100|200)$/, - variants: ['hover'], - }, - { - pattern: /^bg-(red)-(100|200)$/, - variants: ['hover'], - }, - { - pattern: /^bg-(red)-(100|200)$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - .uppercase { - text-transform: uppercase; - } - .hover\:bg-red-100:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .hover\:bg-red-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - background-color: #fee2e2; - } - .bg-red-200 { - background-color: #fecaca; - } - .uppercase { - text-transform: uppercase; - } - .hover\:bg-red-100:hover { - background-color: #fee2e2; - } - .hover\:bg-red-200:hover { - background-color: #fecaca; - } - `) - }) - }) - - it('should safelist when using a custom prefix', () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^tw-bg-red-(100|200)$/g, - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .tw-bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .tw-bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - .tw-uppercase { - text-transform: uppercase; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .tw-bg-red-100 { - background-color: #fee2e2; - } - .tw-bg-red-200 { - background-color: #fecaca; - } - .tw-uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should not safelist when an empty list is provided', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should not safelist when an sparse/holey list is provided', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [, , ,], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should not safelist any invalid variants if provided', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^bg-(red)-(100|200)$/, - variants: ['foo', 'bar'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - .bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - .uppercase { - text-transform: uppercase; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .bg-red-100 { - background-color: #fee2e2; - } - .bg-red-200 { - background-color: #fecaca; - } - .uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should safelist negatives based on a pattern regex', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^-top-1$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .-top-1 { - top: -0.25rem; - } - .uppercase { - text-transform: uppercase; - } - .hover\:-top-1:hover { - top: -0.25rem; - } - `) - }) - }) - - it('should safelist negatives based on a pattern regex', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^bg-red-(400|500)(\/(40|50))?$/, - variants: ['hover'], - }, - { - pattern: /^(fill|ring|text)-red-200\/50$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .bg-red-400 { - --tw-bg-opacity: 1; - background-color: rgb(248 113 113 / var(--tw-bg-opacity)); - } - .bg-red-400\/40 { - background-color: #f8717166; - } - .bg-red-400\/50 { - background-color: #f8717180; - } - .bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .bg-red-500\/40 { - background-color: #ef444466; - } - .bg-red-500\/50 { - background-color: #ef444480; - } - .fill-red-200\/50 { - fill: #fecaca80; - } - .uppercase { - text-transform: uppercase; - } - .text-red-200\/50 { - color: #fecaca80; - } - .ring-red-200\/50 { - --tw-ring-color: #fecaca80; - } - .hover\:bg-red-400:hover { - --tw-bg-opacity: 1; - background-color: rgb(248 113 113 / var(--tw-bg-opacity)); - } - .hover\:bg-red-400\/40:hover { - background-color: #f8717166; - } - .hover\:bg-red-400\/50:hover { - background-color: #f8717180; - } - .hover\:bg-red-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); - } - .hover\:bg-red-500\/40:hover { - background-color: #ef444466; - } - .hover\:bg-red-500\/50:hover { - background-color: #ef444480; - } - .hover\:fill-red-200\/50:hover { - fill: #fecaca80; - } - .hover\:text-red-200\/50:hover { - color: #fecaca80; - } - .hover\:ring-red-200\/50:hover { - --tw-ring-color: #fecaca80; - } - `) - }) - }) - - it('should safelist pattern regex with !important selector', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [{ pattern: /^!grid-cols-(4|5|6)$/ }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .\!grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)) !important; - } - .\!grid-cols-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)) !important; - } - .\!grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)) !important; - } - .uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should safelist pattern regex with custom prefix along with !important selector', () => { - let config = { - prefix: 'tw-', - content: [{ raw: html`
` }], - safelist: [{ pattern: /^!tw-grid-cols-(4|5|6)$/ }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .\!tw-grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)) !important; - } - .\!tw-grid-cols-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)) !important; - } - .\!tw-grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)) !important; - } - .tw-uppercase { - text-transform: uppercase; - } - `) - }) - }) - - it('should safelist pattern regex having !important selector with variants', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^!bg-gray-(500|600|700|800)$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .\!bg-gray-500 { - --tw-bg-opacity: 1 !important; - background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; - } - .\!bg-gray-600 { - --tw-bg-opacity: 1 !important; - background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; - } - .\!bg-gray-700 { - --tw-bg-opacity: 1 !important; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; - } - .\!bg-gray-800 { - --tw-bg-opacity: 1 !important; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; - } - .uppercase { - text-transform: uppercase; - } - .hover\:\!bg-gray-500:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; - } - .hover\:\!bg-gray-600:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; - } - .hover\:\!bg-gray-700:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; - } - .hover\:\!bg-gray-800:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .\!bg-gray-500 { - background-color: #6b7280 !important; - } - .\!bg-gray-600 { - background-color: #4b5563 !important; - } - .\!bg-gray-700 { - background-color: #374151 !important; - } - .\!bg-gray-800 { - background-color: #1f2937 !important; - } - .uppercase { - text-transform: uppercase; - } - .hover\:\!bg-gray-500:hover { - background-color: #6b7280 !important; - } - .hover\:\!bg-gray-600:hover { - background-color: #4b5563 !important; - } - .hover\:\!bg-gray-700:hover { - background-color: #374151 !important; - } - .hover\:\!bg-gray-800:hover { - background-color: #1f2937 !important; - } - `) - }) - }) - - it('should safelist multiple patterns with !important selector', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: /^!text-gray-(700|800|900)$/, - variants: ['hover'], - }, - { - pattern: /^!bg-gray-(200|300|400)$/, - variants: ['hover'], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .\!bg-gray-200 { - --tw-bg-opacity: 1 !important; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; - } - .\!bg-gray-300 { - --tw-bg-opacity: 1 !important; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; - } - .\!bg-gray-400 { - --tw-bg-opacity: 1 !important; - background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; - } - .uppercase { - text-transform: uppercase; - } - .\!text-gray-700 { - --tw-text-opacity: 1 !important; - color: rgb(55 65 81 / var(--tw-text-opacity)) !important; - } - .\!text-gray-800 { - --tw-text-opacity: 1 !important; - color: rgb(31 41 55 / var(--tw-text-opacity)) !important; - } - .\!text-gray-900 { - --tw-text-opacity: 1 !important; - color: rgb(17 24 39 / var(--tw-text-opacity)) !important; - } - .hover\:\!bg-gray-200:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; - } - .hover\:\!bg-gray-300:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; - } - .hover\:\!bg-gray-400:hover { - --tw-bg-opacity: 1 !important; - background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; - } - .hover\:\!text-gray-700:hover { - --tw-text-opacity: 1 !important; - color: rgb(55 65 81 / var(--tw-text-opacity)) !important; - } - .hover\:\!text-gray-800:hover { - --tw-text-opacity: 1 !important; - color: rgb(31 41 55 / var(--tw-text-opacity)) !important; - } - .hover\:\!text-gray-900:hover { - --tw-text-opacity: 1 !important; - color: rgb(17 24 39 / var(--tw-text-opacity)) !important; - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .\!bg-gray-200 { - background-color: #e5e7eb !important; - } - .\!bg-gray-300 { - background-color: #d1d5db !important; - } - .\!bg-gray-400 { - background-color: #9ca3af !important; - } - .uppercase { - text-transform: uppercase; - } - .\!text-gray-700 { - color: #374151 !important; - } - .\!text-gray-800 { - color: #1f2937 !important; - } - .\!text-gray-900 { - color: #111827 !important; - } - .hover\:\!bg-gray-200:hover { - background-color: #e5e7eb !important; - } - .hover\:\!bg-gray-300:hover { - background-color: #d1d5db !important; - } - .hover\:\!bg-gray-400:hover { - background-color: #9ca3af !important; - } - .hover\:\!text-gray-700:hover { - color: #374151 !important; - } - .hover\:\!text-gray-800:hover { - color: #1f2937 !important; - } - .hover\:\!text-gray-900:hover { - color: #111827 !important; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist strings', () => { + let config = { + content: [{ raw: html`
` }], + safelist: ['mt-[20px]', 'font-bold', 'text-gray-200', 'hover:underline'], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .mt-\[20px\] { + margin-top: 20px; + } + .font-bold { + font-weight: 700; + } + .uppercase { + text-transform: uppercase; + } + .text-gray-200 { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); + } + .hover\:underline:hover { + text-decoration-line: underline; + } + `) + }) +}) + +it('should safelist based on a pattern regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + .uppercase { + text-transform: uppercase; + } + .hover\:bg-red-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .hover\:bg-red-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + `) + }) +}) + +it('should not generate duplicates', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + 'uppercase', + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['hover'], + }, + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['hover'], + }, + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + .uppercase { + text-transform: uppercase; + } + .hover\:bg-red-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .hover\:bg-red-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + `) + }) +}) + +it('should safelist when using a custom prefix', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^tw-bg-red-(100|200)$/g, + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .tw-bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .tw-bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + .tw-uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should not safelist when an empty list is provided', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should not safelist when an sparse/holey list is provided', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [, , ,], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should not safelist any invalid variants if provided', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['foo', 'bar'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist negatives based on a pattern regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^-top-1$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .-top-1 { + top: -0.25rem; + } + .uppercase { + text-transform: uppercase; + } + .hover\:-top-1:hover { + top: -0.25rem; + } + `) + }) +}) + +it('should safelist negatives based on a pattern regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^bg-red-(400|500)(\/(40|50))?$/, + variants: ['hover'], + }, + { + pattern: /^(fill|ring|text)-red-200\/50$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .bg-red-400 { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); + } + .bg-red-400\/40 { + background-color: #f8717166; + } + .bg-red-400\/50 { + background-color: #f8717180; + } + .bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + } + .bg-red-500\/40 { + background-color: #ef444466; + } + .bg-red-500\/50 { + background-color: #ef444480; + } + .fill-red-200\/50 { + fill: #fecaca80; + } + .uppercase { + text-transform: uppercase; + } + .text-red-200\/50 { + color: #fecaca80; + } + .ring-red-200\/50 { + --tw-ring-color: #fecaca80; + } + .hover\:bg-red-400:hover { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); + } + .hover\:bg-red-400\/40:hover { + background-color: #f8717166; + } + .hover\:bg-red-400\/50:hover { + background-color: #f8717180; + } + .hover\:bg-red-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + } + .hover\:bg-red-500\/40:hover { + background-color: #ef444466; + } + .hover\:bg-red-500\/50:hover { + background-color: #ef444480; + } + .hover\:fill-red-200\/50:hover { + fill: #fecaca80; + } + .hover\:text-red-200\/50:hover { + color: #fecaca80; + } + .hover\:ring-red-200\/50:hover { + --tw-ring-color: #fecaca80; + } + `) + }) +}) + +it('should safelist pattern regex with !important selector', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [{ pattern: /^!grid-cols-(4|5|6)$/ }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .\!grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; + } + .\!grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)) !important; + } + .\!grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)) !important; + } + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist pattern regex with custom prefix along with !important selector', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + safelist: [{ pattern: /^!tw-grid-cols-(4|5|6)$/ }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .\!tw-grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; + } + .\!tw-grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)) !important; + } + .\!tw-grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)) !important; + } + .tw-uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist pattern regex having !important selector with variants', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^!bg-gray-(500|600|700|800)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\!bg-gray-500 { + --tw-bg-opacity: 1 !important; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; + } + .\!bg-gray-600 { + --tw-bg-opacity: 1 !important; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; + } + .\!bg-gray-700 { + --tw-bg-opacity: 1 !important; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; + } + .\!bg-gray-800 { + --tw-bg-opacity: 1 !important; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; + } + .uppercase { + text-transform: uppercase; + } + .hover\:\!bg-gray-500:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; + } + .hover\:\!bg-gray-600:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; + } + .hover\:\!bg-gray-700:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; + } + .hover\:\!bg-gray-800:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; + } + `) + }) +}) + +it('should safelist multiple patterns with !important selector', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^!text-gray-(700|800|900)$/, + variants: ['hover'], + }, + { + pattern: /^!bg-gray-(200|300|400)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .\!bg-gray-200 { + --tw-bg-opacity: 1 !important; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; + } + .\!bg-gray-300 { + --tw-bg-opacity: 1 !important; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; + } + .\!bg-gray-400 { + --tw-bg-opacity: 1 !important; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; + } + .uppercase { + text-transform: uppercase; + } + .\!text-gray-700 { + --tw-text-opacity: 1 !important; + color: rgb(55 65 81 / var(--tw-text-opacity)) !important; + } + .\!text-gray-800 { + --tw-text-opacity: 1 !important; + color: rgb(31 41 55 / var(--tw-text-opacity)) !important; + } + .\!text-gray-900 { + --tw-text-opacity: 1 !important; + color: rgb(17 24 39 / var(--tw-text-opacity)) !important; + } + .hover\:\!bg-gray-200:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; + } + .hover\:\!bg-gray-300:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; + } + .hover\:\!bg-gray-400:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; + } + .hover\:\!text-gray-700:hover { + --tw-text-opacity: 1 !important; + color: rgb(55 65 81 / var(--tw-text-opacity)) !important; + } + .hover\:\!text-gray-800:hover { + --tw-text-opacity: 1 !important; + color: rgb(31 41 55 / var(--tw-text-opacity)) !important; + } + .hover\:\!text-gray-900:hover { + --tw-text-opacity: 1 !important; + color: rgb(17 24 39 / var(--tw-text-opacity)) !important; + } + `) }) }) diff --git a/tests/screenAtRule.test.js b/tests/screenAtRule.test.js index 6352fcde0..b6f1723c3 100644 --- a/tests/screenAtRule.test.js +++ b/tests/screenAtRule.test.js @@ -1,60 +1,58 @@ import postcss from 'postcss' import plugin from '../src/lib/substituteScreenAtRules' import config from '../stubs/config.full.js' -import { crosscheck, css } from './util/run' +import { css } from './util/run' function run(input, opts = config) { return postcss([plugin({ tailwindConfig: opts })]).process(input, { from: undefined }) } -crosscheck(() => { - test('it can generate media queries from configured screen sizes', () => { - let input = css` - @screen sm { +test('it can generate media queries from configured screen sizes', () => { + let input = css` + @screen sm { + .banana { + color: yellow; + } + } + @screen md { + .banana { + color: red; + } + } + @screen lg { + .banana { + color: green; + } + } + ` + + return run(input, { + theme: { + screens: { + sm: '500px', + md: '750px', + lg: '1000px', + }, + }, + separator: ':', + }).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 500px) { .banana { - color: yellow; + color: #ff0; } } - @screen md { + @media (min-width: 750px) { .banana { color: red; } } - @screen lg { + @media (min-width: 1000px) { .banana { color: green; } } - ` - - return run(input, { - theme: { - screens: { - sm: '500px', - md: '750px', - lg: '1000px', - }, - }, - separator: ':', - }).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @media (min-width: 500px) { - .banana { - color: #ff0; - } - } - @media (min-width: 750px) { - .banana { - color: red; - } - } - @media (min-width: 1000px) { - .banana { - color: green; - } - } - `) - expect(result.warnings().length).toBe(0) - }) + `) + expect(result.warnings().length).toBe(0) }) }) diff --git a/tests/shared-state.test.js b/tests/shared-state.test.js index 4791d07dc..a3a9f2870 100644 --- a/tests/shared-state.test.js +++ b/tests/shared-state.test.js @@ -1,23 +1,20 @@ import { resolveDebug } from '../src/lib/sharedState' -import { crosscheck } from './util/run' -crosscheck(() => { - it.each` - value | expected - ${'true'} | ${true} - ${'1'} | ${true} - ${'false'} | ${false} - ${'0'} | ${false} - ${'*'} | ${true} - ${'tailwindcss'} | ${true} - ${'tailwindcss:*'} | ${true} - ${'other,tailwindcss'} | ${true} - ${'other,tailwindcss:*'} | ${true} - ${'other,-tailwindcss'} | ${false} - ${'other,-tailwindcss:*'} | ${false} - ${'-tailwindcss'} | ${false} - ${'-tailwindcss:*'} | ${false} - `('should resolve the debug ($value) flag correctly ($expected)', ({ value, expected }) => { - expect(resolveDebug(value)).toBe(expected) - }) +it.each` + value | expected + ${'true'} | ${true} + ${'1'} | ${true} + ${'false'} | ${false} + ${'0'} | ${false} + ${'*'} | ${true} + ${'tailwindcss'} | ${true} + ${'tailwindcss:*'} | ${true} + ${'other,tailwindcss'} | ${true} + ${'other,tailwindcss:*'} | ${true} + ${'other,-tailwindcss'} | ${false} + ${'other,-tailwindcss:*'} | ${false} + ${'-tailwindcss'} | ${false} + ${'-tailwindcss:*'} | ${false} +`('should resolve the debug ($value) flag correctly ($expected)', ({ value, expected }) => { + expect(resolveDebug(value)).toBe(expected) }) diff --git a/tests/source-maps.test.js b/tests/source-maps.test.js index e0839bf6e..5f3a99902 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -1,537 +1,522 @@ import postcss from 'postcss' import { parseSourceMaps } from './util/source-maps' -import { crosscheck, runWithSourceMaps as run, html, css, map } from './util/run' +import { runWithSourceMaps as run, html, css, map } from './util/run' -crosscheck(({ stable, oxide }) => { - oxide.test.todo('apply generates source maps') - stable.test('apply generates source maps', async () => { - let config = { - content: [ - { - raw: html` -
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - .with-declaration { - background-color: red; - @apply h-4 w-4 bg-green-500; - } - - .with-comment { - /* sourcemap will work here too */ - @apply h-4 w-4 bg-red-500; - } - - .just-apply { - @apply h-4 w-4 bg-black; - } - ` - - let result = await run(input, config) - let { sources, annotations } = parseSourceMaps(result) - - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') - expect(sources.length).toBe(1) - - expect(annotations).toEqual([ - '2:6 -> 2:6', - '3:8-29 -> 3:8-29', - '4:8-35 -> 4:8-20', - '4:8-35 -> 5:8-19', - '4:8-35 -> 6:8-26', - '4:8-35 -> 7:8-63', - '5:6 -> 8:6', - '7:6 -> 10:6', - '8:8-41 -> 11:8-41', - '9:8-33 -> 12:8-20', - '9:8-33 -> 13:8-19', - '9:8-33 -> 14:8-26', - '9:8-33 -> 15:8-63', - '10:6 -> 16:6', - '13:8 -> 18:6', - '13:8-31 -> 19:8-20', - '13:8-31 -> 20:8-19', - '13:8-31 -> 21:8-26', - '13:8 -> 22:8', - '13:31 -> 23:0', - ]) - }) - - oxide.test.todo('preflight + base have source maps') - stable.test('preflight + base have source maps', async () => { - let config = { - content: [], - } - - let input = css` - @tailwind base; - ` - - let result = await run(input, config) - let { sources, annotations } = parseSourceMaps(result) - - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') - expect(sources.length).toBe(1) - - expect(annotations).toEqual([ - '2:6 -> 1:0', - '2:20-6 -> 3:1-2', - '2:20 -> 6:1', - '2:6 -> 8:0', - '2:6-20 -> 11:2-32', - '2:6-20 -> 12:2-25', - '2:6-20 -> 13:2-29', - '2:6-20 -> 14:2-31', - '2:20 -> 15:0', - '2:6 -> 17:0', - '2:6-20 -> 19:2-18', - '2:20 -> 20:0', - '2:6 -> 22:0', - '2:20 -> 29:1', - '2:6 -> 31:0', - '2:6-20 -> 32:2-26', - '2:6-20 -> 33:2-40', - '2:6-20 -> 34:2-26', - '2:6-20 -> 35:2-21', - '2:6-20 -> 36:2-230', - '2:6-20 -> 37:2-39', - '2:6-20 -> 38:2-41', - '2:20 -> 39:0', - '2:6 -> 41:0', - '2:20 -> 44:1', - '2:6 -> 46:0', - '2:6-20 -> 47:2-19', - '2:6-20 -> 48:2-30', - '2:20 -> 49:0', - '2:6 -> 51:0', - '2:20 -> 55:1', - '2:6 -> 57:0', - '2:6-20 -> 58:2-19', - '2:6-20 -> 59:2-24', - '2:6-20 -> 60:2-31', - '2:20 -> 61:0', - '2:6 -> 63:0', - '2:20 -> 65:1', - '2:6 -> 67:0', - '2:6-20 -> 68:2-35', - '2:20 -> 69:0', - '2:6 -> 71:0', - '2:20 -> 73:1', - '2:6 -> 75:0', - '2:6-20 -> 81:2-20', - '2:6-20 -> 82:2-22', - '2:20 -> 83:0', - '2:6 -> 85:0', - '2:20 -> 87:1', - '2:6 -> 89:0', - '2:6-20 -> 90:2-16', - '2:6-20 -> 91:2-26', - '2:20 -> 92:0', - '2:6 -> 94:0', - '2:20 -> 96:1', - '2:6 -> 98:0', - '2:6-20 -> 100:2-21', - '2:20 -> 101:0', - '2:6 -> 103:0', - '2:20 -> 106:1', - '2:6 -> 108:0', - '2:6-20 -> 112:2-121', - '2:6-20 -> 113:2-24', - '2:20 -> 114:0', - '2:6 -> 116:0', - '2:20 -> 118:1', - '2:6 -> 120:0', - '2:6-20 -> 121:2-16', - '2:20 -> 122:0', - '2:6 -> 124:0', - '2:20 -> 126:1', - '2:6 -> 128:0', - '2:6-20 -> 130:2-16', - '2:6-20 -> 131:2-16', - '2:6-20 -> 132:2-20', - '2:6-20 -> 133:2-26', - '2:20 -> 134:0', - '2:6 -> 136:0', - '2:6-20 -> 137:2-17', - '2:20 -> 138:0', - '2:6 -> 140:0', - '2:6-20 -> 141:2-13', - '2:20 -> 142:0', - '2:6 -> 144:0', - '2:20 -> 148:1', - '2:6 -> 150:0', - '2:6-20 -> 151:2-24', - '2:6-20 -> 152:2-31', - '2:6-20 -> 153:2-35', - '2:20 -> 154:0', - '2:6 -> 156:0', - '2:20 -> 160:1', - '2:6 -> 162:0', - '2:6-20 -> 167:2-30', - '2:6-20 -> 168:2-40', - '2:6-20 -> 169:2-42', - '2:6-20 -> 170:2-25', - '2:6-20 -> 171:2-30', - '2:6-20 -> 172:2-30', - '2:6-20 -> 173:2-24', - '2:6-20 -> 174:2-19', - '2:6-20 -> 175:2-20', - '2:20 -> 176:0', - '2:6 -> 178:0', - '2:20 -> 180:1', - '2:6 -> 182:0', - '2:6-20 -> 184:2-22', - '2:20 -> 185:0', - '2:6 -> 187:0', - '2:20 -> 190:1', - '2:6 -> 192:0', - '2:6-20 -> 196:2-36', - '2:6-20 -> 197:2-39', - '2:6-20 -> 198:2-32', - '2:20 -> 199:0', - '2:6 -> 201:0', - '2:20 -> 203:1', - '2:6 -> 205:0', - '2:6-20 -> 206:2-15', - '2:20 -> 207:0', - '2:6 -> 209:0', - '2:20 -> 211:1', - '2:6 -> 213:0', - '2:6-20 -> 214:2-18', - '2:20 -> 215:0', - '2:6 -> 217:0', - '2:20 -> 219:1', - '2:6 -> 221:0', - '2:6-20 -> 222:2-26', - '2:20 -> 223:0', - '2:6 -> 225:0', - '2:20 -> 227:1', - '2:6 -> 229:0', - '2:6-20 -> 231:2-14', - '2:20 -> 232:0', - '2:6 -> 234:0', - '2:20 -> 237:1', - '2:6 -> 239:0', - '2:6-20 -> 240:2-39', - '2:6-20 -> 241:2-30', - '2:20 -> 242:0', - '2:6 -> 244:0', - '2:20 -> 246:1', - '2:6 -> 248:0', - '2:6-20 -> 249:2-26', - '2:20 -> 250:0', - '2:6 -> 252:0', - '2:20 -> 255:1', - '2:6 -> 257:0', - '2:6-20 -> 258:2-36', - '2:6-20 -> 259:2-23', - '2:20 -> 260:0', - '2:6 -> 262:0', - '2:20 -> 264:1', - '2:6 -> 266:0', - '2:6-20 -> 267:2-20', - '2:20 -> 268:0', - '2:6 -> 270:0', - '2:20 -> 272:1', - '2:6 -> 274:0', - '2:6-20 -> 287:2-11', - '2:20 -> 288:0', - '2:6 -> 290:0', - '2:6-20 -> 291:2-11', - '2:6-20 -> 292:2-12', - '2:20 -> 293:0', - '2:6 -> 295:0', - '2:6-20 -> 296:2-12', - '2:20 -> 297:0', - '2:6 -> 299:0', - '2:6-20 -> 302:2-18', - '2:6-20 -> 303:2-11', - '2:6-20 -> 304:2-12', - '2:20 -> 305:0', - '2:6 -> 307:0', - '2:6-20 -> 308:2-12', - '2:20 -> 309:0', - '2:6 -> 311:0', - '2:20 -> 313:1', - '2:6 -> 315:0', - '2:6-20 -> 316:2-18', - '2:20 -> 317:0', - '2:6 -> 319:0', - '2:20 -> 322:1', - '2:6 -> 324:0', - '2:6-20 -> 326:2-20', - '2:6-20 -> 327:2-24', - '2:20 -> 328:0', - '2:6 -> 330:0', - '2:20 -> 332:1', - '2:6 -> 334:0', - '2:6-20 -> 336:2-17', - '2:20 -> 337:0', - '2:6 -> 339:0', - '2:20 -> 341:1', - '2:6 -> 342:0', - '2:6-20 -> 343:2-17', - '2:20 -> 344:0', - '2:6 -> 346:0', - '2:20 -> 350:1', - '2:6 -> 352:0', - '2:6-20 -> 360:2-24', - '2:6-20 -> 361:2-32', - '2:20 -> 362:0', - '2:6 -> 364:0', - '2:20 -> 366:1', - '2:6 -> 368:0', - '2:6-20 -> 370:2-17', - '2:6-20 -> 371:2-14', - '2:20 -> 372:0', - '2:6-20 -> 374:0-72', - '2:6 -> 375:0', - '2:6-20 -> 376:2-15', - '2:20 -> 377:0', - '2:6 -> 379:0', - '2:6-20 -> 380:2-26', - '2:6-20 -> 381:2-26', - '2:6-20 -> 382:2-21', - '2:6-20 -> 383:2-21', - '2:6-20 -> 384:2-16', - '2:6-20 -> 385:2-16', - '2:6-20 -> 386:2-16', - '2:6-20 -> 387:2-17', - '2:6-20 -> 388:2-17', - '2:6-20 -> 389:2-15', - '2:6-20 -> 390:2-15', - '2:6-20 -> 391:2-20', - '2:6-20 -> 392:2-40', - '2:6-20 -> 393:2-32', - '2:6-20 -> 394:2-31', - '2:6-20 -> 395:2-30', - '2:6-20 -> 396:2-17', - '2:6-20 -> 397:2-22', - '2:6-20 -> 398:2-24', - '2:6-20 -> 399:2-25', - '2:6-20 -> 400:2-26', - '2:6-20 -> 401:2-20', - '2:6-20 -> 402:2-29', - '2:6-20 -> 403:2-30', - '2:6-20 -> 404:2-40', - '2:6-20 -> 405:2-36', - '2:6-20 -> 406:2-29', - '2:6-20 -> 407:2-24', - '2:6-20 -> 408:2-32', - '2:6-20 -> 409:2-14', - '2:6-20 -> 410:2-20', - '2:6-20 -> 411:2-18', - '2:6-20 -> 412:2-19', - '2:6-20 -> 413:2-20', - '2:6-20 -> 414:2-16', - '2:6-20 -> 415:2-18', - '2:6-20 -> 416:2-15', - '2:6-20 -> 417:2-21', - '2:6-20 -> 418:2-23', - '2:6-20 -> 419:2-29', - '2:6-20 -> 420:2-27', - '2:6-20 -> 421:2-28', - '2:6-20 -> 422:2-29', - '2:6-20 -> 423:2-25', - '2:6-20 -> 424:2-26', - '2:6-20 -> 425:2-27', - '2:6 -> 426:2', - '2:20 -> 427:0', - '2:6 -> 429:0', - '2:6-20 -> 430:2-26', - '2:6-20 -> 431:2-26', - '2:6-20 -> 432:2-21', - '2:6-20 -> 433:2-21', - '2:6-20 -> 434:2-16', - '2:6-20 -> 435:2-16', - '2:6-20 -> 436:2-16', - '2:6-20 -> 437:2-17', - '2:6-20 -> 438:2-17', - '2:6-20 -> 439:2-15', - '2:6-20 -> 440:2-15', - '2:6-20 -> 441:2-20', - '2:6-20 -> 442:2-40', - '2:6-20 -> 443:2-32', - '2:6-20 -> 444:2-31', - '2:6-20 -> 445:2-30', - '2:6-20 -> 446:2-17', - '2:6-20 -> 447:2-22', - '2:6-20 -> 448:2-24', - '2:6-20 -> 449:2-25', - '2:6-20 -> 450:2-26', - '2:6-20 -> 451:2-20', - '2:6-20 -> 452:2-29', - '2:6-20 -> 453:2-30', - '2:6-20 -> 454:2-40', - '2:6-20 -> 455:2-36', - '2:6-20 -> 456:2-29', - '2:6-20 -> 457:2-24', - '2:6-20 -> 458:2-32', - '2:6-20 -> 459:2-14', - '2:6-20 -> 460:2-20', - '2:6-20 -> 461:2-18', - '2:6-20 -> 462:2-19', - '2:6-20 -> 463:2-20', - '2:6-20 -> 464:2-16', - '2:6-20 -> 465:2-18', - '2:6-20 -> 466:2-15', - '2:6-20 -> 467:2-21', - '2:6-20 -> 468:2-23', - '2:6-20 -> 469:2-29', - '2:6-20 -> 470:2-27', - '2:6-20 -> 471:2-28', - '2:6-20 -> 472:2-29', - '2:6-20 -> 473:2-25', - '2:6-20 -> 474:2-26', - '2:6-20 -> 475:2-27', - '2:6 -> 476:2', - '2:20 -> 477:0', - ]) - }) - - oxide.test.todo('utilities have source maps') - stable.test('utilities have source maps', async () => { - let config = { - content: [{ raw: `text-red-500` }], - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - let { sources, annotations } = parseSourceMaps(result) - - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') - expect(sources.length).toBe(1) - - expect(annotations).toStrictEqual([ - '2:6 -> 1:0', - '2:6-25 -> 2:4-24', - '2:6 -> 3:4', - '2:25 -> 4:0', - ]) - }) - - oxide.test.todo('components have source maps') - stable.test('components have source maps', async () => { - let config = { - content: [{ raw: `container` }], - } - - let input = css` - @tailwind components; - ` - - let result = await run(input, config) - let { sources, annotations } = parseSourceMaps(result) - - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') - expect(sources.length).toBe(1) - - expect(annotations).toEqual([ - '2:6 -> 1:0', - '2:6 -> 2:4', - '2:26 -> 3:0', - '2:6 -> 4:0', - '2:6 -> 5:4', - '2:6 -> 6:8', - '2:26 -> 7:4', - '2:26 -> 8:0', - '2:6 -> 9:0', - '2:6 -> 10:4', - '2:6 -> 11:8', - '2:26 -> 12:4', - '2:26 -> 13:0', - '2:6 -> 14:0', - '2:6 -> 15:4', - '2:6 -> 16:8', - '2:26 -> 17:4', - '2:26 -> 18:0', - '2:6 -> 19:0', - '2:6 -> 20:4', - '2:6 -> 21:8', - '2:26 -> 22:4', - '2:26 -> 23:0', - '2:6 -> 24:0', - '2:6 -> 25:4', - '2:6 -> 26:8', - '2:26 -> 27:4', - '2:26 -> 28:0', - ]) - }) - - oxide.test.todo('source maps for layer rules are not rewritten to point to @tailwind directives') - stable.test( - 'source maps for layer rules are not rewritten to point to @tailwind directives', - async () => { - let config = { - content: [{ raw: `font-normal foo hover:foo` }], - } - - let utilitiesFile = postcss.parse( - css` - @tailwind utilities; +test.skip('apply generates source maps', async () => { + let config = { + content: [ + { + raw: html` +
+
+
`, - { from: 'components.css', map: { prev: map } } - ) + }, + ], + corePlugins: { preflight: false }, + } - let mainCssFile = postcss.parse( - css` - @layer utilities { - .foo { - background-color: red; - } - } - `, - { from: 'input.css', map: { prev: map } } - ) - - // Just pretend that there's an @import in `mainCssFile` that imports the nodes from `utilitiesFile` - let input = postcss.root({ - nodes: [...utilitiesFile.nodes, ...mainCssFile.nodes], - source: mainCssFile.source, - }) - - let result = await run(input, config) - - let { sources, annotations } = parseSourceMaps(result) - - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') - - // And we should see that the source map for the layer rule is not rewritten - // to point to the @tailwind directive but instead points to the original - expect(sources.length).toBe(2) - expect(sources).toEqual(['components.css', 'input.css']) - - expect(annotations).toEqual([ - '2:10 -> 1:0', - '2:10 -> 2:14', - '2:29 -> 3:0', - '3:12 -> 4:12', - '4:14-35 -> 5:14-35', - '5:12 -> 6:12', - '3:12 -> 7:12', - '4:14-35 -> 8:14-35', - '5:12 -> 9:12', - ]) + let input = css` + .with-declaration { + background-color: red; + @apply h-4 w-4 bg-green-500; } - ) + + .with-comment { + /* sourcemap will work here too */ + @apply h-4 w-4 bg-red-500; + } + + .just-apply { + @apply h-4 w-4 bg-black; + } + ` + + let result = await run(input, config) + let { sources, annotations } = parseSourceMaps(result) + + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') + expect(sources.length).toBe(1) + + expect(annotations).toEqual([ + '2:6 -> 2:6', + '3:8-29 -> 3:8-29', + '4:8-35 -> 4:8-20', + '4:8-35 -> 5:8-19', + '4:8-35 -> 6:8-26', + '4:8-35 -> 7:8-63', + '5:6 -> 8:6', + '7:6 -> 10:6', + '8:8-41 -> 11:8-41', + '9:8-33 -> 12:8-20', + '9:8-33 -> 13:8-19', + '9:8-33 -> 14:8-26', + '9:8-33 -> 15:8-63', + '10:6 -> 16:6', + '13:8 -> 18:6', + '13:8-31 -> 19:8-20', + '13:8-31 -> 20:8-19', + '13:8-31 -> 21:8-26', + '13:8 -> 22:8', + '13:31 -> 23:0', + ]) +}) + +test.skip('preflight + base have source maps', async () => { + let config = { + content: [], + } + + let input = css` + @tailwind base; + ` + + let result = await run(input, config) + let { sources, annotations } = parseSourceMaps(result) + + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') + expect(sources.length).toBe(1) + + expect(annotations).toEqual([ + '2:6 -> 1:0', + '2:20-6 -> 3:1-2', + '2:20 -> 6:1', + '2:6 -> 8:0', + '2:6-20 -> 11:2-32', + '2:6-20 -> 12:2-25', + '2:6-20 -> 13:2-29', + '2:6-20 -> 14:2-31', + '2:20 -> 15:0', + '2:6 -> 17:0', + '2:6-20 -> 19:2-18', + '2:20 -> 20:0', + '2:6 -> 22:0', + '2:20 -> 29:1', + '2:6 -> 31:0', + '2:6-20 -> 32:2-26', + '2:6-20 -> 33:2-40', + '2:6-20 -> 34:2-26', + '2:6-20 -> 35:2-21', + '2:6-20 -> 36:2-230', + '2:6-20 -> 37:2-39', + '2:6-20 -> 38:2-41', + '2:20 -> 39:0', + '2:6 -> 41:0', + '2:20 -> 44:1', + '2:6 -> 46:0', + '2:6-20 -> 47:2-19', + '2:6-20 -> 48:2-30', + '2:20 -> 49:0', + '2:6 -> 51:0', + '2:20 -> 55:1', + '2:6 -> 57:0', + '2:6-20 -> 58:2-19', + '2:6-20 -> 59:2-24', + '2:6-20 -> 60:2-31', + '2:20 -> 61:0', + '2:6 -> 63:0', + '2:20 -> 65:1', + '2:6 -> 67:0', + '2:6-20 -> 68:2-35', + '2:20 -> 69:0', + '2:6 -> 71:0', + '2:20 -> 73:1', + '2:6 -> 75:0', + '2:6-20 -> 81:2-20', + '2:6-20 -> 82:2-22', + '2:20 -> 83:0', + '2:6 -> 85:0', + '2:20 -> 87:1', + '2:6 -> 89:0', + '2:6-20 -> 90:2-16', + '2:6-20 -> 91:2-26', + '2:20 -> 92:0', + '2:6 -> 94:0', + '2:20 -> 96:1', + '2:6 -> 98:0', + '2:6-20 -> 100:2-21', + '2:20 -> 101:0', + '2:6 -> 103:0', + '2:20 -> 106:1', + '2:6 -> 108:0', + '2:6-20 -> 112:2-121', + '2:6-20 -> 113:2-24', + '2:20 -> 114:0', + '2:6 -> 116:0', + '2:20 -> 118:1', + '2:6 -> 120:0', + '2:6-20 -> 121:2-16', + '2:20 -> 122:0', + '2:6 -> 124:0', + '2:20 -> 126:1', + '2:6 -> 128:0', + '2:6-20 -> 130:2-16', + '2:6-20 -> 131:2-16', + '2:6-20 -> 132:2-20', + '2:6-20 -> 133:2-26', + '2:20 -> 134:0', + '2:6 -> 136:0', + '2:6-20 -> 137:2-17', + '2:20 -> 138:0', + '2:6 -> 140:0', + '2:6-20 -> 141:2-13', + '2:20 -> 142:0', + '2:6 -> 144:0', + '2:20 -> 148:1', + '2:6 -> 150:0', + '2:6-20 -> 151:2-24', + '2:6-20 -> 152:2-31', + '2:6-20 -> 153:2-35', + '2:20 -> 154:0', + '2:6 -> 156:0', + '2:20 -> 160:1', + '2:6 -> 162:0', + '2:6-20 -> 167:2-30', + '2:6-20 -> 168:2-40', + '2:6-20 -> 169:2-42', + '2:6-20 -> 170:2-25', + '2:6-20 -> 171:2-30', + '2:6-20 -> 172:2-30', + '2:6-20 -> 173:2-24', + '2:6-20 -> 174:2-19', + '2:6-20 -> 175:2-20', + '2:20 -> 176:0', + '2:6 -> 178:0', + '2:20 -> 180:1', + '2:6 -> 182:0', + '2:6-20 -> 184:2-22', + '2:20 -> 185:0', + '2:6 -> 187:0', + '2:20 -> 190:1', + '2:6 -> 192:0', + '2:6-20 -> 196:2-36', + '2:6-20 -> 197:2-39', + '2:6-20 -> 198:2-32', + '2:20 -> 199:0', + '2:6 -> 201:0', + '2:20 -> 203:1', + '2:6 -> 205:0', + '2:6-20 -> 206:2-15', + '2:20 -> 207:0', + '2:6 -> 209:0', + '2:20 -> 211:1', + '2:6 -> 213:0', + '2:6-20 -> 214:2-18', + '2:20 -> 215:0', + '2:6 -> 217:0', + '2:20 -> 219:1', + '2:6 -> 221:0', + '2:6-20 -> 222:2-26', + '2:20 -> 223:0', + '2:6 -> 225:0', + '2:20 -> 227:1', + '2:6 -> 229:0', + '2:6-20 -> 231:2-14', + '2:20 -> 232:0', + '2:6 -> 234:0', + '2:20 -> 237:1', + '2:6 -> 239:0', + '2:6-20 -> 240:2-39', + '2:6-20 -> 241:2-30', + '2:20 -> 242:0', + '2:6 -> 244:0', + '2:20 -> 246:1', + '2:6 -> 248:0', + '2:6-20 -> 249:2-26', + '2:20 -> 250:0', + '2:6 -> 252:0', + '2:20 -> 255:1', + '2:6 -> 257:0', + '2:6-20 -> 258:2-36', + '2:6-20 -> 259:2-23', + '2:20 -> 260:0', + '2:6 -> 262:0', + '2:20 -> 264:1', + '2:6 -> 266:0', + '2:6-20 -> 267:2-20', + '2:20 -> 268:0', + '2:6 -> 270:0', + '2:20 -> 272:1', + '2:6 -> 274:0', + '2:6-20 -> 287:2-11', + '2:20 -> 288:0', + '2:6 -> 290:0', + '2:6-20 -> 291:2-11', + '2:6-20 -> 292:2-12', + '2:20 -> 293:0', + '2:6 -> 295:0', + '2:6-20 -> 296:2-12', + '2:20 -> 297:0', + '2:6 -> 299:0', + '2:6-20 -> 302:2-18', + '2:6-20 -> 303:2-11', + '2:6-20 -> 304:2-12', + '2:20 -> 305:0', + '2:6 -> 307:0', + '2:6-20 -> 308:2-12', + '2:20 -> 309:0', + '2:6 -> 311:0', + '2:20 -> 313:1', + '2:6 -> 315:0', + '2:6-20 -> 316:2-18', + '2:20 -> 317:0', + '2:6 -> 319:0', + '2:20 -> 322:1', + '2:6 -> 324:0', + '2:6-20 -> 326:2-20', + '2:6-20 -> 327:2-24', + '2:20 -> 328:0', + '2:6 -> 330:0', + '2:20 -> 332:1', + '2:6 -> 334:0', + '2:6-20 -> 336:2-17', + '2:20 -> 337:0', + '2:6 -> 339:0', + '2:20 -> 341:1', + '2:6 -> 342:0', + '2:6-20 -> 343:2-17', + '2:20 -> 344:0', + '2:6 -> 346:0', + '2:20 -> 350:1', + '2:6 -> 352:0', + '2:6-20 -> 360:2-24', + '2:6-20 -> 361:2-32', + '2:20 -> 362:0', + '2:6 -> 364:0', + '2:20 -> 366:1', + '2:6 -> 368:0', + '2:6-20 -> 370:2-17', + '2:6-20 -> 371:2-14', + '2:20 -> 372:0', + '2:6-20 -> 374:0-72', + '2:6 -> 375:0', + '2:6-20 -> 376:2-15', + '2:20 -> 377:0', + '2:6 -> 379:0', + '2:6-20 -> 380:2-26', + '2:6-20 -> 381:2-26', + '2:6-20 -> 382:2-21', + '2:6-20 -> 383:2-21', + '2:6-20 -> 384:2-16', + '2:6-20 -> 385:2-16', + '2:6-20 -> 386:2-16', + '2:6-20 -> 387:2-17', + '2:6-20 -> 388:2-17', + '2:6-20 -> 389:2-15', + '2:6-20 -> 390:2-15', + '2:6-20 -> 391:2-20', + '2:6-20 -> 392:2-40', + '2:6-20 -> 393:2-32', + '2:6-20 -> 394:2-31', + '2:6-20 -> 395:2-30', + '2:6-20 -> 396:2-17', + '2:6-20 -> 397:2-22', + '2:6-20 -> 398:2-24', + '2:6-20 -> 399:2-25', + '2:6-20 -> 400:2-26', + '2:6-20 -> 401:2-20', + '2:6-20 -> 402:2-29', + '2:6-20 -> 403:2-30', + '2:6-20 -> 404:2-40', + '2:6-20 -> 405:2-36', + '2:6-20 -> 406:2-29', + '2:6-20 -> 407:2-24', + '2:6-20 -> 408:2-32', + '2:6-20 -> 409:2-14', + '2:6-20 -> 410:2-20', + '2:6-20 -> 411:2-18', + '2:6-20 -> 412:2-19', + '2:6-20 -> 413:2-20', + '2:6-20 -> 414:2-16', + '2:6-20 -> 415:2-18', + '2:6-20 -> 416:2-15', + '2:6-20 -> 417:2-21', + '2:6-20 -> 418:2-23', + '2:6-20 -> 419:2-29', + '2:6-20 -> 420:2-27', + '2:6-20 -> 421:2-28', + '2:6-20 -> 422:2-29', + '2:6-20 -> 423:2-25', + '2:6-20 -> 424:2-26', + '2:6-20 -> 425:2-27', + '2:6 -> 426:2', + '2:20 -> 427:0', + '2:6 -> 429:0', + '2:6-20 -> 430:2-26', + '2:6-20 -> 431:2-26', + '2:6-20 -> 432:2-21', + '2:6-20 -> 433:2-21', + '2:6-20 -> 434:2-16', + '2:6-20 -> 435:2-16', + '2:6-20 -> 436:2-16', + '2:6-20 -> 437:2-17', + '2:6-20 -> 438:2-17', + '2:6-20 -> 439:2-15', + '2:6-20 -> 440:2-15', + '2:6-20 -> 441:2-20', + '2:6-20 -> 442:2-40', + '2:6-20 -> 443:2-32', + '2:6-20 -> 444:2-31', + '2:6-20 -> 445:2-30', + '2:6-20 -> 446:2-17', + '2:6-20 -> 447:2-22', + '2:6-20 -> 448:2-24', + '2:6-20 -> 449:2-25', + '2:6-20 -> 450:2-26', + '2:6-20 -> 451:2-20', + '2:6-20 -> 452:2-29', + '2:6-20 -> 453:2-30', + '2:6-20 -> 454:2-40', + '2:6-20 -> 455:2-36', + '2:6-20 -> 456:2-29', + '2:6-20 -> 457:2-24', + '2:6-20 -> 458:2-32', + '2:6-20 -> 459:2-14', + '2:6-20 -> 460:2-20', + '2:6-20 -> 461:2-18', + '2:6-20 -> 462:2-19', + '2:6-20 -> 463:2-20', + '2:6-20 -> 464:2-16', + '2:6-20 -> 465:2-18', + '2:6-20 -> 466:2-15', + '2:6-20 -> 467:2-21', + '2:6-20 -> 468:2-23', + '2:6-20 -> 469:2-29', + '2:6-20 -> 470:2-27', + '2:6-20 -> 471:2-28', + '2:6-20 -> 472:2-29', + '2:6-20 -> 473:2-25', + '2:6-20 -> 474:2-26', + '2:6-20 -> 475:2-27', + '2:6 -> 476:2', + '2:20 -> 477:0', + ]) +}) + +test.skip('utilities have source maps', async () => { + let config = { + content: [{ raw: `text-red-500` }], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + let { sources, annotations } = parseSourceMaps(result) + + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') + expect(sources.length).toBe(1) + + expect(annotations).toStrictEqual(['2:6 -> 1:0', '2:6-25 -> 2:4-24', '2:6 -> 3:4', '2:25 -> 4:0']) +}) + +test.skip('components have source maps', async () => { + let config = { + content: [{ raw: `container` }], + } + + let input = css` + @tailwind components; + ` + + let result = await run(input, config) + let { sources, annotations } = parseSourceMaps(result) + + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') + expect(sources.length).toBe(1) + + expect(annotations).toEqual([ + '2:6 -> 1:0', + '2:6 -> 2:4', + '2:26 -> 3:0', + '2:6 -> 4:0', + '2:6 -> 5:4', + '2:6 -> 6:8', + '2:26 -> 7:4', + '2:26 -> 8:0', + '2:6 -> 9:0', + '2:6 -> 10:4', + '2:6 -> 11:8', + '2:26 -> 12:4', + '2:26 -> 13:0', + '2:6 -> 14:0', + '2:6 -> 15:4', + '2:6 -> 16:8', + '2:26 -> 17:4', + '2:26 -> 18:0', + '2:6 -> 19:0', + '2:6 -> 20:4', + '2:6 -> 21:8', + '2:26 -> 22:4', + '2:26 -> 23:0', + '2:6 -> 24:0', + '2:6 -> 25:4', + '2:6 -> 26:8', + '2:26 -> 27:4', + '2:26 -> 28:0', + ]) +}) + +test.skip('source maps for layer rules are not rewritten to point to @tailwind directives', async () => { + let config = { + content: [{ raw: `font-normal foo hover:foo` }], + } + + let utilitiesFile = postcss.parse( + css` + @tailwind utilities; + `, + { from: 'components.css', map: { prev: map } } + ) + + let mainCssFile = postcss.parse( + css` + @layer utilities { + .foo { + background-color: red; + } + } + `, + { from: 'input.css', map: { prev: map } } + ) + + // Just pretend that there's an @import in `mainCssFile` that imports the nodes from `utilitiesFile` + let input = postcss.root({ + nodes: [...utilitiesFile.nodes, ...mainCssFile.nodes], + source: mainCssFile.source, + }) + + let result = await run(input, config) + + let { sources, annotations } = parseSourceMaps(result) + + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') + + // And we should see that the source map for the layer rule is not rewritten + // to point to the @tailwind directive but instead points to the original + expect(sources.length).toBe(2) + expect(sources).toEqual(['components.css', 'input.css']) + + expect(annotations).toEqual([ + '2:10 -> 1:0', + '2:10 -> 2:14', + '2:29 -> 3:0', + '3:12 -> 4:12', + '4:14-35 -> 5:14-35', + '5:12 -> 6:12', + '3:12 -> 7:12', + '4:14-35 -> 8:14-35', + '5:12 -> 9:12', + ]) }) diff --git a/tests/space-utilities.test.js b/tests/space-utilities.test.js new file mode 100644 index 000000000..fb027a337 --- /dev/null +++ b/tests/space-utilities.test.js @@ -0,0 +1,43 @@ +import { run, html, css, defaults } from './util/run' + +test('space-x uses non-logical properties', () => { + let config = { + future: { + logicalSiblingUtilities: false, + }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + `) + }) +}) + +test('space-x uses logical properties', () => { + let config = { + future: { + logicalSiblingUtilities: true, + }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base; @tailwind utilities;', config).then((result) => { + expect(result.css).toMatchCss(css` + ${defaults} + .space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-inline-start: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + margin-inline-end: calc(1rem * var(--tw-space-x-reverse)); + } + `) + }) +}) diff --git a/tests/syntax-lit-html.test.js b/tests/syntax-lit-html.test.js index 952a9b969..a3bdfebda 100644 --- a/tests/syntax-lit-html.test.js +++ b/tests/syntax-lit-html.test.js @@ -1,72 +1,45 @@ -import { crosscheck, run, css } from './util/run' +import { run, css } from './util/run' -crosscheck(({ stable, oxide }) => { - test('it detects classes in lit-html templates', () => { - let config = { - content: [ - { - raw: `html\`\`;`, - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], - } +test('it detects classes in lit-html templates', () => { + let config = { + content: [ + { + raw: `html\`\`;`, + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .rounded { - border-radius: 0.25rem; - } - .bg-blue-400 { - --tw-bg-opacity: 1; - background-color: rgb(96 165 250 / var(--tw-bg-opacity)); - } - .px-4 { - padding-left: 1rem; - padding-right: 1rem; - } - .py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - } - .font-bold { - font-weight: 700; - } - .text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - } - .hover\:bg-blue-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .rounded { - border-radius: 0.25rem; - } - .bg-blue-400 { - background-color: #60a5fa; - } - .px-4 { - padding-left: 1rem; - padding-right: 1rem; - } - .py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - } - .font-bold { - font-weight: 700; - } - .text-white { - color: #fff; - } - .hover\:bg-blue-600:hover { - background-color: #2563eb; - } - `) - }) + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .rounded { + border-radius: 0.25rem; + } + .bg-blue-400 { + --tw-bg-opacity: 1; + background-color: rgb(96 165 250 / var(--tw-bg-opacity)); + } + .px-4 { + padding-left: 1rem; + padding-right: 1rem; + } + .py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + } + .font-bold { + font-weight: 700; + } + .text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + } + .hover\:bg-blue-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); + } + `) }) }) diff --git a/tests/syntax-svelte.test.js b/tests/syntax-svelte.test.js index be2c33e8b..8acd91a5b 100644 --- a/tests/syntax-svelte.test.js +++ b/tests/syntax-svelte.test.js @@ -1,43 +1,40 @@ import path from 'path' -import { crosscheck, run, css } from './util/run' +import { run, css } from './util/run' -crosscheck(({ stable, oxide }) => { - oxide.test.todo('it detects svelte based on the file extension') - stable.test('it detects svelte based on the file extension', () => { - let config = { - content: [path.resolve(__dirname, './syntax-svelte.test.svelte')], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], - } +test('it detects svelte based on the file extension', () => { + let config = { + content: [path.resolve(__dirname, './syntax-svelte.test.svelte')], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } - let input = css` - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .bg-red-500 { + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + } + @media (min-width: 1024px) { + .lg\:hover\:bg-blue-500:hover { --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); } - @media (min-width: 1024px) { - .lg\:hover\:bg-blue-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); - } - } - `) - }) + } + `) }) +}) - oxide.test.todo('using raw with svelte extension') - stable.test('using raw with svelte extension', () => { - let config = { - content: [ - { - raw: ` +test('using raw with svelte extension', () => { + let config = { + content: [ + { + raw: ` @@ -50,32 +47,31 @@ crosscheck(({ stable, oxide }) => { Click me `, - extension: 'svelte', - }, - ], - corePlugins: { preflight: false }, - theme: {}, - plugins: [], - } + extension: 'svelte', + }, + ], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } - let input = css` - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - expect(result.css).toMatchCss(css` - .bg-red-500 { + return run(input, config).then((result) => { + expect(result.css).toMatchCss(css` + .bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + } + @media (min-width: 1024px) { + .lg\:hover\:bg-blue-500:hover { --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); } - @media (min-width: 1024px) { - .lg\:hover\:bg-blue-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); - } - } - `) - }) + } + `) }) }) diff --git a/tests/tailwind-screens.test.js b/tests/tailwind-screens.test.js index f968fd0e3..009c02bc3 100644 --- a/tests/tailwind-screens.test.js +++ b/tests/tailwind-screens.test.js @@ -1,65 +1,63 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(() => { - test('class variants are inserted at `@tailwind variants`', async () => { - let config = { - content: [{ raw: html`
` }], +test('class variants are inserted at `@tailwind variants`', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind utilities; + @tailwind variants; + .foo { + color: black; } + ` - let input = css` - @tailwind utilities; - @tailwind variants; - .foo { - color: black; + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-bold, + .hover\:font-bold:hover { + font-weight: 700; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold, - .hover\:font-bold:hover { + @media (min-width: 768px) { + .md\:font-bold { font-weight: 700; } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - .foo { - color: #000; - } - `) - }) - }) - - test('`@tailwind screens` works as an alias for `@tailwind variants`', async () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind utilities; - @tailwind screens; - .foo { - color: black; } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .font-bold, - .hover\:font-bold:hover { - font-weight: 700; - } - @media (min-width: 768px) { - .md\:font-bold { - font-weight: 700; - } - } - .foo { - color: #000; - } - `) - }) + .foo { + color: #000; + } + `) + }) +}) + +test('`@tailwind screens` works as an alias for `@tailwind variants`', async () => { + let config = { + content: [{ raw: html`
` }], + } + + let input = css` + @tailwind utilities; + @tailwind screens; + .foo { + color: black; + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .font-bold, + .hover\:font-bold:hover { + font-weight: 700; + } + @media (min-width: 768px) { + .md\:font-bold { + font-weight: 700; + } + } + .foo { + color: #000; + } + `) }) }) diff --git a/tests/to-path.test.js b/tests/to-path.test.js index 1e6021ba9..8737194ed 100644 --- a/tests/to-path.test.js +++ b/tests/to-path.test.js @@ -1,21 +1,18 @@ import { toPath } from '../src/util/toPath' -import { crosscheck } from './util/run' -crosscheck(() => { - it('should keep an array as an array', () => { - let input = ['a', 'b', '0', 'c'] +it('should keep an array as an array', () => { + let input = ['a', 'b', '0', 'c'] - expect(toPath(input)).toBe(input) - }) - - it.each` - input | output - ${'a.b.c'} | ${['a', 'b', 'c']} - ${'a[0].b.c'} | ${['a', '0', 'b', 'c']} - ${'.a'} | ${['a']} - ${'[].a'} | ${['a']} - ${'a[1.5][b][c]'} | ${['a', '1.5', 'b', 'c']} - `('should convert "$input" to "$output"', ({ input, output }) => { - expect(toPath(input)).toEqual(output) - }) + expect(toPath(input)).toBe(input) +}) + +it.each` + input | output + ${'a.b.c'} | ${['a', 'b', 'c']} + ${'a[0].b.c'} | ${['a', '0', 'b', 'c']} + ${'.a'} | ${['a']} + ${'[].a'} | ${['a']} + ${'a[1.5][b][c]'} | ${['a', '1.5', 'b', 'c']} +`('should convert "$input" to "$output"', ({ input, output }) => { + expect(toPath(input)).toEqual(output) }) diff --git a/tests/util/apply-important-selector.test.js b/tests/util/apply-important-selector.test.js index 0ec22792f..ab9842d83 100644 --- a/tests/util/apply-important-selector.test.js +++ b/tests/util/apply-important-selector.test.js @@ -1,24 +1,21 @@ -import { crosscheck } from '../util/run' import { applyImportantSelector } from '../../src/util/applyImportantSelector' -crosscheck(() => { - it.each` - before | after - ${'.foo'} | ${'#app :is(.foo)'} - ${'.foo .bar'} | ${'#app :is(.foo .bar)'} - ${'.foo:hover'} | ${'#app :is(.foo:hover)'} - ${'.foo .bar:hover'} | ${'#app :is(.foo .bar:hover)'} - ${'.foo::before'} | ${'#app :is(.foo)::before'} - ${'.foo::before'} | ${'#app :is(.foo)::before'} - ${'.foo::file-selector-button'} | ${'#app :is(.foo)::file-selector-button'} - ${'.foo::-webkit-progress-bar'} | ${'#app :is(.foo)::-webkit-progress-bar'} - ${'.foo:hover::before'} | ${'#app :is(.foo:hover)::before'} - ${':is(.dark :is([dir="rtl"] .foo::before))'} | ${'#app :is(.dark :is([dir="rtl"] .foo))::before'} - ${':is(.dark .foo) .bar'} | ${'#app :is(:is(.dark .foo) .bar)'} - ${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'} - ${':is(.foo)::before'} | ${'#app :is(.foo)::before'} - ${'.foo:before'} | ${'#app :is(.foo):before'} - `('should generate "$after" from "$before"', ({ before, after }) => { - expect(applyImportantSelector(before, '#app')).toEqual(after) - }) +it.each` + before | after + ${'.foo'} | ${'#app :is(.foo)'} + ${'.foo .bar'} | ${'#app :is(.foo .bar)'} + ${'.foo:hover'} | ${'#app :is(.foo:hover)'} + ${'.foo .bar:hover'} | ${'#app :is(.foo .bar:hover)'} + ${'.foo::before'} | ${'#app :is(.foo)::before'} + ${'.foo::before'} | ${'#app :is(.foo)::before'} + ${'.foo::file-selector-button'} | ${'#app :is(.foo)::file-selector-button'} + ${'.foo::-webkit-progress-bar'} | ${'#app :is(.foo)::-webkit-progress-bar'} + ${'.foo:hover::before'} | ${'#app :is(.foo:hover)::before'} + ${':is(.dark :is([dir="rtl"] .foo::before))'} | ${'#app :is(.dark :is([dir="rtl"] .foo))::before'} + ${':is(.dark .foo) .bar'} | ${'#app :is(:is(.dark .foo) .bar)'} + ${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'} + ${':is(.foo)::before'} | ${'#app :is(.foo)::before'} + ${'.foo:before'} | ${'#app :is(.foo):before'} +`('should generate "$after" from "$before"', ({ before, after }) => { + expect(applyImportantSelector(before, '#app')).toEqual(after) }) diff --git a/tests/util/crosscheck.test.js b/tests/util/crosscheck.test.js deleted file mode 100644 index 032473386..000000000 --- a/tests/util/crosscheck.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import { crosscheck } from './run' - -crosscheck(({ stable, oxide, engine }) => { - stable.test('should run on stable', () => { - expect(engine.stable).toBe(true) - expect(engine.oxide).toBe(false) - }) - oxide.test('should run on oxide', () => { - expect(engine.stable).toBe(false) - expect(engine.oxide).toBe(true) - }) - test('should run on both', () => { - oxide.expect(engine.oxide).toBe(true) - oxide.expect(engine.stable).toBe(false) - stable.expect(engine.oxide).toBe(false) - stable.expect(engine.stable).toBe(true) - }) -}) diff --git a/tests/util/run.js b/tests/util/run.js index 964d5d5f5..08dbc3b80 100644 --- a/tests/util/run.js +++ b/tests/util/run.js @@ -1,7 +1,6 @@ import path from 'path' import postcss from 'postcss' import tailwind from '../../src' -import { env } from '../../src/lib/sharedState' export * from './strings' export * from './defaults' @@ -14,8 +13,6 @@ export let map = JSON.stringify({ mappings: '', }) -globalThis.__OXIDE__ = env.ENGINE === 'oxide' - export function run(input, config, plugin = tailwind) { let { currentTestName, testPath } = expect.getState() @@ -34,61 +31,3 @@ export function runWithSourceMaps(input, config, plugin = tailwind) { }, }) } - -let nullTest = Object.assign(function () {}, { - skip: () => {}, - only: () => {}, - each: () => () => {}, - todo: () => {}, -}) -let nullProxy = new Proxy( - { - test: nullTest, - it: nullTest, - xit: nullTest.skip, - fit: nullTest.only, - xdescribe: nullTest.skip, - fdescribe: nullTest.only, - }, - { - get(target, prop, _receiver) { - if (prop in target) { - return target[prop] - } - return Object.assign(() => { - return nullProxy - }, nullProxy) - }, - } -) - -/** - * @typedef {object} CrossCheck - * @property {typeof import('@jest/globals')} oxide - * @property {typeof import('@jest/globals')} stable - * @property {object} engine - * @property {boolean} engine.oxide - * @property {boolean} engine.stable - */ - -/** - * @param {(data: CrossCheck) => void} fn - */ -export function crosscheck(fn) { - let engines = - env.ENGINE === 'oxide' ? [{ engine: 'Stable' }, { engine: 'Oxide' }] : [{ engine: 'Stable' }] - - describe.each(engines)('$engine', ({ engine }) => { - let engines = { - oxide: engine === 'Oxide' ? globalThis : nullProxy, - stable: engine === 'Stable' ? globalThis : nullProxy, - engine: { oxide: engine === 'Oxide', stable: engine === 'Stable' }, - } - - beforeEach(() => { - globalThis.__OXIDE__ = engines.engine.oxide - }) - - fn(engines) - }) -} diff --git a/tests/variants.oxide.test.css b/tests/variants.oxide.test.css deleted file mode 100644 index 5e06f3942..000000000 --- a/tests/variants.oxide.test.css +++ /dev/null @@ -1,412 +0,0 @@ -*, -:before, -:after, -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #3b82f680; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} -.first-letter\:text-2xl:first-letter { - font-size: 1.5rem; - line-height: 2rem; -} -.first-letter\:text-red-500:first-letter { - color: #ef4444; -} -.first-line\:bg-yellow-300:first-line { - background-color: #fde047; -} -.first-line\:underline:first-line { - text-decoration-line: underline; -} -.marker\:text-lg ::marker { - font-size: 1.125rem; - line-height: 1.75rem; -} -.marker\:text-red-500 ::marker { - color: #ef4444; -} -.marker\:text-lg::marker { - font-size: 1.125rem; - line-height: 1.75rem; -} -.marker\:text-red-500::marker { - color: #ef4444; -} -.selection\:bg-blue-500 ::selection { - background-color: #3b82f6; -} -.selection\:text-white ::selection { - color: #fff; -} -.selection\:bg-blue-500::selection { - background-color: #3b82f6; -} -.selection\:text-white::selection { - color: #fff; -} -.file\:bg-blue-500::file-selector-button { - background-color: #3b82f6; -} -.file\:text-white::file-selector-button { - color: #fff; -} -.placeholder\:font-bold::placeholder { - font-weight: 700; -} -.placeholder\:text-red-500::placeholder { - color: #ef4444; -} -.backdrop\:shadow-md::backdrop { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.before\:block:before { - content: var(--tw-content); - display: block; -} -.before\:bg-red-500:before { - content: var(--tw-content); - background-color: #ef4444; -} -.after\:flex:after { - content: var(--tw-content); - display: flex; -} -.after\:uppercase:after { - content: var(--tw-content); - text-transform: uppercase; -} -.first\:shadow-md:first-child, -.last\:shadow-md:last-child, -.only\:shadow-md:only-child, -.odd\:shadow-md:nth-child(2n + 1), -.even\:shadow-md:nth-child(2n), -.first-of-type\:shadow-md:first-of-type, -.last-of-type\:shadow-md:last-of-type, -.only-of-type\:shadow-md:only-of-type, -.visited\:shadow-md:visited, -.target\:shadow-md:target { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.open\:bg-red-200[open] { - background-color: #fecaca; -} -.default\:shadow-md:default, -.checked\:shadow-md:checked, -.indeterminate\:shadow-md:indeterminate, -.placeholder-shown\:shadow-md:placeholder-shown, -.autofill\:shadow-md:autofill, -.optional\:shadow-md:optional, -.required\:shadow-md:required, -.valid\:shadow-md:valid, -.invalid\:shadow-md:invalid, -.in-range\:shadow-md:in-range, -.out-of-range\:shadow-md:out-of-range, -.read-only\:shadow-md:read-only, -.empty\:shadow-md:empty, -.focus-within\:shadow-md:focus-within { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -@keyframes spin { - to { - transform: rotate(360deg); - } -} -.hover\:animate-spin:hover { - animation: 1s linear infinite spin; -} -.hover\:shadow-md:hover { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.file\:hover\:bg-blue-600:hover::file-selector-button { - background-color: #2563eb; -} -.open\:hover\:bg-red-200:hover[open] { - background-color: #fecaca; -} -.focus\:shadow-md:focus, -.focus\:hover\:shadow-md:hover:focus, -.focus-visible\:shadow-md:focus-visible, -.active\:shadow-md:active, -.enabled\:shadow-md:enabled, -.disabled\:shadow-md:disabled, -.group:first-child .group-first\:shadow-md, -.group:last-child .group-last\:shadow-md, -.group:only-child .group-only\:shadow-md, -.group:nth-child(2n + 1) .group-odd\:shadow-md, -.group:nth-child(2n) .group-even\:shadow-md, -.group:first-of-type .group-first-of-type\:shadow-md, -.group:last-of-type .group-last-of-type\:shadow-md, -.group:only-of-type .group-only-of-type\:shadow-md, -.group:visited .group-visited\:shadow-md, -.group:target .group-target\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open] .group-open\:bg-red-200 { - background-color: #fecaca; -} -.group:default .group-default\:shadow-md, -.group:checked .group-checked\:shadow-md, -.group:indeterminate .group-indeterminate\:shadow-md, -.group:placeholder-shown .group-placeholder-shown\:shadow-md, -.group:autofill .group-autofill\:shadow-md, -.group:optional .group-optional\:shadow-md, -.group:required .group-required\:shadow-md, -.group:valid .group-valid\:shadow-md, -.group:invalid .group-invalid\:shadow-md, -.group:in-range .group-in-range\:shadow-md, -.group:out-of-range .group-out-of-range\:shadow-md, -.group:read-only .group-read-only\:shadow-md, -.group:empty .group-empty\:shadow-md, -.group:focus-within .group-focus-within\:shadow-md, -.group:hover .group-hover\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open]:hover .group-open\:group-hover\:space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); - margin-inline-end: calc(0.5rem * var(--tw-space-x-reverse)); -} -.group:focus .group-focus\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open]:focus .group-open\:group-focus\:bg-red-200 { - background-color: #fecaca; -} -.group:focus:hover .group-focus\:group-hover\:shadow-md, -.group:focus-visible .group-focus-visible\:shadow-md, -.group:active .group-active\:shadow-md, -.group:enabled .group-enabled\:shadow-md, -.group:disabled .group-disabled\:shadow-md, -.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:shadow-md, -.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:first\:shadow-md:first-child, -.peer:first-child ~ .peer-first\:shadow-md, -.peer:last-child ~ .peer-last\:shadow-md, -.peer:only-child ~ .peer-only\:shadow-md, -.peer:nth-child(2n + 1) ~ .peer-odd\:shadow-md, -.peer:nth-child(2n) ~ .peer-even\:shadow-md, -.peer:first-of-type ~ .peer-first-of-type\:shadow-md, -.peer:last-of-type ~ .peer-last-of-type\:shadow-md, -.peer:only-of-type ~ .peer-only-of-type\:shadow-md, -.peer:visited ~ .peer-visited\:shadow-md, -.peer:target ~ .peer-target\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.peer[open] ~ .peer-open\:bg-red-200 { - background-color: #fecaca; -} -.peer:default ~ .peer-default\:shadow-md, -.peer:checked ~ .peer-checked\:shadow-md, -.peer:indeterminate ~ .peer-indeterminate\:shadow-md, -.peer:placeholder-shown ~ .peer-placeholder-shown\:shadow-md, -.peer:autofill ~ .peer-autofill\:shadow-md, -.peer:optional ~ .peer-optional\:shadow-md, -.peer:required ~ .peer-required\:shadow-md, -.peer:valid ~ .peer-valid\:shadow-md, -.peer:invalid ~ .peer-invalid\:shadow-md, -.peer:in-range ~ .peer-in-range\:shadow-md, -.peer:out-of-range ~ .peer-out-of-range\:shadow-md, -.peer:read-only ~ .peer-read-only\:shadow-md, -.peer:empty ~ .peer-empty\:shadow-md, -.peer:focus-within ~ .peer-focus-within\:shadow-md, -.peer:hover ~ .peer-hover\:shadow-md, -.peer:focus ~ .peer-focus\:shadow-md, -.peer:focus:hover ~ .peer-focus\:peer-hover\:shadow-md, -.peer:focus-visible ~ .peer-focus-visible\:shadow-md, -.peer:active ~ .peer-active\:shadow-md, -.peer:enabled ~ .peer-enabled\:shadow-md, -.peer:disabled ~ .peer-disabled\:shadow-md, -.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:shadow-md, -.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:first\:shadow-md:first-child, -:is([dir='ltr'] .ltr\:shadow-md), -:is([dir='rtl'] .rtl\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -@media (prefers-reduced-motion: no-preference) { - .motion-safe\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (prefers-reduced-motion: reduce) { - .motion-reduce\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (prefers-contrast: more) { - .contrast-more\:bg-yellow-300 { - background-color: #fde047; - } -} -@media (prefers-contrast: less) { - .contrast-less\:bg-yellow-300 { - background-color: #fde047; - } -} -:is(.dark .dark\:shadow-md), -:is(.dark .group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:shadow-md), -:is(.dark .peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -@media print { - .print\:bg-yellow-300 { - background-color: #fde047; - } -} -@media (min-width: 640px) { - .sm\:shadow-md, - .sm\:active\:shadow-md:active { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (min-width: 768px) { - .md\:shadow-md, - .group:focus .md\:group-focus\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (min-width: 1024px) { - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .lg\:animate-spin { - animation: 1s linear infinite spin; - } - .lg\:shadow-md, - :is(.dark .lg\:dark\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (min-width: 1280px) { - .xl\:shadow-md, - :is(.dark .xl\:dark\:disabled\:shadow-md:disabled) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } -} -@media (min-width: 1536px) { - .\32 xl\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - @media (prefers-reduced-motion: no-preference) { - :is(.dark .\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); - } - } -} -@media (orientation: portrait) { - .portrait\:bg-yellow-300 { - background-color: #fde047; - } -} -@media (orientation: landscape) { - .landscape\:bg-yellow-300 { - background-color: #fde047; - } -} diff --git a/tests/variants.test.css b/tests/variants.test.css index 8a9d481ee..2bb486303 100644 --- a/tests/variants.test.css +++ b/tests/variants.test.css @@ -50,384 +50,209 @@ --tw-backdrop-saturate: ; --tw-backdrop-sepia: ; } -.first-letter\:text-2xl:first-letter { - font-size: 1.5rem; - line-height: 2rem; +.first-letter\:flex:first-letter, +.first-line\:flex:first-line, +.marker\:flex ::marker, +.marker\:flex::marker, +.selection\:flex ::selection, +.selection\:flex::selection { + display: flex; } -.first-letter\:text-red-500:first-letter { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); +.file\:flex::file-selector-button { + display: flex; } -.first-line\:bg-yellow-300:first-line { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); -} -.first-line\:underline:first-line { - text-decoration-line: underline; -} -.marker\:text-lg ::marker { - font-size: 1.125rem; - line-height: 1.75rem; -} -.marker\:text-red-500 ::marker { - color: #ef4444; -} -.marker\:text-lg::marker { - font-size: 1.125rem; - line-height: 1.75rem; -} -.marker\:text-red-500::marker { - color: #ef4444; -} -.selection\:bg-blue-500 ::selection { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); -} -.selection\:text-white ::selection { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} -.selection\:bg-blue-500::selection { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); -} -.selection\:text-white::selection { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} -.file\:bg-blue-500::file-selector-button { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity)); -} -.file\:text-white::file-selector-button { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} -.placeholder\:font-bold::placeholder { - font-weight: 700; -} -.placeholder\:text-red-500::placeholder { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); -} -.backdrop\:shadow-md::backdrop { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.before\:block:before { - content: var(--tw-content); - display: block; -} -.before\:bg-red-500:before { - content: var(--tw-content); - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); +.placeholder\:flex::placeholder, +.backdrop\:flex::backdrop { + display: flex; } +.before\:flex:before, .after\:flex:after { content: var(--tw-content); display: flex; } -.after\:uppercase:after { - content: var(--tw-content); - text-transform: uppercase; +.first\:flex:first-child, +.last\:flex:last-child, +.only\:flex:only-child, +.odd\:flex:nth-child(2n + 1), +.even\:flex:nth-child(2n), +.first-of-type\:flex:first-of-type, +.last-of-type\:flex:last-of-type, +.only-of-type\:flex:only-of-type, +.visited\:flex:visited, +.target\:flex:target, +.open\:flex[open], +.default\:flex:default, +.checked\:flex:checked, +.indeterminate\:flex:indeterminate, +.placeholder-shown\:flex:placeholder-shown, +.autofill\:flex:autofill, +.optional\:flex:optional, +.required\:flex:required, +.valid\:flex:valid, +.invalid\:flex:invalid, +.in-range\:flex:in-range, +.out-of-range\:flex:out-of-range, +.read-only\:flex:read-only, +.empty\:flex:empty, +.focus-within\:flex:focus-within, +.hover\:flex:hover { + display: flex; } -.first\:shadow-md:first-child, -.last\:shadow-md:last-child, -.only\:shadow-md:only-child, -.odd\:shadow-md:nth-child(2n + 1), -.even\:shadow-md:nth-child(2n), -.first-of-type\:shadow-md:first-of-type, -.last-of-type\:shadow-md:last-of-type, -.only-of-type\:shadow-md:only-of-type, -.visited\:shadow-md:visited, -.target\:shadow-md:target { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); +.file\:hover\:flex:hover::file-selector-button { + display: flex; } -.open\:bg-red-200[open] { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); -} -.default\:shadow-md:default, -.checked\:shadow-md:checked, -.indeterminate\:shadow-md:indeterminate, -.placeholder-shown\:shadow-md:placeholder-shown, -.autofill\:shadow-md:autofill, -.optional\:shadow-md:optional, -.required\:shadow-md:required, -.valid\:shadow-md:valid, -.invalid\:shadow-md:invalid, -.in-range\:shadow-md:in-range, -.out-of-range\:shadow-md:out-of-range, -.read-only\:shadow-md:read-only, -.empty\:shadow-md:empty, -.focus-within\:shadow-md:focus-within { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -@keyframes spin { - to { - transform: rotate(360deg); - } -} -.hover\:animate-spin:hover { - animation: 1s linear infinite spin; -} -.hover\:shadow-md:hover { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.file\:hover\:bg-blue-600:hover::file-selector-button { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity)); -} -.open\:hover\:bg-red-200:hover[open] { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); -} -.focus\:shadow-md:focus, -.focus\:hover\:shadow-md:hover:focus, -.focus-visible\:shadow-md:focus-visible, -.active\:shadow-md:active, -.enabled\:shadow-md:enabled, -.disabled\:shadow-md:disabled, -.group:first-child .group-first\:shadow-md, -.group:last-child .group-last\:shadow-md, -.group:only-child .group-only\:shadow-md, -.group:nth-child(2n + 1) .group-odd\:shadow-md, -.group:nth-child(2n) .group-even\:shadow-md, -.group:first-of-type .group-first-of-type\:shadow-md, -.group:last-of-type .group-last-of-type\:shadow-md, -.group:only-of-type .group-only-of-type\:shadow-md, -.group:visited .group-visited\:shadow-md, -.group:target .group-target\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open] .group-open\:bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); -} -.group:default .group-default\:shadow-md, -.group:checked .group-checked\:shadow-md, -.group:indeterminate .group-indeterminate\:shadow-md, -.group:placeholder-shown .group-placeholder-shown\:shadow-md, -.group:autofill .group-autofill\:shadow-md, -.group:optional .group-optional\:shadow-md, -.group:required .group-required\:shadow-md, -.group:valid .group-valid\:shadow-md, -.group:invalid .group-invalid\:shadow-md, -.group:in-range .group-in-range\:shadow-md, -.group:out-of-range .group-out-of-range\:shadow-md, -.group:read-only .group-read-only\:shadow-md, -.group:empty .group-empty\:shadow-md, -.group:focus-within .group-focus-within\:shadow-md, -.group:hover .group-hover\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open]:hover .group-open\:group-hover\:space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} -.group:focus .group-focus\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.group[open]:focus .group-open\:group-focus\:bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); -} -.group:focus:hover .group-focus\:group-hover\:shadow-md, -.group:focus-visible .group-focus-visible\:shadow-md, -.group:active .group-active\:shadow-md, -.group:enabled .group-enabled\:shadow-md, -.group:disabled .group-disabled\:shadow-md, -.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:shadow-md, -.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:first\:shadow-md:first-child, -.peer:first-child ~ .peer-first\:shadow-md, -.peer:last-child ~ .peer-last\:shadow-md, -.peer:only-child ~ .peer-only\:shadow-md, -.peer:nth-child(2n + 1) ~ .peer-odd\:shadow-md, -.peer:nth-child(2n) ~ .peer-even\:shadow-md, -.peer:first-of-type ~ .peer-first-of-type\:shadow-md, -.peer:last-of-type ~ .peer-last-of-type\:shadow-md, -.peer:only-of-type ~ .peer-only-of-type\:shadow-md, -.peer:visited ~ .peer-visited\:shadow-md, -.peer:target ~ .peer-target\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); -} -.peer[open] ~ .peer-open\:bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); -} -.peer:default ~ .peer-default\:shadow-md, -.peer:checked ~ .peer-checked\:shadow-md, -.peer:indeterminate ~ .peer-indeterminate\:shadow-md, -.peer:placeholder-shown ~ .peer-placeholder-shown\:shadow-md, -.peer:autofill ~ .peer-autofill\:shadow-md, -.peer:optional ~ .peer-optional\:shadow-md, -.peer:required ~ .peer-required\:shadow-md, -.peer:valid ~ .peer-valid\:shadow-md, -.peer:invalid ~ .peer-invalid\:shadow-md, -.peer:in-range ~ .peer-in-range\:shadow-md, -.peer:out-of-range ~ .peer-out-of-range\:shadow-md, -.peer:read-only ~ .peer-read-only\:shadow-md, -.peer:empty ~ .peer-empty\:shadow-md, -.peer:focus-within ~ .peer-focus-within\:shadow-md, -.peer:hover ~ .peer-hover\:shadow-md, -.peer:focus ~ .peer-focus\:shadow-md, -.peer:focus:hover ~ .peer-focus\:peer-hover\:shadow-md, -.peer:focus-visible ~ .peer-focus-visible\:shadow-md, -.peer:active ~ .peer-active\:shadow-md, -.peer:enabled ~ .peer-enabled\:shadow-md, -.peer:disabled ~ .peer-disabled\:shadow-md, -.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:shadow-md, -.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:first\:shadow-md:first-child, -:is([dir='ltr'] .ltr\:shadow-md), -:is([dir='rtl'] .rtl\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); +.open\:hover\:flex:hover[open], +.focus\:flex:focus, +.focus\:hover\:flex:hover:focus, +.focus-visible\:flex:focus-visible, +.active\:flex:active, +.enabled\:flex:enabled, +.disabled\:flex:disabled, +.group:first-child .group-first\:flex, +.group:last-child .group-last\:flex, +.group:only-child .group-only\:flex, +.group:nth-child(2n + 1) .group-odd\:flex, +.group:nth-child(2n) .group-even\:flex, +.group:first-of-type .group-first-of-type\:flex, +.group:last-of-type .group-last-of-type\:flex, +.group:only-of-type .group-only-of-type\:flex, +.group:visited .group-visited\:flex, +.group:target .group-target\:flex, +.group[open] .group-open\:flex, +.group:default .group-default\:flex, +.group:checked .group-checked\:flex, +.group:indeterminate .group-indeterminate\:flex, +.group:placeholder-shown .group-placeholder-shown\:flex, +.group:autofill .group-autofill\:flex, +.group:optional .group-optional\:flex, +.group:required .group-required\:flex, +.group:valid .group-valid\:flex, +.group:invalid .group-invalid\:flex, +.group:in-range .group-in-range\:flex, +.group:out-of-range .group-out-of-range\:flex, +.group:read-only .group-read-only\:flex, +.group:empty .group-empty\:flex, +.group:focus-within .group-focus-within\:flex, +.group:hover .group-hover\:flex, +.group[open]:hover .group-open\:group-hover\:flex, +.group:focus .group-focus\:flex, +.group[open]:focus .group-open\:group-focus\:flex, +.group:focus:hover .group-focus\:group-hover\:flex, +.group:focus-visible .group-focus-visible\:flex, +.group:active .group-active\:flex, +.group:enabled .group-enabled\:flex, +.group:disabled .group-disabled\:flex, +.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:flex, +.group:disabled:focus:hover .group-disabled\:group-focus\:group-hover\:first\:flex:first-child, +.peer:first-child ~ .peer-first\:flex, +.peer:last-child ~ .peer-last\:flex, +.peer:only-child ~ .peer-only\:flex, +.peer:nth-child(2n + 1) ~ .peer-odd\:flex, +.peer:nth-child(2n) ~ .peer-even\:flex, +.peer:first-of-type ~ .peer-first-of-type\:flex, +.peer:last-of-type ~ .peer-last-of-type\:flex, +.peer:only-of-type ~ .peer-only-of-type\:flex, +.peer:visited ~ .peer-visited\:flex, +.peer:target ~ .peer-target\:flex, +.peer[open] ~ .peer-open\:flex, +.peer:default ~ .peer-default\:flex, +.peer:checked ~ .peer-checked\:flex, +.peer:indeterminate ~ .peer-indeterminate\:flex, +.peer:placeholder-shown ~ .peer-placeholder-shown\:flex, +.peer:autofill ~ .peer-autofill\:flex, +.peer:optional ~ .peer-optional\:flex, +.peer:required ~ .peer-required\:flex, +.peer:valid ~ .peer-valid\:flex, +.peer:invalid ~ .peer-invalid\:flex, +.peer:in-range ~ .peer-in-range\:flex, +.peer:out-of-range ~ .peer-out-of-range\:flex, +.peer:read-only ~ .peer-read-only\:flex, +.peer:empty ~ .peer-empty\:flex, +.peer:focus-within ~ .peer-focus-within\:flex, +.peer:hover ~ .peer-hover\:flex, +.peer:focus ~ .peer-focus\:flex, +.peer:focus:hover ~ .peer-focus\:peer-hover\:flex, +.peer:focus-visible ~ .peer-focus-visible\:flex, +.peer:active ~ .peer-active\:flex, +.peer:enabled ~ .peer-enabled\:flex, +.peer:disabled ~ .peer-disabled\:flex, +.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:flex, +.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:first\:flex:first-child, +:is([dir='ltr'] .ltr\:flex), +:is([dir='rtl'] .rtl\:flex) { + display: flex; } @media (prefers-reduced-motion: no-preference) { - .motion-safe\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .motion-safe\:flex { + display: flex; } } @media (prefers-reduced-motion: reduce) { - .motion-reduce\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .motion-reduce\:flex { + display: flex; } } @media (prefers-contrast: more) { - .contrast-more\:bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); + .contrast-more\:flex { + display: flex; } } @media (prefers-contrast: less) { - .contrast-less\:bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); + .contrast-less\:flex { + display: flex; } } -:is(.dark .dark\:shadow-md), -:is(.dark .group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:shadow-md), -:is(.dark .peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); +:is(.dark .dark\:flex), +:is(.dark .group:disabled:focus:hover .dark\:group-disabled\:group-focus\:group-hover\:flex), +:is(.dark .peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:flex) { + display: flex; } @media print { - .print\:bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); + .print\:flex { + display: flex; } } @media (min-width: 640px) { - .sm\:shadow-md, - .sm\:active\:shadow-md:active { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .sm\:flex, + .sm\:active\:flex:active { + display: flex; } } @media (min-width: 768px) { - .md\:shadow-md, - .group:focus .md\:group-focus\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .md\:flex, + .group:focus .md\:group-focus\:flex { + display: flex; } } @media (min-width: 1024px) { - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .lg\:animate-spin { - animation: 1s linear infinite spin; - } - .lg\:shadow-md, - :is(.dark .lg\:dark\:shadow-md) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .lg\:flex, + :is(.dark .lg\:dark\:flex) { + display: flex; } } @media (min-width: 1280px) { - .xl\:shadow-md, - :is(.dark .xl\:dark\:disabled\:shadow-md:disabled) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .xl\:flex, + :is(.dark .xl\:dark\:disabled\:flex:disabled) { + display: flex; } } @media (min-width: 1536px) { - .\32 xl\:shadow-md { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + .\32 xl\:flex { + display: flex; } @media (prefers-reduced-motion: no-preference) { - :is(.dark .\32 xl\:dark\:motion-safe\:focus-within\:shadow-md:focus-within) { - --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), - var(--tw-shadow); + :is(.dark .\32 xl\:dark\:motion-safe\:focus-within\:flex:focus-within) { + display: flex; } } } @media (orientation: portrait) { - .portrait\:bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); + .portrait\:flex { + display: flex; } } @media (orientation: landscape) { - .landscape\:bg-yellow-300 { - --tw-bg-opacity: 1; - background-color: rgb(253 224 71 / var(--tw-bg-opacity)); + .landscape\:flex { + display: flex; } } diff --git a/tests/variants.test.html b/tests/variants.test.html index bd40b625f..a9b2c2f16 100644 --- a/tests/variants.test.html +++ b/tests/variants.test.html @@ -1,166 +1,166 @@ -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
    -
  • +
-
-
-
-
-
- +
+
+
+
+
+ -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/tests/variants.test.js b/tests/variants.test.js index da62419d4..f948e1b2f 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -1,414 +1,83 @@ import fs from 'fs' import path from 'path' import postcss from 'postcss' -import { crosscheck, run, html, css, defaults } from './util/run' +import { run, html, css, defaults } from './util/run' -crosscheck(({ stable, oxide }) => { - test('variants', () => { - let config = { - darkMode: 'class', - content: [path.resolve(__dirname, './variants.test.html')], - corePlugins: { preflight: false }, - } +test('variants', () => { + let config = { + darkMode: 'class', + content: [path.resolve(__dirname, './variants.test.html')], + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` - return run(input, config).then((result) => { - stable - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './variants.test.css'), 'utf8') - ) - oxide - .expect(result.css) - .toMatchFormattedCss( - fs.readFileSync(path.resolve(__dirname, './variants.oxide.test.css'), 'utf8') - ) - }) + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss( + fs.readFileSync(path.resolve(__dirname, './variants.test.css'), 'utf8') + ) }) +}) - test('order matters and produces different behaviour', () => { - let config = { - content: [ - { - raw: html` -
-
- `, - }, - ], - } +test('order matters and produces different behaviour', () => { + let config = { + content: [ + { + raw: html` +
+
+ `, + }, + ], + } - return run('@tailwind utilities', config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - .file\:hover\:bg-pink-600:hover::file-selector-button { - --tw-bg-opacity: 1; - background-color: rgb(219 39 119 / var(--tw-bg-opacity)); - } - .hover\:file\:bg-pink-600::file-selector-button:hover { - --tw-bg-opacity: 1; - background-color: rgb(219 39 119 / var(--tw-bg-opacity)); - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - .file\:hover\:bg-pink-600:hover::file-selector-button { - background-color: #db2777; - } - .hover\:file\:bg-pink-600::file-selector-button:hover { - background-color: #db2777; - } - `) - }) - }) - - describe('custom advanced variants', () => { - test('prose-headings usage on its own', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') - }, - ], - } - - return run('@tailwind components;@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - :where(.prose-headings\:text-center) :is(h1, h2, h3, h4) { - text-align: center; - } - `) - }) - }) - - test('prose-headings with another "simple" variant', () => { - let config = { - content: [ - { - raw: html` -
-
- `, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') - }, - ], - } - - return run('@tailwind components;@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - :where(.hover\:prose-headings\:text-center) :is(h1, h2, h3, h4):hover, - :where(.prose-headings\:hover\:text-center:hover) :is(h1, h2, h3, h4) { - text-align: center; - } - `) - }) - }) - - test('prose-headings with another "complex" variant', () => { - let config = { - content: [ - { - raw: html` -
-
- `, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .group:hover :where(.group-hover\:prose-headings\:text-center) :is(h1, h2, h3, h4), - :where(.group:hover .prose-headings\:group-hover\:text-center) :is(h1, h2, h3, h4) { - text-align: center; - } - `) - }) - }) - - test('using variants with multi-class selectors', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant, addComponents }) { - addComponents({ - '.parent .child': { - foo: 'bar', - }, - }) - addVariant('screen', '@media screen') - }, - ], - } - - return run('@tailwind components;@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media screen { - .screen\:parent .child, - .parent .screen\:child { - foo: bar; - } - } - `) - }) - }) - - test('using multiple classNames in your custom variant', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('my-variant', '&:where(.one, .two, .three)') - }, - ], - } - - let input = css` - @tailwind components; - @tailwind utilities; - - @layer components { - .test { - @apply my-variant:italic; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .test:where(.one, .two, .three) { - font-style: italic; - } - .my-variant\:underline:where(.one, .two, .three) { - text-decoration-line: underline; - } - `) - }) - }) - - test('variant format string must include at-rule or & (1)', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('wtf-bbq', 'lol') - }, - ], - } - - await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( - "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." - ) - }) - - test('variant format string must include at-rule or & (2)', async () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('wtf-bbq', () => 'lol') - }, - ], - } - - await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( - "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." - ) - }) - }) - - test('stacked peer variants', async () => { - let config = { - content: [{ raw: 'peer-disabled:peer-focus:peer-hover:border-blue-500' }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - let result = await run(input, config) - stable.expect(result.css).toIncludeCss(css` - .peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:border-blue-500 { - --tw-border-opacity: 1; - border-color: rgb(59 130 246 / var(--tw-border-opacity)); - } - `) - oxide.expect(result.css).toIncludeCss(css` - .peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:border-blue-500 { - border-color: #3b82f6; - } - `) - }) - - it('should properly handle keyframes with multiple variants', async () => { - let config = { - content: [ - { - raw: 'animate-spin hover:animate-spin focus:animate-spin hover:animate-bounce focus:animate-bounce', - }, - ], - } - - let input = css` - @tailwind components; - @tailwind utilities; - ` - - let result = await run(input, config) + return run('@tailwind utilities', config).then((result) => { expect(result.css).toMatchFormattedCss(css` - @keyframes spin { - to { - transform: rotate(360deg); - } + .file\:hover\:flex:hover::file-selector-button { + display: flex; } - .animate-spin { - animation: 1s linear infinite spin; - } - @keyframes bounce { - 0%, - 100% { - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - transform: translateY(-25%); - } - 50% { - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - transform: none; - } - } - .hover\:animate-bounce:hover { - animation: 1s infinite bounce; - } - .hover\:animate-spin:hover { - animation: 1s linear infinite spin; - } - .focus\:animate-bounce:focus { - animation: 1s infinite bounce; - } - .focus\:animate-spin:focus { - animation: 1s linear infinite spin; + .hover\:file\:flex::file-selector-button:hover { + display: flex; } `) }) +}) - test('custom addVariant with more complex media query params', () => { +describe('custom advanced variants', () => { + test('prose-headings usage on its own', () => { let config = { content: [ { - raw: html`
`, + raw: html`
`, }, ], plugins: [ function ({ addVariant }) { - addVariant('magic', '@media screen and (max-width: 600px)') + addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') }, ], } return run('@tailwind components;@tailwind utilities', config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - @media screen and (max-width: 600px) { - .magic\:text-center { - text-align: center; - } - } - `) - }) - }) - - test('custom addVariant with nested media & format shorthand', () => { - let config = { - content: [ - { - raw: html`
`, - }, - ], - plugins: [ - function ({ addVariant }) { - addVariant('magic', '@supports (hover: hover) { @media print { &:disabled } }') - }, - ], - } - - return run('@tailwind components;@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @supports (hover: hover) { - @media print { - .magic\:text-center:disabled { - text-align: center; - } - } - } - `) - }) - }) - - test('before and after variants are a bit special, and forced to the end', () => { - let config = { - content: [ - { - raw: html` -
-
- `, - }, - ], - plugins: [], - } - - return run('@tailwind components;@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .before\:hover\:text-center:hover:before, - .hover\:before\:text-center:hover:before { - content: var(--tw-content); + :where(.prose-headings\:text-center) :is(h1, h2, h3, h4) { text-align: center; } `) }) }) - test('before and after variants are a bit special, and forced to the end (2)', () => { + test('prose-headings with another "simple" variant', () => { let config = { content: [ { raw: html` -
-
+
+
`, }, ], @@ -421,759 +90,1049 @@ crosscheck(({ stable, oxide }) => { return run('@tailwind components;@tailwind utilities', config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - :where(.before\:prose-headings\:text-center) :is(h1, h2, h3, h4):before, - :where(.prose-headings\:before\:text-center) :is(h1, h2, h3, h4):before { - content: var(--tw-content); + :where(.hover\:prose-headings\:text-center) :is(h1, h2, h3, h4):hover, + :where(.prose-headings\:hover\:text-center:hover) :is(h1, h2, h3, h4) { text-align: center; } `) }) }) - test('returning non-strings and non-selectors in addVariant', () => { - /** @type {import('../types/config').Config} */ + test('prose-headings with another "complex" variant', () => { let config = { content: [ { raw: html` -
-
+
+
`, }, ], plugins: [ - function ({ addVariant, e }) { - addVariant('peer-aria-expanded', ({ modifySelectors, separator }) => - // Returning anything other string | string[] | undefined here is not supported - // But we're trying to be lenient here and just throw it out - modifySelectors( - ({ className }) => - `.peer[aria-expanded="true"] ~ .${e(`peer-aria-expanded${separator}${className}`)}` - ) - ) + function ({ addVariant }) { + addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') + }, + ], + } - addVariant('peer-aria-expanded-2', ({ modifySelectors, separator }) => { - let nodes = modifySelectors( - ({ className }) => `.${e(`peer-aria-expanded-2${separator}${className}`)}` - ) + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .group:hover :where(.group-hover\:prose-headings\:text-center) :is(h1, h2, h3, h4), + :where(.group:hover .prose-headings\:group-hover\:text-center) :is(h1, h2, h3, h4) { + text-align: center; + } + `) + }) + }) - return [ - // Returning anything other than strings here is not supported - // But we're trying to be lenient here and just throw it out - nodes, - '.peer[aria-expanded="false"] ~ &', - ] + test('using variants with multi-class selectors', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant, addComponents }) { + addComponents({ + '.parent .child': { + foo: 'bar', + }, }) + addVariant('screen', '@media screen') }, ], } return run('@tailwind components;@tailwind utilities', config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - .peer[aria-expanded='true'] ~ .peer-aria-expanded\:text-center, - .peer[aria-expanded='false'] ~ .peer-aria-expanded-2\:text-center { - text-align: center; + @media screen { + .screen\:parent .child, + .parent .screen\:child { + foo: bar; + } } `) }) }) - it('should not generate variants of user css if it is not inside a layer', () => { + test('using multiple classNames in your custom variant', () => { let config = { - content: [{ raw: html`
` }], - plugins: [], + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('my-variant', '&:where(.one, .two, .three)') + }, + ], } let input = css` @tailwind components; @tailwind utilities; - .foo { - color: red; + @layer components { + .test { + @apply my-variant:italic; + } } ` return run(input, config).then((result) => { return expect(result.css).toMatchFormattedCss(css` - .foo { - color: red; + .test:where(.one, .two, .three) { + font-style: italic; } - `) - }) - }) - - it('should be possible to use responsive modifiers that are defined with special characters', () => { - let config = { - content: [{ raw: html`
` }], - theme: { - screens: { - ' { - return expect(result.css).toMatchFormattedCss(css` - @media (max-width: 399px) { - .\ { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - return run('@tailwind base', config).then((result) => { - return expect(result.css).toMatchFormattedCss( - css` - ${defaults} - ` - ) - }) - }) - - it('variants for components should not be produced in a file without a components layer', () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .sm\:underline { - text-decoration-line: underline; - } - } - `) - }) - }) - - it('variants for utilities should not be produced in a file without a utilities layer', () => { - let config = { - content: [{ raw: html`
` }], - } - - return run('@tailwind components', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .sm\:container { - width: 100%; - } - @media (min-width: 640px) { - .sm\:container { - max-width: 640px; - } - } - @media (min-width: 768px) { - .sm\:container { - max-width: 768px; - } - } - @media (min-width: 1024px) { - .sm\:container { - max-width: 1024px; - } - } - @media (min-width: 1280px) { - .sm\:container { - max-width: 1280px; - } - } - @media (min-width: 1536px) { - .sm\:container { - max-width: 1536px; - } - } - } - `) - }) - }) - - test('The visited variant removes opacity support', () => { - let config = { - content: [ - { - raw: html` - Look, it's a link! - `, - }, - ], - plugins: [], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - .visited\:border-red-500:visited { - border-color: #ef4444; - } - .visited\:bg-red-500:visited { - background-color: #ef4444; - } - .visited\:text-red-500:visited { - color: #ef4444; - } - `) - }) - }) - - it('appends variants to the correct place when using postcss documents', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - const doc = postcss.document() - doc.append(postcss.parse(`a {}`)) - doc.append(postcss.parse(`@tailwind base`)) - doc.append(postcss.parse(`@tailwind utilities`)) - doc.append(postcss.parse(`b {}`)) - - const result = doc.toResult() - - return run(result, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - ${defaults} - .underline { + .my-variant\:underline:where(.one, .two, .three) { text-decoration-line: underline; } - @media (min-width: 640px) { - .sm\:underline { - text-decoration-line: underline; - } - } `) }) }) - it('variants support multiple, grouped selectors (html)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .base1 .foo, - .base1 .bar { - color: red; - } - - .base2 .bar .base2-foo { - color: red; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .sm\:base1 .foo, - .sm\:base1 .bar, - .sm\:base2 .bar .base2-foo { - color: red; - } - } - `) - }) - }) - - it('variants support multiple, grouped selectors (apply)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .base .foo, - .base .bar { - color: red; - } - } - .baz { - @apply sm:base; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .baz .foo, - .baz .bar { - color: red; - } - } - `) - }) - }) - - it('variants only picks the used selectors in a group (html)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .a, - .b { - color: red; - } - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .sm\:b { - color: red; - } - } - `) - }) - }) - - it('variants only picks the used selectors in a group (apply)', () => { - let config = { - content: [{ raw: html`
` }], - plugins: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .a, - .b { - color: red; - } - } - .baz { - @apply sm:b; - } - ` - - return run(input, config).then((result) => { - return expect(result.css).toMatchFormattedCss(css` - @media (min-width: 640px) { - .baz { - color: red; - } - } - `) - }) - }) - - test('hoverOnlyWhenSupported adds hover and pointer media features by default', () => { - let config = { - future: { - hoverOnlyWhenSupported: true, - }, - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - @tailwind components; - @tailwind utilities; - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - ${defaults} - @media (hover: hover) and (pointer: fine) { - .hover\:underline:hover, - .group:hover .group-hover\:underline, - .peer:hover ~ .peer-hover\:underline { - text-decoration-line: underline; - } - } - `) - }) - }) - - test('multi-class utilities handle selector-mutating variants correctly', () => { + test('variant format string must include at-rule or & (1)', async () => { let config = { content: [ { - raw: html`
`, - }, - { - raw: html`
`, + raw: html`
`, }, ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - .foo.bar.baz { - color: red; - } - .foo1 .bar1 .baz1 { - color: red; - } - } - ` - - // The second set of ::after cases (w/ descendant selectors) - // are clearly "wrong" BUT you can't have a descendant of a - // pseudo - element so the utilities `after:foo1` and - // `after:bar1` are non-sensical so this is still - // perfectly fine behavior - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .after\:foo.bar.baz:after, - .after\:bar.foo.baz:after, - .after\:baz.foo.bar:after, - .after\:foo1 .bar1 .baz1:after, - .foo1 .after\:bar1 .baz1:after, - .foo1 .bar1 .after\:baz1:after { - content: var(--tw-content); - color: red; - } - .hover\:foo:hover.bar.baz, - .hover\:bar:hover.foo.baz, - .hover\:baz:hover.foo.bar, - .hover\:foo1:hover .bar1 .baz1, - .foo1 .hover\:bar1:hover .baz1, - .foo1 .bar1 .hover\:baz1:hover, - .group:hover .group-hover\:foo.bar.baz, - .group:hover .group-hover\:bar.foo.baz, - .group:hover .group-hover\:baz.foo.bar, - .group:hover .group-hover\:foo1 .bar1 .baz1, - .foo1 .group:hover .group-hover\:bar1 .baz1, - .foo1 .bar1 .group:hover .group-hover\:baz1, - .peer:checked ~ .peer-checked\:foo.bar.baz, - .peer:checked ~ .peer-checked\:bar.foo.baz, - .peer:checked ~ .peer-checked\:baz.foo.bar, - .peer:checked ~ .peer-checked\:foo1 .bar1 .baz1, - .foo1 .peer:checked ~ .peer-checked\:bar1 .baz1, - .foo1 .bar1 .peer:checked ~ .peer-checked\:baz1 { - color: red; - } - `) - }) - }) - - test('class inside pseudo-class function :has', () => { - let config = { - content: [ - { raw: html`
` }, - { raw: html`
` }, - { raw: html`
` }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - @layer utilities { - :where(.foo) { - color: red; - } - :is(.foo, .bar, .baz) { - color: orange; - } - :is(.foo) { - color: yellow; - } - html:has(.foo) { - color: green; - } - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - :where(.foo) { - color: red; - } - :is(.foo, .bar, .baz) { - color: orange; - } - .foo { - color: #ff0; - } - html:has(.foo) { - color: green; - } - :where(.hover\:foo:hover) { - color: red; - } - :is(.hover\:foo:hover, .bar, .baz), - :is(.foo, .hover\:bar:hover, .baz), - :is(.foo, .bar, .hover\:baz:hover) { - color: orange; - } - .hover\:foo:hover { - color: #ff0; - } - html:has(.hover\:foo:hover) { - color: green; - } - @media (min-width: 640px) { - :where(.sm\:foo) { - color: red; - } - :is(.sm\:foo, .bar, .baz), - :is(.foo, .sm\:bar, .baz), - :is(.foo, .bar, .sm\:baz) { - color: orange; - } - .sm\:foo { - color: #ff0; - } - html:has(.sm\:foo) { - color: green; - } - } - `) - }) - }) - - test('variant functions returning arrays should output correct results when nesting', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, plugins: [ - function ({ addUtilities, addVariant }) { - addVariant('test', () => ['@media (test)']) - addUtilities({ - '.foo': { - display: 'grid', - '> *': { - 'grid-column': 'span 2', - }, - }, - }) + function ({ addVariant }) { + addVariant('wtf-bbq', 'lol') }, ], } - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - @media (test) { - .test\:foo { - display: grid; - } - .test\:foo > * { - grid-column: span 2; - } - } - `) + await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( + "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." + ) }) - test('variants with slashes in them work', () => { + test('variant format string must include at-rule or & (2)', async () => { let config = { content: [ { - raw: html`
ar-1/10
`, + raw: html`
`, }, ], - theme: { - extend: { - screens: { - 'ar-1/10': { raw: '(min-aspect-ratio: 1/10)' }, - }, - }, - }, - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - @media (min-aspect-ratio: 1 / 10) { - .ar-1\/10\:text-red-500 { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); - } - } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - @media (min-aspect-ratio: 1 / 10) { - .ar-1\/10\:text-red-500 { - color: #ef4444; - } - } - `) - }) - }) - - test('variants with slashes support modifiers', () => { - let config = { - content: [ - { - raw: html`
ar-1/10
`, - }, - ], - corePlugins: { preflight: false }, plugins: [ - function ({ matchVariant }) { - matchVariant( - 'ar', - (value, { modifier }) => { - return [`@media (min-aspect-ratio: ${value}) and (foo: ${modifier})`] - }, - { values: { '1/10': '1/10' } } - ) + function ({ addVariant }) { + addVariant('wtf-bbq', () => 'lol') }, ], } - let input = css` - @tailwind utilities; - ` + await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( + "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." + ) + }) +}) - return run(input, config).then((result) => { - stable.expect(result.css).toMatchFormattedCss(css` - @media (min-aspect-ratio: 1 / 10) and (foo: 20) { - .ar-1\/10\/20\:text-red-500 { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); - } +test('stacked peer variants', async () => { + let config = { + content: [{ raw: 'peer-disabled:peer-focus:peer-hover:flex' }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + let result = await run(input, config) + expect(result.css).toIncludeCss(css` + .peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:flex { + display: flex; + } + `) +}) + +it('should properly handle keyframes with multiple variants', async () => { + let config = { + content: [ + { + raw: 'animate-spin hover:animate-spin focus:animate-spin hover:animate-bounce focus:animate-bounce', + }, + ], + } + + let input = css` + @tailwind components; + @tailwind utilities; + ` + + let result = await run(input, config) + expect(result.css).toMatchFormattedCss(css` + @keyframes spin { + to { + transform: rotate(360deg); + } + } + .animate-spin { + animation: 1s linear infinite spin; + } + @keyframes bounce { + 0%, + 100% { + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + transform: translateY(-25%); + } + 50% { + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + transform: none; + } + } + .hover\:animate-bounce:hover { + animation: 1s infinite bounce; + } + .hover\:animate-spin:hover { + animation: 1s linear infinite spin; + } + .focus\:animate-bounce:focus { + animation: 1s infinite bounce; + } + .focus\:animate-spin:focus { + animation: 1s linear infinite spin; + } + `) +}) + +test('custom addVariant with more complex media query params', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('magic', '@media screen and (max-width: 600px)') + }, + ], + } + + return run('@tailwind components;@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media screen and (max-width: 600px) { + .magic\:text-center { + text-align: center; } - `) - oxide.expect(result.css).toMatchFormattedCss(css` - @media (min-aspect-ratio: 1 / 10) and (foo: 20) { - .ar-1\/10\/20\:text-red-500 { - color: #ef4444; - } - } - `) - }) - }) - - test('arbitrary variant selectors should not re-order scrollbar pseudo classes', async () => { - let config = { - content: [ - { - raw: html` -
-
-
-
-
-
-
- `, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - .\[\&\:\:-webkit-resizer\:hover\]\:underline::-webkit-resizer:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar-button\:hover\]\:underline::-webkit-scrollbar-button:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar-corner\:hover\]\:underline::-webkit-scrollbar-corner:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar-thumb\:hover\]\:underline::-webkit-scrollbar-thumb:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar-track-piece\:hover\]\:underline::-webkit-scrollbar-track-piece:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar-track\:hover\]\:underline::-webkit-scrollbar-track:hover { - text-decoration-line: underline; - } - .\[\&\:\:-webkit-scrollbar\:hover\]\:underline::-webkit-scrollbar:hover { - text-decoration-line: underline; - } - `) - }) - - test('stacking dark and rtl variants', async () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - :is(.dark :is([dir='rtl'] .dark\:rtl\:italic)) { - font-style: italic; - } - `) - }) - - test('stacking dark and rtl variants with pseudo elements', async () => { - let config = { - darkMode: 'class', - content: [ - { - raw: html`
`, - }, - ], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - let result = await run(input, config) - - expect(result.css).toMatchFormattedCss(css` - :is(.dark :is([dir='rtl'] .dark\:rtl\:placeholder\:italic))::placeholder { - font-style: italic; } `) }) }) + +test('custom addVariant with nested media & format shorthand', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('magic', '@supports (hover: hover) { @media print { &:disabled } }') + }, + ], + } + + return run('@tailwind components;@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @supports (hover: hover) { + @media print { + .magic\:text-center:disabled { + text-align: center; + } + } + } + `) + }) +}) + +test('before and after variants are a bit special, and forced to the end', () => { + let config = { + content: [ + { + raw: html` +
+
+ `, + }, + ], + plugins: [], + } + + return run('@tailwind components;@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .before\:hover\:text-center:hover:before, + .hover\:before\:text-center:hover:before { + content: var(--tw-content); + text-align: center; + } + `) + }) +}) + +test('before and after variants are a bit special, and forced to the end (2)', () => { + let config = { + content: [ + { + raw: html` +
+
+ `, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('prose-headings', ':where(&) :is(h1, h2, h3, h4)') + }, + ], + } + + return run('@tailwind components;@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + :where(.before\:prose-headings\:text-center) :is(h1, h2, h3, h4):before, + :where(.prose-headings\:before\:text-center) :is(h1, h2, h3, h4):before { + content: var(--tw-content); + text-align: center; + } + `) + }) +}) + +test('returning non-strings and non-selectors in addVariant', () => { + /** @type {import('../types/config').Config} */ + let config = { + content: [ + { + raw: html` +
+
+ `, + }, + ], + plugins: [ + function ({ addVariant, e }) { + addVariant('peer-aria-expanded', ({ modifySelectors, separator }) => + // Returning anything other string | string[] | undefined here is not supported + // But we're trying to be lenient here and just throw it out + modifySelectors( + ({ className }) => + `.peer[aria-expanded="true"] ~ .${e(`peer-aria-expanded${separator}${className}`)}` + ) + ) + + addVariant('peer-aria-expanded-2', ({ modifySelectors, separator }) => { + let nodes = modifySelectors( + ({ className }) => `.${e(`peer-aria-expanded-2${separator}${className}`)}` + ) + + return [ + // Returning anything other than strings here is not supported + // But we're trying to be lenient here and just throw it out + nodes, + '.peer[aria-expanded="false"] ~ &', + ] + }) + }, + ], + } + + return run('@tailwind components;@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .peer[aria-expanded='true'] ~ .peer-aria-expanded\:text-center, + .peer[aria-expanded='false'] ~ .peer-aria-expanded-2\:text-center { + text-align: center; + } + `) + }) +}) + +it('should not generate variants of user css if it is not inside a layer', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + } + + let input = css` + @tailwind components; + @tailwind utilities; + + .foo { + color: red; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .foo { + color: red; + } + `) + }) +}) + +it('should be possible to use responsive modifiers that are defined with special characters', () => { + let config = { + content: [{ raw: html`
` }], + theme: { + screens: { + ' { + return expect(result.css).toMatchFormattedCss(css` + @media (max-width: 399px) { + .\ { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base', config).then((result) => { + return expect(result.css).toMatchFormattedCss( + css` + ${defaults} + ` + ) + }) +}) + +it('variants for components should not be produced in a file without a components layer', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:underline { + text-decoration-line: underline; + } + } + `) + }) +}) + +it('variants for utilities should not be produced in a file without a utilities layer', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind components', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:container { + width: 100%; + } + @media (min-width: 640px) { + .sm\:container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .sm\:container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .sm\:container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .sm\:container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .sm\:container { + max-width: 1536px; + } + } + } + `) + }) +}) + +test('The visited variant removes opacity support', () => { + let config = { + content: [ + { + raw: html` + Look, it's a link! + `, + }, + ], + plugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .visited\:border-red-500:visited { + border-color: #ef4444; + } + .visited\:bg-red-500:visited { + background-color: #ef4444; + } + .visited\:text-red-500:visited { + color: #ef4444; + } + `) + }) +}) + +it('appends variants to the correct place when using postcss documents', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + corePlugins: { preflight: false }, + } + + const doc = postcss.document() + doc.append(postcss.parse(`a {}`)) + doc.append(postcss.parse(`@tailwind base`)) + doc.append(postcss.parse(`@tailwind utilities`)) + doc.append(postcss.parse(`b {}`)) + + const result = doc.toResult() + + return run(result, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + ${defaults} + .underline { + text-decoration-line: underline; + } + @media (min-width: 640px) { + .sm\:underline { + text-decoration-line: underline; + } + } + `) + }) +}) + +it('variants support multiple, grouped selectors (html)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .base1 .foo, + .base1 .bar { + color: red; + } + + .base2 .bar .base2-foo { + color: red; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:base1 .foo, + .sm\:base1 .bar, + .sm\:base2 .bar .base2-foo { + color: red; + } + } + `) + }) +}) + +it('variants support multiple, grouped selectors (apply)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .base .foo, + .base .bar { + color: red; + } + } + .baz { + @apply sm:base; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .baz .foo, + .baz .bar { + color: red; + } + } + `) + }) +}) + +it('variants only picks the used selectors in a group (html)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .a, + .b { + color: red; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:b { + color: red; + } + } + `) + }) +}) + +it('variants only picks the used selectors in a group (apply)', () => { + let config = { + content: [{ raw: html`
` }], + plugins: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .a, + .b { + color: red; + } + } + .baz { + @apply sm:b; + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .baz { + color: red; + } + } + `) + }) +}) + +test('hoverOnlyWhenSupported adds hover and pointer media features by default', () => { + let config = { + future: { + hoverOnlyWhenSupported: true, + }, + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + @media (hover: hover) and (pointer: fine) { + .hover\:underline:hover, + .group:hover .group-hover\:underline, + .peer:hover ~ .peer-hover\:underline { + text-decoration-line: underline; + } + } + `) + }) +}) + +test('multi-class utilities handle selector-mutating variants correctly', () => { + let config = { + content: [ + { + raw: html`
`, + }, + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .foo.bar.baz { + color: red; + } + .foo1 .bar1 .baz1 { + color: red; + } + } + ` + + // The second set of ::after cases (w/ descendant selectors) + // are clearly "wrong" BUT you can't have a descendant of a + // pseudo - element so the utilities `after:foo1` and + // `after:bar1` are non-sensical so this is still + // perfectly fine behavior + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .after\:foo.bar.baz:after, + .after\:bar.foo.baz:after, + .after\:baz.foo.bar:after, + .after\:foo1 .bar1 .baz1:after, + .foo1 .after\:bar1 .baz1:after, + .foo1 .bar1 .after\:baz1:after { + content: var(--tw-content); + color: red; + } + .hover\:foo:hover.bar.baz, + .hover\:bar:hover.foo.baz, + .hover\:baz:hover.foo.bar, + .hover\:foo1:hover .bar1 .baz1, + .foo1 .hover\:bar1:hover .baz1, + .foo1 .bar1 .hover\:baz1:hover, + .group:hover .group-hover\:foo.bar.baz, + .group:hover .group-hover\:bar.foo.baz, + .group:hover .group-hover\:baz.foo.bar, + .group:hover .group-hover\:foo1 .bar1 .baz1, + .foo1 .group:hover .group-hover\:bar1 .baz1, + .foo1 .bar1 .group:hover .group-hover\:baz1, + .peer:checked ~ .peer-checked\:foo.bar.baz, + .peer:checked ~ .peer-checked\:bar.foo.baz, + .peer:checked ~ .peer-checked\:baz.foo.bar, + .peer:checked ~ .peer-checked\:foo1 .bar1 .baz1, + .foo1 .peer:checked ~ .peer-checked\:bar1 .baz1, + .foo1 .bar1 .peer:checked ~ .peer-checked\:baz1 { + color: red; + } + `) + }) +}) + +test('class inside pseudo-class function :has', () => { + let config = { + content: [ + { raw: html`
` }, + { raw: html`
` }, + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + :where(.foo) { + color: red; + } + :is(.foo, .bar, .baz) { + color: orange; + } + :is(.foo) { + color: yellow; + } + html:has(.foo) { + color: green; + } + } + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + :where(.foo) { + color: red; + } + :is(.foo, .bar, .baz) { + color: orange; + } + .foo { + color: #ff0; + } + html:has(.foo) { + color: green; + } + :where(.hover\:foo:hover) { + color: red; + } + :is(.hover\:foo:hover, .bar, .baz), + :is(.foo, .hover\:bar:hover, .baz), + :is(.foo, .bar, .hover\:baz:hover) { + color: orange; + } + .hover\:foo:hover { + color: #ff0; + } + html:has(.hover\:foo:hover) { + color: green; + } + @media (min-width: 640px) { + :where(.sm\:foo) { + color: red; + } + :is(.sm\:foo, .bar, .baz), + :is(.foo, .sm\:bar, .baz), + :is(.foo, .bar, .sm\:baz) { + color: orange; + } + .sm\:foo { + color: #ff0; + } + html:has(.sm\:foo) { + color: green; + } + } + `) + }) +}) + +test('variant functions returning arrays should output correct results when nesting', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + plugins: [ + function ({ addUtilities, addVariant }) { + addVariant('test', () => ['@media (test)']) + addUtilities({ + '.foo': { + display: 'grid', + '> *': { + 'grid-column': 'span 2', + }, + }, + }) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + @media (test) { + .test\:foo { + display: grid; + } + .test\:foo > * { + grid-column: span 2; + } + } + `) +}) + +test('variants with slashes in them work', () => { + let config = { + content: [ + { + raw: html`
ar-1/10
`, + }, + ], + theme: { + extend: { + screens: { + 'ar-1/10': { raw: '(min-aspect-ratio: 1/10)' }, + }, + }, + }, + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-aspect-ratio: 1 / 10) { + .ar-1\/10\:flex { + display: flex; + } + } + `) + }) +}) + +test('variants with slashes support modifiers', () => { + let config = { + content: [ + { + raw: html`
ar-1/10
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ matchVariant }) { + matchVariant( + 'ar', + (value, { modifier }) => { + return [`@media (min-aspect-ratio: ${value}) and (foo: ${modifier})`] + }, + { values: { '1/10': '1/10' } } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-aspect-ratio: 1 / 10) and (foo: 20) { + .ar-1\/10\/20\:flex { + display: flex; + } + } + `) + }) +}) + +test('arbitrary variant selectors should not re-order scrollbar pseudo classes', async () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .\[\&\:\:-webkit-resizer\:hover\]\:underline::-webkit-resizer:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar-button\:hover\]\:underline::-webkit-scrollbar-button:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar-corner\:hover\]\:underline::-webkit-scrollbar-corner:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar-thumb\:hover\]\:underline::-webkit-scrollbar-thumb:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar-track-piece\:hover\]\:underline::-webkit-scrollbar-track-piece:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar-track\:hover\]\:underline::-webkit-scrollbar-track:hover { + text-decoration-line: underline; + } + .\[\&\:\:-webkit-scrollbar\:hover\]\:underline::-webkit-scrollbar:hover { + text-decoration-line: underline; + } + `) +}) + +test('stacking dark and rtl variants', async () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + :is(.dark :is([dir='rtl'] .dark\:rtl\:italic)) { + font-style: italic; + } + `) +}) + +test('stacking dark and rtl variants with pseudo elements', async () => { + let config = { + darkMode: 'class', + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + :is(.dark :is([dir='rtl'] .dark\:rtl\:placeholder\:italic))::placeholder { + font-style: italic; + } + `) +}) diff --git a/tests/warnings.test.js b/tests/warnings.test.js index d2be7189f..d74f1dd52 100644 --- a/tests/warnings.test.js +++ b/tests/warnings.test.js @@ -1,64 +1,61 @@ -import { crosscheck, run, html, css } from './util/run' +import { run, html, css } from './util/run' -crosscheck(({ oxide, stable }) => { - // The oxide engine will use auto content if no `content` is given - oxide.test.todo('it warns when there is no content key') - stable.test('it warns when there is no content key', async () => { - let config = { - corePlugins: { preflight: false }, - } +test('it warns when there is no content key', async () => { + let config = { + presets: [], // Prevents the default config from being merged in and therefore `content: ['auto']` is not used. + corePlugins: { preflight: false }, + } - let input = css` - @tailwind base; - ` + let input = css` + @tailwind base; + ` - await run(input, config) + await run(input, config) - expect().toHaveBeenWarnedWith(['content-problems']) - }) - - test('it warns when there is an empty content key', async () => { - let config = { - content: [], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind base; - ` - - await run(input, config) - - expect().toHaveBeenWarnedWith(['content-problems']) - }) - - test('it warns when there are no utilities generated', async () => { - let config = { - content: [{ raw: html`nothing here matching a utility` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - expect().toHaveBeenWarnedWith(['content-problems']) - }) - - it('warnings are not thrown when only variant utilities are generated', async () => { - let config = { - content: [{ raw: html`
` }], - corePlugins: { preflight: false }, - } - - let input = css` - @tailwind utilities; - ` - - await run(input, config) - - expect().not.toHaveBeenWarned() - }) + expect().toHaveBeenWarnedWith(['content-problems']) +}) + +test('it warns when there is an empty content key', async () => { + let config = { + content: [], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + ` + + await run(input, config) + + expect().toHaveBeenWarnedWith(['content-problems']) +}) + +test('it warns when there are no utilities generated', async () => { + let config = { + content: [{ raw: html`nothing here matching a utility` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + expect().toHaveBeenWarnedWith(['content-problems']) +}) + +it('warnings are not thrown when only variant utilities are generated', async () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + await run(input, config) + + expect().not.toHaveBeenWarned() }) diff --git a/tests/withAlphaVariable.test.js b/tests/withAlphaVariable.test.js index 29d39c7aa..d543dfc6e 100644 --- a/tests/withAlphaVariable.test.js +++ b/tests/withAlphaVariable.test.js @@ -1,243 +1,240 @@ import withAlphaVariable from '../src/util/withAlphaVariable' -import { crosscheck } from './util/run' -crosscheck(() => { - test('it adds the right custom property', () => { - expect( - withAlphaVariable({ color: '#ff0000', property: 'color', variable: '--tw-text-opacity' }) - ).toEqual({ - '--tw-text-opacity': '1', - color: 'rgb(255 0 0 / var(--tw-text-opacity))', - }) - expect( - withAlphaVariable({ - color: 'hsl(240 100% 50%)', - property: 'color', - variable: '--tw-text-opacity', - }) - ).toEqual({ - '--tw-text-opacity': '1', - color: 'hsl(240 100% 50% / var(--tw-text-opacity))', - }) +test('it adds the right custom property', () => { + expect( + withAlphaVariable({ color: '#ff0000', property: 'color', variable: '--tw-text-opacity' }) + ).toEqual({ + '--tw-text-opacity': '1', + color: 'rgb(255 0 0 / var(--tw-text-opacity))', }) - - test('it ignores colors that cannot be parsed', () => { - expect( - withAlphaVariable({ - color: 'currentColor', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'currentColor', - }) - expect( - withAlphaVariable({ - color: 'rgb(255, 0)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgb(255, 0)', - }) - expect( - withAlphaVariable({ - color: 'rgb(255)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgb(255)', - }) - expect( - withAlphaVariable({ - color: 'rgb(255, 0, 0, 255)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgb(255, 0, 0, 255)', - }) - expect( - withAlphaVariable({ - color: 'rgb(var(--color))', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgb(var(--color))', - }) - }) - - test('it ignores colors that already have an alpha channel', () => { - expect( - withAlphaVariable({ - color: '#ff0000ff', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': '#ff0000ff', - }) - expect( - withAlphaVariable({ - color: '#ff000080', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': '#ff000080', - }) - expect( - withAlphaVariable({ - color: '#f00a', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': '#f00a', - }) - expect( - withAlphaVariable({ - color: '#f00f', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': '#f00f', - }) - expect( - withAlphaVariable({ - color: 'rgba(255, 255, 255, 1)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgba(255, 255, 255, 1)', - }) - expect( - withAlphaVariable({ - color: 'rgba(255, 255, 255, 0.5)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgba(255, 255, 255, 0.5)', - }) - expect( - withAlphaVariable({ - color: 'rgba(255 255 255 / 0.5)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'rgba(255 255 255 / 0.5)', - }) - expect( - withAlphaVariable({ - color: 'hsla(240, 100%, 50%, 1)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'hsla(240, 100%, 50%, 1)', - }) - expect( - withAlphaVariable({ - color: 'hsla(240, 100%, 50%, 0.5)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'hsla(240, 100%, 50%, 0.5)', - }) - expect( - withAlphaVariable({ - color: 'hsl(240 100% 50% / 0.5)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - 'background-color': 'hsl(240 100% 50% / 0.5)', - }) - }) - - test('it allows a closure to be passed', () => { - expect( - withAlphaVariable({ - color: ({ opacityVariable }) => `rgba(0, 0, 0, var(${opacityVariable}))`, - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'rgba(0, 0, 0, var(--tw-bg-opacity))', - }) - expect( - withAlphaVariable({ - color: ({ opacityValue }) => `rgba(0, 0, 0, ${opacityValue})`, - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'rgba(0, 0, 0, var(--tw-bg-opacity))', - }) - }) - - test('it transforms rgb and hsl to space-separated rgb and hsl', () => { - expect( - withAlphaVariable({ - color: 'rgb(50, 50, 50)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'rgb(50 50 50 / var(--tw-bg-opacity))', - }) - expect( - withAlphaVariable({ - color: 'rgb(50 50 50)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'rgb(50 50 50 / var(--tw-bg-opacity))', - }) - expect( - withAlphaVariable({ - color: 'hsl(50, 50%, 50%)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'hsl(50 50% 50% / var(--tw-bg-opacity))', - }) - expect( - withAlphaVariable({ - color: 'hsl(50 50% 50%)', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'hsl(50 50% 50% / var(--tw-bg-opacity))', - }) - }) - - test('it transforms named colors to rgb', () => { - expect( - withAlphaVariable({ - color: 'red', - property: 'background-color', - variable: '--tw-bg-opacity', - }) - ).toEqual({ - '--tw-bg-opacity': '1', - 'background-color': 'rgb(255 0 0 / var(--tw-bg-opacity))', + expect( + withAlphaVariable({ + color: 'hsl(240 100% 50%)', + property: 'color', + variable: '--tw-text-opacity', }) + ).toEqual({ + '--tw-text-opacity': '1', + color: 'hsl(240 100% 50% / var(--tw-text-opacity))', + }) +}) + +test('it ignores colors that cannot be parsed', () => { + expect( + withAlphaVariable({ + color: 'currentColor', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'currentColor', + }) + expect( + withAlphaVariable({ + color: 'rgb(255, 0)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgb(255, 0)', + }) + expect( + withAlphaVariable({ + color: 'rgb(255)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgb(255)', + }) + expect( + withAlphaVariable({ + color: 'rgb(255, 0, 0, 255)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgb(255, 0, 0, 255)', + }) + expect( + withAlphaVariable({ + color: 'rgb(var(--color))', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgb(var(--color))', + }) +}) + +test('it ignores colors that already have an alpha channel', () => { + expect( + withAlphaVariable({ + color: '#ff0000ff', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': '#ff0000ff', + }) + expect( + withAlphaVariable({ + color: '#ff000080', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': '#ff000080', + }) + expect( + withAlphaVariable({ + color: '#f00a', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': '#f00a', + }) + expect( + withAlphaVariable({ + color: '#f00f', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': '#f00f', + }) + expect( + withAlphaVariable({ + color: 'rgba(255, 255, 255, 1)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgba(255, 255, 255, 1)', + }) + expect( + withAlphaVariable({ + color: 'rgba(255, 255, 255, 0.5)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgba(255, 255, 255, 0.5)', + }) + expect( + withAlphaVariable({ + color: 'rgba(255 255 255 / 0.5)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'rgba(255 255 255 / 0.5)', + }) + expect( + withAlphaVariable({ + color: 'hsla(240, 100%, 50%, 1)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'hsla(240, 100%, 50%, 1)', + }) + expect( + withAlphaVariable({ + color: 'hsla(240, 100%, 50%, 0.5)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'hsla(240, 100%, 50%, 0.5)', + }) + expect( + withAlphaVariable({ + color: 'hsl(240 100% 50% / 0.5)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + 'background-color': 'hsl(240 100% 50% / 0.5)', + }) +}) + +test('it allows a closure to be passed', () => { + expect( + withAlphaVariable({ + color: ({ opacityVariable }) => `rgba(0, 0, 0, var(${opacityVariable}))`, + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'rgba(0, 0, 0, var(--tw-bg-opacity))', + }) + expect( + withAlphaVariable({ + color: ({ opacityValue }) => `rgba(0, 0, 0, ${opacityValue})`, + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'rgba(0, 0, 0, var(--tw-bg-opacity))', + }) +}) + +test('it transforms rgb and hsl to space-separated rgb and hsl', () => { + expect( + withAlphaVariable({ + color: 'rgb(50, 50, 50)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'rgb(50 50 50 / var(--tw-bg-opacity))', + }) + expect( + withAlphaVariable({ + color: 'rgb(50 50 50)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'rgb(50 50 50 / var(--tw-bg-opacity))', + }) + expect( + withAlphaVariable({ + color: 'hsl(50, 50%, 50%)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'hsl(50 50% 50% / var(--tw-bg-opacity))', + }) + expect( + withAlphaVariable({ + color: 'hsl(50 50% 50%)', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'hsl(50 50% 50% / var(--tw-bg-opacity))', + }) +}) + +test('it transforms named colors to rgb', () => { + expect( + withAlphaVariable({ + color: 'red', + property: 'background-color', + variable: '--tw-bg-opacity', + }) + ).toEqual({ + '--tw-bg-opacity': '1', + 'background-color': 'rgb(255 0 0 / var(--tw-bg-opacity))', }) }) diff --git a/turbo.json b/turbo.json index ef1c9043d..8ddd398f5 100644 --- a/turbo.json +++ b/turbo.json @@ -2,7 +2,7 @@ "$schema": "https://turbo.build/schema.json", "pipeline": { "//#build": { - "outputs": ["lib/**", "peers/**", "types/generated/**"], + "outputs": ["lib/**", "types/generated/**"], "inputs": ["src/**", "oxide/**", "nesting/**", "oxide-node-api-shim/**", "types/**"] }, "test": {